diff --git a/.github/workflows/cpp-ci-serial-programs-base.yml b/.github/workflows/cpp-ci-serial-programs-base.yml index 09d52a5ba1..d467c9db0b 100644 --- a/.github/workflows/cpp-ci-serial-programs-base.yml +++ b/.github/workflows/cpp-ci-serial-programs-base.yml @@ -64,7 +64,7 @@ jobs: cd Arduino-Source sudo apt update sudo apt upgrade - sudo apt install clang-tools libopencv-dev + sudo apt install clang-tools libopencv-dev libsystemd-dev sudo apt install ./3rdPartyBinaries/libdpp-10.0.28-linux-x64.deb diff --git a/3rdParty/sdbus-cpp/include/sdbus-c++/AdaptorInterfaces.h b/3rdParty/sdbus-cpp/include/sdbus-c++/AdaptorInterfaces.h new file mode 100644 index 0000000000..550a0959f7 --- /dev/null +++ b/3rdParty/sdbus-cpp/include/sdbus-c++/AdaptorInterfaces.h @@ -0,0 +1,152 @@ +/** + * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland + * (C) 2016 - 2024 Stanislav Angelovic + * + * @file AdaptorInterfaces.h + * + * Created on: Nov 8, 2016 + * Project: sdbus-c++ + * Description: High-level D-Bus IPC C++ library based on sd-bus + * + * This file is part of sdbus-c++. + * + * sdbus-c++ is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * sdbus-c++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with sdbus-c++. If not, see . + */ + +#ifndef SDBUS_CXX_ADAPTORINTERFACES_H_ +#define SDBUS_CXX_ADAPTORINTERFACES_H_ + +#include +#include +#include +#include + +// Forward declarations +namespace sdbus { + class IConnection; +} + +namespace sdbus { + + /********************************************//** + * @class ObjectHolder + * + * ObjectHolder is a helper that simply owns and provides + * access to an object to other classes in the inheritance + * hierarchy of an object based on generated interface classes. + * + ***********************************************/ + class ObjectHolder + { + protected: + ObjectHolder(std::unique_ptr&& object) + : object_(std::move(object)) + { + } + + const IObject& getObject() const + { + assert(object_ != nullptr); + return *object_; + } + + IObject& getObject() + { + assert(object_ != nullptr); + return *object_; + } + + private: + std::unique_ptr object_; + }; + + /********************************************//** + * @class AdaptorInterfaces + * + * AdaptorInterfaces is a helper template class that joins all interface classes of a remote + * D-Bus object generated by sdbus-c++-xml2cpp to be used on the server (the adaptor) side, + * including some auxiliary classes. AdaptorInterfaces is the class that native-like object + * implementation classes written by users should inherit from and implement all pure virtual + * methods. So the _Interfaces template parameter is a list of sdbus-c++-xml2cpp-generated + * adaptor-side interface classes representing interfaces (with methods, signals and properties) + * of the D-Bus object. + * + * In the final adaptor class inherited from AdaptorInterfaces, one needs to make sure: + * 1. to call `registerAdaptor();` in the class constructor, and, conversely, + * 2. to call `unregisterAdaptor();` in the class destructor, + * so that the object API vtable is registered and unregistered at the proper time. + * + ***********************************************/ + template + class AdaptorInterfaces + : protected ObjectHolder + , public _Interfaces... + { + public: + /*! + * @brief Creates object instance + * + * @param[in] connection D-Bus connection where the object will publish itself + * @param[in] objectPath Path of the D-Bus object + * + * For more information, consult @ref createObject(sdbus::IConnection&,std::string) + */ + AdaptorInterfaces(IConnection& connection, ObjectPath objectPath) + : ObjectHolder(createObject(connection, std::move(objectPath))) + , _Interfaces(getObject())... + { + } + + /*! + * @brief Adds object vtable (i.e. D-Bus API) definitions for all interfaces it implements + * + * This function must be called in the constructor of the final adaptor class that implements AdaptorInterfaces. + * + * See also @ref IObject::addVTable() + */ + void registerAdaptor() + { + (_Interfaces::registerAdaptor(), ...); + } + + /*! + * @brief Unregisters adaptors's API and removes it from the bus + * + * This function must be called in the destructor of the final adaptor class that implements AdaptorInterfaces. + * + * For more information, see underlying @ref IObject::unregister() + */ + void unregisterAdaptor() + { + getObject().unregister(); + } + + /*! + * @brief Returns reference to the underlying IObject instance + */ + using ObjectHolder::getObject; + + protected: + using base_type = AdaptorInterfaces; + + AdaptorInterfaces(const AdaptorInterfaces&) = delete; + AdaptorInterfaces& operator=(const AdaptorInterfaces&) = delete; + AdaptorInterfaces(AdaptorInterfaces&&) = delete; + AdaptorInterfaces& operator=(AdaptorInterfaces&&) = delete; + ~AdaptorInterfaces() = default; + }; + +} + +#endif /* SDBUS_CXX_ADAPTORINTERFACES_H_ */ diff --git a/3rdParty/sdbus-cpp/include/sdbus-c++/ConvenienceApiClasses.h b/3rdParty/sdbus-cpp/include/sdbus-c++/ConvenienceApiClasses.h new file mode 100644 index 0000000000..d644ab8ebe --- /dev/null +++ b/3rdParty/sdbus-cpp/include/sdbus-c++/ConvenienceApiClasses.h @@ -0,0 +1,290 @@ +/** + * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland + * (C) 2016 - 2024 Stanislav Angelovic + * + * @file ConvenienceApiClasses.h + * + * Created on: Jan 19, 2017 + * Project: sdbus-c++ + * Description: High-level D-Bus IPC C++ library based on sd-bus + * + * This file is part of sdbus-c++. + * + * sdbus-c++ is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * sdbus-c++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with sdbus-c++. If not, see . + */ + +#ifndef SDBUS_CXX_CONVENIENCEAPICLASSES_H_ +#define SDBUS_CXX_CONVENIENCEAPICLASSES_H_ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +// Forward declarations +namespace sdbus { + class IObject; + class IProxy; + class Error; + class PendingAsyncCall; +} + +namespace sdbus { + + class VTableAdder + { + public: + void forInterface(InterfaceName interfaceName); + void forInterface(std::string interfaceName); + [[nodiscard]] Slot forInterface(InterfaceName interfaceName, return_slot_t); + [[nodiscard]] Slot forInterface(std::string interfaceName, return_slot_t); + + private: + friend IObject; + VTableAdder(IObject& object, std::vector vtable); + + private: + IObject& object_; + std::vector vtable_; + }; + + class SignalEmitter + { + public: + SignalEmitter& onInterface(const InterfaceName& interfaceName); + SignalEmitter& onInterface(const std::string& interfaceName); + SignalEmitter& onInterface(const char* interfaceName); + template void withArguments(_Args&&... args); + + SignalEmitter(SignalEmitter&& other) = default; + ~SignalEmitter() noexcept(false); + + private: + friend IObject; + SignalEmitter(IObject& object, const SignalName& signalName); + SignalEmitter(IObject& object, const char* signalName); + + private: + IObject& object_; + const char* signalName_; + Signal signal_; + int exceptions_{}; // Number of active exceptions when SignalEmitter is constructed + }; + + class MethodInvoker + { + public: + MethodInvoker& onInterface(const InterfaceName& interfaceName); + MethodInvoker& onInterface(const std::string& interfaceName); + MethodInvoker& onInterface(const char* interfaceName); + MethodInvoker& withTimeout(uint64_t usec); + template + MethodInvoker& withTimeout(const std::chrono::duration<_Rep, _Period>& timeout); + template MethodInvoker& withArguments(_Args&&... args); + template void storeResultsTo(_Args&... args); + void dontExpectReply(); + + MethodInvoker(MethodInvoker&& other) = default; + ~MethodInvoker() noexcept(false); + + private: + friend IProxy; + MethodInvoker(IProxy& proxy, const MethodName& methodName); + MethodInvoker(IProxy& proxy, const char* methodName); + + private: + IProxy& proxy_; + const char* methodName_; + uint64_t timeout_{}; + MethodCall method_; + int exceptions_{}; // Number of active exceptions when MethodInvoker is constructed + bool methodCalled_{}; + }; + + class AsyncMethodInvoker + { + public: + AsyncMethodInvoker& onInterface(const InterfaceName& interfaceName); + AsyncMethodInvoker& onInterface(const std::string& interfaceName); + AsyncMethodInvoker& onInterface(const char* interfaceName); + AsyncMethodInvoker& withTimeout(uint64_t usec); + template + AsyncMethodInvoker& withTimeout(const std::chrono::duration<_Rep, _Period>& timeout); + template AsyncMethodInvoker& withArguments(_Args&&... args); + template PendingAsyncCall uponReplyInvoke(_Function&& callback); + template [[nodiscard]] Slot uponReplyInvoke(_Function&& callback, return_slot_t); + // Returned future will be std::future for no (void) D-Bus method return value + // or std::future for single D-Bus method return value + // or std::future> for multiple method return values + template std::future> getResultAsFuture(); + + private: + friend IProxy; + AsyncMethodInvoker(IProxy& proxy, const MethodName& methodName); + AsyncMethodInvoker(IProxy& proxy, const char* methodName); + template async_reply_handler makeAsyncReplyHandler(_Function&& callback); + + private: + IProxy& proxy_; + const char* methodName_; + uint64_t timeout_{}; + MethodCall method_; + }; + + class SignalSubscriber + { + public: + SignalSubscriber& onInterface(const InterfaceName& interfaceName); + SignalSubscriber& onInterface(const std::string& interfaceName); + SignalSubscriber& onInterface(const char* interfaceName); + template void call(_Function&& callback); + template [[nodiscard]] Slot call(_Function&& callback, return_slot_t); + + private: + friend IProxy; + SignalSubscriber(IProxy& proxy, const SignalName& signalName); + SignalSubscriber(IProxy& proxy, const char* signalName); + template signal_handler makeSignalHandler(_Function&& callback); + + private: + IProxy& proxy_; + const char* signalName_; + const char* interfaceName_{}; + }; + + class PropertyGetter + { + public: + Variant onInterface(std::string_view interfaceName); + + private: + friend IProxy; + PropertyGetter(IProxy& proxy, std::string_view propertyName); + + static constexpr const char* DBUS_PROPERTIES_INTERFACE_NAME = "org.freedesktop.DBus.Properties"; + + private: + IProxy& proxy_; + std::string_view propertyName_; + }; + + class AsyncPropertyGetter + { + public: + AsyncPropertyGetter& onInterface(std::string_view interfaceName); + template PendingAsyncCall uponReplyInvoke(_Function&& callback); + template [[nodiscard]] Slot uponReplyInvoke(_Function&& callback, return_slot_t); + std::future getResultAsFuture(); + + private: + friend IProxy; + AsyncPropertyGetter(IProxy& proxy, std::string_view propertyName); + + static constexpr const char* DBUS_PROPERTIES_INTERFACE_NAME = "org.freedesktop.DBus.Properties"; + + private: + IProxy& proxy_; + std::string_view propertyName_; + std::string_view interfaceName_; + }; + + class PropertySetter + { + public: + PropertySetter& onInterface(std::string_view interfaceName); + template void toValue(const _Value& value); + template void toValue(const _Value& value, dont_expect_reply_t); + void toValue(const Variant& value); + void toValue(const Variant& value, dont_expect_reply_t); + + private: + friend IProxy; + PropertySetter(IProxy& proxy, std::string_view propertyName); + + static constexpr const char* DBUS_PROPERTIES_INTERFACE_NAME = "org.freedesktop.DBus.Properties"; + + private: + IProxy& proxy_; + std::string_view propertyName_; + std::string_view interfaceName_; + }; + + class AsyncPropertySetter + { + public: + AsyncPropertySetter& onInterface(std::string_view interfaceName); + template AsyncPropertySetter& toValue(_Value&& value); + AsyncPropertySetter& toValue(Variant value); + template PendingAsyncCall uponReplyInvoke(_Function&& callback); + template [[nodiscard]] Slot uponReplyInvoke(_Function&& callback, return_slot_t); + std::future getResultAsFuture(); + + private: + friend IProxy; + AsyncPropertySetter(IProxy& proxy, std::string_view propertyName); + + static constexpr const char* DBUS_PROPERTIES_INTERFACE_NAME = "org.freedesktop.DBus.Properties"; + + private: + IProxy& proxy_; + std::string_view propertyName_; + std::string_view interfaceName_; + Variant value_; + }; + + class AllPropertiesGetter + { + public: + std::map onInterface(std::string_view interfaceName); + + private: + friend IProxy; + AllPropertiesGetter(IProxy& proxy); + + static constexpr const char* DBUS_PROPERTIES_INTERFACE_NAME = "org.freedesktop.DBus.Properties"; + + private: + IProxy& proxy_; + }; + + class AsyncAllPropertiesGetter + { + public: + AsyncAllPropertiesGetter& onInterface(std::string_view interfaceName); + template PendingAsyncCall uponReplyInvoke(_Function&& callback); + template [[nodiscard]] Slot uponReplyInvoke(_Function&& callback, return_slot_t); + std::future> getResultAsFuture(); + + private: + friend IProxy; + AsyncAllPropertiesGetter(IProxy& proxy); + + static constexpr const char* DBUS_PROPERTIES_INTERFACE_NAME = "org.freedesktop.DBus.Properties"; + + private: + IProxy& proxy_; + std::string_view interfaceName_; + }; + +} // namespace sdbus + +#endif /* SDBUS_CXX_CONVENIENCEAPICLASSES_H_ */ diff --git a/3rdParty/sdbus-cpp/include/sdbus-c++/ConvenienceApiClasses.inl b/3rdParty/sdbus-cpp/include/sdbus-c++/ConvenienceApiClasses.inl new file mode 100644 index 0000000000..4be40a9fb5 --- /dev/null +++ b/3rdParty/sdbus-cpp/include/sdbus-c++/ConvenienceApiClasses.inl @@ -0,0 +1,718 @@ +/** + * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland + * (C) 2016 - 2024 Stanislav Angelovic + * + * @file ConvenienceApiClasses.inl + * + * Created on: Dec 19, 2016 + * Project: sdbus-c++ + * Description: High-level D-Bus IPC C++ library based on sd-bus + * + * This file is part of sdbus-c++. + * + * sdbus-c++ is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * sdbus-c++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with sdbus-c++. If not, see . + */ + +#ifndef SDBUS_CPP_CONVENIENCEAPICLASSES_INL_ +#define SDBUS_CPP_CONVENIENCEAPICLASSES_INL_ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace sdbus { + + /*** ------------- ***/ + /*** VTableAdder ***/ + /*** ------------- ***/ + + inline VTableAdder::VTableAdder(IObject& object, std::vector vtable) + : object_(object) + , vtable_(std::move(vtable)) + { + } + + inline void VTableAdder::forInterface(InterfaceName interfaceName) + { + object_.addVTable(std::move(interfaceName), std::move(vtable_)); + } + + inline void VTableAdder::forInterface(std::string interfaceName) + { + forInterface(InterfaceName{std::move(interfaceName)}); + } + + [[nodiscard]] inline Slot VTableAdder::forInterface(InterfaceName interfaceName, return_slot_t) + { + return object_.addVTable(std::move(interfaceName), std::move(vtable_), return_slot); + } + + [[nodiscard]] inline Slot VTableAdder::forInterface(std::string interfaceName, return_slot_t) + { + return forInterface(InterfaceName{std::move(interfaceName)}, return_slot); + } + + /*** ------------- ***/ + /*** SignalEmitter ***/ + /*** ------------- ***/ + + inline SignalEmitter::SignalEmitter(IObject& object, const SignalName& signalName) + : SignalEmitter(object, signalName.c_str()) + { + } + + inline SignalEmitter::SignalEmitter(IObject& object, const char* signalName) + : object_(object) + , signalName_(signalName) + , exceptions_(std::uncaught_exceptions()) + { + } + + inline SignalEmitter::~SignalEmitter() noexcept(false) // since C++11, destructors must + { // explicitly be allowed to throw + // Don't emit the signal if SignalEmitter threw an exception in one of its methods + if (std::uncaught_exceptions() != exceptions_) + return; + + // emitSignal() can throw. But as the SignalEmitter shall always be used as an unnamed, + // temporary object, i.e. not as a stack-allocated object, the double-exception situation + // shall never happen. I.e. it should not happen that this destructor is directly called + // in the stack-unwinding process of another flying exception (which would lead to immediate + // termination). It can be called indirectly in the destructor of another object, but that's + // fine and safe provided that the caller catches exceptions thrown from here. + // Therefore, we can allow emitSignal() to throw even if we are in the destructor. + // Bottomline is, to be on the safe side, the caller must take care of catching and reacting + // to the exception thrown from here if the caller is a destructor itself. + object_.emitSignal(signal_); + } + + inline SignalEmitter& SignalEmitter::onInterface(const InterfaceName& interfaceName) + { + return onInterface(interfaceName.c_str()); + } + + inline SignalEmitter& SignalEmitter::onInterface(const std::string& interfaceName) + { + return onInterface(interfaceName.c_str()); + } + + inline SignalEmitter& SignalEmitter::onInterface(const char* interfaceName) + { + signal_ = object_.createSignal(interfaceName, signalName_); + + return *this; + } + + template + inline void SignalEmitter::withArguments(_Args&&... args) + { + assert(signal_.isValid()); // onInterface() must be placed/called prior to withArguments() + + detail::serialize_pack(signal_, std::forward<_Args>(args)...); + } + + /*** ------------- ***/ + /*** MethodInvoker ***/ + /*** ------------- ***/ + + inline MethodInvoker::MethodInvoker(IProxy& proxy, const MethodName& methodName) + : MethodInvoker(proxy, methodName.c_str()) + { + } + + inline MethodInvoker::MethodInvoker(IProxy& proxy, const char* methodName) + : proxy_(proxy) + , methodName_(methodName) + , exceptions_(std::uncaught_exceptions()) + { + } + + inline MethodInvoker::~MethodInvoker() noexcept(false) // since C++11, destructors must + { // explicitly be allowed to throw + // Don't call the method if it has been called already or if MethodInvoker + // threw an exception in one of its methods + if (methodCalled_ || std::uncaught_exceptions() != exceptions_) + return; + + // callMethod() can throw. But as the MethodInvoker shall always be used as an unnamed, + // temporary object, i.e. not as a stack-allocated object, the double-exception situation + // shall never happen. I.e. it should not happen that this destructor is directly called + // in the stack-unwinding process of another flying exception (which would lead to immediate + // termination). It can be called indirectly in the destructor of another object, but that's + // fine and safe provided that the caller catches exceptions thrown from here. + // Therefore, we can allow callMethod() to throw even if we are in the destructor. + // Bottomline is, to be on the safe side, the caller must take care of catching and reacting + // to the exception thrown from here if the caller is a destructor itself. + proxy_.callMethod(method_, timeout_); + } + + inline MethodInvoker& MethodInvoker::onInterface(const InterfaceName& interfaceName) + { + return onInterface(interfaceName.c_str()); + } + + inline MethodInvoker& MethodInvoker::onInterface(const std::string& interfaceName) + { + return onInterface(interfaceName.c_str()); + } + + inline MethodInvoker& MethodInvoker::onInterface(const char* interfaceName) + { + method_ = proxy_.createMethodCall(interfaceName, methodName_); + + return *this; + } + + inline MethodInvoker& MethodInvoker::withTimeout(uint64_t usec) + { + timeout_ = usec; + + return *this; + } + + template + inline MethodInvoker& MethodInvoker::withTimeout(const std::chrono::duration<_Rep, _Period>& timeout) + { + auto microsecs = std::chrono::duration_cast(timeout); + return withTimeout(microsecs.count()); + } + + template + inline MethodInvoker& MethodInvoker::withArguments(_Args&&... args) + { + assert(method_.isValid()); // onInterface() must be placed/called prior to this function + + detail::serialize_pack(method_, std::forward<_Args>(args)...); + + return *this; + } + + template + inline void MethodInvoker::storeResultsTo(_Args&... args) + { + assert(method_.isValid()); // onInterface() must be placed/called prior to this function + + auto reply = proxy_.callMethod(method_, timeout_); + methodCalled_ = true; + + detail::deserialize_pack(reply, args...); + } + + inline void MethodInvoker::dontExpectReply() + { + assert(method_.isValid()); // onInterface() must be placed/called prior to this function + + method_.dontExpectReply(); + } + + /*** ------------------ ***/ + /*** AsyncMethodInvoker ***/ + /*** ------------------ ***/ + + inline AsyncMethodInvoker::AsyncMethodInvoker(IProxy& proxy, const MethodName& methodName) + : AsyncMethodInvoker(proxy, methodName.c_str()) + { + } + + inline AsyncMethodInvoker::AsyncMethodInvoker(IProxy& proxy, const char* methodName) + : proxy_(proxy) + , methodName_(methodName) + { + } + + inline AsyncMethodInvoker& AsyncMethodInvoker::onInterface(const InterfaceName& interfaceName) + { + return onInterface(interfaceName.c_str()); + } + + inline AsyncMethodInvoker& AsyncMethodInvoker::onInterface(const std::string& interfaceName) + { + return onInterface(interfaceName.c_str()); + } + + inline AsyncMethodInvoker& AsyncMethodInvoker::onInterface(const char* interfaceName) + { + method_ = proxy_.createMethodCall(interfaceName, methodName_); + + return *this; + } + + inline AsyncMethodInvoker& AsyncMethodInvoker::withTimeout(uint64_t usec) + { + timeout_ = usec; + + return *this; + } + + template + inline AsyncMethodInvoker& AsyncMethodInvoker::withTimeout(const std::chrono::duration<_Rep, _Period>& timeout) + { + auto microsecs = std::chrono::duration_cast(timeout); + return withTimeout(microsecs.count()); + } + + template + inline AsyncMethodInvoker& AsyncMethodInvoker::withArguments(_Args&&... args) + { + assert(method_.isValid()); // onInterface() must be placed/called prior to this function + + detail::serialize_pack(method_, std::forward<_Args>(args)...); + + return *this; + } + + template + PendingAsyncCall AsyncMethodInvoker::uponReplyInvoke(_Function&& callback) + { + assert(method_.isValid()); // onInterface() must be placed/called prior to this function + + return proxy_.callMethodAsync(method_, makeAsyncReplyHandler(std::forward<_Function>(callback)), timeout_); + } + + template + [[nodiscard]] Slot AsyncMethodInvoker::uponReplyInvoke(_Function&& callback, return_slot_t) + { + assert(method_.isValid()); // onInterface() must be placed/called prior to this function + + return proxy_.callMethodAsync( method_ + , makeAsyncReplyHandler(std::forward<_Function>(callback)) + , timeout_ + , return_slot ); + } + + template + inline async_reply_handler AsyncMethodInvoker::makeAsyncReplyHandler(_Function&& callback) + { + return [callback = std::forward<_Function>(callback)](MethodReply reply, std::optional error) + { + // Create a tuple of callback input arguments' types, which will be used + // as a storage for the argument values deserialized from the message. + tuple_of_function_input_arg_types_t<_Function> args; + + // Deserialize input arguments from the message into the tuple (if no error occurred). + if (!error) + { + try + { + reply >> args; + } + catch (const Error& e) + { + // Pass message deserialization exceptions to the client via callback error parameter, + // instead of propagating them up the message loop call stack. + sdbus::apply(callback, e, args); + return; + } + } + + // Invoke callback with input arguments from the tuple. + sdbus::apply(callback, std::move(error), args); + }; + } + + template + std::future> AsyncMethodInvoker::getResultAsFuture() + { + auto promise = std::make_shared>>(); + auto future = promise->get_future(); + + uponReplyInvoke([promise = std::move(promise)](std::optional error, _Args... args) + { + if (!error) + if constexpr (!std::is_void_v>) + promise->set_value({std::move(args)...}); + else + promise->set_value(); + else + promise->set_exception(std::make_exception_ptr(*std::move(error))); + }); + + // Will be std::future for no D-Bus method return value + // or std::future for single D-Bus method return value + // or std::future> for multiple method return values + return future; + } + + /*** ---------------- ***/ + /*** SignalSubscriber ***/ + /*** ---------------- ***/ + + inline SignalSubscriber::SignalSubscriber(IProxy& proxy, const SignalName& signalName) + : SignalSubscriber(proxy, signalName.c_str()) + { + } + + inline SignalSubscriber::SignalSubscriber(IProxy& proxy, const char* signalName) + : proxy_(proxy) + , signalName_(signalName) + { + } + + inline SignalSubscriber& SignalSubscriber::onInterface(const InterfaceName& interfaceName) + { + return onInterface(interfaceName.c_str()); + } + + inline SignalSubscriber& SignalSubscriber::onInterface(const std::string& interfaceName) + { + return onInterface(interfaceName.c_str()); + } + + inline SignalSubscriber& SignalSubscriber::onInterface(const char* interfaceName) + { + interfaceName_ = std::move(interfaceName); + + return *this; + } + + template + inline void SignalSubscriber::call(_Function&& callback) + { + assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function + + proxy_.registerSignalHandler( interfaceName_ + , signalName_ + , makeSignalHandler(std::forward<_Function>(callback)) ); + } + + template + [[nodiscard]] inline Slot SignalSubscriber::call(_Function&& callback, return_slot_t) + { + assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function + + return proxy_.registerSignalHandler( interfaceName_ + , signalName_ + , makeSignalHandler(std::forward<_Function>(callback)) + , return_slot ); + } + + template + inline signal_handler SignalSubscriber::makeSignalHandler(_Function&& callback) + { + return [callback = std::forward<_Function>(callback)](Signal signal) + { + // Create a tuple of callback input arguments' types, which will be used + // as a storage for the argument values deserialized from the signal message. + tuple_of_function_input_arg_types_t<_Function> signalArgs; + + // The signal handler can take pure signal parameters only, or an additional `std::optional` as its first + // parameter. In the former case, if the deserialization fails (e.g. due to signature mismatch), + // the failure is ignored (and signal simply dropped). In the latter case, the deserialization failure + // will be communicated to the client's signal handler as a valid Error object inside the std::optional parameter. + if constexpr (has_error_param_v<_Function>) + { + // Deserialize input arguments from the signal message into the tuple + try + { + signal >> signalArgs; + } + catch (const sdbus::Error& e) + { + // Pass message deserialization exceptions to the client via callback error parameter, + // instead of propagating them up the message loop call stack. + sdbus::apply(callback, e, signalArgs); + return; + } + + // Invoke callback with no error and input arguments from the tuple. + sdbus::apply(callback, {}, signalArgs); + } + else + { + // Deserialize input arguments from the signal message into the tuple + signal >> signalArgs; + + // Invoke callback with input arguments from the tuple. + sdbus::apply(callback, signalArgs); + } + }; + } + + /*** -------------- ***/ + /*** PropertyGetter ***/ + /*** -------------- ***/ + + inline PropertyGetter::PropertyGetter(IProxy& proxy, std::string_view propertyName) + : proxy_(proxy) + , propertyName_(std::move(propertyName)) + { + } + + inline Variant PropertyGetter::onInterface(std::string_view interfaceName) + { + Variant var; + proxy_.callMethod("Get") + .onInterface(DBUS_PROPERTIES_INTERFACE_NAME) + .withArguments(interfaceName, propertyName_) + .storeResultsTo(var); + return var; + } + + /*** ------------------- ***/ + /*** AsyncPropertyGetter ***/ + /*** ------------------- ***/ + + inline AsyncPropertyGetter::AsyncPropertyGetter(IProxy& proxy, std::string_view propertyName) + : proxy_(proxy) + , propertyName_(std::move(propertyName)) + { + } + + inline AsyncPropertyGetter& AsyncPropertyGetter::onInterface(std::string_view interfaceName) + { + interfaceName_ = std::move(interfaceName); + + return *this; + } + + template + PendingAsyncCall AsyncPropertyGetter::uponReplyInvoke(_Function&& callback) + { + static_assert( std::is_invocable_r_v, Variant> + , "Property get callback function must accept std::optional and property value as Variant" ); + + assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function + + return proxy_.callMethodAsync("Get") + .onInterface(DBUS_PROPERTIES_INTERFACE_NAME) + .withArguments(interfaceName_, propertyName_) + .uponReplyInvoke(std::forward<_Function>(callback)); + } + + template + [[nodiscard]] Slot AsyncPropertyGetter::uponReplyInvoke(_Function&& callback, return_slot_t) + { + static_assert( std::is_invocable_r_v, Variant> + , "Property get callback function must accept std::optional and property value as Variant" ); + + assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function + + return proxy_.callMethodAsync("Get") + .onInterface(DBUS_PROPERTIES_INTERFACE_NAME) + .withArguments(interfaceName_, propertyName_) + .uponReplyInvoke(std::forward<_Function>(callback), return_slot); + } + + inline std::future AsyncPropertyGetter::getResultAsFuture() + { + assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function + + return proxy_.callMethodAsync("Get") + .onInterface(DBUS_PROPERTIES_INTERFACE_NAME) + .withArguments(interfaceName_, propertyName_) + .getResultAsFuture(); + } + + /*** -------------- ***/ + /*** PropertySetter ***/ + /*** -------------- ***/ + + inline PropertySetter::PropertySetter(IProxy& proxy, std::string_view propertyName) + : proxy_(proxy) + , propertyName_(std::move(propertyName)) + { + } + + inline PropertySetter& PropertySetter::onInterface(std::string_view interfaceName) + { + interfaceName_ = std::move(interfaceName); + + return *this; + } + + template + inline void PropertySetter::toValue(const _Value& value) + { + PropertySetter::toValue(Variant{value}); + } + + template + inline void PropertySetter::toValue(const _Value& value, dont_expect_reply_t) + { + PropertySetter::toValue(Variant{value}, dont_expect_reply); + } + + inline void PropertySetter::toValue(const Variant& value) + { + assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function + + proxy_.callMethod("Set") + .onInterface(DBUS_PROPERTIES_INTERFACE_NAME) + .withArguments(interfaceName_, propertyName_, value); + } + + inline void PropertySetter::toValue(const Variant& value, dont_expect_reply_t) + { + assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function + + proxy_.callMethod("Set") + .onInterface(DBUS_PROPERTIES_INTERFACE_NAME) + .withArguments(interfaceName_, propertyName_, value) + .dontExpectReply(); + } + + /*** ------------------- ***/ + /*** AsyncPropertySetter ***/ + /*** ------------------- ***/ + + inline AsyncPropertySetter::AsyncPropertySetter(IProxy& proxy, std::string_view propertyName) + : proxy_(proxy) + , propertyName_(propertyName) + { + } + + inline AsyncPropertySetter& AsyncPropertySetter::onInterface(std::string_view interfaceName) + { + interfaceName_ = std::move(interfaceName); + + return *this; + } + + template + inline AsyncPropertySetter& AsyncPropertySetter::toValue(_Value&& value) + { + return AsyncPropertySetter::toValue(Variant{std::forward<_Value>(value)}); + } + + inline AsyncPropertySetter& AsyncPropertySetter::toValue(Variant value) + { + value_ = std::move(value); + + return *this; + } + + template + PendingAsyncCall AsyncPropertySetter::uponReplyInvoke(_Function&& callback) + { + static_assert( std::is_invocable_r_v> + , "Property set callback function must accept std::optional only" ); + + assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function + + return proxy_.callMethodAsync("Set") + .onInterface(DBUS_PROPERTIES_INTERFACE_NAME) + .withArguments(interfaceName_, propertyName_, std::move(value_)) + .uponReplyInvoke(std::forward<_Function>(callback)); + } + + template + [[nodiscard]] Slot AsyncPropertySetter::uponReplyInvoke(_Function&& callback, return_slot_t) + { + static_assert( std::is_invocable_r_v> + , "Property set callback function must accept std::optional only" ); + + assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function + + return proxy_.callMethodAsync("Set") + .onInterface(DBUS_PROPERTIES_INTERFACE_NAME) + .withArguments(interfaceName_, propertyName_, std::move(value_)) + .uponReplyInvoke(std::forward<_Function>(callback), return_slot); + } + + inline std::future AsyncPropertySetter::getResultAsFuture() + { + assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function + + return proxy_.callMethodAsync("Set") + .onInterface(DBUS_PROPERTIES_INTERFACE_NAME) + .withArguments(interfaceName_, propertyName_, std::move(value_)) + .getResultAsFuture<>(); + } + + /*** ------------------- ***/ + /*** AllPropertiesGetter ***/ + /*** ------------------- ***/ + + inline AllPropertiesGetter::AllPropertiesGetter(IProxy& proxy) + : proxy_(proxy) + { + } + + inline std::map AllPropertiesGetter::onInterface(std::string_view interfaceName) + { + std::map props; + proxy_.callMethod("GetAll") + .onInterface(DBUS_PROPERTIES_INTERFACE_NAME) + .withArguments(std::move(interfaceName)) + .storeResultsTo(props); + return props; + } + + /*** ------------------------ ***/ + /*** AsyncAllPropertiesGetter ***/ + /*** ------------------------ ***/ + + inline AsyncAllPropertiesGetter::AsyncAllPropertiesGetter(IProxy& proxy) + : proxy_(proxy) + { + } + + inline AsyncAllPropertiesGetter& AsyncAllPropertiesGetter::onInterface(std::string_view interfaceName) + { + interfaceName_ = std::move(interfaceName); + + return *this; + } + + template + PendingAsyncCall AsyncAllPropertiesGetter::uponReplyInvoke(_Function&& callback) + { + static_assert( std::is_invocable_r_v, std::map> + , "All properties get callback function must accept std::optional and a map of property names to their values" ); + + assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function + + return proxy_.callMethodAsync("GetAll") + .onInterface(DBUS_PROPERTIES_INTERFACE_NAME) + .withArguments(interfaceName_) + .uponReplyInvoke(std::forward<_Function>(callback)); + } + + template + [[nodiscard]] Slot AsyncAllPropertiesGetter::uponReplyInvoke(_Function&& callback, return_slot_t) + { + static_assert( std::is_invocable_r_v, std::map> + , "All properties get callback function must accept std::optional and a map of property names to their values" ); + + assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function + + return proxy_.callMethodAsync("GetAll") + .onInterface(DBUS_PROPERTIES_INTERFACE_NAME) + .withArguments(interfaceName_) + .uponReplyInvoke(std::forward<_Function>(callback), return_slot); + } + + inline std::future> AsyncAllPropertiesGetter::getResultAsFuture() + { + assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function + + return proxy_.callMethodAsync("GetAll") + .onInterface(DBUS_PROPERTIES_INTERFACE_NAME) + .withArguments(interfaceName_) + .getResultAsFuture>(); + } + +} // namespace sdbus + +#endif /* SDBUS_CPP_CONVENIENCEAPICLASSES_INL_ */ diff --git a/3rdParty/sdbus-cpp/include/sdbus-c++/Error.h b/3rdParty/sdbus-cpp/include/sdbus-c++/Error.h new file mode 100644 index 0000000000..4f6e2e9d8e --- /dev/null +++ b/3rdParty/sdbus-cpp/include/sdbus-c++/Error.h @@ -0,0 +1,106 @@ +/** + * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland + * (C) 2016 - 2024 Stanislav Angelovic + * + * @file Error.h + * + * Created on: Nov 8, 2016 + * Project: sdbus-c++ + * Description: High-level D-Bus IPC C++ library based on sd-bus + * + * This file is part of sdbus-c++. + * + * sdbus-c++ is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * sdbus-c++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with sdbus-c++. If not, see . + */ + +#ifndef SDBUS_CXX_ERROR_H_ +#define SDBUS_CXX_ERROR_H_ + +#include +#include +#include + +namespace sdbus { + + /********************************************//** + * @class Error + * + * Represents a common sdbus-c++ exception. + * + ***********************************************/ + class Error + : public std::runtime_error + { + public: + // Strong type representing the D-Bus error name + class Name : public std::string + { + public: + Name() = default; + explicit Name(std::string value) + : std::string(std::move(value)) + {} + explicit Name(const char* value) + : std::string(value) + {} + + using std::string::operator=; + }; + + explicit Error(Name name, const char* message = nullptr) + : Error(std::move(name), std::string(message ? message : "")) + { + } + + Error(Name name, std::string message) + : std::runtime_error(!message.empty() ? "[" + name + "] " + message : "[" + name + "]") + , name_(std::move(name)) + , message_(std::move(message)) + { + } + + [[nodiscard]] const Name& getName() const + { + return name_; + } + + [[nodiscard]] const std::string& getMessage() const + { + return message_; + } + + [[nodiscard]] bool isValid() const + { + return !getName().empty(); + } + + private: + Name name_; + std::string message_; + }; + + Error createError(int errNo, std::string customMsg = {}); + + inline const Error::Name SDBUSCPP_ERROR_NAME{"org.sdbuscpp.Error"}; +} + +#define SDBUS_THROW_ERROR(_MSG, _ERRNO) \ + throw sdbus::createError((_ERRNO), (_MSG)) \ + /**/ + +#define SDBUS_THROW_ERROR_IF(_COND, _MSG, _ERRNO) \ + if (!(_COND)) ; else SDBUS_THROW_ERROR((_MSG), (_ERRNO)) \ + /**/ + +#endif /* SDBUS_CXX_ERROR_H_ */ diff --git a/3rdParty/sdbus-cpp/include/sdbus-c++/Flags.h b/3rdParty/sdbus-cpp/include/sdbus-c++/Flags.h new file mode 100644 index 0000000000..d85a4aa51d --- /dev/null +++ b/3rdParty/sdbus-cpp/include/sdbus-c++/Flags.h @@ -0,0 +1,99 @@ +/** + * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland + * (C) 2016 - 2024 Stanislav Angelovic + * + * @file Flags.h + * + * Created on: Dec 31, 2018 + * Project: sdbus-c++ + * Description: High-level D-Bus IPC C++ library based on sd-bus + * + * This file is part of sdbus-c++. + * + * sdbus-c++ is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * sdbus-c++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with sdbus-c++. If not, see . + */ + +#ifndef SDBUS_CXX_FLAGS_H_ +#define SDBUS_CXX_FLAGS_H_ + +#include +#include + +namespace sdbus { + + // D-Bus interface, method, signal or property flags + class Flags + { + public: + enum GeneralFlags : uint8_t + { DEPRECATED = 0 + , METHOD_NO_REPLY = 1 + , PRIVILEGED = 2 + }; + + enum PropertyUpdateBehaviorFlags : uint8_t + { EMITS_CHANGE_SIGNAL = 3 + , EMITS_INVALIDATION_SIGNAL = 4 + , EMITS_NO_SIGNAL = 5 + , CONST_PROPERTY_VALUE = 6 + }; + + enum : uint8_t + { FLAG_COUNT = 7 + }; + + Flags() + { + // EMITS_CHANGE_SIGNAL is on by default + flags_.set(EMITS_CHANGE_SIGNAL, true); + } + + void set(GeneralFlags flag, bool value = true) + { + flags_.set(flag, value); + } + + void set(PropertyUpdateBehaviorFlags flag, bool value = true) + { + flags_.set(EMITS_CHANGE_SIGNAL, false); + flags_.set(EMITS_INVALIDATION_SIGNAL, false); + flags_.set(EMITS_NO_SIGNAL, false); + flags_.set(CONST_PROPERTY_VALUE, false); + + flags_.set(flag, value); + } + + [[nodiscard]] bool test(GeneralFlags flag) const + { + return flags_.test(flag); + } + + [[nodiscard]] bool test(PropertyUpdateBehaviorFlags flag) const + { + return flags_.test(flag); + } + + [[nodiscard]] uint64_t toSdBusInterfaceFlags() const; + [[nodiscard]] uint64_t toSdBusMethodFlags() const; + [[nodiscard]] uint64_t toSdBusSignalFlags() const; + [[nodiscard]] uint64_t toSdBusPropertyFlags() const; + [[nodiscard]] uint64_t toSdBusWritablePropertyFlags() const; + + private: + std::bitset flags_; + }; + +} + +#endif /* SDBUS_CXX_FLAGS_H_ */ diff --git a/3rdParty/sdbus-cpp/include/sdbus-c++/IConnection.h b/3rdParty/sdbus-cpp/include/sdbus-c++/IConnection.h new file mode 100644 index 0000000000..42caa76b4b --- /dev/null +++ b/3rdParty/sdbus-cpp/include/sdbus-c++/IConnection.h @@ -0,0 +1,584 @@ +/** + * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland + * (C) 2016 - 2024 Stanislav Angelovic + * + * @file IConnection.h + * + * Created on: Nov 8, 2016 + * Project: sdbus-c++ + * Description: High-level D-Bus IPC C++ library based on sd-bus + * + * This file is part of sdbus-c++. + * + * sdbus-c++ is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * sdbus-c++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with sdbus-c++. If not, see . + */ + +#ifndef SDBUS_CXX_ICONNECTION_H_ +#define SDBUS_CXX_ICONNECTION_H_ + +#include + +#include +#include +#include +#include +#include + +// Forward declarations +struct sd_bus; +struct sd_event; +namespace sdbus { + class Message; + class ObjectPath; + class BusName; + using ServiceName = BusName; +} + +namespace sdbus { + + /********************************************//** + * @class IConnection + * + * An interface to D-Bus bus connection. Incorporates implementation + * of both synchronous and asynchronous D-Bus I/O event loop. + * + * All methods throw sdbus::Error in case of failure. All methods in + * this class are thread-aware, but not thread-safe. + * + ***********************************************/ + class IConnection + { + public: + struct PollData; + + virtual ~IConnection() = default; + + /*! + * @brief Enters I/O event loop on this bus connection + * + * The incoming D-Bus messages are processed in the loop. The method + * blocks indefinitely, until unblocked through leaveEventLoop(). + * + * @throws sdbus::Error in case of failure + */ + virtual void enterEventLoop() = 0; + + /*! + * @brief Enters I/O event loop on this bus connection in a separate thread + * + * The same as enterEventLoop, except that it doesn't block + * because it runs the loop in a separate, internally managed thread. + */ + virtual void enterEventLoopAsync() = 0; + + /*! + * @brief Leaves the I/O event loop running on this bus connection + * + * This causes the loop to exit and frees the thread serving the loop + * + * @throws sdbus::Error in case of failure + */ + virtual void leaveEventLoop() = 0; + + /*! + * @brief Attaches the bus connection to an sd-event event loop + * + * @param[in] event sd-event event loop object + * @param[in] priority Specified priority + * + * @throws sdbus::Error in case of failure + * + * See `man sd_bus_attach_event'. + */ + virtual void attachSdEventLoop(sd_event *event, int priority = 0) = 0; + + /*! + * @brief Detaches the bus connection from an sd-event event loop + * + * @throws sdbus::Error in case of failure + */ + virtual void detachSdEventLoop() = 0; + + /*! + * @brief Gets current sd-event event loop for the bus connection + * + * @return Pointer to event loop object if attached, nullptr otherwise + */ + virtual sd_event *getSdEventLoop() = 0; + + /*! + * @brief Returns fd's, I/O events and timeout data to be used in an external event loop + * + * This function is useful to hook up a bus connection object with an + * external (like GMainLoop, boost::asio, etc.) or manual event loop + * involving poll() or a similar I/O polling call. + * + * Before **each** invocation of the I/O polling call, this function + * should be invoked. Returned PollData::fd file descriptor should + * be polled for the events indicated by PollData::events, and the I/O + * call should block for that up to the returned PollData::timeout. + * + * Additionally, returned PollData::eventFd should be polled for POLLIN + * events. + * + * After each I/O polling call the bus connection needs to process + * incoming or outgoing data, by invoking processPendingEvent(). + * + * Note that the returned timeout should be considered only a maximum + * sleeping time. It is permissible (and even expected) that shorter + * timeouts are used by the calling program, in case other event sources + * are polled in the same event loop. Note that the returned time-value + * is absolute, based of CLOCK_MONOTONIC and specified in microseconds. + * Use PollData::getPollTimeout() to have the timeout value converted + * in a form that can be passed to poll(2). + * + * The bus connection conveniently integrates sd-event event loop. + * To attach the bus connection to an sd-event event loop, use + * attachSdEventLoop() function. + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] virtual PollData getEventLoopPollData() const = 0; + + /*! + * @brief Processes a pending event + * + * @returns True if an event was processed, false if no operations were pending + * + * This function drives the D-Bus connection. It processes pending I/O events. + * Queued outgoing messages (or parts thereof) are sent out. Queued incoming + * messages are dispatched to registered callbacks. Timeouts are recalculated. + * + * It returns false when no operations were pending and true if a message was + * processed. When false is returned the caller should synchronously poll for + * I/O events before calling into processPendingEvent() again. + * Don't forget to call getEventLoopPollData() each time before the next poll. + * + * You don't need to directly call this method or getEventLoopPollData() method + * when using convenient, internal bus connection event loops through + * enterEventLoop() or enterEventLoopAsync() calls, or when the bus is + * connected to an sd-event event loop through attachSdEventLoop(). + * It is invoked automatically when necessary. + * + * @throws sdbus::Error in case of failure + */ + virtual bool processPendingEvent() = 0; + + /*! + * @brief Provides access to the currently processed D-Bus message + * + * This method provides access to the currently processed incoming D-Bus message. + * "Currently processed" means that the registered callback handler(s) for that message + * are being invoked. This method is meant to be called from within a callback handler + * (e.g. from a D-Bus signal handler, or async method reply handler, etc.). In such a case it is + * guaranteed to return a valid D-Bus message instance for which the handler is called. + * If called from other contexts/threads, it may return a valid or invalid message, depending + * on whether a message was processed or not at the time of the call. + * + * @return Currently processed D-Bus message + */ + [[nodiscard]] virtual Message getCurrentlyProcessedMessage() const = 0; + + /*! + * @brief Sets general method call timeout + * + * @param[in] timeout Timeout value in microseconds + * + * General method call timeout is used for all method calls upon this connection. + * Method call-specific timeout overrides this general setting. + * + * Supported by libsystemd>=v240. + * + * @throws sdbus::Error in case of failure + */ + virtual void setMethodCallTimeout(uint64_t timeout) = 0; + + /*! + * @copydoc IConnection::setMethodCallTimeout(uint64_t) + */ + template + void setMethodCallTimeout(const std::chrono::duration<_Rep, _Period>& timeout); + + /*! + * @brief Gets general method call timeout + * + * @return Timeout value in microseconds + * + * Supported by libsystemd>=v240. + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] virtual uint64_t getMethodCallTimeout() const = 0; + + /*! + * @brief Adds an ObjectManager at the specified D-Bus object path + * @param[in] objectPath Object path at which the ObjectManager interface shall be installed + * + * Creates an ObjectManager interface at the specified object path on + * the connection. This is a convenient way to interrogate a connection + * to see what objects it has. + * + * This call creates a floating registration. The ObjectManager will + * be there for the object path until the connection is destroyed. + * + * Another, recommended way to add object managers is directly through IObject API. + * + * @throws sdbus::Error in case of failure + */ + virtual void addObjectManager(const ObjectPath& objectPath) = 0; + + /*! + * @brief Adds an ObjectManager at the specified D-Bus object path + * @param[in] objectPath Object path at which the ObjectManager interface shall be installed + * @return Slot handle owning the registration + * + * Creates an ObjectManager interface at the specified object path on + * the connection. This is a convenient way to interrogate a connection + * to see what objects it has. + * + * This call returns an owning slot. The lifetime of the ObjectManager + * interface is bound to the lifetime of the returned slot instance. + * + * Another, recommended way to add object managers is directly through IObject API. + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] virtual Slot addObjectManager(const ObjectPath& objectPath, return_slot_t) = 0; + + /*! + * @brief Installs a floating match rule for messages received on this bus connection + * + * @param[in] match Match expression to filter incoming D-Bus message + * @param[in] callback Callback handler to be called upon processing an inbound D-Bus message matching the rule + * + * The method installs a match rule for messages received on the specified bus connection. + * The syntax of the match rule expression passed in match is described in the D-Bus specification. + * The specified handler function callback is called for each incoming message matching the specified + * expression. The match is installed synchronously when connected to a bus broker, i.e. the call + * sends a control message requesting the match to be added to the broker and waits until the broker + * confirms the match has been installed successfully. + * + * The method installs a floating match rule for messages received on the specified bus connection. + * Floating means that the bus connection object owns the match rule, i.e. lifetime of the match rule + * is bound to the lifetime of the bus connection. + * + * For more information, consult `man sd_bus_add_match`. + * + * @throws sdbus::Error in case of failure + */ + virtual void addMatch(const std::string& match, message_handler callback) = 0; + + /*! + * @brief Installs a match rule for messages received on this bus connection + * + * @param[in] match Match expression to filter incoming D-Bus message + * @param[in] callback Callback handler to be called upon processing an inbound D-Bus message matching the rule + * @return RAII-style slot handle representing the ownership of the subscription + * + * The method installs a match rule for messages received on the specified bus connection. + * The syntax of the match rule expression passed in match is described in the D-Bus specification. + * The specified handler function callback is called for each incoming message matching the specified + * expression. The match is installed synchronously when connected to a bus broker, i.e. the call + * sends a control message requesting the match to be added to the broker and waits until the broker + * confirms the match has been installed successfully. + * + * The lifetime of the match rule is bound to the lifetime of the returned slot instance. Destroying + * the slot instance implies uninstalling of the match rule from the bus connection. + * + * For more information, consult `man sd_bus_add_match`. + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] virtual Slot addMatch(const std::string& match, message_handler callback, return_slot_t) = 0; + + /*! + * @brief Asynchronously installs a floating match rule for messages received on this bus connection + * + * @param[in] match Match expression to filter incoming D-Bus message + * @param[in] callback Callback handler to be called upon processing an inbound D-Bus message matching the rule + * @param[in] installCallback Callback handler to be called upon processing an inbound D-Bus message matching the rule + * + * This method operates the same as `addMatch()` above, just that it installs the match rule asynchronously, + * in a non-blocking fashion. A request is sent to the broker, but the call does not wait for a response. + * The `installCallback' callable is called when the response is later received, with the response message + * from the broker as parameter. If it's an empty function object, a default implementation is used that + * terminates the bus connection should installing the match fail. + * + * The method installs a floating match rule for messages received on the specified bus connection. + * Floating means that the bus connection object owns the match rule, i.e. lifetime of the match rule + * is bound to the lifetime of the bus connection. + * + * For more information, consult `man sd_bus_add_match_async`. + * + * @throws sdbus::Error in case of failure + */ + virtual void addMatchAsync(const std::string& match, message_handler callback, message_handler installCallback) = 0; + + /*! + * @brief Asynchronously installs a match rule for messages received on this bus connection + * + * @param[in] match Match expression to filter incoming D-Bus message + * @param[in] callback Callback handler to be called upon processing an inbound D-Bus message matching the rule + * @param[in] installCallback Callback handler to be called upon processing an inbound D-Bus message matching the rule + * @return RAII-style slot handle representing the ownership of the subscription + * + * This method operates the same as `addMatch()` above, just that it installs the match rule asynchronously, + * in a non-blocking fashion. A request is sent to the broker, but the call does not wait for a response. + * The `installCallback' callable is called when the response is later received, with the response message + * from the broker as parameter. If it's an empty function object, a default implementation is used that + * terminates the bus connection should installing the match fail. + * + * The lifetime of the match rule is bound to the lifetime of the returned slot instance. Destroying + * the slot instance implies the uninstalling of the match rule from the bus connection. + * + * For more information, consult `man sd_bus_add_match_async`. + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] virtual Slot addMatchAsync( const std::string& match + , message_handler callback + , message_handler installCallback + , return_slot_t ) = 0; + + /*! + * @brief Retrieves the unique name of a connection. E.g. ":1.xx" + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] virtual BusName getUniqueName() const = 0; + + /*! + * @brief Requests a well-known D-Bus service name on a bus + * + * @param[in] name Name to request + * + * @throws sdbus::Error in case of failure + */ + virtual void requestName(const ServiceName& name) = 0; + + /*! + * @brief Releases an acquired well-known D-Bus service name on a bus + * + * @param[in] name Name to release + * + * @throws sdbus::Error in case of failure + */ + virtual void releaseName(const ServiceName& name) = 0; + + /*! + * @struct PollData + * + * Carries poll data needed for integration with external event loop implementations. + * + * See getEventLoopPollData() for more info. + */ + struct PollData + { + /*! + * The read fd to be monitored by the event loop. + */ + int fd; + + /*! + * The events to use for poll(2) alongside fd. + */ + short int events; + + /*! + * Absolute timeout value in microseconds, based of CLOCK_MONOTONIC. + * + * Call getPollTimeout() to get timeout recalculated to relative timeout that can be passed to poll(2). + */ + std::chrono::microseconds timeout; + + /*! + * An additional event fd to be monitored by the event loop for POLLIN events. + */ + int eventFd; + + /*! + * Returns the timeout as relative value from now. + * + * Returned value is std::chrono::microseconds::max() if the timeout is indefinite. + * + * @return Relative timeout as a time duration + */ + [[nodiscard]] std::chrono::microseconds getRelativeTimeout() const; + + /*! + * Returns relative timeout in the form which can be passed as argument 'timeout' to poll(2) + * + * @return -1 if the timeout is indefinite. 0 if the poll(2) shouldn't block. + * An integer in milliseconds otherwise. + */ + [[nodiscard]] int getPollTimeout() const; + }; + }; + + template + inline void IConnection::setMethodCallTimeout(const std::chrono::duration<_Rep, _Period>& timeout) + { + auto microsecs = std::chrono::duration_cast(timeout); + return setMethodCallTimeout(microsecs.count()); + } + + /*! + * @brief Creates/opens D-Bus session bus connection when in a user context, and a system bus connection, otherwise. + * + * @return Connection instance + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] std::unique_ptr createBusConnection(); + + /*! + * @brief Creates/opens D-Bus session bus connection with a name when in a user context, and a system bus connection with a name, otherwise. + * + * @param[in] name Name to request on the connection after its opening + * @return Connection instance + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] std::unique_ptr createBusConnection(const ServiceName& name); + + /*! + * @brief Creates/opens D-Bus system bus connection + * + * @return Connection instance + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] std::unique_ptr createSystemBusConnection(); + + /*! + * @brief Creates/opens D-Bus system bus connection with a name + * + * @param[in] name Name to request on the connection after its opening + * @return Connection instance + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] std::unique_ptr createSystemBusConnection(const ServiceName& name); + + /*! + * @brief Creates/opens D-Bus session bus connection + * + * @return Connection instance + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] std::unique_ptr createSessionBusConnection(); + + /*! + * @brief Creates/opens D-Bus session bus connection with a name + * + * @param[in] name Name to request on the connection after its opening + * @return Connection instance + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] std::unique_ptr createSessionBusConnection(const ServiceName& name); + + /*! + * @brief Creates/opens D-Bus session bus connection at a custom address + * + * @param[in] address ";"-separated list of addresses of bus brokers to try to connect + * @return Connection instance + * + * @throws sdbus::Error in case of failure + * + * Consult manual pages for `sd_bus_set_address` of the underlying sd-bus library for more information. + */ + [[nodiscard]] std::unique_ptr createSessionBusConnectionWithAddress(const std::string& address); + + /*! + * @brief Creates/opens D-Bus system connection on a remote host using ssh + * + * @param[in] host Name of the host to connect + * @return Connection instance + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] std::unique_ptr createRemoteSystemBusConnection(const std::string& host); + + /*! + * @brief Opens direct D-Bus connection at a custom address + * + * @param[in] address ";"-separated list of addresses of bus brokers to try to connect to + * @return Connection instance + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] std::unique_ptr createDirectBusConnection(const std::string& address); + + /*! + * @brief Opens direct D-Bus connection at the given file descriptor + * + * @param[in] fd File descriptor used to communicate directly from/to a D-Bus server + * @return Connection instance + * + * The underlying sdbus-c++ connection instance takes over ownership of fd, so the caller can let it go. + * If, however, the call throws an exception, the ownership of fd remains with the caller. + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] std::unique_ptr createDirectBusConnection(int fd); + + /*! + * @brief Opens direct D-Bus connection at fd as a server + * + * @param[in] fd File descriptor to use for server DBus connection + * @return Server connection instance + * + * This creates a new, custom bus object in server mode. One can then call createDirectBusConnection() + * on client side to connect to this bus. + * + * The underlying sdbus-c++ connection instance takes over ownership of fd, so the caller can let it go. + * If, however, the call throws an exception, the ownership of fd remains with the caller. + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] std::unique_ptr createServerBus(int fd); + + /*! + * @brief Creates sdbus-c++ bus connection representation out of underlying sd_bus instance + * + * @param[in] bus File descriptor to use for server DBus connection + * @return Connection instance + * + * This functions is helpful in cases where clients need a custom, tweaked configuration of their + * bus object. Since sdbus-c++ does not provide C++ API for all bus connection configuration + * functions of the underlying sd-bus library, clients can use these sd-bus functions themselves + * to create and configure their sd_bus object, and create sdbus-c++ IConnection on top of it. + * + * The IConnection instance assumes unique ownership of the provided bus object. The bus object + * must have been started by the client before this call. + * The bus object will get flushed, closed, and unreffed when the IConnection instance is destroyed. + * + * @throws sdbus::Error in case of failure + * + * Code example: + * @code + * sd_bus* bus{}; + * ::sd_bus_new(&bus); + * ::sd_bus_set_address(bus, address); + * ::sd_bus_set_anonymous(bus, true); + * ::sd_bus_start(bus); + * auto con = sdbus::createBusConnection(bus); // IConnection consumes sd_bus object + * @endcode + */ + [[nodiscard]] std::unique_ptr createBusConnection(sd_bus *bus); +} + +#endif /* SDBUS_CXX_ICONNECTION_H_ */ diff --git a/3rdParty/sdbus-cpp/include/sdbus-c++/IObject.h b/3rdParty/sdbus-cpp/include/sdbus-c++/IObject.h new file mode 100644 index 0000000000..21953bcc13 --- /dev/null +++ b/3rdParty/sdbus-cpp/include/sdbus-c++/IObject.h @@ -0,0 +1,484 @@ +/** + * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland + * (C) 2016 - 2024 Stanislav Angelovic + * + * @file IObject.h + * + * Created on: Nov 8, 2016 + * Project: sdbus-c++ + * Description: High-level D-Bus IPC C++ library based on sd-bus + * + * This file is part of sdbus-c++. + * + * sdbus-c++ is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * sdbus-c++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with sdbus-c++. If not, see . + */ + +#ifndef SDBUS_CXX_IOBJECT_H_ +#define SDBUS_CXX_IOBJECT_H_ + +#include +#include +#include +#include + +#include +#include +#include +#include + +// Forward declarations +namespace sdbus { + class Signal; + class IConnection; + class ObjectPath; +} + +namespace sdbus { + + /********************************************//** + * @class IObject + * + * IObject class represents a D-Bus object instance identified by a specific object path. + * D-Bus object provides its interfaces, methods, signals and properties on a bus + * identified by a specific bus name. + * + * All IObject member methods throw @c sdbus::Error in case of D-Bus or sdbus-c++ error. + * The IObject class has been designed as thread-aware. However, the operation of + * creating and sending asynchronous method replies, as well as creating and emitting + * signals, is thread-safe by design. + * + ***********************************************/ + class IObject + { + public: // High-level, convenience API + virtual ~IObject() = default; + + /*! + * @brief Adds a declaration of methods, properties and signals of the object at a given interface + * + * @param[in] vtable Individual instances of VTable item structures stored in a vector + * @return VTableAdder high-level helper class + * + * This method is used to declare attributes for the object under the given interface. + * Parameter `vtable' represents a vtable definition that may contain method declarations + * (using MethodVTableItem struct), property declarations (using PropertyVTableItem + * struct), signal declarations (using SignalVTableItem struct), or global interface + * flags (using InterfaceFlagsVTableItem struct). + * + * An interface can have any number of vtables attached to it. + * + * Consult manual pages for the underlying `sd_bus_add_object_vtable` function for more information. + * + * The method can be called at any time during object's lifetime. + * + * When called like `addVTable(vtable).forInterface(interface)`, then an internal registration + * slot is created for that vtable and its lifetime is tied to the lifetime of the Object instance. + * When called like `addVTable(items...).forInterface(interface, sdbus::return_slot)`, then an internal + * registration slot is created for the vtable and is returned to the caller. Keeping the slot means + * keep the registration "alive". Destroying the slot means that the vtable is not needed anymore, + * and the vtable gets removed from the object. This allows for "dynamic" object API where vtables + * can be added or removed by the user at runtime. + * + * The function provides strong exception guarantee. The state of the object remains + * unmodified in face of an exception. + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] VTableAdder addVTable(std::vector vtable); + + /*! + * @brief Adds a declaration of methods, properties and signals of the object at a given interface + * + * @param[in] items Individual instances of VTable item structures + * @return VTableAdder high-level helper class + * + * This method is used to declare attributes for the object under the given interface. + * Parameter pack contains vtable definition that may contain method declarations + * (using MethodVTableItem struct), property declarations (using PropertyVTableItem + * struct), signal declarations (using SignalVTableItem struct), or global interface + * flags (using InterfaceFlagsVTableItem struct). + * + * An interface can have any number of vtables attached to it. + * + * Consult manual pages for the underlying `sd_bus_add_object_vtable` function for more information. + * + * The method can be called at any time during object's lifetime. + * + * When called like `addVTable(items...).forInterface(interface)`, then an internal registration + * slot is created for that vtable and its lifetime is tied to the lifetime of the Object instance. + * When called like `addVTable(items...).forInterface(interface, sdbus::return_slot)`, then an internal + * registration slot is created for the vtable and is returned to the caller. Keeping the slot means + * keep the registration "alive". Destroying the slot means that the vtable is not needed anymore, + * and the vtable gets removed from the object. This allows for "dynamic" object API where vtables + * can be added or removed by the user at runtime. + * + * The function provides strong exception guarantee. The state of the object remains + * unmodified in face of an exception. + * + * @throws sdbus::Error in case of failure + */ + template < typename... VTableItems + , typename = std::enable_if_t<(is_one_of_variants_types> && ...)> > + [[nodiscard]] VTableAdder addVTable(VTableItems&&... items); + + /*! + * @brief Emits signal on D-Bus + * + * @param[in] signalName Name of the signal + * @return A helper object for convenient emission of signals + * + * This is a high-level, convenience way of emitting D-Bus signals that abstracts + * from the D-Bus message concept. Signal arguments are automatically serialized + * in a message and D-Bus signatures automatically deduced from the provided native arguments. + * + * Example of use: + * @code + * int arg1 = ...; + * double arg2 = ...; + * SignalName fooSignal{"fooSignal"}; + * object_.emitSignal(fooSignal).onInterface("com.kistler.foo").withArguments(arg1, arg2); + * @endcode + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] SignalEmitter emitSignal(const SignalName& signalName); + + /*! + * @copydoc IObject::emitSignal(const SignalName&) + */ + [[nodiscard]] SignalEmitter emitSignal(const std::string& signalName); + + /*! + * @copydoc IObject::emitSignal(const SignalName&) + */ + [[nodiscard]] SignalEmitter emitSignal(const char* signalName); + + /*! + * @brief Emits PropertyChanged signal for specified properties under a given interface of this object path + * + * @param[in] interfaceName Name of an interface that properties belong to + * @param[in] propNames Names of properties that will be included in the PropertiesChanged signal + * + * @throws sdbus::Error in case of failure + */ + virtual void emitPropertiesChangedSignal(const InterfaceName& interfaceName, const std::vector& propNames) = 0; + + /*! + * @copydoc IObject::emitPropertiesChangedSignal(const InterfaceName&,const std::vector&) + */ + virtual void emitPropertiesChangedSignal(const char* interfaceName, const std::vector& propNames) = 0; + + /*! + * @brief Emits PropertyChanged signal for all properties on a given interface of this object path + * + * @param[in] interfaceName Name of an interface + * + * @throws sdbus::Error in case of failure + */ + virtual void emitPropertiesChangedSignal(const InterfaceName& interfaceName) = 0; + + /*! + * @copydoc IObject::emitPropertiesChangedSignal(const InterfaceName&) + */ + virtual void emitPropertiesChangedSignal(const char* interfaceName) = 0; + + /*! + * @brief Emits InterfacesAdded signal on this object path + * + * This emits an InterfacesAdded signal on this object path, by iterating all registered + * interfaces on the path. All properties are queried and included in the signal. + * This call is equivalent to emitInterfacesAddedSignal() with an explicit list of + * registered interfaces. However, unlike emitInterfacesAddedSignal(interfaces), this + * call can figure out the list of supported interfaces itself. Furthermore, it properly + * adds the builtin org.freedesktop.DBus.* interfaces. + * + * @throws sdbus::Error in case of failure + */ + virtual void emitInterfacesAddedSignal() = 0; + + /*! + * @brief Emits InterfacesAdded signal on this object path + * + * This emits an InterfacesAdded signal on this object path with explicitly provided list + * of registered interfaces. Since v2.0, sdbus-c++ supports dynamically addable/removable + * object interfaces and their vtables, so this method now makes more sense. + * + * @throws sdbus::Error in case of failure + */ + virtual void emitInterfacesAddedSignal(const std::vector& interfaces) = 0; + + /*! + * @brief Emits InterfacesRemoved signal on this object path + * + * This is like sd_bus_emit_object_added(), but emits an InterfacesRemoved signal on this + * object path. This only includes any registered interfaces but skips the properties. + * This function shall be called (just) before destroying the object. + * + * @throws sdbus::Error in case of failure + */ + virtual void emitInterfacesRemovedSignal() = 0; + + /*! + * @brief Emits InterfacesRemoved signal on this object path + * + * This emits an InterfacesRemoved signal on the given path with explicitly provided list + * of registered interfaces. Since v2.0, sdbus-c++ supports dynamically addable/removable + * object interfaces and their vtables, so this method now makes more sense. + * + * @throws sdbus::Error in case of failure + */ + virtual void emitInterfacesRemovedSignal(const std::vector& interfaces) = 0; + + /*! + * @brief Adds an ObjectManager interface at the path of this D-Bus object + * + * Creates an ObjectManager interface at the specified object path on + * the connection. This is a convenient way to interrogate a connection + * to see what objects it has. + * + * This call creates a so-called floating registration. This means that + * the ObjectManager interface stays there for the lifetime of the object. + * + * @throws sdbus::Error in case of failure + */ + virtual void addObjectManager() = 0; + + /*! + * @brief Adds an ObjectManager interface at the path of this D-Bus object + * + * @return Slot handle owning the registration + * + * Creates an ObjectManager interface at the specified object path on + * the connection. This is a convenient way to interrogate a connection + * to see what objects it has. + * + * The lifetime of the ObjectManager interface is bound to the lifetime + * of the returned slot instance. + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] virtual Slot addObjectManager(return_slot_t) = 0; + + /*! + * @brief Provides D-Bus connection used by the object + * + * @return Reference to the D-Bus connection + */ + [[nodiscard]] virtual sdbus::IConnection& getConnection() const = 0; + + /*! + * @brief Returns object path of the underlying DBus object + */ + [[nodiscard]] virtual const ObjectPath& getObjectPath() const = 0; + + /*! + * @brief Provides access to the currently processed D-Bus message + * + * This method provides access to the currently processed incoming D-Bus message. + * "Currently processed" means that the registered callback handler(s) for that message + * are being invoked. This method is meant to be called from within a callback handler + * (e.g. from a D-Bus signal handler, or async method reply handler, etc.). In such a case it is + * guaranteed to return a valid D-Bus message instance for which the handler is called. + * If called from other contexts/threads, it may return a valid or invalid message, depending + * on whether a message was processed or not at the time of the call. + * + * @return Currently processed D-Bus message + */ + [[nodiscard]] virtual Message getCurrentlyProcessedMessage() const = 0; + + /*! + * @brief Unregisters object's API and removes object from the bus + * + * This method unregisters the object, its interfaces, methods, signals and properties + * from the bus. Unregistration is done automatically also in object's destructor. This + * method makes sense if, in the process of object removal, we need to make sure that + * callbacks are unregistered explicitly before the final destruction of the object instance. + * + * @throws sdbus::Error in case of failure + */ + virtual void unregister() = 0; + + public: // Lower-level, message-based API + /*! + * @brief Adds a declaration of methods, properties and signals of the object at a given interface + * + * @param[in] interfaceName Name of an interface the the vtable is registered for + * @param[in] items Individual instances of VTable item structures + * + * This method is used to declare attributes for the object under the given interface. + * Parameter `items' represents a vtable definition that may contain method declarations + * (using MethodVTableItem struct), property declarations (using PropertyVTableItem + * struct), signal declarations (using SignalVTableItem struct), or global interface + * flags (using InterfaceFlagsVTableItem struct). + * + * An interface can have any number of vtables attached to it. + * + * Consult manual pages for the underlying `sd_bus_add_object_vtable` function for more information. + * + * The method can be called at any time during object's lifetime. For each vtable an internal + * registration slot is created and its lifetime is tied to the lifetime of the Object instance. + * + * The function provides strong exception guarantee. The state of the object remains + * unmodified in face of an exception. + * + * @throws sdbus::Error in case of failure + */ + template < typename... VTableItems + , typename = std::enable_if_t<(is_one_of_variants_types> && ...)> > + void addVTable(InterfaceName interfaceName, VTableItems&&... items); + + /*! + * @brief Adds a declaration of methods, properties and signals of the object at a given interface + * + * @param[in] interfaceName Name of an interface the the vtable is registered for + * @param[in] vtable A list of individual descriptions in the form of VTable item instances + * + * This method is used to declare attributes for the object under the given interface. + * The `vtable' parameter may contain method declarations (using MethodVTableItem struct), + * property declarations (using PropertyVTableItem struct), signal declarations (using + * SignalVTableItem struct), or global interface flags (using InterfaceFlagsVTableItem struct). + * + * An interface can have any number of vtables attached to it. + * + * Consult manual pages for the underlying `sd_bus_add_object_vtable` function for more information. + * + * The method can be called at any time during object's lifetime. For each vtable an internal + * registration slot is created and its lifetime is tied to the lifetime of the Object instance. + * + * The function provides strong exception guarantee. The state of the object remains + * unmodified in face of an exception. + * + * @throws sdbus::Error in case of failure + */ + virtual void addVTable(InterfaceName interfaceName, std::vector vtable) = 0; + + /*! + * @brief Adds a declaration of methods, properties and signals of the object at a given interface + * + * @param[in] interfaceName Name of an interface the the vtable is registered for + * @param[in] vtable A list of individual descriptions in the form of VTable item instances + * + * This method is used to declare attributes for the object under the given interface. + * The `vtable' parameter may contain method declarations (using MethodVTableItem struct), + * property declarations (using PropertyVTableItem struct), signal declarations (using + * SignalVTableItem struct), or global interface flags (using InterfaceFlagsVTableItem struct). + * + * An interface can have any number of vtables attached to it. + * + * Consult manual pages for the underlying `sd_bus_add_object_vtable` function for more information. + * + * The method can be called at any time during object's lifetime. For each vtable an internal + * registration slot is created and is returned to the caller. The returned slot should be destroyed + * when the vtable is not needed anymore. This allows for "dynamic" object API where vtables + * can be added or removed by the user at runtime. + * + * The function provides strong exception guarantee. The state of the object remains + * unmodified in face of an exception. + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] virtual Slot addVTable(InterfaceName interfaceName, std::vector vtable, return_slot_t) = 0; + + /*! + * @brief Creates a signal message + * + * @param[in] interfaceName Name of an interface that the signal belongs under + * @param[in] signalName Name of the signal + * @return A signal message + * + * Serialize signal arguments into the returned message and emit the signal by passing + * the message with serialized arguments to the @c emitSignal function. + * Alternatively, use higher-level API @c emitSignal(const std::string& signalName) defined below. + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] virtual Signal createSignal(const InterfaceName& interfaceName, const SignalName& signalName) const = 0; + + /*! + * @brief Emits signal for this object path + * + * @param[in] message Signal message to be sent out + * + * Note: To avoid messing with messages, use higher-level API defined below. + * + * @throws sdbus::Error in case of failure + */ + virtual void emitSignal(const sdbus::Signal& message) = 0; + + protected: // Internal API for efficiency reasons used by high-level API helper classes + friend SignalEmitter; + + [[nodiscard]] virtual Signal createSignal(const char* interfaceName, const char* signalName) const = 0; + }; + + // Out-of-line member definitions + + inline SignalEmitter IObject::emitSignal(const SignalName& signalName) + { + return SignalEmitter(*this, signalName); + } + + inline SignalEmitter IObject::emitSignal(const std::string& signalName) + { + return SignalEmitter(*this, signalName.c_str()); + } + + inline SignalEmitter IObject::emitSignal(const char* signalName) + { + return SignalEmitter(*this, signalName); + } + + template + void IObject::addVTable(InterfaceName interfaceName, VTableItems&&... items) + { + addVTable(std::move(interfaceName), {std::forward(items)...}); + } + + template + VTableAdder IObject::addVTable(VTableItems&&... items) + { + return addVTable(std::vector{std::forward(items)...}); + } + + inline VTableAdder IObject::addVTable(std::vector vtable) + { + return VTableAdder(*this, std::move(vtable)); + } + + /*! + * @brief Creates instance representing a D-Bus object + * + * @param[in] connection D-Bus connection to be used by the object + * @param[in] objectPath Path of the D-Bus object + * @return Pointer to the object representation instance + * + * The provided connection will be used by the object to export methods, + * issue signals and provide properties. + * + * Creating a D-Bus object instance is (thread-)safe even upon the connection + * which is already running its I/O event loop. + * + * Code example: + * @code + * auto proxy = sdbus::createObject(connection, "/com/kistler/foo"); + * @endcode + */ + [[nodiscard]] std::unique_ptr createObject(sdbus::IConnection& connection, ObjectPath objectPath); + +} + +#include +#include + +#endif /* SDBUS_CXX_IOBJECT_H_ */ diff --git a/3rdParty/sdbus-cpp/include/sdbus-c++/IProxy.h b/3rdParty/sdbus-cpp/include/sdbus-c++/IProxy.h new file mode 100644 index 0000000000..5c21191a20 --- /dev/null +++ b/3rdParty/sdbus-cpp/include/sdbus-c++/IProxy.h @@ -0,0 +1,981 @@ +/** + * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland + * (C) 2016 - 2024 Stanislav Angelovic + * + * @file IProxy.h + * + * Created on: Nov 8, 2016 + * Project: sdbus-c++ + * Description: High-level D-Bus IPC C++ library based on sd-bus + * + * This file is part of sdbus-c++. + * + * sdbus-c++ is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * sdbus-c++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with sdbus-c++. If not, see . + */ + +#ifndef SDBUS_CXX_IPROXY_H_ +#define SDBUS_CXX_IPROXY_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include + +// Forward declarations +namespace sdbus { + class MethodCall; + class MethodReply; + class IConnection; + class ObjectPath; + class PendingAsyncCall; + namespace internal { + class Proxy; + } +} + +namespace sdbus { + + /********************************************//** + * @class IProxy + * + * IProxy class represents a proxy object, which is a convenient local object created + * to represent a remote D-Bus object in another process. + * The proxy enables calling methods on remote objects, receiving signals from remote + * objects, and getting/setting properties of remote objects. + * + * All IProxy member methods throw @c sdbus::Error in case of D-Bus or sdbus-c++ error. + * The IProxy class has been designed as thread-aware. However, the operation of + * creating and sending method calls (both synchronously and asynchronously) is + * thread-safe by design. + * + ***********************************************/ + class IProxy + { + public: // High-level, convenience API + virtual ~IProxy() = default; + + /*! + * @brief Calls method on the D-Bus object + * + * @param[in] methodName Name of the method + * @return A helper object for convenient invocation of the method + * + * This is a high-level, convenience way of calling D-Bus methods that abstracts + * from the D-Bus message concept. Method arguments/return value are automatically (de)serialized + * in a message and D-Bus signatures automatically deduced from the provided native arguments + * and return values. + * + * Example of use: + * @code + * int result, a = ..., b = ...; + * MethodName multiply{"multiply"}; + * object_.callMethod(multiply).onInterface(INTERFACE_NAME).withArguments(a, b).storeResultsTo(result); + * @endcode + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] MethodInvoker callMethod(const MethodName& methodName); + + /*! + * @copydoc IProxy::callMethod(const MethodName&) + */ + [[nodiscard]] MethodInvoker callMethod(const std::string& methodName); + + /*! + * @copydoc IProxy::callMethod(const MethodName&) + */ + [[nodiscard]] MethodInvoker callMethod(const char* methodName); + + /*! + * @brief Calls method on the D-Bus object asynchronously + * + * @param[in] methodName Name of the method + * @return A helper object for convenient asynchronous invocation of the method + * + * This is a high-level, convenience way of calling D-Bus methods that abstracts + * from the D-Bus message concept. Method arguments/return value are automatically (de)serialized + * in a message and D-Bus signatures automatically deduced from the provided native arguments + * and return values. + * + * Example of use: + * @code + * int a = ..., b = ...; + * MethodName multiply{"multiply"}; + * object_.callMethodAsync(multiply).onInterface(INTERFACE_NAME).withArguments(a, b).uponReplyInvoke([](int result) + * { + * std::cout << "Got result of multiplying " << a << " and " << b << ": " << result << std::endl; + * }); + * @endcode + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] AsyncMethodInvoker callMethodAsync(const MethodName& methodName); + + /*! + * @copydoc IProxy::callMethodAsync(const MethodName&) + */ + [[nodiscard]] AsyncMethodInvoker callMethodAsync(const std::string& methodName); + + /*! + * @copydoc IProxy::callMethodAsync(const MethodName&) + */ + [[nodiscard]] AsyncMethodInvoker callMethodAsync(const char* methodName); + + /*! + * @brief Registers signal handler for a given signal of the D-Bus object + * + * @param[in] signalName Name of the signal + * @return A helper object for convenient registration of the signal handler + * + * This is a high-level, convenience way of registering to D-Bus signals that abstracts + * from the D-Bus message concept. Signal arguments are automatically serialized + * in a message and D-Bus signatures automatically deduced from the parameters + * of the provided native signal callback. + * + * A signal can be subscribed to and unsubscribed from at any time during proxy + * lifetime. The subscription is active immediately after the call. + * + * Example of use: + * @code + * object_.uponSignal("stateChanged").onInterface("com.kistler.foo").call([this](int arg1, double arg2){ this->onStateChanged(arg1, arg2); }); + * sdbus::InterfaceName foo{"com.kistler.foo"}; + * sdbus::SignalName levelChanged{"levelChanged"}; + * object_.uponSignal(levelChanged).onInterface(foo).call([this](uint16_t level){ this->onLevelChanged(level); }); + * @endcode + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] SignalSubscriber uponSignal(const SignalName& signalName); + + /*! + * @copydoc IProxy::uponSignal(const SignalName&) + */ + [[nodiscard]] SignalSubscriber uponSignal(const std::string& signalName); + + /*! + * @copydoc IProxy::uponSignal(const SignalName&) + */ + [[nodiscard]] SignalSubscriber uponSignal(const char* signalName); + + /*! + * @brief Gets value of a property of the D-Bus object + * + * @param[in] propertyName Name of the property + * @return A helper object for convenient getting of property value + * + * This is a high-level, convenience way of reading D-Bus property values that abstracts + * from the D-Bus message concept. sdbus::Variant is returned which shall then be converted + * to the real property type (implicit conversion is supported). + * + * Example of use: + * @code + * int state = object.getProperty("state").onInterface("com.kistler.foo"); + * sdbus::InterfaceName foo{"com.kistler.foo"}; + * sdbus::PropertyName level{"level"}; + * int level = object.getProperty(level).onInterface(foo); + * @endcode + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] PropertyGetter getProperty(const PropertyName& propertyName); + + /*! + * @copydoc IProxy::getProperty(const PropertyName&) + */ + [[nodiscard]] PropertyGetter getProperty(std::string_view propertyName); + + /*! + * @brief Gets value of a property of the D-Bus object asynchronously + * + * @param[in] propertyName Name of the property + * @return A helper object for convenient asynchronous getting of property value + * + * This is a high-level, convenience way of reading D-Bus property values that abstracts + * from the D-Bus message concept. + * + * Example of use: + * @code + * std::future state = object.getPropertyAsync("state").onInterface("com.kistler.foo").getResultAsFuture(); + * auto callback = [](std::optional err, const sdbus::Variant& value){ ... }; + * object.getPropertyAsync("state").onInterface("com.kistler.foo").uponReplyInvoke(std::move(callback)); + * @endcode + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] AsyncPropertyGetter getPropertyAsync(const PropertyName& propertyName); + + /*! + * @copydoc IProxy::getPropertyAsync(const PropertyName&) + */ + [[nodiscard]] AsyncPropertyGetter getPropertyAsync(std::string_view propertyName); + + /*! + * @brief Sets value of a property of the D-Bus object + * + * @param[in] propertyName Name of the property + * @return A helper object for convenient setting of property value + * + * This is a high-level, convenience way of writing D-Bus property values that abstracts + * from the D-Bus message concept. + * Setting property value with NoReply flag is also supported. + * + * Example of use: + * @code + * int state = ...; + * object_.setProperty("state").onInterface("com.kistler.foo").toValue(state); + * // Or we can just send the set message call without waiting for the reply + * object_.setProperty("state").onInterface("com.kistler.foo").toValue(state, dont_expect_reply); + * @endcode + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] PropertySetter setProperty(const PropertyName& propertyName); + + /*! + * @copydoc IProxy::setProperty(const PropertyName&) + */ + [[nodiscard]] PropertySetter setProperty(std::string_view propertyName); + + /*! + * @brief Sets value of a property of the D-Bus object asynchronously + * + * @param[in] propertyName Name of the property + * @return A helper object for convenient asynchronous setting of property value + * + * This is a high-level, convenience way of writing D-Bus property values that abstracts + * from the D-Bus message concept. + * + * Example of use: + * @code + * int state = ...; + * // We can wait until the set operation finishes by waiting on the future + * std::future res = object_.setPropertyAsync("state").onInterface("com.kistler.foo").toValue(state).getResultAsFuture(); + * @endcode + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] AsyncPropertySetter setPropertyAsync(const PropertyName& propertyName); + + /*! + * @copydoc IProxy::setPropertyAsync(const PropertyName&) + */ + [[nodiscard]] AsyncPropertySetter setPropertyAsync(std::string_view propertyName); + + /*! + * @brief Gets values of all properties of the D-Bus object + * + * @return A helper object for convenient getting of properties' values + * + * This is a high-level, convenience way of reading D-Bus properties' values that abstracts + * from the D-Bus message concept. + * + * Example of use: + * @code + * auto props = object.getAllProperties().onInterface("com.kistler.foo"); + * @endcode + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] AllPropertiesGetter getAllProperties(); + + /*! + * @brief Gets values of all properties of the D-Bus object asynchronously + * + * @return A helper object for convenient asynchronous getting of properties' values + * + * This is a high-level, convenience way of reading D-Bus properties' values that abstracts + * from the D-Bus message concept. + * + * Example of use: + * @code + * auto callback = [](std::optional err, const std::map>& properties){ ... }; + * auto props = object.getAllPropertiesAsync().onInterface("com.kistler.foo").uponReplyInvoke(std::move(callback)); + * @endcode + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] AsyncAllPropertiesGetter getAllPropertiesAsync(); + + /*! + * @brief Provides D-Bus connection used by the proxy + * + * @return Reference to the D-Bus connection + */ + [[nodiscard]] virtual sdbus::IConnection& getConnection() const = 0; + + /*! + * @brief Returns object path of the underlying DBus object + */ + [[nodiscard]] virtual const ObjectPath& getObjectPath() const = 0; + + /*! + * @brief Provides access to the currently processed D-Bus message + * + * This method provides access to the currently processed incoming D-Bus message. + * "Currently processed" means that the registered callback handler(s) for that message + * are being invoked. This method is meant to be called from within a callback handler + * (e.g. from a D-Bus signal handler, or async method reply handler, etc.). In such a case it is + * guaranteed to return a valid D-Bus message instance for which the handler is called. + * If called from other contexts/threads, it may return a valid or invalid message, depending + * on whether a message was processed or not at the time of the call. + * + * @return Currently processed D-Bus message + */ + [[nodiscard]] virtual Message getCurrentlyProcessedMessage() const = 0; + + /*! + * @brief Unregisters proxy's signal handlers and stops receiving replies to pending async calls + * + * Unregistration is done automatically also in proxy's destructor. This method makes + * sense if, in the process of proxy removal, we need to make sure that callbacks + * are unregistered explicitly before the final destruction of the proxy instance. + * + * @throws sdbus::Error in case of failure + */ + virtual void unregister() = 0; + + public: // Lower-level, message-based API + /*! + * @brief Creates a method call message + * + * @param[in] interfaceName Name of an interface that provides a given method + * @param[in] methodName Name of the method + * @return A method call message + * + * Serialize method arguments into the returned message and invoke the method by passing + * the message with serialized arguments to the @c callMethod function. + * Alternatively, use higher-level API @c callMethod(const std::string& methodName) defined below. + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] virtual MethodCall createMethodCall(const InterfaceName& interfaceName, const MethodName& methodName) const = 0; + + /*! + * @brief Calls method on the remote D-Bus object + * + * @param[in] message Message representing a method call + * @return A method reply message + * + * The call does not block if the method call has dont-expect-reply flag set. In that case, + * the call returns immediately and the return value is an empty, invalid method reply. + * + * The call blocks otherwise, waiting for the remote peer to send back a reply or an error, + * or until the call times out. + * + * While blocking, other concurrent operations (in other threads) on the underlying bus + * connection are stalled until the call returns. This is not an issue in vast majority of + * (simple, single-threaded) applications. In asynchronous, multi-threaded designs involving + * shared bus connections, this may be an issue. It is advised to instead use an asynchronous + * callMethod() function overload, which does not block the bus connection, or do the synchronous + * call from another Proxy instance created just before the call and then destroyed (which is + * anyway quite a typical approach in D-Bus implementations). Such proxy instance must have + * its own bus connection. So-called light-weight proxies (ones running without an event loop thread) + * tag are designed for exactly that purpose. + * + * The default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout(). + * + * Note: To avoid messing with messages, use API on a higher level of abstraction defined below. + * + * @throws sdbus::Error in case of failure (also in case the remote function returned an error) + */ + virtual MethodReply callMethod(const MethodCall& message) = 0; + + /*! + * @brief Calls method on the remote D-Bus object + * + * @param[in] message Message representing a method call + * @param[in] timeout Method call timeout (in microseconds) + * @return A method reply message + * + * The call does not block if the method call has dont-expect-reply flag set. In that case, + * the call returns immediately and the return value is an empty, invalid method reply. + * + * The call blocks otherwise, waiting for the remote peer to send back a reply or an error, + * or until the call times out. + * + * While blocking, other concurrent operations (in other threads) on the underlying bus + * connection are stalled until the call returns. This is not an issue in vast majority of + * (simple, single-threaded) applications. In asynchronous, multi-threaded designs involving + * shared bus connections, this may be an issue. It is advised to instead use an asynchronous + * callMethod() function overload, which does not block the bus connection, or do the synchronous + * call from another Proxy instance created just before the call and then destroyed (which is + * anyway quite a typical approach in D-Bus implementations). Such proxy instance must have + * its own bus connection. So-called light-weight proxies (ones running without an event loop thread) + * tag are designed for exactly that purpose. + * + * If timeout is zero, the default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout(). + * + * Note: To avoid messing with messages, use API on a higher level of abstraction defined below. + * + * @throws sdbus::Error in case of failure (also in case the remote function returned an error) + */ + virtual MethodReply callMethod(const MethodCall& message, uint64_t timeout) = 0; + + /*! + * @copydoc IProxy::callMethod(const MethodCall&,uint64_t) + */ + template + MethodReply callMethod(const MethodCall& message, const std::chrono::duration<_Rep, _Period>& timeout); + + /*! + * @brief Calls method on the D-Bus object asynchronously + * + * @param[in] message Message representing an async method call + * @param[in] asyncReplyCallback Handler for the async reply + * @return Observing handle for the the pending asynchronous call + * + * This is a callback-based way of asynchronously calling a remote D-Bus method. + * + * The call itself is non-blocking. It doesn't wait for the reply. Once the reply arrives, + * the provided async reply handler will get invoked from the context of the bus + * connection I/O event loop thread. + * + * An non-owning, observing async call handle is returned that can be used to query call status or cancel the call. + * + * The default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout(). + * + * Note: To avoid messing with messages, use API on a higher level of abstraction defined below. + * + * @throws sdbus::Error in case of failure + */ + virtual PendingAsyncCall callMethodAsync(const MethodCall& message, async_reply_handler asyncReplyCallback) = 0; + + /*! + * @brief Calls method on the D-Bus object asynchronously + * + * @param[in] message Message representing an async method call + * @param[in] asyncReplyCallback Handler for the async reply + * @return RAII-style slot handle representing the ownership of the async call + * + * This is a callback-based way of asynchronously calling a remote D-Bus method. + * + * The call itself is non-blocking. It doesn't wait for the reply. Once the reply arrives, + * the provided async reply handler will get invoked from the context of the bus + * connection I/O event loop thread. + * + * A slot (an owning handle) is returned for the async call. Lifetime of the call is bound to the lifetime of the slot. + * The slot can be used to cancel the method call at a later time by simply destroying it. + * + * The default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout(). + * + * Note: To avoid messing with messages, use API on a higher level of abstraction defined below. + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] virtual Slot callMethodAsync( const MethodCall& message + , async_reply_handler asyncReplyCallback + , return_slot_t ) = 0; + + /*! + * @brief Calls method on the D-Bus object asynchronously, with custom timeout + * + * @param[in] message Message representing an async method call + * @param[in] asyncReplyCallback Handler for the async reply + * @param[in] timeout Method call timeout (in microseconds) + * @return Observing handle for the the pending asynchronous call + * + * This is a callback-based way of asynchronously calling a remote D-Bus method. + * + * The call itself is non-blocking. It doesn't wait for the reply. Once the reply arrives, + * the provided async reply handler will get invoked from the context of the bus + * connection I/O event loop thread. + * + * An non-owning, observing async call handle is returned that can be used to query call status or cancel the call. + * + * If timeout is zero, the default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout(). + * + * Note: To avoid messing with messages, use API on a higher level of abstraction defined below. + * + * @throws sdbus::Error in case of failure + */ + virtual PendingAsyncCall callMethodAsync( const MethodCall& message + , async_reply_handler asyncReplyCallback + , uint64_t timeout ) = 0; + + /*! + * @brief Calls method on the D-Bus object asynchronously, with custom timeout + * + * @param[in] message Message representing an async method call + * @param[in] asyncReplyCallback Handler for the async reply + * @param[in] timeout Method call timeout (in microseconds) + * @return RAII-style slot handle representing the ownership of the async call + * + * This is a callback-based way of asynchronously calling a remote D-Bus method. + * + * The call itself is non-blocking. It doesn't wait for the reply. Once the reply arrives, + * the provided async reply handler will get invoked from the context of the bus + * connection I/O event loop thread. + * + * A slot (an owning handle) is returned for the async call. Lifetime of the call is bound to the lifetime of the slot. + * The slot can be used to cancel the method call at a later time by simply destroying it. + * + * If timeout is zero, the default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout(). + * + * Note: To avoid messing with messages, use API on a higher level of abstraction defined below. + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] virtual Slot callMethodAsync( const MethodCall& message + , async_reply_handler asyncReplyCallback + , uint64_t timeout + , return_slot_t ) = 0; + + /*! + * @copydoc IProxy::callMethod(const MethodCall&,async_reply_handler,uint64_t) + */ + template + PendingAsyncCall callMethodAsync( const MethodCall& message + , async_reply_handler asyncReplyCallback + , const std::chrono::duration<_Rep, _Period>& timeout ); + + /*! + * @copydoc IProxy::callMethod(const MethodCall&,async_reply_handler,uint64_t,return_slot_t) + */ + template + [[nodiscard]] Slot callMethodAsync( const MethodCall& message + , async_reply_handler asyncReplyCallback + , const std::chrono::duration<_Rep, _Period>& timeout + , return_slot_t ); + + /*! + * @brief Calls method on the D-Bus object asynchronously + * + * @param[in] message Message representing an async method call + * @param[in] Tag denoting a std::future-based overload + * @return Future object providing access to the future method reply message + * + * This is a std::future-based way of asynchronously calling a remote D-Bus method. + * + * The call itself is non-blocking. It doesn't wait for the reply. Once the reply arrives, + * the provided future object will be set to contain the reply (or sdbus::Error + * in case the remote method threw an exception). + * + * The default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout(). + * + * Note: To avoid messing with messages, use higher-level API defined below. + * + * @throws sdbus::Error in case of failure + */ + virtual std::future callMethodAsync(const MethodCall& message, with_future_t) = 0; + + /*! + * @brief Calls method on the D-Bus object asynchronously, with custom timeout + * + * @param[in] message Message representing an async method call + * @param[in] timeout Method call timeout + * @param[in] Tag denoting a std::future-based overload + * @return Future object providing access to the future method reply message + * + * This is a std::future-based way of asynchronously calling a remote D-Bus method. + * + * The call itself is non-blocking. It doesn't wait for the reply. Once the reply arrives, + * the provided future object will be set to contain the reply (or sdbus::Error + * in case the remote method threw an exception, or the call timed out). + * + * If timeout is zero, the default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout(). + * + * Note: To avoid messing with messages, use higher-level API defined below. + * + * @throws sdbus::Error in case of failure + */ + virtual std::future callMethodAsync( const MethodCall& message + , uint64_t timeout + , with_future_t ) = 0; + + /*! + * @copydoc IProxy::callMethod(const MethodCall&,uint64_t,with_future_t) + */ + template + std::future callMethodAsync( const MethodCall& message + , const std::chrono::duration<_Rep, _Period>& timeout + , with_future_t ); + + /*! + * @brief Registers a handler for the desired signal emitted by the D-Bus object + * + * @param[in] interfaceName Name of an interface that the signal belongs to + * @param[in] signalName Name of the signal + * @param[in] signalHandler Callback that implements the body of the signal handler + * + * A signal can be subscribed to at any time during proxy lifetime. The subscription + * is active immediately after the call, and stays active for the entire lifetime + * of the Proxy object. + * + * To be able to unsubscribe from the signal at a later time, use the registerSignalHandler() + * overload with request_slot tag. + * + * @throws sdbus::Error in case of failure + */ + virtual void registerSignalHandler( const InterfaceName& interfaceName + , const SignalName& signalName + , signal_handler signalHandler ) = 0; + + /*! + * @brief Registers a handler for the desired signal emitted by the D-Bus object + * + * @param[in] interfaceName Name of an interface that the signal belongs to + * @param[in] signalName Name of the signal + * @param[in] signalHandler Callback that implements the body of the signal handler + * + * @return RAII-style slot handle representing the ownership of the subscription + * + * A signal can be subscribed to and unsubscribed from at any time during proxy + * lifetime. The subscription is active immediately after the call. The lifetime + * of the subscription is bound to the lifetime of the slot object. The subscription + * is unregistered by letting go of the slot object. + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] virtual Slot registerSignalHandler( const InterfaceName& interfaceName + , const SignalName& signalName + , signal_handler signalHandler + , return_slot_t ) = 0; + + protected: // Internal API for efficiency reasons used by high-level API helper classes + friend MethodInvoker; + friend AsyncMethodInvoker; + friend SignalSubscriber; + + [[nodiscard]] virtual MethodCall createMethodCall(const char* interfaceName, const char* methodName) const = 0; + virtual void registerSignalHandler( const char* interfaceName + , const char* signalName + , signal_handler signalHandler ) = 0; + [[nodiscard]] virtual Slot registerSignalHandler( const char* interfaceName + , const char* signalName + , signal_handler signalHandler + , return_slot_t ) = 0; + }; + + /********************************************//** + * @class PendingAsyncCall + * + * PendingAsyncCall represents a simple handle type to cancel the delivery + * of the asynchronous D-Bus call result to the application. + * + * The handle is lifetime-independent from the originating Proxy object. + * It's safe to call its methods even after the Proxy has gone. + * + ***********************************************/ + class PendingAsyncCall + { + public: + PendingAsyncCall() = default; + + /*! + * @brief Cancels the delivery of the pending asynchronous call result + * + * This function effectively removes the callback handler registered to the + * async D-Bus method call result delivery. Does nothing if the call was + * completed already, or if the originating Proxy object has gone meanwhile. + */ + void cancel(); + + /*! + * @brief Answers whether the asynchronous call is still pending + * + * @return True if the call is pending, false if the call has been fully completed + * + * Pending call in this context means a call whose results have not arrived, or + * have arrived and are currently being processed by the callback handler. + */ + [[nodiscard]] bool isPending() const; + + private: + friend internal::Proxy; + PendingAsyncCall(std::weak_ptr callInfo); + + private: + std::weak_ptr callInfo_; + }; + + // Out-of-line member definitions + + template + inline MethodReply IProxy::callMethod(const MethodCall& message, const std::chrono::duration<_Rep, _Period>& timeout) + { + auto microsecs = std::chrono::duration_cast(timeout); + return callMethod(message, microsecs.count()); + } + + template + inline PendingAsyncCall IProxy::callMethodAsync( const MethodCall& message + , async_reply_handler asyncReplyCallback + , const std::chrono::duration<_Rep, _Period>& timeout ) + { + auto microsecs = std::chrono::duration_cast(timeout); + return callMethodAsync(message, std::move(asyncReplyCallback), microsecs.count()); + } + + template + inline Slot IProxy::callMethodAsync( const MethodCall& message + , async_reply_handler asyncReplyCallback + , const std::chrono::duration<_Rep, _Period>& timeout + , return_slot_t ) + { + auto microsecs = std::chrono::duration_cast(timeout); + return callMethodAsync(message, std::move(asyncReplyCallback), microsecs.count(), return_slot); + } + + template + inline std::future IProxy::callMethodAsync( const MethodCall& message + , const std::chrono::duration<_Rep, _Period>& timeout + , with_future_t ) + { + auto microsecs = std::chrono::duration_cast(timeout); + return callMethodAsync(message, microsecs.count(), with_future); + } + + inline MethodInvoker IProxy::callMethod(const MethodName& methodName) + { + return MethodInvoker(*this, methodName); + } + + inline MethodInvoker IProxy::callMethod(const std::string& methodName) + { + return MethodInvoker(*this, methodName.c_str()); + } + + inline MethodInvoker IProxy::callMethod(const char* methodName) + { + return MethodInvoker(*this, methodName); + } + + inline AsyncMethodInvoker IProxy::callMethodAsync(const MethodName& methodName) + { + return AsyncMethodInvoker(*this, methodName); + } + + inline AsyncMethodInvoker IProxy::callMethodAsync(const std::string& methodName) + { + return AsyncMethodInvoker(*this, methodName.c_str()); + } + + inline AsyncMethodInvoker IProxy::callMethodAsync(const char* methodName) + { + return AsyncMethodInvoker(*this, methodName); + } + + inline SignalSubscriber IProxy::uponSignal(const SignalName& signalName) + { + return SignalSubscriber(*this, signalName); + } + + inline SignalSubscriber IProxy::uponSignal(const std::string& signalName) + { + return SignalSubscriber(*this, signalName.c_str()); + } + + inline SignalSubscriber IProxy::uponSignal(const char* signalName) + { + return SignalSubscriber(*this, signalName); + } + + inline PropertyGetter IProxy::getProperty(const PropertyName& propertyName) + { + return PropertyGetter(*this, propertyName); + } + + inline PropertyGetter IProxy::getProperty(std::string_view propertyName) + { + return PropertyGetter(*this, std::move(propertyName)); + } + + inline AsyncPropertyGetter IProxy::getPropertyAsync(const PropertyName& propertyName) + { + return AsyncPropertyGetter(*this, propertyName); + } + + inline AsyncPropertyGetter IProxy::getPropertyAsync(std::string_view propertyName) + { + return AsyncPropertyGetter(*this, std::move(propertyName)); + } + + inline PropertySetter IProxy::setProperty(const PropertyName& propertyName) + { + return PropertySetter(*this, propertyName); + } + + inline PropertySetter IProxy::setProperty(std::string_view propertyName) + { + return PropertySetter(*this, std::move(propertyName)); + } + + inline AsyncPropertySetter IProxy::setPropertyAsync(const PropertyName& propertyName) + { + return AsyncPropertySetter(*this, propertyName); + } + + inline AsyncPropertySetter IProxy::setPropertyAsync(std::string_view propertyName) + { + return AsyncPropertySetter(*this, std::move(propertyName)); + } + + inline AllPropertiesGetter IProxy::getAllProperties() + { + return AllPropertiesGetter(*this); + } + + inline AsyncAllPropertiesGetter IProxy::getAllPropertiesAsync() + { + return AsyncAllPropertiesGetter(*this); + } + + /*! + * @brief Creates a proxy object for a specific remote D-Bus object + * + * @param[in] connection D-Bus connection to be used by the proxy object + * @param[in] destination Bus name that provides the remote D-Bus object + * @param[in] objectPath Path of the remote D-Bus object + * @return Pointer to the proxy object instance + * + * The provided connection will be used by the proxy to issue calls against the object, + * and signals, if any, will be subscribed to on this connection. The caller still + * remains the owner of the connection (the proxy just keeps a reference to it), and + * should make sure that an I/O event loop is running on that connection, so the proxy + * may receive incoming signals and asynchronous method replies. + * + * The destination parameter may be an empty string (useful e.g. in case of direct + * D-Bus connections to a custom server bus). + * + * Code example: + * @code + * auto proxy = sdbus::createProxy(connection, "com.kistler.foo", "/com/kistler/foo"); + * @endcode + */ + [[nodiscard]] std::unique_ptr createProxy( sdbus::IConnection& connection + , ServiceName destination + , ObjectPath objectPath ); + + /*! + * @brief Creates a proxy object for a specific remote D-Bus object + * + * @param[in] connection D-Bus connection to be used by the proxy object + * @param[in] destination Bus name that provides the remote D-Bus object + * @param[in] objectPath Path of the remote D-Bus object + * @return Pointer to the object proxy instance + * + * The provided connection will be used by the proxy to issue calls against the object, + * and signals, if any, will be subscribed to on this connection. The Object proxy becomes + * an exclusive owner of this connection, and will automatically start a procesing loop + * upon that connection in a separate internal thread. Handlers for incoming signals and + * asynchronous method replies will be executed in the context of that thread. + * + * The destination parameter may be an empty string (useful e.g. in case of direct + * D-Bus connections to a custom server bus). + * + * Code example: + * @code + * auto proxy = sdbus::createProxy(std::move(connection), "com.kistler.foo", "/com/kistler/foo"); + * @endcode + */ + [[nodiscard]] std::unique_ptr createProxy( std::unique_ptr&& connection + , ServiceName destination + , ObjectPath objectPath ); + + /*! + * @brief Creates a light-weight proxy object for a specific remote D-Bus object + * + * @param[in] connection D-Bus connection to be used by the proxy object + * @param[in] destination Bus name that provides the remote D-Bus object + * @param[in] objectPath Path of the remote D-Bus object + * @return Pointer to the object proxy instance + * + * The provided connection will be used by the proxy to issue calls against the object. + * The Object proxy becomes an exclusive owner of this connection, but will not start + * an event loop thread on this connection. This is cheap construction and is suitable + * for short-lived proxies created just to execute simple synchronous D-Bus calls and + * then destroyed. Such blocking request-reply calls will work without an event loop + * (but signals, async calls, etc. won't). + * + * The destination parameter may be an empty string (useful e.g. in case of direct + * D-Bus connections to a custom server bus). + * + * Code example: + * @code + * auto proxy = sdbus::createProxy(std::move(connection), "com.kistler.foo", "/com/kistler/foo", sdbus::dont_run_event_loop_thread); + * @endcode + */ + [[nodiscard]] std::unique_ptr createProxy( std::unique_ptr&& connection + , ServiceName destination + , ObjectPath objectPath + , dont_run_event_loop_thread_t ); + + /*! + * @brief Creates a light-weight proxy object for a specific remote D-Bus object + * + * Does the same thing as createProxy(std::unique_ptr&&, ServiceName, ObjectPath, dont_run_event_loop_thread_t); + */ + [[nodiscard]] std::unique_ptr createLightWeightProxy( std::unique_ptr&& connection + , ServiceName destination + , ObjectPath objectPath ); + + /*! + * @brief Creates a proxy object for a specific remote D-Bus object + * + * @param[in] destination Bus name that provides the remote D-Bus object + * @param[in] objectPath Path of the remote D-Bus object + * @return Pointer to the object proxy instance + * + * No D-Bus connection is provided here, so the object proxy will create and manage + * his own connection, and will automatically start an event loop upon that connection + * in a separate internal thread. Handlers for incoming signals and asynchronous + * method replies will be executed in the context of that thread. + * + * Code example: + * @code + * auto proxy = sdbus::createProxy("com.kistler.foo", "/com/kistler/foo"); + * @endcode + */ + [[nodiscard]] std::unique_ptr createProxy( ServiceName destination + , ObjectPath objectPath ); + + /*! + * @brief Creates a light-weight proxy object for a specific remote D-Bus object + * + * @param[in] destination Bus name that provides the remote D-Bus object + * @param[in] objectPath Path of the remote D-Bus object + * @return Pointer to the object proxy instance + * + * No D-Bus connection is provided here, so the object proxy will create and manage + * his own connection, but it will not start an event loop thread. This is cheap + * construction and is suitable for short-lived proxies created just to execute simple + * synchronous D-Bus calls and then destroyed. Such blocking request-reply calls + * will work without an event loop (but signals, async calls, etc. won't). + * + * Code example: + * @code + * auto proxy = sdbus::createProxy("com.kistler.foo", "/com/kistler/foo", sdbus::dont_run_event_loop_thread ); + * @endcode + */ + [[nodiscard]] std::unique_ptr createProxy( ServiceName destination + , ObjectPath objectPath + , dont_run_event_loop_thread_t ); + + /*! + * @brief Creates a light-weight proxy object for a specific remote D-Bus object + * + * Does the same thing as createProxy(ServiceName, ObjectPath, dont_run_event_loop_thread_t); + */ + [[nodiscard]] std::unique_ptr createLightWeightProxy(ServiceName destination, ObjectPath objectPath); + +} + +#include + +#endif /* SDBUS_CXX_IPROXY_H_ */ diff --git a/3rdParty/sdbus-cpp/include/sdbus-c++/Message.h b/3rdParty/sdbus-cpp/include/sdbus-c++/Message.h new file mode 100644 index 0000000000..4be8d85898 --- /dev/null +++ b/3rdParty/sdbus-cpp/include/sdbus-c++/Message.h @@ -0,0 +1,782 @@ +/** + * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland + * (C) 2016 - 2024 Stanislav Angelovic + * + * @file Message.h + * + * Created on: Nov 9, 2016 + * Project: sdbus-c++ + * Description: High-level D-Bus IPC C++ library based on sd-bus + * + * This file is part of sdbus-c++. + * + * sdbus-c++ is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * sdbus-c++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with sdbus-c++. If not, see . + */ + +#ifndef SDBUS_CXX_MESSAGE_H_ +#define SDBUS_CXX_MESSAGE_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#ifdef __has_include +# if __has_include() +# include +# endif +#endif +#include +#include +#include +#include +#include +#include + +// Forward declarations +namespace sdbus { + class Variant; + class ObjectPath; + class Signature; + template class Struct; + class UnixFd; + class MethodReply; + namespace internal { + class IConnection; + } +} + +namespace sdbus { + + /********************************************//** + * @class Message + * + * Message represents a D-Bus message, which can be either method call message, + * method reply message, signal message, or a plain message. + * + * Serialization and deserialization functions are provided for types supported + * by D-Bus. + * + * You mostly don't need to work with this class directly if you use high-level + * APIs of @c IObject and @c IProxy. + * + ***********************************************/ + class [[nodiscard]] Message + { + public: + Message(const Message&) noexcept; + Message& operator=(const Message&) noexcept; + Message(Message&& other) noexcept; + Message& operator=(Message&& other) noexcept; + ~Message(); + + Message& operator<<(bool item); + Message& operator<<(int16_t item); + Message& operator<<(int32_t item); + Message& operator<<(int64_t item); + Message& operator<<(uint8_t item); + Message& operator<<(uint16_t item); + Message& operator<<(uint32_t item); + Message& operator<<(uint64_t item); + Message& operator<<(double item); + Message& operator<<(const char *item); + Message& operator<<(const std::string &item); + Message& operator<<(std::string_view item); + Message& operator<<(const Variant &item); + template + Message& operator<<(const std::variant& value); + Message& operator<<(const ObjectPath &item); + Message& operator<<(const Signature &item); + Message& operator<<(const UnixFd &item); + template + Message& operator<<(const std::vector<_Element, _Allocator>& items); + template + Message& operator<<(const std::array<_Element, _Size>& items); +#ifdef __cpp_lib_span + template + Message& operator<<(const std::span<_Element, _Extent>& items); +#endif + template >> + Message& operator<<(const _Enum& item); + template + Message& operator<<(const DictEntry<_Key, _Value>& value); + template + Message& operator<<(const std::map<_Key, _Value, _Compare, _Allocator>& items); + template + Message& operator<<(const std::unordered_map<_Key, _Value, _Hash, _KeyEqual, _Allocator>& items); + template + Message& operator<<(const Struct<_ValueTypes...>& item); + template + Message& operator<<(const std::tuple<_ValueTypes...>& item); + + Message& operator>>(bool& item); + Message& operator>>(int16_t& item); + Message& operator>>(int32_t& item); + Message& operator>>(int64_t& item); + Message& operator>>(uint8_t& item); + Message& operator>>(uint16_t& item); + Message& operator>>(uint32_t& item); + Message& operator>>(uint64_t& item); + Message& operator>>(double& item); + Message& operator>>(char*& item); + Message& operator>>(std::string &item); + Message& operator>>(Variant &item); + template + Message& operator>>(std::variant& value); + Message& operator>>(ObjectPath &item); + Message& operator>>(Signature &item); + Message& operator>>(UnixFd &item); + template + Message& operator>>(std::vector<_Element, _Allocator>& items); + template + Message& operator>>(std::array<_Element, _Size>& items); +#ifdef __cpp_lib_span + template + Message& operator>>(std::span<_Element, _Extent>& items); +#endif + template >> + Message& operator>>(_Enum& item); + template + Message& operator>>(DictEntry<_Key, _Value>& value); + template + Message& operator>>(std::map<_Key, _Value, _Compare, _Allocator>& items); + template + Message& operator>>(std::unordered_map<_Key, _Value, _Hash, _KeyEqual, _Allocator>& items); + template + Message& operator>>(Struct<_ValueTypes...>& item); + template + Message& operator>>(std::tuple<_ValueTypes...>& item); + + template + Message& openContainer(); + Message& openContainer(const char* signature); + Message& closeContainer(); + template + Message& openDictEntry(); + Message& openDictEntry(const char* signature); + Message& closeDictEntry(); + template + Message& openVariant(); + Message& openVariant(const char* signature); + Message& closeVariant(); + template + Message& openStruct(); + Message& openStruct(const char* signature); + Message& closeStruct(); + + template + Message& enterContainer(); + Message& enterContainer(const char* signature); + Message& exitContainer(); + template + Message& enterDictEntry(); + Message& enterDictEntry(const char* signature); + Message& exitDictEntry(); + template + Message& enterVariant(); + Message& enterVariant(const char* signature); + Message& exitVariant(); + template + Message& enterStruct(); + Message& enterStruct(const char* signature); + Message& exitStruct(); + + Message& appendArray(char type, const void *ptr, size_t size); + Message& readArray(char type, const void **ptr, size_t *size); + + template + Message& serializeDictionary(const _Callback& callback); + template + Message& serializeDictionary(const std::initializer_list>& dictEntries); + template + Message& deserializeDictionary(const _Callback& callback); + + explicit operator bool() const; + void clearFlags(); + + const char* getInterfaceName() const; + const char* getMemberName() const; + const char* getSender() const; + const char* getPath() const; + const char* getDestination() const; + uint64_t getCookie() const; + std::pair peekType() const; + bool isValid() const; + bool isEmpty() const; + bool isAtEnd(bool complete) const; + + void copyTo(Message& destination, bool complete) const; + void seal(); + void rewind(bool complete); + + pid_t getCredsPid() const; + uid_t getCredsUid() const; + uid_t getCredsEuid() const; + gid_t getCredsGid() const; + gid_t getCredsEgid() const; + std::vector getCredsSupplementaryGids() const; + std::string getSELinuxContext() const; + + class Factory; + + private: + template + void serializeArray(const _Array& items); + template + void deserializeArray(_Array& items); + template + void deserializeArrayFast(_Array& items); + template + void deserializeArrayFast(std::vector<_Element, _Allocator>& items); + template + void deserializeArraySlow(_Array& items); + template + void deserializeArraySlow(std::vector<_Element, _Allocator>& items); + + protected: + Message() = default; + explicit Message(internal::IConnection* connection) noexcept; + Message(void *msg, internal::IConnection* connection) noexcept; + Message(void *msg, internal::IConnection* connection, adopt_message_t) noexcept; + + friend Factory; + + protected: + void* msg_{}; + internal::IConnection* connection_{}; + mutable bool ok_{true}; + }; + + class MethodCall : public Message + { + using Message::Message; + friend Factory; + + public: + MethodCall() = default; + + MethodReply send(uint64_t timeout) const; + [[nodiscard]] Slot send(void* callback, void* userData, uint64_t timeout, return_slot_t) const; + + MethodReply createReply() const; + MethodReply createErrorReply(const sdbus::Error& error) const; + + void dontExpectReply(); + bool doesntExpectReply() const; + + protected: + MethodCall(void *msg, internal::IConnection* connection, adopt_message_t) noexcept; + + private: + MethodReply sendWithReply(uint64_t timeout = 0) const; + MethodReply sendWithNoReply() const; + }; + + class MethodReply : public Message + { + using Message::Message; + friend Factory; + + public: + MethodReply() = default; + void send() const; + uint64_t getReplyCookie() const; + }; + + class Signal : public Message + { + using Message::Message; + friend Factory; + + public: + Signal() = default; + void setDestination(const std::string& destination); + void setDestination(const char* destination); + void send() const; + }; + + class PropertySetCall : public Message + { + using Message::Message; + friend Factory; + + public: + PropertySetCall() = default; + }; + + class PropertyGetReply : public Message + { + using Message::Message; + friend Factory; + + public: + PropertyGetReply() = default; + }; + + // Represents any of the above message types, or just a message that serves as a container for data + class PlainMessage : public Message + { + using Message::Message; + friend Factory; + + public: + PlainMessage() = default; + }; + + PlainMessage createPlainMessage(); + + template + inline Message& Message::operator<<(const std::variant& value) + { + std::visit([this](const auto& inner) + { + openVariant(); + *this << inner; + closeVariant(); + }, value); + + return *this; + } + + template + inline Message& Message::operator<<(const std::vector<_Element, _Allocator>& items) + { + serializeArray(items); + + return *this; + } + + template + inline Message& Message::operator<<(const std::array<_Element, _Size>& items) + { + serializeArray(items); + + return *this; + } + +#ifdef __cpp_lib_span + template + inline Message& Message::operator<<(const std::span<_Element, _Extent>& items) + { + serializeArray(items); + + return *this; + } +#endif + + template + inline Message& Message::operator<<(const _Enum &item) + { + return operator<<(static_cast>(item)); + } + + template + inline void Message::serializeArray(const _Array& items) + { + using ElementType = typename _Array::value_type; + + // Use faster, one-step serialization of contiguous array of elements of trivial D-Bus types except bool, + // otherwise use step-by-step serialization of individual elements. + if constexpr (signature_of::is_trivial_dbus_type && !std::is_same_v) + { + constexpr auto signature = as_null_terminated(signature_of_v); + appendArray(*signature.data(), items.data(), items.size() * sizeof(ElementType)); + } + else + { + openContainer(); + + for (const auto& item : items) + *this << item; + + closeContainer(); + } + } + + template + inline Message& Message::operator<<(const DictEntry<_Key, _Value>& value) + { + openDictEntry<_Key, _Value>(); + *this << value.first; + *this << value.second; + closeDictEntry(); + + return *this; + } + + template + inline Message& Message::operator<<(const std::map<_Key, _Value, _Compare, _Allocator>& items) + { + serializeDictionary<_Key, _Value>([&items](Message& msg){ for (const auto& item : items) msg << item; }); + + return *this; + } + + template + inline Message& Message::operator<<(const std::unordered_map<_Key, _Value, _Hash, _KeyEqual, _Allocator>& items) + { + serializeDictionary<_Key, _Value>([&items](Message& msg){ for (const auto& item : items) msg << item; }); + + return *this; + } + + template + inline Message& Message::serializeDictionary(const std::initializer_list>& items) + { + serializeDictionary<_Key, _Value>([&](Message& msg){ for (const auto& item : items) msg << item; }); + + return *this; + } + + template + inline Message& Message::serializeDictionary(const _Callback& callback) + { + openContainer>(); + callback(*this); + closeContainer(); + + return *this; + } + + namespace detail + { + template + void serialize_pack(Message& msg, _Args&&... args) + { + (void)(msg << ... << args); + } + + template + void serialize_tuple( Message& msg + , const _Tuple& t + , std::index_sequence<_Is...>) + { + serialize_pack(msg, std::get<_Is>(t)...); + } + } + + template + inline Message& Message::operator<<(const Struct<_ValueTypes...>& item) + { + openStruct<_ValueTypes...>(); + detail::serialize_tuple(*this, item, std::index_sequence_for<_ValueTypes...>{}); + closeStruct(); + + return *this; + } + + template + inline Message& Message::operator<<(const std::tuple<_ValueTypes...>& item) + { + detail::serialize_tuple(*this, item, std::index_sequence_for<_ValueTypes...>{}); + return *this; + } + + namespace detail + { + template + bool deserialize_variant(Message& msg, std::variant<_Elements...>& value, const char* signature) + { + constexpr auto elemSignature = as_null_terminated(sdbus::signature_of_v<_Element>); + if (std::strcmp(signature, elemSignature.data()) != 0) + return false; + + _Element temp; + msg.enterVariant(signature); + msg >> temp; + msg.exitVariant(); + value = std::move(temp); + return true; + } + } + + template + inline Message& Message::operator>>(std::variant& value) + { + auto [type, contents] = peekType(); + bool result = (detail::deserialize_variant(*this, value, contents) || ...); + SDBUS_THROW_ERROR_IF(!result, "Failed to deserialize variant: signature did not match any of the variant types", EINVAL); + return *this; + } + + template + inline Message& Message::operator>>(std::vector<_Element, _Allocator>& items) + { + deserializeArray(items); + + return *this; + } + + template + inline Message& Message::operator>>(std::array<_Element, _Size>& items) + { + deserializeArray(items); + + return *this; + } + +#ifdef __cpp_lib_span + template + inline Message& Message::operator>>(std::span<_Element, _Extent>& items) + { + deserializeArray(items); + + return *this; + } +#endif + + template + inline Message& Message::operator>>(_Enum& item) + { + std::underlying_type_t<_Enum> val; + *this >> val; + item = static_cast<_Enum>(val); + return *this; + } + + template + inline void Message::deserializeArray(_Array& items) + { + using ElementType = typename _Array::value_type; + + // Use faster, one-step deserialization of contiguous array of elements of trivial D-Bus types except bool, + // otherwise use step-by-step deserialization of individual elements. + if constexpr (signature_of::is_trivial_dbus_type && !std::is_same_v) + { + deserializeArrayFast(items); + } + else + { + deserializeArraySlow(items); + } + } + + template + inline void Message::deserializeArrayFast(_Array& items) + { + using ElementType = typename _Array::value_type; + + size_t arraySize{}; + const ElementType* arrayPtr{}; + + constexpr auto signature = as_null_terminated(sdbus::signature_of_v); + readArray(*signature.data(), (const void**)&arrayPtr, &arraySize); + + size_t elementsInMsg = arraySize / sizeof(ElementType); + bool notEnoughSpace = items.size() < elementsInMsg; + SDBUS_THROW_ERROR_IF(notEnoughSpace, "Failed to deserialize array: not enough space in destination sequence", EINVAL); + + std::copy_n(arrayPtr, elementsInMsg, items.begin()); + } + + template + void Message::deserializeArrayFast(std::vector<_Element, _Allocator>& items) + { + size_t arraySize{}; + const _Element* arrayPtr{}; + + constexpr auto signature = as_null_terminated(sdbus::signature_of_v<_Element>); + readArray(*signature.data(), (const void**)&arrayPtr, &arraySize); + + items.insert(items.end(), arrayPtr, arrayPtr + (arraySize / sizeof(_Element))); + } + + template + inline void Message::deserializeArraySlow(_Array& items) + { + using ElementType = typename _Array::value_type; + + if(!enterContainer()) + return; + + for (auto& elem : items) + if (!(*this >> elem)) + break; // Keep the rest in the destination sequence untouched + + SDBUS_THROW_ERROR_IF(!isAtEnd(false), "Failed to deserialize array: not enough space in destination sequence", EINVAL); + + clearFlags(); + + exitContainer(); + } + + template + void Message::deserializeArraySlow(std::vector<_Element, _Allocator>& items) + { + if(!enterContainer<_Element>()) + return; + + while (true) + { + _Element elem; + if (*this >> elem) + items.emplace_back(std::move(elem)); + else + break; + } + + clearFlags(); + + exitContainer(); + } + + template + inline Message& Message::operator>>(DictEntry<_Key, _Value>& value) + { + if (!enterDictEntry<_Key, _Value>()) + return *this; + *this >> value.first >> value.second; + exitDictEntry(); + + return *this; + } + + template + inline Message& Message::operator>>(std::map<_Key, _Value, _Compare, _Allocator>& items) + { + deserializeDictionary<_Key, _Value>([&items](auto dictEntry){ items.insert(std::move(dictEntry)); }); + + return *this; + } + + template + inline Message& Message::operator>>(std::unordered_map<_Key, _Value, _Hash, _KeyEqual, _Allocator>& items) + { + deserializeDictionary<_Key, _Value>([&items](auto dictEntry){ items.insert(std::move(dictEntry)); }); + + return *this; + } + + template + inline Message& Message::deserializeDictionary(const _Callback& callback) + { + if (!enterContainer>()) + return *this; + + while (true) + { + DictEntry<_Key, _Value> dictEntry; + *this >> dictEntry; + if (!*this) + break; + callback(std::move(dictEntry)); + } + clearFlags(); + + exitContainer(); + + return *this; + } + + namespace detail + { + template + void deserialize_pack(Message& msg, _Args&... args) + { + (void)(msg >> ... >> args); + } + + template + void deserialize_tuple( Message& msg + , _Tuple& t + , std::index_sequence<_Is...> ) + { + deserialize_pack(msg, std::get<_Is>(t)...); + } + } + + template + inline Message& Message::operator>>(Struct<_ValueTypes...>& item) + { + if (!enterStruct<_ValueTypes...>()) + return *this; + + detail::deserialize_tuple(*this, item, std::index_sequence_for<_ValueTypes...>{}); + + exitStruct(); + + return *this; + } + + template + inline Message& Message::operator>>(std::tuple<_ValueTypes...>& item) + { + detail::deserialize_tuple(*this, item, std::index_sequence_for<_ValueTypes...>{}); + return *this; + } + + template + inline Message& Message::openContainer() + { + constexpr auto signature = as_null_terminated(signature_of_v<_ElementType>); + return openContainer(signature.data()); + } + + template + inline Message& Message::openDictEntry() + { + constexpr auto signature = as_null_terminated(signature_of_v>); + return openDictEntry(signature.data()); + } + + template + inline Message& Message::openVariant() + { + constexpr auto signature = as_null_terminated(signature_of_v<_ValueType>); + return openVariant(signature.data()); + } + + template + inline Message& Message::openStruct() + { + constexpr auto signature = as_null_terminated(signature_of_v>); + return openStruct(signature.data()); + } + + template + inline Message& Message::enterContainer() + { + constexpr auto signature = as_null_terminated(signature_of_v<_ElementType>); + return enterContainer(signature.data()); + } + + template + inline Message& Message::enterDictEntry() + { + constexpr auto signature = as_null_terminated(signature_of_v>); + return enterDictEntry(signature.data()); + } + + template + inline Message& Message::enterVariant() + { + constexpr auto signature = as_null_terminated(signature_of_v<_ValueType>); + return enterVariant(signature.data()); + } + + template + inline Message& Message::enterStruct() + { + constexpr auto signature = as_null_terminated(signature_of_v>); + return enterStruct(signature.data()); + } + +} + +#endif /* SDBUS_CXX_MESSAGE_H_ */ diff --git a/3rdParty/sdbus-cpp/include/sdbus-c++/MethodResult.h b/3rdParty/sdbus-cpp/include/sdbus-c++/MethodResult.h new file mode 100644 index 0000000000..d6f8540f01 --- /dev/null +++ b/3rdParty/sdbus-cpp/include/sdbus-c++/MethodResult.h @@ -0,0 +1,93 @@ +/** + * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland + * (C) 2016 - 2024 Stanislav Angelovic + * + * @file MethodResult.h + * + * Created on: Nov 8, 2016 + * Project: sdbus-c++ + * Description: High-level D-Bus IPC C++ library based on sd-bus + * + * This file is part of sdbus-c++. + * + * sdbus-c++ is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * sdbus-c++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with sdbus-c++. If not, see . + */ + +#ifndef SDBUS_CXX_METHODRESULT_H_ +#define SDBUS_CXX_METHODRESULT_H_ + +#include + +#include + +// Forward declarations +namespace sdbus { + class Error; +} + +namespace sdbus { + + /********************************************//** + * @class Result + * + * Represents result of an asynchronous server-side method. + * An instance is provided to the method and shall be set + * by the method to either method return value or an error. + * + ***********************************************/ + template + class Result + { + public: + Result() = default; + Result(MethodCall call); + + Result(const Result&) = delete; + Result& operator=(const Result&) = delete; + + Result(Result&& other) = default; + Result& operator=(Result&& other) = default; + + void returnResults(const _Results&... results) const; + void returnError(const Error& error) const; + + private: + MethodCall call_; + }; + + template + inline Result<_Results...>::Result(MethodCall call) + : call_(std::move(call)) + { + } + + template + inline void Result<_Results...>::returnResults(const _Results&... results) const + { + assert(call_.isValid()); + auto reply = call_.createReply(); + (void)(reply << ... << results); + reply.send(); + } + + template + inline void Result<_Results...>::returnError(const Error& error) const + { + auto reply = call_.createErrorReply(error); + reply.send(); + } + +} + +#endif /* SDBUS_CXX_METHODRESULT_H_ */ diff --git a/3rdParty/sdbus-cpp/include/sdbus-c++/ProxyInterfaces.h b/3rdParty/sdbus-cpp/include/sdbus-c++/ProxyInterfaces.h new file mode 100644 index 0000000000..72f25c6be4 --- /dev/null +++ b/3rdParty/sdbus-cpp/include/sdbus-c++/ProxyInterfaces.h @@ -0,0 +1,217 @@ +/** + * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland + * (C) 2016 - 2024 Stanislav Angelovic + * + * @file ProxyInterfaces.h + * + * Created on: Apr 8, 2019 + * Project: sdbus-c++ + * Description: High-level D-Bus IPC C++ library based on sd-bus + * + * This file is part of sdbus-c++. + * + * sdbus-c++ is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * sdbus-c++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with sdbus-c++. If not, see . + */ + +#ifndef SDBUS_CXX_PROXYINTERFACES_H_ +#define SDBUS_CXX_PROXYINTERFACES_H_ + +#include +#include +#include +#include + +// Forward declarations +namespace sdbus { + class IConnection; +} + +namespace sdbus { + + /********************************************//** + * @class ProxyObjectHolder + * + * ProxyObjectHolder is a helper that simply owns and provides + * access to a proxy object to other classes in the inheritance + * hierarchy of a native-like proxy object based on generated + * interface classes. + * + ***********************************************/ + class ProxyObjectHolder + { + protected: + ProxyObjectHolder(std::unique_ptr&& proxy) + : proxy_(std::move(proxy)) + { + assert(proxy_ != nullptr); + } + + const IProxy& getProxy() const + { + assert(proxy_ != nullptr); + return *proxy_; + } + + IProxy& getProxy() + { + assert(proxy_ != nullptr); + return *proxy_; + } + + private: + std::unique_ptr proxy_; + }; + + /********************************************//** + * @class ProxyInterfaces + * + * ProxyInterfaces is a helper template class that joins all interface classes of a remote + * D-Bus object generated by sdbus-c++-xml2cpp to be used on the client (the proxy) side, + * including some auxiliary classes. ProxyInterfaces is the class that native-like proxy + * implementation classes written by users should inherit from and implement all pure virtual + * methods. So the _Interfaces template parameter is a list of sdbus-c++-xml2cpp-generated + * proxy-side interface classes representing interfaces of the corresponding remote D-Bus object. + * + * In the final adaptor class inherited from ProxyInterfaces, one needs to make sure: + * 1. to call `registerProxy();` in the class constructor, and, conversely, + * 2. to call `unregisterProxy();` in the class destructor, + * so that the signals are subscribed to and unsubscribed from at a proper time. + * + ***********************************************/ + template + class ProxyInterfaces + : protected ProxyObjectHolder + , public _Interfaces... + { + public: + /*! + * @brief Creates native-like proxy object instance + * + * @param[in] destination Bus name that provides a D-Bus object + * @param[in] objectPath Path of the D-Bus object + * + * This constructor overload creates a proxy that manages its own D-Bus connection(s). + * For more information on its behavior, consult @ref createProxy(std::string,std::string) + */ + ProxyInterfaces(ServiceName destination, ObjectPath objectPath) + : ProxyObjectHolder(createProxy(std::move(destination), std::move(objectPath))) + , _Interfaces(getProxy())... + { + } + + /*! + * @brief Creates native-like proxy object instance + * + * @param[in] destination Bus name that provides a D-Bus object + * @param[in] objectPath Path of the D-Bus object + * + * This constructor overload creates a proxy that manages its own D-Bus connection(s). + * For more information on its behavior, consult @ref createProxy(std::string,std::string,sdbus::dont_run_event_loop_thread_t) + */ + ProxyInterfaces(ServiceName destination, ObjectPath objectPath, dont_run_event_loop_thread_t) + : ProxyObjectHolder(createProxy(std::move(destination), std::move(objectPath), dont_run_event_loop_thread)) + , _Interfaces(getProxy())... + { + } + + /*! + * @brief Creates native-like proxy object instance + * + * @param[in] connection D-Bus connection to be used by the proxy object + * @param[in] destination Bus name that provides a D-Bus object + * @param[in] objectPath Path of the D-Bus object + * + * The proxy created this way just references a D-Bus connection owned and managed by the user. + * For more information on its behavior, consult @ref createProxy(IConnection&,std::string,std::string) + */ + ProxyInterfaces(IConnection& connection, ServiceName destination, ObjectPath objectPath) + : ProxyObjectHolder(createProxy(connection, std::move(destination), std::move(objectPath))) + , _Interfaces(getProxy())... + { + } + + /*! + * @brief Creates native-like proxy object instance + * + * @param[in] connection D-Bus connection to be used by the proxy object + * @param[in] destination Bus name that provides a D-Bus object + * @param[in] objectPath Path of the D-Bus object + * + * The proxy created this way becomes an owner of the connection. + * For more information on its behavior, consult @ref createProxy(std::unique_ptr&&,std::string,std::string) + */ + ProxyInterfaces(std::unique_ptr&& connection, ServiceName destination, ObjectPath objectPath) + : ProxyObjectHolder(createProxy(std::move(connection), std::move(destination), std::move(objectPath))) + , _Interfaces(getProxy())... + { + } + + /*! + * @brief Creates native-like proxy object instance + * + * @param[in] connection D-Bus connection to be used by the proxy object + * @param[in] destination Bus name that provides a D-Bus object + * @param[in] objectPath Path of the D-Bus object + * + * The proxy created this way becomes an owner of the connection. + * For more information on its behavior, consult @ref createProxy(std::unique_ptr&&,std::string,std::string,sdbus::dont_run_event_loop_thread_t) + */ + ProxyInterfaces(std::unique_ptr&& connection, ServiceName destination, ObjectPath objectPath, dont_run_event_loop_thread_t) + : ProxyObjectHolder(createProxy(std::move(connection), std::move(destination), std::move(objectPath), dont_run_event_loop_thread)) + , _Interfaces(getProxy())... + { + } + + /*! + * @brief Registers handlers for D-Bus signals of the remote object + * + * This function must be called in the constructor of the final proxy class that implements ProxyInterfaces. + * + * See also @ref IProxy::registerSignalHandler() + */ + void registerProxy() + { + (_Interfaces::registerProxy(), ...); + } + + /*! + * @brief Unregisters the proxy so it no more receives signals and async call replies + * + * This function must be called in the destructor of the final proxy class that implements ProxyInterfaces. + * + * See underlying @ref IProxy::unregister() + */ + void unregisterProxy() + { + getProxy().unregister(); + } + + /*! + * @brief Returns reference to the underlying IProxy instance + */ + using ProxyObjectHolder::getProxy; + + protected: + using base_type = ProxyInterfaces; + + ProxyInterfaces(const ProxyInterfaces&) = delete; + ProxyInterfaces& operator=(const ProxyInterfaces&) = delete; + ProxyInterfaces(ProxyInterfaces&&) = delete; + ProxyInterfaces& operator=(ProxyInterfaces&&) = delete; + ~ProxyInterfaces() = default; + }; + +} + +#endif /* SDBUS_CXX_INTERFACES_H_ */ diff --git a/3rdParty/sdbus-cpp/include/sdbus-c++/StandardInterfaces.h b/3rdParty/sdbus-cpp/include/sdbus-c++/StandardInterfaces.h new file mode 100644 index 0000000000..83007b0ec9 --- /dev/null +++ b/3rdParty/sdbus-cpp/include/sdbus-c++/StandardInterfaces.h @@ -0,0 +1,530 @@ +/** + * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland + * (C) 2016 - 2024 Stanislav Angelovic + * + * @file StandardInterfaces.h + * + * Created on: Dec 13, 2016 + * Project: sdbus-c++ + * Description: High-level D-Bus IPC C++ library based on sd-bus + * + * This file is part of sdbus-c++. + * + * sdbus-c++ is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * sdbus-c++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with sdbus-c++. If not, see . + */ + +#ifndef SDBUS_CXX_STANDARDINTERFACES_H_ +#define SDBUS_CXX_STANDARDINTERFACES_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace sdbus { + + // Proxy for peer + class Peer_proxy + { + static inline const char* INTERFACE_NAME = "org.freedesktop.DBus.Peer"; + + protected: + Peer_proxy(sdbus::IProxy& proxy) + : m_proxy(proxy) + { + } + + Peer_proxy(const Peer_proxy&) = delete; + Peer_proxy& operator=(const Peer_proxy&) = delete; + Peer_proxy(Peer_proxy&&) = delete; + Peer_proxy& operator=(Peer_proxy&&) = delete; + + ~Peer_proxy() = default; + + void registerProxy() + { + } + + public: + void Ping() + { + m_proxy.callMethod("Ping").onInterface(INTERFACE_NAME); + } + + std::string GetMachineId() + { + std::string machineUUID; + m_proxy.callMethod("GetMachineId").onInterface(INTERFACE_NAME).storeResultsTo(machineUUID); + return machineUUID; + } + + private: + sdbus::IProxy& m_proxy; + }; + + // Proxy for introspection + class Introspectable_proxy + { + static inline const char* INTERFACE_NAME = "org.freedesktop.DBus.Introspectable"; + + protected: + Introspectable_proxy(sdbus::IProxy& proxy) + : m_proxy(proxy) + { + } + + Introspectable_proxy(const Introspectable_proxy&) = delete; + Introspectable_proxy& operator=(const Introspectable_proxy&) = delete; + Introspectable_proxy(Introspectable_proxy&&) = delete; + Introspectable_proxy& operator=(Introspectable_proxy&&) = delete; + + ~Introspectable_proxy() = default; + + void registerProxy() + { + } + + public: + std::string Introspect() + { + std::string xml; + m_proxy.callMethod("Introspect").onInterface(INTERFACE_NAME).storeResultsTo(xml); + return xml; + } + + private: + sdbus::IProxy& m_proxy; + }; + + // Proxy for properties + class Properties_proxy + { + static inline const char* INTERFACE_NAME = "org.freedesktop.DBus.Properties"; + + protected: + Properties_proxy(sdbus::IProxy& proxy) + : m_proxy(proxy) + { + } + + Properties_proxy(const Properties_proxy&) = delete; + Properties_proxy& operator=(const Properties_proxy&) = delete; + Properties_proxy(Properties_proxy&&) = delete; + Properties_proxy& operator=(Properties_proxy&&) = delete; + + ~Properties_proxy() = default; + + void registerProxy() + { + m_proxy + .uponSignal("PropertiesChanged") + .onInterface(INTERFACE_NAME) + .call([this]( const InterfaceName& interfaceName + , const std::map& changedProperties + , const std::vector& invalidatedProperties ) + { + this->onPropertiesChanged(interfaceName, changedProperties, invalidatedProperties); + }); + } + + virtual void onPropertiesChanged( const InterfaceName& interfaceName + , const std::map& changedProperties + , const std::vector& invalidatedProperties ) = 0; + + public: + sdbus::Variant Get(const InterfaceName& interfaceName, const PropertyName& propertyName) + { + return m_proxy.getProperty(propertyName).onInterface(interfaceName); + } + + sdbus::Variant Get(std::string_view interfaceName, std::string_view propertyName) + { + return m_proxy.getProperty(propertyName).onInterface(interfaceName); + } + + template + PendingAsyncCall GetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, _Function&& callback) + { + return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).uponReplyInvoke(std::forward<_Function>(callback)); + } + + template + [[nodiscard]] Slot GetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, _Function&& callback, return_slot_t) + { + return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).uponReplyInvoke(std::forward<_Function>(callback), return_slot); + } + + std::future GetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, with_future_t) + { + return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).getResultAsFuture(); + } + + template + PendingAsyncCall GetAsync(std::string_view interfaceName, std::string_view propertyName, _Function&& callback) + { + return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).uponReplyInvoke(std::forward<_Function>(callback)); + } + + template + [[nodiscard]] Slot GetAsync(std::string_view interfaceName, std::string_view propertyName, _Function&& callback, return_slot_t) + { + return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).uponReplyInvoke(std::forward<_Function>(callback), return_slot); + } + + std::future GetAsync(std::string_view interfaceName, std::string_view propertyName, with_future_t) + { + return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).getResultAsFuture(); + } + + void Set(const InterfaceName& interfaceName, const PropertyName& propertyName, const sdbus::Variant& value) + { + m_proxy.setProperty(propertyName).onInterface(interfaceName).toValue(value); + } + + void Set(std::string_view interfaceName, const std::string_view propertyName, const sdbus::Variant& value) + { + m_proxy.setProperty(propertyName).onInterface(interfaceName).toValue(value); + } + + void Set(const InterfaceName& interfaceName, const PropertyName& propertyName, const sdbus::Variant& value, dont_expect_reply_t) + { + m_proxy.setProperty(propertyName).onInterface(interfaceName).toValue(value, dont_expect_reply); + } + + void Set(std::string_view interfaceName, const std::string_view propertyName, const sdbus::Variant& value, dont_expect_reply_t) + { + m_proxy.setProperty(propertyName).onInterface(interfaceName).toValue(value, dont_expect_reply); + } + + template + PendingAsyncCall SetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, const sdbus::Variant& value, _Function&& callback) + { + return m_proxy.setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).uponReplyInvoke(std::forward<_Function>(callback)); + } + + template + [[nodiscard]] Slot SetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, const sdbus::Variant& value, _Function&& callback, return_slot_t) + { + return m_proxy.setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).uponReplyInvoke(std::forward<_Function>(callback), return_slot); + } + + std::future SetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, const sdbus::Variant& value, with_future_t) + { + return m_proxy.setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).getResultAsFuture(); + } + + template + PendingAsyncCall SetAsync(std::string_view interfaceName, std::string_view propertyName, const sdbus::Variant& value, _Function&& callback) + { + return m_proxy.setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).uponReplyInvoke(std::forward<_Function>(callback)); + } + + template + [[nodiscard]] Slot SetAsync(std::string_view interfaceName, std::string_view propertyName, const sdbus::Variant& value, _Function&& callback, return_slot_t) + { + return m_proxy.setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).uponReplyInvoke(std::forward<_Function>(callback), return_slot); + } + + std::future SetAsync(std::string_view interfaceName, std::string_view propertyName, const sdbus::Variant& value, with_future_t) + { + return m_proxy.setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).getResultAsFuture(); + } + + std::map GetAll(const InterfaceName& interfaceName) + { + return m_proxy.getAllProperties().onInterface(interfaceName); + } + + std::map GetAll(std::string_view interfaceName) + { + return m_proxy.getAllProperties().onInterface(interfaceName); + } + + template + PendingAsyncCall GetAllAsync(const InterfaceName& interfaceName, _Function&& callback) + { + return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).uponReplyInvoke(std::forward<_Function>(callback)); + } + + template + [[nodiscard]] Slot GetAllAsync(const InterfaceName& interfaceName, _Function&& callback, return_slot_t) + { + return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).uponReplyInvoke(std::forward<_Function>(callback), return_slot); + } + + std::future> GetAllAsync(const InterfaceName& interfaceName, with_future_t) + { + return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).getResultAsFuture(); + } + + template + PendingAsyncCall GetAllAsync(std::string_view interfaceName, _Function&& callback) + { + return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).uponReplyInvoke(std::forward<_Function>(callback)); + } + + template + [[nodiscard]] Slot GetAllAsync(std::string_view interfaceName, _Function&& callback, return_slot_t) + { + return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).uponReplyInvoke(std::forward<_Function>(callback), return_slot); + } + + std::future> GetAllAsync(std::string_view interfaceName, with_future_t) + { + return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).getResultAsFuture(); + } + + private: + sdbus::IProxy& m_proxy; + }; + + // Proxy for object manager + class ObjectManager_proxy + { + static inline const char* INTERFACE_NAME = "org.freedesktop.DBus.ObjectManager"; + + protected: + ObjectManager_proxy(sdbus::IProxy& proxy) + : m_proxy(proxy) + { + } + + ObjectManager_proxy(const ObjectManager_proxy&) = delete; + ObjectManager_proxy& operator=(const ObjectManager_proxy&) = delete; + ObjectManager_proxy(ObjectManager_proxy&&) = delete; + ObjectManager_proxy& operator=(ObjectManager_proxy&&) = delete; + + ~ObjectManager_proxy() = default; + + void registerProxy() + { + m_proxy + .uponSignal("InterfacesAdded") + .onInterface(INTERFACE_NAME) + .call([this]( const sdbus::ObjectPath& objectPath + , const std::map>& interfacesAndProperties ) + { + this->onInterfacesAdded(objectPath, interfacesAndProperties); + }); + + m_proxy + .uponSignal("InterfacesRemoved") + .onInterface(INTERFACE_NAME) + .call([this]( const sdbus::ObjectPath& objectPath + , const std::vector& interfaces ) + { + this->onInterfacesRemoved(objectPath, interfaces); + }); + } + + virtual void onInterfacesAdded( const sdbus::ObjectPath& objectPath + , const std::map>& interfacesAndProperties) = 0; + virtual void onInterfacesRemoved( const sdbus::ObjectPath& objectPath + , const std::vector& interfaces) = 0; + + public: + std::map>> GetManagedObjects() + { + std::map>> objectsInterfacesAndProperties; + m_proxy.callMethod("GetManagedObjects").onInterface(INTERFACE_NAME).storeResultsTo(objectsInterfacesAndProperties); + return objectsInterfacesAndProperties; + } + + template + PendingAsyncCall GetManagedObjectsAsync(_Function&& callback) + { + return m_proxy.callMethodAsync("GetManagedObjects").onInterface(INTERFACE_NAME).uponReplyInvoke(std::forward<_Function>(callback)); + } + + template + [[nodiscard]] Slot GetManagedObjectsAsync(_Function&& callback, return_slot_t) + { + return m_proxy.callMethodAsync("GetManagedObjects").onInterface(INTERFACE_NAME).uponReplyInvoke(std::forward<_Function>(callback), return_slot); + } + + std::future>>> GetManagedObjectsAsync(with_future_t) + { + return m_proxy.callMethodAsync("GetManagedObjects").onInterface(INTERFACE_NAME).getResultAsFuture>>>(); + } + + private: + sdbus::IProxy& m_proxy; + }; + + // Adaptors for the above-listed standard D-Bus interfaces are not necessary because the functionality + // is provided by underlying libsystemd implementation. The exception is Properties_adaptor, + // ObjectManager_adaptor and ManagedObject_adaptor, which provide convenience functionality to emit signals. + + // Adaptor for properties + class Properties_adaptor + { + static inline const char* INTERFACE_NAME = "org.freedesktop.DBus.Properties"; + + protected: + Properties_adaptor(sdbus::IObject& object) : m_object(object) + { + } + + Properties_adaptor(const Properties_adaptor&) = delete; + Properties_adaptor& operator=(const Properties_adaptor&) = delete; + Properties_adaptor(Properties_adaptor&&) = delete; + Properties_adaptor& operator=(Properties_adaptor&&) = delete; + + ~Properties_adaptor() = default; + + void registerAdaptor() + { + } + + public: + void emitPropertiesChangedSignal(const InterfaceName& interfaceName, const std::vector& properties) + { + m_object.emitPropertiesChangedSignal(interfaceName, properties); + } + + void emitPropertiesChangedSignal(const char* interfaceName, const std::vector& properties) + { + m_object.emitPropertiesChangedSignal(interfaceName, properties); + } + + void emitPropertiesChangedSignal(const InterfaceName& interfaceName) + { + m_object.emitPropertiesChangedSignal(interfaceName); + } + + void emitPropertiesChangedSignal(const char* interfaceName) + { + m_object.emitPropertiesChangedSignal(interfaceName); + } + + private: + sdbus::IObject& m_object; + }; + + /*! + * @brief Object Manager Convenience Adaptor + * + * Adding this class as _Interfaces.. template parameter of class AdaptorInterfaces + * implements the *GetManagedObjects()* method of the [org.freedesktop.DBus.ObjectManager.GetManagedObjects](https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager) + * interface. + * + * Note that there can be multiple object managers in a path hierarchy. InterfacesAdded/InterfacesRemoved + * signals are sent from the closest object manager at either the same path or the closest parent path of an object. + */ + class ObjectManager_adaptor + { + static inline const char* INTERFACE_NAME = "org.freedesktop.DBus.ObjectManager"; + + protected: + explicit ObjectManager_adaptor(sdbus::IObject& object) : m_object(object) + { + } + + ObjectManager_adaptor(const ObjectManager_adaptor&) = delete; + ObjectManager_adaptor& operator=(const ObjectManager_adaptor&) = delete; + ObjectManager_adaptor(ObjectManager_adaptor&&) = delete; + ObjectManager_adaptor& operator=(ObjectManager_adaptor&&) = delete; + + ~ObjectManager_adaptor() = default; + + void registerAdaptor() + { + m_object.addObjectManager(); + } + + private: + sdbus::IObject& m_object; + }; + + /*! + * @brief Managed Object Convenience Adaptor + * + * Adding this class as _Interfaces.. template parameter of class AdaptorInterfaces + * will extend the resulting object adaptor with emitInterfacesAddedSignal()/emitInterfacesRemovedSignal() + * according to org.freedesktop.DBus.ObjectManager.InterfacesAdded/.InterfacesRemoved. + * + * Note that objects which implement this adaptor require an object manager (e.g via ObjectManager_adaptor) to be + * instantiated on one of it's parent object paths or the same path. InterfacesAdded/InterfacesRemoved + * signals are sent from the closest object manager at either the same path or the closest parent path of an object. + */ + class ManagedObject_adaptor + { + protected: + explicit ManagedObject_adaptor(sdbus::IObject& object) + : m_object(object) + { + } + + ManagedObject_adaptor(const ManagedObject_adaptor&) = delete; + ManagedObject_adaptor& operator=(const ManagedObject_adaptor&) = delete; + ManagedObject_adaptor(ManagedObject_adaptor&&) = delete; + ManagedObject_adaptor& operator=(ManagedObject_adaptor&&) = delete; + + ~ManagedObject_adaptor() = default; + + void registerAdaptor() + { + } + + public: + /*! + * @brief Emits InterfacesAdded signal for this object path + * + * See IObject::emitInterfacesAddedSignal(). + */ + void emitInterfacesAddedSignal() + { + m_object.emitInterfacesAddedSignal(); + } + + /*! + * @brief Emits InterfacesAdded signal for this object path + * + * See IObject::emitInterfacesAddedSignal(). + */ + void emitInterfacesAddedSignal(const std::vector& interfaces) + { + m_object.emitInterfacesAddedSignal(interfaces); + } + + /*! + * @brief Emits InterfacesRemoved signal for this object path + * + * See IObject::emitInterfacesRemovedSignal(). + */ + void emitInterfacesRemovedSignal() + { + m_object.emitInterfacesRemovedSignal(); + } + + /*! + * @brief Emits InterfacesRemoved signal for this object path + * + * See IObject::emitInterfacesRemovedSignal(). + */ + void emitInterfacesRemovedSignal(const std::vector& interfaces) + { + m_object.emitInterfacesRemovedSignal(interfaces); + } + + private: + sdbus::IObject& m_object; + }; + +} + +#endif /* SDBUS_CXX_STANDARDINTERFACES_H_ */ diff --git a/3rdParty/sdbus-cpp/include/sdbus-c++/TypeTraits.h b/3rdParty/sdbus-cpp/include/sdbus-c++/TypeTraits.h new file mode 100644 index 0000000000..e9143fa640 --- /dev/null +++ b/3rdParty/sdbus-cpp/include/sdbus-c++/TypeTraits.h @@ -0,0 +1,702 @@ +/** + * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland + * (C) 2016 - 2024 Stanislav Angelovic + * + * @file TypeTraits.h + * + * Created on: Nov 9, 2016 + * Project: sdbus-c++ + * Description: High-level D-Bus IPC C++ library based on sd-bus + * + * This file is part of sdbus-c++. + * + * sdbus-c++ is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * sdbus-c++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with sdbus-c++. If not, see . + */ + +#ifndef SDBUS_CXX_TYPETRAITS_H_ +#define SDBUS_CXX_TYPETRAITS_H_ + +#include + +#include +#include +#include +#include +#include +#include +#ifdef __has_include +# if __has_include() +# include +# endif +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +// Forward declarations +namespace sdbus { + class Variant; + template class Struct; + class ObjectPath; + class Signature; + class UnixFd; + template using DictEntry = std::pair<_T1, _T2>; + class BusName; + class InterfaceName; + class MemberName; + class MethodCall; + class MethodReply; + class Signal; + class Message; + class PropertySetCall; + class PropertyGetReply; + template class Result; + class Error; + template struct signature_of; +} + +namespace sdbus { + + // Callbacks from sdbus-c++ + using method_callback = std::function; + using async_reply_handler = std::function error)>; + using signal_handler = std::function; + using message_handler = std::function; + using property_set_callback = std::function; + using property_get_callback = std::function; + + // Type-erased RAII-style handle to callbacks/subscriptions registered to sdbus-c++ + using Slot = std::unique_ptr>; + + // Tag specifying that an owning handle (so-called slot) of the logical resource shall be provided to the client + struct return_slot_t { explicit return_slot_t() = default; }; + inline constexpr return_slot_t return_slot{}; + // Tag specifying that the library shall own the slot resulting from the call of the function (so-called floating slot) + struct floating_slot_t { explicit floating_slot_t() = default; }; + inline constexpr floating_slot_t floating_slot{}; + // Tag denoting the assumption that the caller has already obtained message ownership + struct adopt_message_t { explicit adopt_message_t() = default; }; + inline constexpr adopt_message_t adopt_message{}; + // Tag denoting the assumption that the caller has already obtained fd ownership + struct adopt_fd_t { explicit adopt_fd_t() = default; }; + inline constexpr adopt_fd_t adopt_fd{}; + // Tag specifying that the proxy shall not run an event loop thread on its D-Bus connection. + // Such proxies are typically created to carry out a simple synchronous D-Bus call(s) and then are destroyed. + struct dont_run_event_loop_thread_t { explicit dont_run_event_loop_thread_t() = default; }; + inline constexpr dont_run_event_loop_thread_t dont_run_event_loop_thread{}; + // Tag denoting an asynchronous call that returns std::future as a handle + struct with_future_t { explicit with_future_t() = default; }; + inline constexpr with_future_t with_future{}; + // Tag denoting a call where the reply shouldn't be waited for + struct dont_expect_reply_t { explicit dont_expect_reply_t() = default; }; + inline constexpr dont_expect_reply_t dont_expect_reply{}; + // Tag denoting that the variant shall embed the other variant as its value, instead of creating a copy + struct embed_variant_t { explicit embed_variant_t() = default; }; + inline constexpr embed_variant_t embed_variant{}; + + // Helper for static assert + template constexpr bool always_false = false; + + // Helper operator+ for concatenation of `std::array`s + template + constexpr std::array<_T, _N1 + _N2> operator+(std::array<_T, _N1> lhs, std::array<_T, _N2> rhs); + + // Template specializations for getting D-Bus signatures from C++ types + template + constexpr auto signature_of_v = signature_of<_T>::value; + + template + struct signature_of + { + static constexpr bool is_valid = false; + static constexpr bool is_trivial_dbus_type = false; + + static constexpr void* value = [] + { + // See using-sdbus-c++.md, section "Extending sdbus-c++ type system", + // on how to teach sdbus-c++ about your custom types + static_assert(always_false<_T>, "Unsupported D-Bus type (specialize `signature_of` for your custom types)"); + }; + }; + + template + struct signature_of : signature_of<_T> + {}; + + template + struct signature_of : signature_of<_T> + {}; + + template + struct signature_of : signature_of<_T> + {}; + + template + struct signature_of<_T&> : signature_of<_T> + {}; + + template + struct signature_of<_T&&> : signature_of<_T> + {}; + + template <> + struct signature_of + { + static constexpr std::array value{}; + static constexpr bool is_valid = true; + static constexpr bool is_trivial_dbus_type = false; + }; + + template <> + struct signature_of + { + static constexpr std::array value{'b'}; + static constexpr bool is_valid = true; + static constexpr bool is_trivial_dbus_type = true; + }; + + template <> + struct signature_of + { + static constexpr std::array value{'y'}; + static constexpr bool is_valid = true; + static constexpr bool is_trivial_dbus_type = true; + }; + + template <> + struct signature_of + { + static constexpr std::array value{'n'}; + static constexpr bool is_valid = true; + static constexpr bool is_trivial_dbus_type = true; + }; + + template <> + struct signature_of + { + static constexpr std::array value{'q'}; + static constexpr bool is_valid = true; + static constexpr bool is_trivial_dbus_type = true; + }; + + template <> + struct signature_of + { + static constexpr std::array value{'i'}; + static constexpr bool is_valid = true; + static constexpr bool is_trivial_dbus_type = true; + }; + + template <> + struct signature_of + { + static constexpr std::array value{'u'}; + static constexpr bool is_valid = true; + static constexpr bool is_trivial_dbus_type = true; + }; + + template <> + struct signature_of + { + static constexpr std::array value{'x'}; + static constexpr bool is_valid = true; + static constexpr bool is_trivial_dbus_type = true; + }; + + template <> + struct signature_of + { + static constexpr std::array value{'t'}; + static constexpr bool is_valid = true; + static constexpr bool is_trivial_dbus_type = true; + }; + + template <> + struct signature_of + { + static constexpr std::array value{'d'}; + static constexpr bool is_valid = true; + static constexpr bool is_trivial_dbus_type = true; + }; + + template <> + struct signature_of + { + static constexpr std::array value{'s'}; + static constexpr bool is_valid = true; + static constexpr bool is_trivial_dbus_type = false; + }; + + template <> + struct signature_of : signature_of + {}; + + template <> + struct signature_of : signature_of + {}; + + template <> + struct signature_of : signature_of + {}; + + template + struct signature_of : signature_of + {}; + + template + struct signature_of : signature_of + {}; + + template <> + struct signature_of : signature_of + {}; + + template <> + struct signature_of : signature_of + {}; + + template <> + struct signature_of : signature_of + {}; + + template + struct signature_of> + { + static constexpr std::array contents = (signature_of_v<_ValueTypes> + ...); + static constexpr std::array value = std::array{'('} + contents + std::array{')'}; + static constexpr char type_value{'r'}; /* Not actually used in signatures on D-Bus, see specs */ + static constexpr bool is_valid = true; + static constexpr bool is_trivial_dbus_type = false; + }; + + template <> + struct signature_of + { + static constexpr std::array value{'v'}; + static constexpr bool is_valid = true; + static constexpr bool is_trivial_dbus_type = false; + }; + + template + struct signature_of> : signature_of + {}; + + template <> + struct signature_of + { + static constexpr std::array value{'o'}; + static constexpr bool is_valid = true; + static constexpr bool is_trivial_dbus_type = false; + }; + + template <> + struct signature_of + { + static constexpr std::array value{'g'}; + static constexpr bool is_valid = true; + static constexpr bool is_trivial_dbus_type = false; + }; + + template <> + struct signature_of + { + static constexpr std::array value{'h'}; + static constexpr bool is_valid = true; + static constexpr bool is_trivial_dbus_type = false; + }; + + template + struct signature_of> + { + static constexpr std::array value = std::array{'{'} + signature_of_v> + std::array{'}'}; + static constexpr char type_value{'e'}; /* Not actually used in signatures on D-Bus, see specs */ + static constexpr bool is_valid = true; + static constexpr bool is_trivial_dbus_type = false; + }; + + template + struct signature_of> + { + static constexpr std::array value = std::array{'a'} + signature_of_v<_Element>; + static constexpr bool is_valid = true; + static constexpr bool is_trivial_dbus_type = false; + }; + + template + struct signature_of> : signature_of> + { + }; + +#ifdef __cpp_lib_span + template + struct signature_of> : signature_of> + { + }; +#endif + + template // is_const_v and is_volatile_v to avoid ambiguity conflicts with const and volatile specializations of signature_of + struct signature_of<_Enum, typename std::enable_if_t && !std::is_const_v<_Enum> && !std::is_volatile_v<_Enum>>> + : signature_of> + {}; + + template + struct signature_of> + { + static constexpr std::array value = std::array{'a'} + signature_of_v>; + static constexpr bool is_valid = true; + static constexpr bool is_trivial_dbus_type = false; + }; + + template + struct signature_of> + : signature_of> + { + }; + + template + struct signature_of> // A simple concatenation of signatures of _Types + { + static constexpr std::array value = (std::array{} + ... + signature_of_v<_Types>); + static constexpr bool is_valid = false; + static constexpr bool is_trivial_dbus_type = false; + }; + + // To simplify conversions of arrays to C strings + template + constexpr auto as_null_terminated(std::array<_T, _N> arr) + { + return arr + std::array<_T, 1>{0}; + } + + // Function traits implementation inspired by (c) kennytm, + // https://github.com/kennytm/utils/blob/master/traits.hpp + template + struct function_traits : function_traits + {}; + + template + struct function_traits : function_traits<_Type> + {}; + + template + struct function_traits<_Type&> : function_traits<_Type> + {}; + + template + struct function_traits_base + { + typedef _ReturnType result_type; + typedef std::tuple<_Args...> arguments_type; + typedef std::tuple...> decayed_arguments_type; + + typedef _ReturnType function_type(_Args...); + + static constexpr std::size_t arity = sizeof...(_Args); + +// template +// struct arg; +// +// template +// struct arg<_Idx, std::enable_if_t<(_Idx < arity)>> +// { +// typedef std::tuple_element_t<_Idx, arguments_type> type; +// }; +// +// template +// struct arg<_Idx, std::enable_if_t> +// { +// typedef void type; +// }; + + template + struct arg + { + typedef std::tuple_element_t<_Idx, std::tuple<_Args...>> type; + }; + + template + using arg_t = typename arg<_Idx>::type; + }; + + template + struct function_traits<_ReturnType(_Args...)> : function_traits_base<_ReturnType, _Args...> + { + static constexpr bool is_async = false; + static constexpr bool has_error_param = false; + }; + + template + struct function_traits, _Args...)> : function_traits_base + { + static constexpr bool has_error_param = true; + }; + + template + struct function_traits, _Args...)> : function_traits_base, _Args...> + { + static constexpr bool is_async = true; + using async_result_t = Result<_Results...>; + }; + + template + struct function_traits&&, _Args...)> : function_traits_base, _Args...> + { + static constexpr bool is_async = true; + using async_result_t = Result<_Results...>; + }; + + template + struct function_traits<_ReturnType(*)(_Args...)> : function_traits<_ReturnType(_Args...)> + {}; + + template + struct function_traits<_ReturnType(_ClassType::*)(_Args...)> : function_traits<_ReturnType(_Args...)> + { + typedef _ClassType& owner_type; + }; + + template + struct function_traits<_ReturnType(_ClassType::*)(_Args...) const> : function_traits<_ReturnType(_Args...)> + { + typedef const _ClassType& owner_type; + }; + + template + struct function_traits<_ReturnType(_ClassType::*)(_Args...) volatile> : function_traits<_ReturnType(_Args...)> + { + typedef volatile _ClassType& owner_type; + }; + + template + struct function_traits<_ReturnType(_ClassType::*)(_Args...) const volatile> : function_traits<_ReturnType(_Args...)> + { + typedef const volatile _ClassType& owner_type; + }; + + template + struct function_traits> : function_traits + {}; + + template + constexpr auto is_async_method_v = function_traits<_Function>::is_async; + + template + constexpr auto has_error_param_v = function_traits<_Function>::has_error_param; + + template + using function_arguments_t = typename function_traits<_FunctionType>::arguments_type; + + template + using function_argument_t = typename function_traits<_FunctionType>::template arg_t<_Idx>; + + template + constexpr auto function_argument_count_v = function_traits<_FunctionType>::arity; + + template + using function_result_t = typename function_traits<_FunctionType>::result_type; + + template + struct tuple_of_function_input_arg_types + { + typedef typename function_traits<_Function>::decayed_arguments_type type; + }; + + template + using tuple_of_function_input_arg_types_t = typename tuple_of_function_input_arg_types<_Function>::type; + + template + struct tuple_of_function_output_arg_types + { + typedef typename function_traits<_Function>::result_type type; + }; + + template + using tuple_of_function_output_arg_types_t = typename tuple_of_function_output_arg_types<_Function>::type; + + template + struct signature_of_function_input_arguments : signature_of> + { + static std::string value_as_string() + { + constexpr auto signature = as_null_terminated(signature_of_v>); + return signature.data(); + } + }; + + template + inline auto signature_of_function_input_arguments_v = signature_of_function_input_arguments<_Function>::value_as_string(); + + template + struct signature_of_function_output_arguments : signature_of> + { + static std::string value_as_string() + { + constexpr auto signature = as_null_terminated(signature_of_v>); + return signature.data(); + } + }; + + template + inline auto signature_of_function_output_arguments_v = signature_of_function_output_arguments<_Function>::value_as_string(); + + // std::future stuff for return values of async calls + template struct future_return + { + typedef std::tuple<_Args...> type; + }; + + template <> struct future_return<> + { + typedef void type; + }; + + template struct future_return<_Type> + { + typedef _Type type; + }; + + template + using future_return_t = typename future_return<_Args...>::type; + + // Credit: Piotr Skotnicki (https://stackoverflow.com/a/57639506) + template + constexpr bool is_one_of_variants_types = false; + + template + constexpr bool is_one_of_variants_types, _QueriedType> + = (std::is_same_v<_QueriedType, _VariantTypes> || ...); + + // Wrapper (tag) denoting we want to serialize user-defined struct + // into a D-Bus message as a dictionary of strings to variants. + template + struct as_dictionary + { + explicit as_dictionary(const _Struct& s) : m_struct(s) {} + const _Struct& m_struct; + }; + + template + const _Type& as_dictionary_if_struct(const _Type& object) + { + return object; // identity in case _Type is not struct (user-defined structs shall provide an overload) + } + + // By default, the dict-as-struct deserialization strategy is strict. + // Strict means that every key of the deserialized dictionary must have its counterpart member in the struct, otherwise an exception is thrown. + // Relaxed means that a key that does not have a matching struct member is silently ignored. + // The behavior can be overridden for user-defined struct by specializing this variable template. + template + constexpr auto strict_dict_as_struct_deserialization_v = true; + + // By default, the struct-as-dict serialization strategy is single-level only (as opposed to nested). + // Single-level means that only the specific struct is serialized as a dictionary, serializing members that are structs always as structs. + // Nested means that the struct *and* its members that are structs are all serialized as a dictionary. If nested strategy is also + // defined for the nested struct, then the same behavior applies for that struct, recursively. + // The behavior can be overridden for user-defined struct by specializing this variable template. + template + constexpr auto nested_struct_as_dict_serialization_v = false; + + namespace detail + { + template + constexpr decltype(auto) apply_impl( _Function&& f + , Result<_Args...>&& r + , _Tuple&& t + , std::index_sequence<_I...> ) + { + return std::forward<_Function>(f)(std::move(r), std::get<_I>(std::forward<_Tuple>(t))...); + } + + template + decltype(auto) apply_impl( _Function&& f + , std::optional e + , _Tuple&& t + , std::index_sequence<_I...> ) + { + return std::forward<_Function>(f)(std::move(e), std::get<_I>(std::forward<_Tuple>(t))...); + } + + // For non-void returning functions, apply_impl simply returns function return value (a tuple of values). + // For void-returning functions, apply_impl returns an empty tuple. + template + constexpr decltype(auto) apply_impl( _Function&& f + , _Tuple&& t + , std::index_sequence<_I...> ) + { + if constexpr (!std::is_void_v>) + return std::forward<_Function>(f)(std::get<_I>(std::forward<_Tuple>(t))...); + else + return std::forward<_Function>(f)(std::get<_I>(std::forward<_Tuple>(t))...), std::tuple<>{}; + } + } + + // Convert tuple `t' of values into a list of arguments + // and invoke function `f' with those arguments. + template + constexpr decltype(auto) apply(_Function&& f, _Tuple&& t) + { + return detail::apply_impl( std::forward<_Function>(f) + , std::forward<_Tuple>(t) + , std::make_index_sequence>::value>{} ); + } + + // Convert tuple `t' of values into a list of arguments + // and invoke function `f' with those arguments. + template + constexpr decltype(auto) apply(_Function&& f, Result<_Args...>&& r, _Tuple&& t) + { + return detail::apply_impl( std::forward<_Function>(f) + , std::move(r) + , std::forward<_Tuple>(t) + , std::make_index_sequence>::value>{} ); + } + + // Convert tuple `t' of values into a list of arguments + // and invoke function `f' with those arguments. + template + decltype(auto) apply(_Function&& f, std::optional e, _Tuple&& t) + { + return detail::apply_impl( std::forward<_Function>(f) + , std::move(e) + , std::forward<_Tuple>(t) + , std::make_index_sequence>::value>{} ); + } + + // Convenient concatenation of arrays + template + constexpr std::array<_T, _N1 + _N2> operator+(std::array<_T, _N1> lhs, std::array<_T, _N2> rhs) + { + std::array<_T, _N1 + _N2> result{}; + std::size_t index = 0; + + for (auto& el : lhs) { + result[index] = std::move(el); + ++index; + } + for (auto& el : rhs) { + result[index] = std::move(el); + ++index; + } + + return result; + } + +} + +#endif /* SDBUS_CXX_TYPETRAITS_H_ */ diff --git a/3rdParty/sdbus-cpp/include/sdbus-c++/Types.h b/3rdParty/sdbus-cpp/include/sdbus-c++/Types.h new file mode 100644 index 0000000000..b75e3faa1f --- /dev/null +++ b/3rdParty/sdbus-cpp/include/sdbus-c++/Types.h @@ -0,0 +1,597 @@ +/** + * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland + * (C) 2016 - 2024 Stanislav Angelovic + * + * @file Types.h + * + * Created on: Nov 23, 2016 + * Project: sdbus-c++ + * Description: High-level D-Bus IPC C++ library based on sd-bus + * + * This file is part of sdbus-c++. + * + * sdbus-c++ is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * sdbus-c++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with sdbus-c++. If not, see . + */ + +#ifndef SDBUS_CXX_TYPES_H_ +#define SDBUS_CXX_TYPES_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace sdbus { + + /********************************************//** + * @class Variant + * + * Variant can hold value of any D-Bus-supported type. + * + * Note: Even though thread-aware, Variant objects are not thread-safe. + * Some const methods are conceptually const, but not physically const, + * thus are not thread-safe. This is by design: normally, clients + * should process a single Variant object in a single thread at a time. + * Otherwise they need to take care of synchronization by themselves. + * + ***********************************************/ + class Variant + { + public: + Variant(); + + template + explicit Variant(const _ValueType& value) : Variant() + { + msg_.openVariant<_ValueType>(); + msg_ << value; + msg_.closeVariant(); + msg_.seal(); + } + + Variant(const Variant& value, embed_variant_t) : Variant() + { + msg_.openVariant(); + msg_ << value; + msg_.closeVariant(); + msg_.seal(); + } + + template + explicit Variant(const as_dictionary<_Struct>& value) : Variant() + { + msg_.openVariant>(); + msg_ << as_dictionary(value.m_struct); + msg_.closeVariant(); + msg_.seal(); + } + + template + Variant(const std::variant<_Elements...>& value) + : Variant() + { + msg_ << value; + msg_.seal(); + } + + template + _ValueType get() const + { + msg_.rewind(false); + + msg_.enterVariant<_ValueType>(); + _ValueType val; + msg_ >> val; + msg_.exitVariant(); + return val; + } + + // Only allow conversion operator for true D-Bus type representations in C++ + template ::is_valid>> + explicit operator _ValueType() const + { + return get<_ValueType>(); + } + + template + operator std::variant<_Elements...>() const + { + std::variant<_Elements...> result; + msg_.rewind(false); + msg_ >> result; + return result; + } + + template + bool containsValueOfType() const + { + constexpr auto signature = as_null_terminated(signature_of_v<_Type>); + return std::strcmp(signature.data(), peekValueType()) == 0; + } + + bool isEmpty() const; + + void serializeTo(Message& msg) const; + void deserializeFrom(Message& msg); + const char* peekValueType() const; + + private: + mutable PlainMessage msg_{}; + }; + + /********************************************//** + * @class Struct + * + * Representation of struct D-Bus type + * + * Struct implements tuple protocol, i.e. it's a tuple-like class. + * It can be used with std::get<>(), std::tuple_element, + * std::tuple_size and in structured bindings. + * + ***********************************************/ + template + class Struct + : public std::tuple<_ValueTypes...> + { + public: + using std::tuple<_ValueTypes...>::tuple; + + Struct() = default; + + explicit Struct(const std::tuple<_ValueTypes...>& t) + : std::tuple<_ValueTypes...>(t) + { + } + + template + auto& get() + { + return std::get<_I>(*this); + } + + template + const auto& get() const + { + return std::get<_I>(*this); + } + }; + + template + Struct(_Elements...) -> Struct<_Elements...>; + + template + constexpr Struct...> + make_struct(_Elements&&... args) + { + typedef Struct...> result_type; + return result_type(std::forward<_Elements>(args)...); + } + + /********************************************//** + * @class ObjectPath + * + * Strong type representing the D-Bus object path + * + ***********************************************/ + class ObjectPath : public std::string + { + public: + ObjectPath() = default; + explicit ObjectPath(std::string value) + : std::string(std::move(value)) + {} + explicit ObjectPath(const char* value) + : std::string(value) + {} + + using std::string::operator=; + }; + + /********************************************//** + * @class BusName + * + * Strong type representing the D-Bus bus/service/connection name + * + ***********************************************/ + class BusName : public std::string + { + public: + BusName() = default; + explicit BusName(std::string value) + : std::string(std::move(value)) + {} + explicit BusName(const char* value) + : std::string(value) + {} + + using std::string::operator=; + }; + + using ServiceName = BusName; + using ConnectionName = BusName; + + /********************************************//** + * @class InterfaceName + * + * Strong type representing the D-Bus interface name + * + ***********************************************/ + class InterfaceName : public std::string + { + public: + InterfaceName() = default; + explicit InterfaceName(std::string value) + : std::string(std::move(value)) + {} + explicit InterfaceName(const char* value) + : std::string(value) + {} + + using std::string::operator=; + }; + + /********************************************//** + * @class MemberName + * + * Strong type representing the D-Bus member name + * + ***********************************************/ + class MemberName : public std::string + { + public: + MemberName() = default; + explicit MemberName(std::string value) + : std::string(std::move(value)) + {} + explicit MemberName(const char* value) + : std::string(value) + {} + + using std::string::operator=; + }; + + using MethodName = MemberName; + using SignalName = MemberName; + using PropertyName = MemberName; + + /********************************************//** + * @class Signature + * + * Strong type representing the D-Bus object path + * + ***********************************************/ + class Signature : public std::string + { + public: + Signature() = default; + explicit Signature(std::string value) + : std::string(std::move(value)) + {} + explicit Signature(const char* value) + : std::string(value) + {} + + using std::string::operator=; + }; + + /********************************************//** + * @struct UnixFd + * + * UnixFd is a representation of file descriptor D-Bus type that owns + * the underlying fd, provides access to it, and closes the fd when + * the UnixFd goes out of scope. + * + * UnixFd can be default constructed (owning invalid fd), or constructed from + * an explicitly provided fd by either duplicating or adopting that fd as-is. + * + ***********************************************/ + class UnixFd + { + public: + UnixFd() = default; + + explicit UnixFd(int fd) + : fd_(checkedDup(fd)) + { + } + + UnixFd(int fd, adopt_fd_t) + : fd_(fd) + { + } + + UnixFd(const UnixFd& other) + { + *this = other; + } + + UnixFd& operator=(const UnixFd& other) + { + if (this == &other) + { + return *this; + } + close(); + fd_ = checkedDup(other.fd_); + return *this; + } + + UnixFd(UnixFd&& other) + { + *this = std::move(other); + } + + UnixFd& operator=(UnixFd&& other) + { + if (this == &other) + { + return *this; + } + close(); + fd_ = std::exchange(other.fd_, -1); + return *this; + } + + ~UnixFd() + { + close(); + } + + [[nodiscard]] int get() const + { + return fd_; + } + + void reset(int fd = -1) + { + *this = UnixFd{fd}; + } + + void reset(int fd, adopt_fd_t) + { + *this = UnixFd{fd, adopt_fd}; + } + + int release() + { + return std::exchange(fd_, -1); + } + + [[nodiscard]] bool isValid() const + { + return fd_ >= 0; + } + + private: + /// Closes file descriptor, but does not set it to -1. + void close(); + + /// Returns negative argument unchanged. + /// Otherwise, call ::dup and throw on failure. + static int checkedDup(int fd); + + int fd_ = -1; + }; + + /********************************************//** + * @typedef DictEntry + * + * DictEntry is implemented as std::pair, a standard + * value_type in STL(-like) associative containers. + * + ***********************************************/ + template + using DictEntry = std::pair<_T1, _T2>; + +} + +// Making sdbus::Struct implement the tuple-protocol, i.e. be a tuple-like type +template +struct std::tuple_element<_I, sdbus::Struct<_ValueTypes...>> + : std::tuple_element<_I, std::tuple<_ValueTypes...>> +{}; +template +struct std::tuple_size> + : std::tuple_size> +{}; + +/********************************************//** + * @name SDBUSCPP_REGISTER_STRUCT + * + * A convenient way to extend sdbus-c++ type system with user-defined structs. + * + * The macro teaches sdbus-c++ to recognize the user-defined struct + * as a valid C++ representation of a D-Bus Struct type, and enables + * clients to use their struct conveniently instead of the (too + * generic and less expressive) `sdbus::Struct<...>` in sdbus-c++ API. + * + * It also enables to serialize a user-defined struct as an a{sv} dictionary, + * and to deserialize an a{sv} dictionary into the user-defined struct. + * + * The first argument is the struct type name and the remaining arguments + * are names of struct members. Members must be of types supported by + * sdbus-c++ (or of user-defined types that sdbus-c++ was taught to support). + * Members can be other structs (nesting is supported). + * The macro must be placed in the global namespace. + * + * For example, given the user-defined struct `ABC`: + * + * namespace foo { + * struct ABC + * { + * int number; + * std::string name; + * std::vector data; + * }; + * } + * + * one can teach sdbus-c++ about the contents of this struct simply with: + * + * SDBUSCPP_REGISTER_STRUCT(foo::ABC, number, name, data); + * + * Up to 16 struct members are supported by the macro. + * + ***********************************************/ +#define SDBUSCPP_REGISTER_STRUCT(STRUCT, ...) \ + namespace sdbus { \ + static_assert(SDBUSCPP_PP_NARG(__VA_ARGS__) <= 16, \ + "Not more than 16 struct members are supported, please open an issue if you need more"); \ + \ + template <> \ + struct signature_of \ + : signature_of> \ + {}; \ + \ + inline auto as_dictionary_if_struct(const STRUCT& object) \ + { \ + return as_dictionary(object); \ + } \ + \ + inline sdbus::Message& operator<<(sdbus::Message& msg, const STRUCT& items) \ + { \ + return msg << sdbus::Struct{std::forward_as_tuple(SDBUSCPP_STRUCT_MEMBERS(items, __VA_ARGS__))}; \ + } \ + \ + inline Message& operator<<(Message& msg, const as_dictionary& s) \ + { \ + if constexpr (!nested_struct_as_dict_serialization_v) \ + return msg.serializeDictionary({SDBUSCPP_STRUCT_MEMBERS_AS_DICT_ENTRIES(s.m_struct, __VA_ARGS__)}); \ + else \ + return msg.serializeDictionary({SDBUSCPP_STRUCT_MEMBERS_AS_NESTED_DICT_ENTRIES(s.m_struct, __VA_ARGS__)}); \ + } \ + \ + inline Message& operator>>(Message& msg, STRUCT& s) \ + { \ + /* First, try to deserialize as a struct */ \ + if (msg.peekType().first == signature_of::type_value) \ + { \ + Struct sdbusStruct{std::forward_as_tuple(SDBUSCPP_STRUCT_MEMBERS(s, __VA_ARGS__))}; \ + return msg >> sdbusStruct; \ + } \ + \ + /* Otherwise try to deserialize as a dictionary of strings to variants */ \ + \ + return msg.deserializeDictionary([&s](const auto& dictEntry) \ + { \ + const std::string& key = dictEntry.first; /* Intentionally not using structured bindings */ \ + const Variant& value = dictEntry.second; \ + \ + using namespace std::string_literals; \ + /* This also handles members which are structs serialized as dict of strings to variants, recursively */ \ + SDBUSCPP_FIND_AND_DESERIALIZE_STRUCT_MEMBERS(s, __VA_ARGS__) \ + SDBUS_THROW_ERROR_IF( strict_dict_as_struct_deserialization_v \ + , ("Failed to deserialize struct from a dictionary: could not find field '"s += key) += "' in struct 'my::Struct'" \ + , EINVAL ); \ + }); \ + } \ + } \ + /**/ + +/********************************************//** + * @name SDBUSCPP_ENABLE_RELAXED_DICT2STRUCT_DESERIALIZATION + * + * Enables relaxed deserialization of an a{sv} dictionary into a user-defined struct STRUCT. + * + * The default (strict) deserialization mode is that if there are entries in the dictionary + * which do not have a corresponding field in the struct, an exception is thrown. + * In the relaxed mode, such entries are silently skipped. + * + * The macro can only be used in combination with SDBUSCPP_REGISTER_STRUCT macro, + * and must be placed before SDBUSCPP_REGISTER_STRUCT macro. + ***********************************************/ +#define SDBUSCPP_ENABLE_RELAXED_DICT2STRUCT_DESERIALIZATION(STRUCT) \ + template <> \ + constexpr auto sdbus::strict_dict_as_struct_deserialization_v = false; \ + /**/ + +/********************************************//** + * @name SDBUSCPP_ENABLE_NESTED_STRUCT2DICT_SERIALIZATION + * + * Enables nested serialization of user-defined struct STRUCT as an a{sv} dictionary. + * + * By default, STRUCT fields which are structs themselves are serialized as D-Bus structs. + * This macro tells sdbus-c++ to also serialize nested structs, in a recursive fashion, + * as a{sv} dictionaries. + * + * The macro can only be used in combination with SDBUSCPP_REGISTER_STRUCT macro, + * and must be placed before SDBUSCPP_REGISTER_STRUCT macro. + ***********************************************/ +#define SDBUSCPP_ENABLE_NESTED_STRUCT2DICT_SERIALIZATION(STRUCT) \ + template <> \ + constexpr auto sdbus::nested_struct_as_dict_serialization_v = true \ + /**/ + +/*! + * @cond SDBUSCPP_INTERNAL + * + * Internal helper preprocessor facilities + */ +#define SDBUSCPP_STRUCT_MEMBERS(STRUCT, ...) \ + SDBUSCPP_PP_CAT(SDBUSCPP_FOR_EACH_, SDBUSCPP_PP_NARG(__VA_ARGS__))(SDBUSCPP_STRUCT_MEMBER, SDBUSCPP_PP_COMMA, STRUCT, __VA_ARGS__) \ + /**/ +#define SDBUSCPP_STRUCT_MEMBER(STRUCT, MEMBER) STRUCT.MEMBER + +#define SDBUSCPP_STRUCT_MEMBER_TYPES(STRUCT, ...) \ + SDBUSCPP_PP_CAT(SDBUSCPP_FOR_EACH_, SDBUSCPP_PP_NARG(__VA_ARGS__))(SDBUSCPP_STRUCT_MEMBER_TYPE, SDBUSCPP_PP_COMMA, STRUCT, __VA_ARGS__) \ + /**/ +#define SDBUSCPP_STRUCT_MEMBER_TYPE(STRUCT, MEMBER) decltype(STRUCT::MEMBER) + +#define SDBUSCPP_STRUCT_MEMBERS_AS_DICT_ENTRIES(STRUCT, ...) \ + SDBUSCPP_PP_CAT(SDBUSCPP_FOR_EACH_, SDBUSCPP_PP_NARG(__VA_ARGS__))(SDBUSCPP_STRUCT_MEMBER_AS_DICT_ENTRY, SDBUSCPP_PP_COMMA, STRUCT, __VA_ARGS__) \ + /**/ +#define SDBUSCPP_STRUCT_MEMBER_AS_DICT_ENTRY(STRUCT, MEMBER) {#MEMBER, Variant{STRUCT.MEMBER}} + +#define SDBUSCPP_STRUCT_MEMBERS_AS_NESTED_DICT_ENTRIES(STRUCT, ...) \ + SDBUSCPP_PP_CAT(SDBUSCPP_FOR_EACH_, SDBUSCPP_PP_NARG(__VA_ARGS__))(SDBUSCPP_STRUCT_MEMBER_AS_NESTED_DICT_ENTRY, SDBUSCPP_PP_COMMA, STRUCT, __VA_ARGS__) \ + /**/ +#define SDBUSCPP_STRUCT_MEMBER_AS_NESTED_DICT_ENTRY(STRUCT, MEMBER) {#MEMBER, Variant{as_dictionary_if_struct(STRUCT.MEMBER)}} + +#define SDBUSCPP_FIND_AND_DESERIALIZE_STRUCT_MEMBERS(STRUCT, ...) \ + SDBUSCPP_PP_CAT(SDBUSCPP_FOR_EACH_, SDBUSCPP_PP_NARG(__VA_ARGS__))(SDBUSCPP_FIND_AND_DESERIALIZE_STRUCT_MEMBER, SDBUSCPP_PP_SPACE, STRUCT, __VA_ARGS__) \ + /**/ +#define SDBUSCPP_FIND_AND_DESERIALIZE_STRUCT_MEMBER(STRUCT, MEMBER) if (key == #MEMBER) STRUCT.MEMBER = value.get(); else + +#define SDBUSCPP_FOR_EACH_1(M, D, S, M1) M(S, M1) +#define SDBUSCPP_FOR_EACH_2(M, D, S, M1, M2) M(S, M1) D M(S, M2) +#define SDBUSCPP_FOR_EACH_3(M, D, S, M1, M2, M3) M(S, M1) D M(S, M2) D M(S, M3) +#define SDBUSCPP_FOR_EACH_4(M, D, S, M1, M2, M3, M4) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) +#define SDBUSCPP_FOR_EACH_5(M, D, S, M1, M2, M3, M4, M5) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) +#define SDBUSCPP_FOR_EACH_6(M, D, S, M1, M2, M3, M4, M5, M6) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) +#define SDBUSCPP_FOR_EACH_7(M, D, S, M1, M2, M3, M4, M5, M6, M7) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7) +#define SDBUSCPP_FOR_EACH_8(M, D, S, M1, M2, M3, M4, M5, M6, M7, M8) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7) D M(S, M8) +#define SDBUSCPP_FOR_EACH_9(M, D, S, M1, M2, M3, M4, M5, M6, M7, M8, M9) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7) D M(S, M8) D M(S, M9) +#define SDBUSCPP_FOR_EACH_10(M, D, S, M1, M2, M3, M4, M5, M6, M7, M8, M9, M10) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7) D M(S, M8) D M(S, M9) D M(S, M10) +#define SDBUSCPP_FOR_EACH_11(M, D, S, M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7) D M(S, M8) D M(S, M9) D M(S, M10) D M(S, M11) +#define SDBUSCPP_FOR_EACH_12(M, D, S, M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11, M12) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7) D M(S, M8) D M(S, M9) D M(S, M10) D M(S, M11) D M(S, M12) +#define SDBUSCPP_FOR_EACH_13(M, D, S, M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11, M12, M13) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7) D M(S, M8) D M(S, M9) D M(S, M10) D M(S, M11) D M(S, M12) D M(S, M13) +#define SDBUSCPP_FOR_EACH_14(M, D, S, M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11, M12, M13, M14) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7) D M(S, M8) D M(S, M9) D M(S, M10) D M(S, M11) D M(S, M12) D M(S, M13) D M(S, M14) +#define SDBUSCPP_FOR_EACH_15(M, D, S, M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11, M12, M13, M14, M15) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7) D M(S, M8) D M(S, M9) D M(S, M10) D M(S, M11) D M(S, M12) D M(S, M13) D M(S, M14) D M(S, M15) +#define SDBUSCPP_FOR_EACH_16(M, D, S, M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11, M12, M13, M14, M15, M16) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7) D M(S, M8) D M(S, M9) D M(S, M10) D M(S, M11) D M(S, M12) D M(S, M13) D M(S, M14) D M(S, M15) D M(S, M16) + +#define SDBUSCPP_PP_CAT(X, Y) SDBUSCPP_PP_CAT_IMPL(X, Y) +#define SDBUSCPP_PP_CAT_IMPL(X, Y) X##Y +#define SDBUSCPP_PP_NARG(...) SDBUSCPP_PP_NARG_IMPL(__VA_ARGS__, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) +#define SDBUSCPP_PP_NARG_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _N, ...) _N + +#define SDBUSCPP_PP_COMMA , +#define SDBUSCPP_PP_SPACE + +#endif /* SDBUS_CXX_TYPES_H_ */ diff --git a/3rdParty/sdbus-cpp/include/sdbus-c++/VTableItems.h b/3rdParty/sdbus-cpp/include/sdbus-c++/VTableItems.h new file mode 100644 index 0000000000..019a57b82d --- /dev/null +++ b/3rdParty/sdbus-cpp/include/sdbus-c++/VTableItems.h @@ -0,0 +1,112 @@ +/** + * (C) 2016 - 2024 Stanislav Angelovic + * + * @file VTableItems.h + * + * Created on: Dec 14, 2023 + * Project: sdbus-c++ + * Description: High-level D-Bus IPC C++ library based on sd-bus + * + * This file is part of sdbus-c++. + * + * sdbus-c++ is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * sdbus-c++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with sdbus-c++. If not, see . + */ + +#ifndef SDBUS_CXX_VTABLEITEMS_H_ +#define SDBUS_CXX_VTABLEITEMS_H_ + +#include +#include +#include + +#include +#include +#include + +namespace sdbus { + + struct MethodVTableItem + { + template MethodVTableItem& implementedAs(_Function&& callback); + MethodVTableItem& withInputParamNames(std::vector names); + template MethodVTableItem& withInputParamNames(_String... names); + MethodVTableItem& withOutputParamNames(std::vector names); + template MethodVTableItem& withOutputParamNames(_String... names); + MethodVTableItem& markAsDeprecated(); + MethodVTableItem& markAsPrivileged(); + MethodVTableItem& withNoReply(); + + MethodName name; + Signature inputSignature; + std::vector inputParamNames; + Signature outputSignature; + std::vector outputParamNames; + method_callback callbackHandler; + Flags flags; + }; + + MethodVTableItem registerMethod(MethodName methodName); + MethodVTableItem registerMethod(std::string methodName); + + struct SignalVTableItem + { + template SignalVTableItem& withParameters(); + template SignalVTableItem& withParameters(std::vector names); + template SignalVTableItem& withParameters(_String... names); + SignalVTableItem& markAsDeprecated(); + + SignalName name; + Signature signature; + std::vector paramNames; + Flags flags; + }; + + SignalVTableItem registerSignal(SignalName signalName); + SignalVTableItem registerSignal(std::string signalName); + + struct PropertyVTableItem + { + template PropertyVTableItem& withGetter(_Function&& callback); + template PropertyVTableItem& withSetter(_Function&& callback); + PropertyVTableItem& markAsDeprecated(); + PropertyVTableItem& markAsPrivileged(); + PropertyVTableItem& withUpdateBehavior(Flags::PropertyUpdateBehaviorFlags behavior); + + PropertyName name; + Signature signature; + property_get_callback getter; + property_set_callback setter; + Flags flags; + }; + + PropertyVTableItem registerProperty(PropertyName propertyName); + PropertyVTableItem registerProperty(std::string propertyName); + + struct InterfaceFlagsVTableItem + { + InterfaceFlagsVTableItem& markAsDeprecated(); + InterfaceFlagsVTableItem& markAsPrivileged(); + InterfaceFlagsVTableItem& withNoReplyMethods(); + InterfaceFlagsVTableItem& withPropertyUpdateBehavior(Flags::PropertyUpdateBehaviorFlags behavior); + + Flags flags; + }; + + InterfaceFlagsVTableItem setInterfaceFlags(); + + using VTableItem = std::variant; + +} // namespace sdbus + +#endif /* SDBUS_CXX_VTABLEITEMS_H_ */ diff --git a/3rdParty/sdbus-cpp/include/sdbus-c++/VTableItems.inl b/3rdParty/sdbus-cpp/include/sdbus-c++/VTableItems.inl new file mode 100644 index 0000000000..127006dfef --- /dev/null +++ b/3rdParty/sdbus-cpp/include/sdbus-c++/VTableItems.inl @@ -0,0 +1,301 @@ +/** + * (C) 2016 - 2024 Stanislav Angelovic + * + * @file VTableItems.inl + * + * Created on: Dec 14, 2023 + * Project: sdbus-c++ + * Description: High-level D-Bus IPC C++ library based on sd-bus + * + * This file is part of sdbus-c++. + * + * sdbus-c++ is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * sdbus-c++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with sdbus-c++. If not, see . + */ + +#ifndef SDBUS_CPP_VTABLEITEMS_INL_ +#define SDBUS_CPP_VTABLEITEMS_INL_ + +#include +#include + +#include +#include +#include + +namespace sdbus { + + /*** -------------------- ***/ + /*** Method VTable Item ***/ + /*** -------------------- ***/ + + template + MethodVTableItem& MethodVTableItem::implementedAs(_Function&& callback) + { + inputSignature = signature_of_function_input_arguments_v<_Function>; + outputSignature = signature_of_function_output_arguments_v<_Function>; + callbackHandler = [callback = std::forward<_Function>(callback)](MethodCall call) + { + // Create a tuple of callback input arguments' types, which will be used + // as a storage for the argument values deserialized from the message. + tuple_of_function_input_arg_types_t<_Function> inputArgs; + + // Deserialize input arguments from the message into the tuple. + call >> inputArgs; + + if constexpr (!is_async_method_v<_Function>) + { + // Invoke callback with input arguments from the tuple. + auto ret = sdbus::apply(callback, inputArgs); + + // Store output arguments to the reply message and send it back. + auto reply = call.createReply(); + reply << ret; + reply.send(); + } + else + { + // Invoke callback with input arguments from the tuple and with result object to be set later + using AsyncResult = typename function_traits<_Function>::async_result_t; + sdbus::apply(callback, AsyncResult{std::move(call)}, std::move(inputArgs)); + } + }; + + return *this; + } + + inline MethodVTableItem& MethodVTableItem::withInputParamNames(std::vector names) + { + inputParamNames = std::move(names); + + return *this; + } + + template + inline MethodVTableItem& MethodVTableItem::withInputParamNames(_String... names) + { + static_assert(std::conjunction_v...>, "Parameter names must be (convertible to) strings"); + + return withInputParamNames({names...}); + } + + inline MethodVTableItem& MethodVTableItem::withOutputParamNames(std::vector names) + { + outputParamNames = std::move(names); + + return *this; + } + + template + inline MethodVTableItem& MethodVTableItem::withOutputParamNames(_String... names) + { + static_assert(std::conjunction_v...>, "Parameter names must be (convertible to) strings"); + + return withOutputParamNames({names...}); + } + + inline MethodVTableItem& MethodVTableItem::markAsDeprecated() + { + flags.set(Flags::DEPRECATED); + + return *this; + } + + inline MethodVTableItem& MethodVTableItem::markAsPrivileged() + { + flags.set(Flags::PRIVILEGED); + + return *this; + } + + inline MethodVTableItem& MethodVTableItem::withNoReply() + { + flags.set(Flags::METHOD_NO_REPLY); + + return *this; + } + + inline MethodVTableItem registerMethod(MethodName methodName) + { + return {std::move(methodName), {}, {}, {}, {}, {}, {}}; + } + + inline MethodVTableItem registerMethod(std::string methodName) + { + return registerMethod(MethodName{std::move(methodName)}); + } + + /*** -------------------- ***/ + /*** Signal VTable Item ***/ + /*** -------------------- ***/ + + template + inline SignalVTableItem& SignalVTableItem::withParameters() + { + signature = signature_of_function_input_arguments_v; + + return *this; + } + + template + inline SignalVTableItem& SignalVTableItem::withParameters(std::vector names) + { + paramNames = std::move(names); + + return withParameters<_Args...>(); + } + + template + inline SignalVTableItem& SignalVTableItem::withParameters(_String... names) + { + static_assert(std::conjunction_v...>, "Parameter names must be (convertible to) strings"); + static_assert(sizeof...(_Args) == sizeof...(_String), "Numbers of signal parameters and their names don't match"); + + return withParameters<_Args...>({names...}); + } + + inline SignalVTableItem& SignalVTableItem::markAsDeprecated() + { + flags.set(Flags::DEPRECATED); + + return *this; + } + + inline SignalVTableItem registerSignal(SignalName signalName) + { + return {std::move(signalName), {}, {}, {}}; + } + + inline SignalVTableItem registerSignal(std::string signalName) + { + return registerSignal(SignalName{std::move(signalName)}); + } + + /*** -------------------- ***/ + /*** Property VTable Item ***/ + /*** -------------------- ***/ + + template + inline PropertyVTableItem& PropertyVTableItem::withGetter(_Function&& callback) + { + static_assert(function_argument_count_v<_Function> == 0, "Property getter function must not take any arguments"); + static_assert(!std::is_void>::value, "Property getter function must return property value"); + + if (signature.empty()) + signature = signature_of_function_output_arguments_v<_Function>; + + getter = [callback = std::forward<_Function>(callback)](PropertyGetReply& reply) + { + // Get the propety value and serialize it into the pre-constructed reply message + reply << callback(); + }; + + return *this; + } + + template + inline PropertyVTableItem& PropertyVTableItem::withSetter(_Function&& callback) + { + static_assert(function_argument_count_v<_Function> == 1, "Property setter function must take one parameter - the property value"); + static_assert(std::is_void>::value, "Property setter function must not return any value"); + + if (signature.empty()) + signature = signature_of_function_input_arguments_v<_Function>; + + setter = [callback = std::forward<_Function>(callback)](PropertySetCall call) + { + // Default-construct property value + using property_type = function_argument_t<_Function, 0>; + std::decay_t property; + + // Deserialize property value from the incoming call message + call >> property; + + // Invoke setter with the value + callback(property); + }; + + return *this; + } + + inline PropertyVTableItem& PropertyVTableItem::markAsDeprecated() + { + flags.set(Flags::DEPRECATED); + + return *this; + } + + inline PropertyVTableItem& PropertyVTableItem::markAsPrivileged() + { + flags.set(Flags::PRIVILEGED); + + return *this; + } + + inline PropertyVTableItem& PropertyVTableItem::withUpdateBehavior(Flags::PropertyUpdateBehaviorFlags behavior) + { + flags.set(behavior); + + return *this; + } + + inline PropertyVTableItem registerProperty(PropertyName propertyName) + { + return {std::move(propertyName), {}, {}, {}, {}}; + } + + inline PropertyVTableItem registerProperty(std::string propertyName) + { + return registerProperty(PropertyName{std::move(propertyName)}); + } + + /*** --------------------------- ***/ + /*** Interface Flags VTable Item ***/ + /*** --------------------------- ***/ + + inline InterfaceFlagsVTableItem& InterfaceFlagsVTableItem::markAsDeprecated() + { + flags.set(Flags::DEPRECATED); + + return *this; + } + + inline InterfaceFlagsVTableItem& InterfaceFlagsVTableItem::markAsPrivileged() + { + flags.set(Flags::PRIVILEGED); + + return *this; + } + + inline InterfaceFlagsVTableItem& InterfaceFlagsVTableItem::withNoReplyMethods() + { + flags.set(Flags::METHOD_NO_REPLY); + + return *this; + } + + inline InterfaceFlagsVTableItem& InterfaceFlagsVTableItem::withPropertyUpdateBehavior(Flags::PropertyUpdateBehaviorFlags behavior) + { + flags.set(behavior); + + return *this; + } + + inline InterfaceFlagsVTableItem setInterfaceFlags() + { + return {}; + } + +} // namespace sdbus + +#endif /* SDBUS_CPP_VTABLEITEMS_INL_ */ diff --git a/3rdParty/sdbus-cpp/include/sdbus-c++/sdbus-c++.h b/3rdParty/sdbus-cpp/include/sdbus-c++/sdbus-c++.h new file mode 100644 index 0000000000..8bc3fe69d4 --- /dev/null +++ b/3rdParty/sdbus-cpp/include/sdbus-c++/sdbus-c++.h @@ -0,0 +1,38 @@ +/** + * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland + * (C) 2016 - 2024 Stanislav Angelovic + * + * @file sdbus-c++.h + * + * Created on: Jan 19, 2017 + * Project: sdbus-c++ + * Description: High-level D-Bus IPC C++ library based on sd-bus + * + * This file is part of sdbus-c++. + * + * sdbus-c++ is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * sdbus-c++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with sdbus-c++. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/3rdParty/sdbus-cpp/lib/cmake/sdbus-c++/sdbus-c++-config-version.cmake b/3rdParty/sdbus-cpp/lib/cmake/sdbus-c++/sdbus-c++-config-version.cmake new file mode 100644 index 0000000000..a9711194e3 --- /dev/null +++ b/3rdParty/sdbus-cpp/lib/cmake/sdbus-c++/sdbus-c++-config-version.cmake @@ -0,0 +1,65 @@ +# This is a basic version file for the Config-mode of find_package(). +# It is used by write_basic_package_version_file() as input file for configure_file() +# to create a version-file which can be installed along a config.cmake file. +# +# The created file sets PACKAGE_VERSION_EXACT if the current version string and +# the requested version string are exactly the same and it sets +# PACKAGE_VERSION_COMPATIBLE if the current version is >= requested version, +# but only if the requested major version is the same as the current one. +# The variable CVF_VERSION must be set before calling configure_file(). + + +set(PACKAGE_VERSION "2.2.1") + +if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION) + set(PACKAGE_VERSION_COMPATIBLE FALSE) +else() + + if("2.2.1" MATCHES "^([0-9]+)\\.") + set(CVF_VERSION_MAJOR "${CMAKE_MATCH_1}") + if(NOT CVF_VERSION_MAJOR VERSION_EQUAL 0) + string(REGEX REPLACE "^0+" "" CVF_VERSION_MAJOR "${CVF_VERSION_MAJOR}") + endif() + else() + set(CVF_VERSION_MAJOR "2.2.1") + endif() + + if(PACKAGE_FIND_VERSION_RANGE) + # both endpoints of the range must have the expected major version + math (EXPR CVF_VERSION_MAJOR_NEXT "${CVF_VERSION_MAJOR} + 1") + if (NOT PACKAGE_FIND_VERSION_MIN_MAJOR STREQUAL CVF_VERSION_MAJOR + OR ((PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE" AND NOT PACKAGE_FIND_VERSION_MAX_MAJOR STREQUAL CVF_VERSION_MAJOR) + OR (PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE" AND NOT PACKAGE_FIND_VERSION_MAX VERSION_LESS_EQUAL CVF_VERSION_MAJOR_NEXT))) + set(PACKAGE_VERSION_COMPATIBLE FALSE) + elseif(PACKAGE_FIND_VERSION_MIN_MAJOR STREQUAL CVF_VERSION_MAJOR + AND ((PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE" AND PACKAGE_VERSION VERSION_LESS_EQUAL PACKAGE_FIND_VERSION_MAX) + OR (PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE" AND PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION_MAX))) + set(PACKAGE_VERSION_COMPATIBLE TRUE) + else() + set(PACKAGE_VERSION_COMPATIBLE FALSE) + endif() + else() + if(PACKAGE_FIND_VERSION_MAJOR STREQUAL CVF_VERSION_MAJOR) + set(PACKAGE_VERSION_COMPATIBLE TRUE) + else() + set(PACKAGE_VERSION_COMPATIBLE FALSE) + endif() + + if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION) + set(PACKAGE_VERSION_EXACT TRUE) + endif() + endif() +endif() + + +# if the installed or the using project don't have CMAKE_SIZEOF_VOID_P set, ignore it: +if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "" OR "8" STREQUAL "") + return() +endif() + +# check that the installed version has the same 32/64bit-ness as the one which is currently searching: +if(NOT CMAKE_SIZEOF_VOID_P STREQUAL "8") + math(EXPR installedBits "8 * 8") + set(PACKAGE_VERSION "${PACKAGE_VERSION} (${installedBits}bit)") + set(PACKAGE_VERSION_UNSUITABLE TRUE) +endif() diff --git a/3rdParty/sdbus-cpp/lib/cmake/sdbus-c++/sdbus-c++-config.cmake b/3rdParty/sdbus-cpp/lib/cmake/sdbus-c++/sdbus-c++-config.cmake new file mode 100644 index 0000000000..a3a9a5f805 --- /dev/null +++ b/3rdParty/sdbus-cpp/lib/cmake/sdbus-c++/sdbus-c++-config.cmake @@ -0,0 +1,28 @@ + +####### Expanded from @PACKAGE_INIT@ by configure_package_config_file() ####### +####### Any changes to this file will be overwritten by the next CMake run #### +####### The input file was sdbus-c++-config.cmake.in ######## + +get_filename_component(PACKAGE_PREFIX_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../" ABSOLUTE) + +macro(set_and_check _var _file) + set(${_var} "${_file}") + if(NOT EXISTS "${_file}") + message(FATAL_ERROR "File or directory ${_file} referenced by variable ${_var} does not exist !") + endif() +endmacro() + +macro(check_required_components _NAME) + foreach(comp ${${_NAME}_FIND_COMPONENTS}) + if(NOT ${_NAME}_${comp}_FOUND) + if(${_NAME}_FIND_REQUIRED_${comp}) + set(${_NAME}_FOUND FALSE) + endif() + endif() + endforeach() +endmacro() + +#################################################################################### + +include("${CMAKE_CURRENT_LIST_DIR}/sdbus-c++-targets.cmake") +check_required_components("sdbus-c++") diff --git a/3rdParty/sdbus-cpp/lib/cmake/sdbus-c++/sdbus-c++-targets-release.cmake b/3rdParty/sdbus-cpp/lib/cmake/sdbus-c++/sdbus-c++-targets-release.cmake new file mode 100644 index 0000000000..d21aa91bdc --- /dev/null +++ b/3rdParty/sdbus-cpp/lib/cmake/sdbus-c++/sdbus-c++-targets-release.cmake @@ -0,0 +1,19 @@ +#---------------------------------------------------------------- +# Generated CMake target import file for configuration "Release". +#---------------------------------------------------------------- + +# Commands may need to know the format version. +set(CMAKE_IMPORT_FILE_VERSION 1) + +# Import target "SDBusCpp::sdbus-c++" for configuration "Release" +set_property(TARGET SDBusCpp::sdbus-c++ APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE) +set_target_properties(SDBusCpp::sdbus-c++ PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "C;CXX" + IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/lib/libsdbus-c++.a" + ) + +list(APPEND _cmake_import_check_targets SDBusCpp::sdbus-c++ ) +list(APPEND _cmake_import_check_files_for_SDBusCpp::sdbus-c++ "${_IMPORT_PREFIX}/lib/libsdbus-c++.a" ) + +# Commands beyond this point should not need to know the version. +set(CMAKE_IMPORT_FILE_VERSION) diff --git a/3rdParty/sdbus-cpp/lib/cmake/sdbus-c++/sdbus-c++-targets.cmake b/3rdParty/sdbus-cpp/lib/cmake/sdbus-c++/sdbus-c++-targets.cmake new file mode 100644 index 0000000000..aeb772a44e --- /dev/null +++ b/3rdParty/sdbus-cpp/lib/cmake/sdbus-c++/sdbus-c++-targets.cmake @@ -0,0 +1,114 @@ +# Generated by CMake + +if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" LESS 2.8) + message(FATAL_ERROR "CMake >= 3.0.0 required") +endif() +if(CMAKE_VERSION VERSION_LESS "3.0.0") + message(FATAL_ERROR "CMake >= 3.0.0 required") +endif() +cmake_policy(PUSH) +cmake_policy(VERSION 3.0.0...4.1) +#---------------------------------------------------------------- +# Generated CMake target import file. +#---------------------------------------------------------------- + +# Commands may need to know the format version. +set(CMAKE_IMPORT_FILE_VERSION 1) + +# Protect against multiple inclusion, which would fail when already imported targets are added once more. +set(_cmake_targets_defined "") +set(_cmake_targets_not_defined "") +set(_cmake_expected_targets "") +foreach(_cmake_expected_target IN ITEMS SDBusCpp::sdbus-c++ SDBusCpp::sdbus-c++-objlib) + list(APPEND _cmake_expected_targets "${_cmake_expected_target}") + if(TARGET "${_cmake_expected_target}") + list(APPEND _cmake_targets_defined "${_cmake_expected_target}") + else() + list(APPEND _cmake_targets_not_defined "${_cmake_expected_target}") + endif() +endforeach() +unset(_cmake_expected_target) +if(_cmake_targets_defined STREQUAL _cmake_expected_targets) + unset(_cmake_targets_defined) + unset(_cmake_targets_not_defined) + unset(_cmake_expected_targets) + unset(CMAKE_IMPORT_FILE_VERSION) + cmake_policy(POP) + return() +endif() +if(NOT _cmake_targets_defined STREQUAL "") + string(REPLACE ";" ", " _cmake_targets_defined_text "${_cmake_targets_defined}") + string(REPLACE ";" ", " _cmake_targets_not_defined_text "${_cmake_targets_not_defined}") + message(FATAL_ERROR "Some (but not all) targets in this export set were already defined.\nTargets Defined: ${_cmake_targets_defined_text}\nTargets not yet defined: ${_cmake_targets_not_defined_text}\n") +endif() +unset(_cmake_targets_defined) +unset(_cmake_targets_not_defined) +unset(_cmake_expected_targets) + + +# Compute the installation prefix relative to this file. +get_filename_component(_IMPORT_PREFIX "${CMAKE_CURRENT_LIST_FILE}" PATH) +get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH) +get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH) +get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH) +if(_IMPORT_PREFIX STREQUAL "/") + set(_IMPORT_PREFIX "") +endif() + +# Create imported target SDBusCpp::sdbus-c++ +add_library(SDBusCpp::sdbus-c++ STATIC IMPORTED) + +set_target_properties(SDBusCpp::sdbus-c++ PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include" + INTERFACE_LINK_LIBRARIES "\$" +) + +# Create imported target SDBusCpp::sdbus-c++-objlib +add_library(SDBusCpp::sdbus-c++-objlib INTERFACE IMPORTED) + +set_target_properties(SDBusCpp::sdbus-c++-objlib PROPERTIES + INTERFACE_LINK_LIBRARIES "PkgConfig::Systemd;Threads::Threads" +) + +# Load information for each installed configuration. +file(GLOB _cmake_config_files "${CMAKE_CURRENT_LIST_DIR}/sdbus-c++-targets-*.cmake") +foreach(_cmake_config_file IN LISTS _cmake_config_files) + include("${_cmake_config_file}") +endforeach() +unset(_cmake_config_file) +unset(_cmake_config_files) + +# Cleanup temporary variables. +set(_IMPORT_PREFIX) + +# Loop over all imported files and verify that they actually exist +foreach(_cmake_target IN LISTS _cmake_import_check_targets) + if(CMAKE_VERSION VERSION_LESS "3.28" + OR NOT DEFINED _cmake_import_check_xcframework_for_${_cmake_target} + OR NOT IS_DIRECTORY "${_cmake_import_check_xcframework_for_${_cmake_target}}") + foreach(_cmake_file IN LISTS "_cmake_import_check_files_for_${_cmake_target}") + if(NOT EXISTS "${_cmake_file}") + message(FATAL_ERROR "The imported target \"${_cmake_target}\" references the file + \"${_cmake_file}\" +but this file does not exist. Possible reasons include: +* The file was deleted, renamed, or moved to another location. +* An install or uninstall procedure did not complete successfully. +* The installation package was faulty and contained + \"${CMAKE_CURRENT_LIST_FILE}\" +but not all the files it references. +") + endif() + endforeach() + endif() + unset(_cmake_file) + unset("_cmake_import_check_files_for_${_cmake_target}") +endforeach() +unset(_cmake_target) +unset(_cmake_import_check_targets) + +# This file does not depend on other imported targets which have +# been exported from the same project but in a separate export set. + +# Commands beyond this point should not need to know the version. +set(CMAKE_IMPORT_FILE_VERSION) +cmake_policy(POP) diff --git a/3rdParty/sdbus-cpp/lib/libsdbus-c++.a b/3rdParty/sdbus-cpp/lib/libsdbus-c++.a new file mode 100644 index 0000000000..3e8cd171ef Binary files /dev/null and b/3rdParty/sdbus-cpp/lib/libsdbus-c++.a differ diff --git a/3rdParty/sdbus-cpp/lib/pkgconfig/sdbus-c++.pc b/3rdParty/sdbus-cpp/lib/pkgconfig/sdbus-c++.pc new file mode 100644 index 0000000000..94c8362764 --- /dev/null +++ b/3rdParty/sdbus-cpp/lib/pkgconfig/sdbus-c++.pc @@ -0,0 +1,11 @@ +prefix=/usr/local +exec_prefix=/usr/local +libdir=/usr/local/lib +includedir=/usr/local/include + +Name: sdbus-c++ +Description: C++ library on top of sd-bus, a systemd D-Bus library +Requires: libsystemd +Version: 2.2.1 +Libs: -L${libdir} -lsdbus-c++ +Cflags: -I${includedir} diff --git a/SerialPrograms/CMakeLists.txt b/SerialPrograms/CMakeLists.txt index ec74150758..1245b2af0a 100644 --- a/SerialPrograms/CMakeLists.txt +++ b/SerialPrograms/CMakeLists.txt @@ -395,7 +395,7 @@ else() # macOS and Linux if(NOT IS_DIRECTORY ${EXTRACTED_ONNX_PATH}) message(STATUS "ONNX_ROOT_PATH not found. Downloading ONNX Runtime v${ONNXRUNTIME_VERSION}...") - + # logic for downloading... set(DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/${ONNXRUNTIME_TGZ_NAME}") file(DOWNLOAD @@ -447,6 +447,16 @@ else() # macOS and Linux else() message(FATAL_ERROR "Could not find ONNX Runtime headers or library.") endif() + + # Systemd DBus Library + message(STATUS "Using bundled sdbus-c++ library.") + target_include_directories(SerialProgramsLib PRIVATE + ${REPO_ROOT_DIR}/3rdParty/sdbus-cpp/include + ) + target_link_libraries(SerialProgramsLib PRIVATE + ${REPO_ROOT_DIR}/3rdParty/sdbus-cpp/lib/libsdbus-c++.a + systemd #sdbus-c++ relies on libsystemd, almost all distros should have this + ) endif() # end Linux # Find OpenCV diff --git a/SerialPrograms/Source/CommonFramework/Environment/SystemSleep.cpp b/SerialPrograms/Source/CommonFramework/Environment/SystemSleep.cpp index b679e936c3..3988946c81 100644 --- a/SerialPrograms/Source/CommonFramework/Environment/SystemSleep.cpp +++ b/SerialPrograms/Source/CommonFramework/Environment/SystemSleep.cpp @@ -11,6 +11,8 @@ #include "SystemSleep_Windows.tpp" #elif __APPLE__ #include "SystemSleep_Apple.tpp" +#elif __linux__ +#include "SystemSleep_Linux.tpp" #else namespace PokemonAutomation{ SystemSleepController& SystemSleepController::instance(){ diff --git a/SerialPrograms/Source/CommonFramework/Environment/SystemSleep_Linux.tpp b/SerialPrograms/Source/CommonFramework/Environment/SystemSleep_Linux.tpp new file mode 100644 index 0000000000..a6940957ed --- /dev/null +++ b/SerialPrograms/Source/CommonFramework/Environment/SystemSleep_Linux.tpp @@ -0,0 +1,165 @@ +/* OS Sleep (Linux) + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#include +#include "Common/Cpp/Concurrency/ConditionVariable.h" +#include "Common/Cpp/Concurrency/AsyncTask.h" +#include "Common/Cpp/Concurrency/ThreadPool.h" +#include "CommonFramework/Logging/Logger.h" +#include "CommonFramework/Tools/GlobalThreadPools.h" +#include "SystemSleep.h" + +namespace PokemonAutomation{ + + +class LinuxSleepController final : public SystemSleepController{ +public: + LinuxSleepController() + : m_screen_on_requests(0) + , m_no_sleep_requests(0) + , m_stopping(false) + , m_thread(GlobalThreadPools::unlimited_normal().dispatch_now_blocking( + [this]{ thread_loop(); }) + ) + {} + virtual ~LinuxSleepController(){ + stop(); + } + virtual void stop() noexcept override{ + if (!m_thread){ + return; + } + { + std::lock_guard lg(m_lock); + m_stopping = true; + } + m_cv.notify_all(); + m_thread.wait_and_ignore_exceptions(); + m_inhibit_fd.reset(); + if (m_state.load(std::memory_order_relaxed) != SleepSuppress::NONE){ + try{ + global_logger_tagged().log( + "Destroying LinuxSleepController with active requests...", + COLOR_RED + ); + }catch (...){} + } + } + + virtual void push_screen_on() override{ + std::lock_guard lg(m_lock); + m_screen_on_requests++; + m_cv.notify_all(); + } + virtual void pop_screen_on() override{ + std::lock_guard lg(m_lock); + m_screen_on_requests--; + m_cv.notify_all(); + } + virtual void push_no_sleep() override{ + std::lock_guard lg(m_lock); + m_no_sleep_requests++; + m_cv.notify_all(); + } + virtual void pop_no_sleep() override{ + std::lock_guard lg(m_lock); + m_no_sleep_requests--; + m_cv.notify_all(); + } + +private: + void thread_loop() + { + auto connection = sdbus::createSystemBusConnection(); + auto proxy = sdbus::createProxy( + *connection, + sdbus::BusName{"org.freedesktop.login1"}, + sdbus::ObjectPath{"/org/freedesktop/login1"} + ); + + while (true) + { + std::unique_lock lg(m_lock); + if (m_stopping) + { + return; + } + + SleepSuppress before_state = m_state.load(std::memory_order_relaxed); + SleepSuppress after_state = SleepSuppress::NONE; + + bool need_inhibit = false; + std::string what; + if (m_no_sleep_requests > 0) + { + need_inhibit = true; + what = "sleep"; + after_state = SleepSuppress::NO_SLEEP; + } + else if (m_screen_on_requests > 0) + { + need_inhibit = true; + what = "idle"; + after_state = SleepSuppress::SCREEN_ON; + } + if (need_inhibit && !m_inhibit_fd) + { + try + { + sdbus::UnixFd fd; + proxy->callMethod("Inhibit") + .onInterface("org.freedesktop.login1.Manager") + .withArguments(what, "PokemonAutomation", "Keeping system awake", "block") + .storeResultsTo(fd); + m_inhibit_fd = std::make_unique(std::move(fd)); + + global_logger_tagged().log("Acquired sleep inhibitor", COLOR_BLUE); + } + + catch (const sdbus::Error &e) + { + global_logger_tagged().log(std::string("DBus error: ") + e.what(), COLOR_RED); + } + } + else if (!need_inhibit && m_inhibit_fd) + { + m_inhibit_fd.reset(); + global_logger_tagged().log("Released sleep inhibitor", COLOR_BLUE); + } + + if (before_state != after_state) + { + m_state.store(after_state, std::memory_order_release); + notify_listeners(after_state); + } + + m_cv.wait(lg); + } + } + +private: + size_t m_screen_on_requests = 0; + size_t m_no_sleep_requests = 0; + + bool m_stopping; + ConditionVariable m_cv; + AsyncTask m_thread; + + std::unique_ptr m_inhibit_fd; +}; + + +SystemSleepController& SystemSleepController::instance(){ + static LinuxSleepController controller; + return controller; +} + + + + + + +} diff --git a/SerialPrograms/cmake/SourceFiles.cmake b/SerialPrograms/cmake/SourceFiles.cmake index 846f06ef95..5890df225c 100644 --- a/SerialPrograms/cmake/SourceFiles.cmake +++ b/SerialPrograms/cmake/SourceFiles.cmake @@ -364,6 +364,7 @@ file(GLOB LIBRARY_SOURCES Source/CommonFramework/Environment/SystemSleep.cpp Source/CommonFramework/Environment/SystemSleep.h Source/CommonFramework/Environment/SystemSleep_Apple.tpp + Source/CommonFramework/Environment/SystemSleep_Linux.tpp Source/CommonFramework/Environment/SystemSleep_Windows.tpp Source/CommonFramework/ErrorReports/ErrorReports.cpp Source/CommonFramework/ErrorReports/ErrorReports.h