diff --git a/CMakeLists.txt b/CMakeLists.txt index fbf92d5..5626f41 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -164,6 +164,17 @@ if(BUILD_CODE_GEN) add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/tools") endif() +#---------------------------------- +# EXAMPLES +#---------------------------------- + +option(BUILD_EXAMPLES "Build example programs (default OFF)" OFF) + +if(BUILD_EXAMPLES) + message(STATUS "Building with examples") + add_subdirectory(examples) +endif() + #---------------------------------- # DOCUMENTATION #---------------------------------- diff --git a/README.md b/README.md index 5312daf..1ffc81d 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,10 @@ $ sudo cmake --build . --target install This is a global CMake flag, promoted in sdbus-c++ project to a CMake option. Use this to control whether sdbus-c++ is built as either a shared or static library. Default value: `ON`. +* `BUILD_EXAMPLES` [boolean] + + Build example programs which are located in the _example_ directory. Examples are not installed. Default value: `OFF` + Dependencies ------------ diff --git a/docs/using-sdbus-c++.md b/docs/using-sdbus-c++.md index a4c703b..37c38b5 100644 --- a/docs/using-sdbus-c++.md +++ b/docs/using-sdbus-c++.md @@ -1252,7 +1252,7 @@ For example, for our `Concatenator` example above in this tutorial, we may want Note that signals of afore-mentioned standard D-Bus interfaces are not emitted by the library automatically. It's clients who are supposed to emit them. -Working examples of using standard D-Bus interfaces can be found in [sdbus-c++ integration tests](/tests/integrationtests/DBusStandardInterfacesTests.cpp). +Working examples of using standard D-Bus interfaces can be found in [sdbus-c++ integration tests](/tests/integrationtests/DBusStandardInterfacesTests.cpp) or the [examples](/examples) directory. Conclusion ---------- diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000..5befa9a --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,6 @@ + +add_executable(obj-manager-server org.freedesktop.DBus.ObjectManager/obj-manager-server.cpp) +target_link_libraries(obj-manager-server sdbus-c++) + +add_executable(obj-manager-client org.freedesktop.DBus.ObjectManager/obj-manager-client.cpp) +target_link_libraries(obj-manager-client sdbus-c++) \ No newline at end of file diff --git a/examples/org.freedesktop.DBus.ObjectManager/examplemanager-planet1-client-glue.h b/examples/org.freedesktop.DBus.ObjectManager/examplemanager-planet1-client-glue.h new file mode 100644 index 0000000..020019c --- /dev/null +++ b/examples/org.freedesktop.DBus.ObjectManager/examplemanager-planet1-client-glue.h @@ -0,0 +1,50 @@ + +/* + * This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT! + */ + +#ifndef __sdbuscpp__examplemanager_planet1_client_glue_h__proxy__H__ +#define __sdbuscpp__examplemanager_planet1_client_glue_h__proxy__H__ + +#include +#include +#include + +namespace org { +namespace sdbuscpp { +namespace ExampleManager { + +class Planet1_proxy +{ +public: + static constexpr const char* INTERFACE_NAME = "org.sdbuscpp.ExampleManager.Planet1"; + +protected: + Planet1_proxy(sdbus::IProxy& proxy) + : proxy_(proxy) + { + } + + ~Planet1_proxy() = default; + +public: + uint64_t GetPopulation() + { + uint64_t result; + proxy_.callMethod("GetPopulation").onInterface(INTERFACE_NAME).storeResultsTo(result); + return result; + } + +public: + std::string Name() + { + return proxy_.getProperty("Name").onInterface(INTERFACE_NAME); + } + +private: + sdbus::IProxy& proxy_; +}; + +}}} // namespaces + +#endif diff --git a/examples/org.freedesktop.DBus.ObjectManager/examplemanager-planet1-server-glue.h b/examples/org.freedesktop.DBus.ObjectManager/examplemanager-planet1-server-glue.h new file mode 100644 index 0000000..db0e02c --- /dev/null +++ b/examples/org.freedesktop.DBus.ObjectManager/examplemanager-planet1-server-glue.h @@ -0,0 +1,44 @@ + +/* + * This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT! + */ + +#ifndef __sdbuscpp__examplemanager_planet1_server_glue_h__adaptor__H__ +#define __sdbuscpp__examplemanager_planet1_server_glue_h__adaptor__H__ + +#include +#include +#include + +namespace org { +namespace sdbuscpp { +namespace ExampleManager { + +class Planet1_adaptor +{ +public: + static constexpr const char* INTERFACE_NAME = "org.sdbuscpp.ExampleManager.Planet1"; + +protected: + Planet1_adaptor(sdbus::IObject& object) + : object_(object) + { + object_.registerMethod("GetPopulation").onInterface(INTERFACE_NAME).withOutputParamNames("population").implementedAs([this](){ return this->GetPopulation(); }); + object_.registerProperty("Name").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Name(); }); + } + + ~Planet1_adaptor() = default; + +private: + virtual uint64_t GetPopulation() = 0; + +private: + virtual std::string Name() = 0; + +private: + sdbus::IObject& object_; +}; + +}}} // namespaces + +#endif diff --git a/examples/org.freedesktop.DBus.ObjectManager/obj-manager-client.cpp b/examples/org.freedesktop.DBus.ObjectManager/obj-manager-client.cpp new file mode 100644 index 0000000..60e7d14 --- /dev/null +++ b/examples/org.freedesktop.DBus.ObjectManager/obj-manager-client.cpp @@ -0,0 +1,110 @@ +/** + * Example of a D-Bus client which implements org.freedesktop.DBus.ObjectManager + * + * The example uses the generated stub API layer to listen to interfaces added to new objects under + * "org.sdbuscpp.examplemanager". If added, we access "org.sdbuscpp.ExampleManager.Planet1" to print + * info like this: + * /org/sdbuscpp/examplemanager/Planet1/Earth added: org.sdbuscpp.ExampleManager.Planet1 + * Earth has a population of 7874965825. + * + */ + +#include "examplemanager-planet1-client-glue.h" +#include +#include +#include + +class PlanetProxy : public sdbus::ProxyInterfaces< org::sdbuscpp::ExampleManager::Planet1_proxy > +{ +public: + PlanetProxy(sdbus::IConnection& connection, std::string destination, std::string path) + : ProxyInterfaces(connection, std::move(destination), std::move(path)) + { + registerProxy(); + } + + ~PlanetProxy() + { + unregisterProxy(); + } +}; + +class ManagerProxy : public sdbus::ProxyInterfaces< sdbus::ObjectManager_proxy > +{ +public: + ManagerProxy(sdbus::IConnection& connection, const std::string& destination, std::string path) + : ProxyInterfaces(connection, destination, std::move(path)) + , m_connection(connection) + , m_destination(destination) + { + registerProxy(); + } + + ~ManagerProxy() + { + unregisterProxy(); + } + + void handleExistingObjects() + { + std::map>> objectsInterfacesAndProperties; + objectsInterfacesAndProperties = GetManagedObjects(); + for (const auto& [object, interfacesAndProperties] : objectsInterfacesAndProperties) { + onInterfacesAdded(object, interfacesAndProperties); + } + } + +private: + void onInterfacesAdded( const sdbus::ObjectPath& objectPath + , const std::map>& interfacesAndProperties) override + { + std::cout << objectPath << " added:\t"; + for (const auto& [interface, _] : interfacesAndProperties) { + std::cout << interface << " "; + } + std::cout << std::endl; + + // Parse and print some more info + auto planetInterface = interfacesAndProperties.find(org::sdbuscpp::ExampleManager::Planet1_proxy::INTERFACE_NAME); + if (planetInterface == interfacesAndProperties.end()) { + return; + } + const auto& properties = planetInterface->second; + // get a property which was passed as part of the signal. + const auto& name = properties.at("Name").get(); + // or create a proxy instance to the newly added object. + PlanetProxy planet(m_connection, m_destination, objectPath); + std::cout << name << " has a population of " << planet.GetPopulation() << ".\n" << std::endl; + } + + void onInterfacesRemoved( const sdbus::ObjectPath& objectPath + , const std::vector& interfaces) override + { + std::cout << objectPath << " removed:\t"; + for (const auto& interface : interfaces) { + std::cout << interface << " "; + } + std::cout << std::endl; + } + + sdbus::IConnection& m_connection; + std::string m_destination; +}; + +int main() +{ + auto connection = sdbus::createSessionBusConnection(); + + auto managerProxy = std::make_unique(*connection, "org.sdbuscpp.examplemanager", "/org/sdbuscpp/examplemanager"); + try { + managerProxy->handleExistingObjects(); + } + catch (const sdbus::Error& e) { + if (e.getName() == "org.freedesktop.DBus.Error.ServiceUnknown") { + std::cout << "Waiting for server to start ..." << std::endl; + } + } + + connection->enterEventLoop(); + return 0; +} \ No newline at end of file diff --git a/examples/org.freedesktop.DBus.ObjectManager/obj-manager-server.cpp b/examples/org.freedesktop.DBus.ObjectManager/obj-manager-server.cpp new file mode 100644 index 0000000..78510e9 --- /dev/null +++ b/examples/org.freedesktop.DBus.ObjectManager/obj-manager-server.cpp @@ -0,0 +1,108 @@ +/** + * Example of a D-Bus server which implements org.freedesktop.DBus.ObjectManager + * + * The example uses the generated stub API layer to register an object manager under "org.sdbuscpp.examplemanager" + * and add objects underneath which implement "org.sdbuscpp.ExampleManager.Planet1". + * + * We add and remove objects after a few seconds and print info like this: + * Creating PlanetAdaptor in 5 4 3 2 1 + * Creating PlanetAdaptor in 5 4 3 2 1 + * Creating PlanetAdaptor in 5 4 3 2 1 + * Removing PlanetAdaptor in 5 4 3 2 1 + * Removing PlanetAdaptor in 5 4 3 2 1 + */ + +#include "examplemanager-planet1-server-glue.h" +#include +#include +#include +#include +#include + +class ManagerAdaptor : public sdbus::AdaptorInterfaces< sdbus::ObjectManager_adaptor > +{ +public: + ManagerAdaptor(sdbus::IConnection& connection, std::string path) + : AdaptorInterfaces(connection, std::move(path)) + { + registerAdaptor(); + } + + ~ManagerAdaptor() + { + unregisterAdaptor(); + } +}; + +class PlanetAdaptor : public sdbus::AdaptorInterfaces< org::sdbuscpp::ExampleManager::Planet1_adaptor, + sdbus::ManagedObject_adaptor, + sdbus::Properties_adaptor > +{ +public: + PlanetAdaptor(sdbus::IConnection& connection, std::string path, std::string name, uint64_t poulation) + : AdaptorInterfaces(connection, std::move(path)) + , m_name(std::move(name)) + , m_population(poulation) + { + registerAdaptor(); + emitInterfacesAddedSignal({org::sdbuscpp::ExampleManager::Planet1_adaptor::INTERFACE_NAME}); + } + + ~PlanetAdaptor() + { + emitInterfacesRemovedSignal({org::sdbuscpp::ExampleManager::Planet1_adaptor::INTERFACE_NAME}); + unregisterAdaptor(); + } + + uint64_t GetPopulation() override + { + return m_population; + } + + std::string Name() override + { + return m_name; + } + +private: + std::string m_name; + uint64_t m_population; +}; + +void printCountDown(const std::string& message, int seconds) +{ + std::cout << message << std::flush; + for (int i = seconds; i > 0; i--) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + std::cout << i << " " << std::flush; + } + std::cout << std::endl; +} + +int main() +{ + auto connection = sdbus::createSessionBusConnection(); + connection->requestName("org.sdbuscpp.examplemanager"); + connection->enterEventLoopAsync(); + + auto manager = std::make_unique(*connection, "/org/sdbuscpp/examplemanager"); + while (true) + { + printCountDown("Creating PlanetAdaptor in ", 5); + auto earth = std::make_unique(*connection, "/org/sdbuscpp/examplemanager/Planet1/Earth", "Earth", 7'874'965'825); + printCountDown("Creating PlanetAdaptor in ", 5); + auto trantor = std::make_unique(*connection, "/org/sdbuscpp/examplemanager/Planet1/Trantor", "Trantor", 40'000'000'000); + printCountDown("Creating PlanetAdaptor in ", 5); + auto laconia = std::make_unique(*connection, "/org/sdbuscpp/examplemanager/Planet1/Laconia", "Laconia", 231'721); + printCountDown("Removing PlanetAdaptor in ", 5); + earth.reset(); + printCountDown("Removing PlanetAdaptor in ", 5); + trantor.reset(); + printCountDown("Removing PlanetAdaptor in ", 5); + laconia.reset(); + } + + connection->releaseName("org.sdbuscpp.examplemanager"); + connection->leaveEventLoop(); + return 0; +} \ No newline at end of file diff --git a/examples/org.freedesktop.DBus.ObjectManager/org.sdbuscpp.ExampleManager.Planet1.xml b/examples/org.freedesktop.DBus.ObjectManager/org.sdbuscpp.ExampleManager.Planet1.xml new file mode 100644 index 0000000..3e4e98f --- /dev/null +++ b/examples/org.freedesktop.DBus.ObjectManager/org.sdbuscpp.ExampleManager.Planet1.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + +