aboutsummaryrefslogtreecommitdiff
path: root/source/apps
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@gmail.com>2024-05-17 17:58:38 +0200
committerFelix Morgner <felix.morgner@gmail.com>2024-05-17 17:58:38 +0200
commit577fc0845718ed8ad5bebf02a277c0579a817f77 (patch)
tree3d1cdc53c426a0ba60a7996619a7b787850bb3b3 /source/apps
parentde5bf7ca3b7a2bf6be35b86486b00dc6a071b950 (diff)
downloadwanda-577fc0845718ed8ad5bebf02a277c0579a817f77.tar.xz
wanda-577fc0845718ed8ad5bebf02a277c0579a817f77.zip
wanda: restructure source layoutHEADdevelop
Diffstat (limited to 'source/apps')
-rw-r--r--source/apps/CMakeLists.txt54
-rw-r--r--source/apps/include/wanda/wandac/cli.hpp27
-rw-r--r--source/apps/include/wanda/wandac/listener.hpp26
-rw-r--r--source/apps/src/wandac/cli.cpp30
-rw-r--r--source/apps/src/wandac/listener.cpp29
-rw-r--r--source/apps/src/wandac/main.cpp43
-rw-r--r--source/apps/src/wandad/main.cpp148
-rw-r--r--source/apps/tests/wandac/cli.cpp80
8 files changed, 437 insertions, 0 deletions
diff --git a/source/apps/CMakeLists.txt b/source/apps/CMakeLists.txt
new file mode 100644
index 0000000..2da5537
--- /dev/null
+++ b/source/apps/CMakeLists.txt
@@ -0,0 +1,54 @@
+include("CheckIPOSupported")
+
+check_ipo_supported(RESULT "WANDA_IPO_SUPPORTED")
+
+find_package("lyra" REQUIRED)
+
+add_library("wandac-components" OBJECT
+ "src/wandac/cli.cpp"
+ "src/wandac/listener.cpp"
+)
+
+target_link_libraries("wandac-components" PUBLIC
+ "wanda"
+ "bfg::lyra"
+)
+
+target_include_directories("wandac-components" PUBLIC "include")
+
+add_executable("wandac"
+ "src/wandac/main.cpp"
+)
+
+target_link_libraries("wandac" PRIVATE
+ "wandac-components"
+)
+
+add_executable("wandac-tests"
+ "tests/wandac/cli.cpp"
+)
+
+target_link_libraries("wandac-tests" PRIVATE
+ "wandac-components"
+ "Catch2::Catch2WithMain"
+)
+
+catch_discover_tests("wandac-tests")
+
+add_executable("wandad"
+ "src/wandad/main.cpp"
+)
+
+target_link_libraries("wandad" PRIVATE
+ "wanda"
+ "bfg::lyra"
+)
+
+set_target_properties("wandac" "wandad" PROPERTIES
+ INTERPROCEDURAL_OPTIMIZATION ${WANDA_IPO_SUPPORTED}
+)
+
+install(TARGETS
+ "wandac"
+ "wandad"
+)
diff --git a/source/apps/include/wanda/wandac/cli.hpp b/source/apps/include/wanda/wandac/cli.hpp
new file mode 100644
index 0000000..c04b138
--- /dev/null
+++ b/source/apps/include/wanda/wandac/cli.hpp
@@ -0,0 +1,27 @@
+#ifndef WANDA_APP_WANDAC_CLI_HPP
+#define WANDA_APP_WANDAC_CLI_HPP
+
+#include <lyra/args.hpp>
+#include <lyra/cli_parser.hpp>
+
+#include <iosfwd>
+#include <string>
+
+namespace wandac
+{
+
+ struct cli
+ {
+ auto parse(lyra::args arguments, std::ostream & error_stream) -> bool;
+ auto print_usage(std::ostream & output_stream) -> void;
+
+ std::string command{};
+ bool help{};
+
+ private:
+ lyra::cli_parser parser{};
+ };
+
+} // namespace wandac
+
+#endif \ No newline at end of file
diff --git a/source/apps/include/wanda/wandac/listener.hpp b/source/apps/include/wanda/wandac/listener.hpp
new file mode 100644
index 0000000..28b0cfc
--- /dev/null
+++ b/source/apps/include/wanda/wandac/listener.hpp
@@ -0,0 +1,26 @@
+#ifndef WANDA_APP_WANDAC_LISTENER_HPP
+#define WANDA_APP_WANDAC_LISTENER_HPP
+
+#include "wanda/wandac/cli.hpp"
+
+#include <wanda/control/commander.hpp>
+
+#include <boost/asio/io_context.hpp>
+
+namespace wandac
+{
+
+ struct listener : wanda::control::commander::listener
+ {
+ listener(wandac::cli const & cli, boost::asio::io_context & service);
+
+ auto on_connected(wanda::control::commander & commander) -> void override ;
+
+ private:
+ wandac::cli const & m_cli;
+ boost::asio::io_context & m_service;
+ };
+
+} // namespace wandac
+
+#endif \ No newline at end of file
diff --git a/source/apps/src/wandac/cli.cpp b/source/apps/src/wandac/cli.cpp
new file mode 100644
index 0000000..6be1e18
--- /dev/null
+++ b/source/apps/src/wandac/cli.cpp
@@ -0,0 +1,30 @@
+#include "wanda/wandac/cli.hpp"
+
+#include <lyra/arg.hpp>
+#include <lyra/help.hpp>
+
+#include <ostream>
+
+namespace wandac
+{
+
+ auto cli::parse(lyra::args arguments, std::ostream & error_stream) -> bool
+ {
+ parser |= //
+ lyra::help(help) | //
+ lyra::arg{command, "command"}("The command to send to the deamon").required();
+
+ auto result = parser.parse(arguments);
+
+ if (!result)
+ {
+ error_stream << "Error while processing command line arguments: " << result.message() << '\n' << parser << '\n';
+ return false;
+ }
+
+ return true;
+ }
+
+ auto cli::print_usage(std::ostream & output_stream) -> void { output_stream << parser << '\n'; }
+
+} // namespace wandac
diff --git a/source/apps/src/wandac/listener.cpp b/source/apps/src/wandac/listener.cpp
new file mode 100644
index 0000000..29ccace
--- /dev/null
+++ b/source/apps/src/wandac/listener.cpp
@@ -0,0 +1,29 @@
+#include "wanda/wandac/listener.hpp"
+
+#include "wanda/wandac/cli.hpp"
+
+#include <wanda/control/commander.hpp>
+#include <wanda/proto/command.hpp>
+
+#include <boost/asio/io_context.hpp>
+
+namespace wandac
+{
+
+ listener::listener(wandac::cli const & cli, boost::asio::io_context & service)
+ : m_cli{cli}
+ , m_service{service}
+ {
+ }
+
+ auto listener::on_connected(wanda::control::commander & commander) -> void
+ {
+ if (m_cli.command == "change")
+ {
+ commander.send(wanda::proto::make_change_command());
+
+ post(m_service, [&] { commander.stop(); });
+ }
+ }
+
+} // namespace wandac
diff --git a/source/apps/src/wandac/main.cpp b/source/apps/src/wandac/main.cpp
new file mode 100644
index 0000000..1533a62
--- /dev/null
+++ b/source/apps/src/wandac/main.cpp
@@ -0,0 +1,43 @@
+#include "wanda/wandac/cli.hpp"
+#include "wanda/wandac/listener.hpp"
+
+#include <wanda/control/commander.hpp>
+#include <wanda/system/environment.hpp>
+#include <wanda/system/logging.hpp>
+#include <wanda/system/xdg.hpp>
+
+#include <boost/asio/io_context.hpp>
+#include <spdlog/sinks/stdout_color_sinks.h>
+
+#include <cstdlib>
+#include <filesystem>
+#include <iostream>
+
+int main(int argc, char const * const * argv)
+{
+ auto cli = wandac::cli{};
+ if (!cli.parse({argc, argv}, std::cerr))
+ {
+ return EXIT_FAILURE;
+ }
+ else if (cli.help)
+ {
+ cli.print_usage(std::cout);
+ return EXIT_SUCCESS;
+ }
+
+ wanda::system::initialize_logger(std::make_shared<spdlog::sinks::stderr_color_sink_st>());
+
+ auto environment = wanda::system::environment{};
+ auto runtime_dir = wanda::system::xdg_path_for(wanda::system::xdg_directory::runtime_dir, environment);
+ auto interface = runtime_dir / ".wanda_interface";
+ auto service = boost::asio::io_context{};
+ auto listener = wandac::listener{cli, service};
+
+ auto commander = wanda::control::commander{service, interface, listener};
+
+ wanda::system::get_logger()->info("trying to connect to wanda control interface on '{}'", interface.native());
+ commander.start();
+
+ service.run();
+}
diff --git a/source/apps/src/wandad/main.cpp b/source/apps/src/wandad/main.cpp
new file mode 100644
index 0000000..9feac82
--- /dev/null
+++ b/source/apps/src/wandad/main.cpp
@@ -0,0 +1,148 @@
+#include <wanda/control/interface.hpp>
+#include <wanda/proto/command.hpp>
+#include <wanda/std_ext/optional.hpp>
+#include <wanda/system/environment.hpp>
+#include <wanda/system/filesystem.hpp>
+#include <wanda/system/logging.hpp>
+#include <wanda/system/setting.hpp>
+#include <wanda/system/wallpaper.hpp>
+#include <wanda/system/xdg.hpp>
+
+#include <boost/asio/io_context.hpp>
+#include <boost/asio/signal_set.hpp>
+#include <lyra/lyra.hpp>
+#include <spdlog/sinks/stdout_color_sinks.h>
+
+#include <csignal>
+#include <cstdlib>
+#include <filesystem>
+#include <iostream>
+#include <set>
+#include <string>
+
+namespace
+{
+ constexpr auto image_filter = [](auto const & path) {
+ static auto const extensions = std::set<std::filesystem::path>{
+ std::filesystem::path{".jpg"},
+ std::filesystem::path{".png"},
+ };
+
+ if (!std::filesystem::is_regular_file(path))
+ {
+ return false;
+ }
+
+ return extensions.find(path.extension()) != extensions.cend();
+ };
+
+ struct cli
+ {
+ std::string wallpaper_directory{};
+ bool help{};
+
+ lyra::cli_parser parser{};
+
+ auto parse(int argc, char const * const * argv, std::ostream & error)
+ {
+ parser |= //
+ lyra::help(help) | //
+ lyra::arg{wallpaper_directory, "directory"}("The wallpaper source directory").required();
+
+ auto result = parser.parse({argc, argv});
+
+ if (!result)
+ {
+ error << "Error while processing command line arguments: " << result.message() << '\n' << parser << '\n';
+ return false;
+ }
+
+ return true;
+ }
+ };
+
+ struct listener : wanda::control::interface::listener
+ {
+ listener(std::vector<std::filesystem::path> const & wallpapers)
+ : m_wallpapers{wallpapers}
+ {
+ }
+
+ void on_received(wanda::control::interface & interface, wanda::proto::command command) override
+ {
+ switch (command.id)
+ {
+ case wanda::proto::command_id::change: {
+ auto wallpaper = wanda::system::random_pick(m_wallpapers);
+ wanda::system::get_logger()->info("changing wallpaper to '{}'", wallpaper.native());
+ wanda::system::set_wallpaper(wallpaper);
+ break;
+ }
+ default:
+ wanda::system::get_logger()->error("received unknown command '{}'", static_cast<int>(command.id));
+ }
+ }
+
+ private:
+ std::vector<std::filesystem::path> const & m_wallpapers;
+ };
+
+} // namespace
+
+int main(int argc, char const * const * argv)
+{
+ using namespace wanda::std_ext;
+
+ auto cli = ::cli{};
+ if (!cli.parse(argc, argv, std::cerr))
+ {
+ return EXIT_FAILURE;
+ }
+ else if (cli.help)
+ {
+ std::cout << cli.parser << '\n';
+ return EXIT_SUCCESS;
+ }
+
+ wanda::system::initialize_logger(std::make_shared<spdlog::sinks::stdout_color_sink_st>());
+ wanda::system::get_logger()->info("wanda is starting up");
+
+ with(wanda::system::scan({cli.wallpaper_directory}, image_filter),
+ [&](auto const & list) {
+ auto service = boost::asio::io_context{};
+ auto socket_path =
+ wanda::system::xdg_path_for(wanda::system::xdg_directory::runtime_dir, wanda::system::environment{}) /
+ ".wanda_interface";
+
+ wanda::system::get_logger()->info("starting control interface on '{}'", socket_path.native());
+ auto listener = ::listener{list};
+ auto interface = wanda::control::make_interface(service, socket_path, listener);
+
+ if (!interface)
+ {
+ wanda::system::get_logger()->error("failed to start control interface");
+ return;
+ }
+
+ if (interface->start())
+ {
+ return;
+ }
+
+ auto signals = boost::asio::signal_set{service, SIGINT, SIGTERM};
+ signals.async_wait([&](auto const & error, auto const signal) {
+ if (!error)
+ {
+ wanda::system::get_logger()->info("Received signal {}. terminating...", signal);
+ interface->shutdown();
+ service.stop();
+ }
+ });
+
+ auto wallpaper = wanda::system::random_pick(list);
+ wanda::system::set_wallpaper(wallpaper);
+
+ service.run();
+ }) ||
+ [&] { wanda::system::get_logger()->error("wallpaper directory does not exist"); };
+}
diff --git a/source/apps/tests/wandac/cli.cpp b/source/apps/tests/wandac/cli.cpp
new file mode 100644
index 0000000..57d2a8f
--- /dev/null
+++ b/source/apps/tests/wandac/cli.cpp
@@ -0,0 +1,80 @@
+#include "wanda/wandac/cli.hpp"
+
+#include <catch2/catch_all.hpp>
+#include <catch2/catch_test_macros.hpp>
+#include <lyra/args.hpp>
+
+#include <sstream>
+#include <string>
+
+using namespace std::string_literals;
+
+namespace wanda::tests::app::wandac
+{
+
+ template<typename ...Ts>
+ auto make_argument_list(Ts const & ... args) -> lyra::args
+ {
+ return {"wanda"s, static_cast<std::string>(args)...};
+ }
+
+ SCENARIO("Empty argument list parsing", "[app][client][cli]")
+ {
+ GIVEN("A fresh cli instance and error stream")
+ {
+ auto cli = ::wandac::cli{};
+ auto error_stream = std::ostringstream{};
+
+ WHEN("invoking parse without any program arguments")
+ {
+ auto result = cli.parse(make_argument_list(), error_stream);
+
+ THEN("the return value is false") { REQUIRE_FALSE(result); }
+ THEN("the error stream is not empty") { REQUIRE_FALSE(error_stream.view().empty()); }
+ THEN("the help flag is not set") { REQUIRE_FALSE(cli.help); }
+ THEN("the command is empty") { REQUIRE(cli.command.empty()); }
+ }
+ }
+ }
+
+ SCENARIO("Valid argument list parsing", "[app][client][cli]")
+ {
+ GIVEN("A fresh cli instance and error stream")
+ {
+ auto cli = ::wandac::cli{};
+ auto error_stream = std::ostringstream{};
+
+ AND_GIVEN("'-h' in the argument list")
+ {
+ auto argument_list = make_argument_list("-h");
+
+ WHEN("invoking parse without additional arguments")
+ {
+ auto result = cli.parse(argument_list, error_stream);
+
+ THEN("the return value is true") { REQUIRE(result); }
+ THEN("the error stream is empty") { REQUIRE(error_stream.view().empty()); }
+ THEN("the help flag is set") { REQUIRE(cli.help); }
+ THEN("the command is empty") { REQUIRE(cli.command.empty()); }
+ }
+ }
+
+ AND_GIVEN("'change' in the argument list")
+ {
+ auto argument_list = make_argument_list("change");
+
+ WHEN("invoking parse without additional arguments")
+ {
+ auto result = cli.parse(argument_list, error_stream);
+
+ THEN("the return valis is true") { REQUIRE(result); }
+ THEN("the error stream is empty") { REQUIRE(error_stream.view().empty()); }
+ THEN("the help flag is not set") { REQUIRE_FALSE(cli.help); }
+ THEN("the command is not empty") { REQUIRE_FALSE(cli.command.empty()); }
+ THEN("the command is 'change'") { REQUIRE(cli.command == "change"); }
+ }
+ }
+ }
+ }
+
+} // namespace wanda::tests::app::wandac