aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/control_connection.cpp86
-rw-r--r--src/control_connection.hpp76
-rw-r--r--src/control_interface.cpp106
-rw-r--r--src/control_interface.hpp55
-rw-r--r--src/keyed.hpp20
-rw-r--r--src/main.cpp8
6 files changed, 351 insertions, 0 deletions
diff --git a/src/control_connection.cpp b/src/control_connection.cpp
new file mode 100644
index 0000000..88a136e
--- /dev/null
+++ b/src/control_connection.cpp
@@ -0,0 +1,86 @@
+#include "control_connection.hpp"
+
+namespace wanda
+{
+
+control_connection::pointer make_control_connection(control_connection::protocol::socket &&socket)
+{
+ return std::make_shared<control_connection>(control_connection::key{}, std::move(socket));
+}
+
+control_connection::control_connection(control_connection::key key, control_connection::protocol::socket socket)
+ : keyed{key},
+ m_socket{std::move(socket)}
+{
+}
+
+bool control_connection::add(std::shared_ptr<control_connection::listener> listener)
+{
+ auto [_, inserted] = m_listeners.insert(listener);
+ return inserted;
+}
+
+bool control_connection::remove(std::shared_ptr<control_connection::listener> listener)
+{
+ return m_listeners.erase(listener);
+}
+
+void control_connection::start()
+{
+ if (!m_running)
+ {
+ m_running = true;
+ perform_read();
+ }
+}
+
+void control_connection::close()
+{
+ if (auto error = boost::system::error_code{}; m_socket.cancel(error))
+ {
+ for (auto &listener : m_listeners)
+ {
+ listener->on_error(shared_from_this(), error);
+ }
+ }
+
+ if (auto error = boost::system::error_code{}; m_socket.close(error))
+ {
+ for (auto &listener : m_listeners)
+ {
+ listener->on_error(shared_from_this(), error);
+ }
+ }
+
+ for (auto &listener : m_listeners)
+ {
+ listener->on_close(shared_from_this());
+ }
+ m_listeners.clear();
+}
+
+void control_connection::perform_read()
+{
+ boost::asio::async_read_until(m_socket, m_in, '\n', [that = shared_from_this(), this](auto const &error, auto const length) {
+ if (error)
+ {
+ for (auto &listener : m_listeners)
+ {
+ listener->on_error(shared_from_this(), error);
+ }
+ close();
+ }
+ else
+ {
+ std::string message{};
+ std::getline(m_input, message);
+ for (auto &listener : m_listeners)
+ {
+ listener->on_received(shared_from_this(), message);
+ }
+ perform_read();
+ }
+ });
+}
+
+} // namespace wanda \ No newline at end of file
diff --git a/src/control_connection.hpp b/src/control_connection.hpp
new file mode 100644
index 0000000..e868f99
--- /dev/null
+++ b/src/control_connection.hpp
@@ -0,0 +1,76 @@
+#ifndef WANDA_CONTROL_CONNECTION_HPP
+#define WANDA_CONTROL_CONNECTION_HPP
+
+#include "keyed.hpp"
+
+#include <boost/asio.hpp>
+
+#include <memory>
+#include <set>
+#include <string>
+
+namespace wanda
+{
+
+struct control_connection : keyed<control_connection>, std::enable_shared_from_this<control_connection>
+{
+ using protocol = boost::asio::local::stream_protocol;
+ using pointer = std::shared_ptr<control_connection>;
+
+ struct listener
+ {
+ virtual void on_close(pointer connection) {}
+ virtual void on_received(pointer connection, std::string message) {}
+ virtual void on_error(pointer connection, boost::system::error_code) {}
+ };
+
+ /**
+ * @internal
+ * @brief Construct a new control connection object
+ *
+ * @param socket The socket connected to the remote control endpoint
+ */
+ control_connection(key, protocol::socket socket);
+
+ /**
+ * @brief Add the given listener to this control connection's listener set
+ *
+ * @returns <code>true</code> iff. the listener was not already in the listener set
+ */
+ bool add(std::shared_ptr<listener> listener);
+
+ /**
+ * @brief Remove the given listener from this control connection's listener set
+ *
+ * @return <code>true</code> iff. the listener was previously registered with this control connection
+ */
+ bool remove(std::shared_ptr<listener> listener);
+
+ /**
+ * @brief Start I/O processing for this control connection
+ */
+ void start();
+
+ /**
+ * @brief Close this control connection
+ */
+ void close();
+
+ private:
+ friend pointer make_control_connection(protocol::socket &&socket);
+
+ void perform_read();
+
+ protocol::socket m_socket;
+ boost::asio::streambuf m_in{};
+ boost::asio::streambuf m_out{};
+ std::istream m_input{&m_in};
+ std::set<std::shared_ptr<listener>> m_listeners{};
+ bool m_running{};
+};
+
+control_connection::pointer make_control_connection(control_connection::protocol::socket &&socket);
+
+} // namespace wanda
+
+#endif \ No newline at end of file
diff --git a/src/control_interface.cpp b/src/control_interface.cpp
new file mode 100644
index 0000000..b662da5
--- /dev/null
+++ b/src/control_interface.cpp
@@ -0,0 +1,106 @@
+#include "control_interface.hpp"
+
+#include <boost/system/error_code.hpp>
+
+#include <algorithm>
+#include <iterator>
+#include <string>
+#include <utility>
+
+#include <iostream>
+
+namespace wanda
+{
+
+// 'socket_deleter' implementation
+
+socket_deleter::~socket_deleter()
+{
+ if (std::filesystem::exists(path))
+ {
+ std::filesystem::remove(path);
+ }
+}
+
+// 'control_interface' implementation
+
+control_interface::control_interface(control_interface::key key, boost::asio::io_service &service, control_interface::protocol::endpoint endpoint)
+ : keyed{key},
+ m_service{service},
+ m_endpoint{std::move(endpoint)},
+ m_socket{m_service},
+ m_acceptor{m_service}
+{
+}
+
+boost::system::error_code control_interface::start()
+{
+ if (auto error = boost::system::error_code{}; m_acceptor.open(m_endpoint.protocol(), error))
+ {
+ return error;
+ }
+
+ if (auto error = boost::system::error_code{}; m_acceptor.set_option(boost::asio::socket_base::reuse_address(true), error))
+ {
+ return error;
+ }
+
+ if (auto error = boost::system::error_code{}; m_acceptor.bind(m_endpoint, error))
+ {
+ return error;
+ }
+
+ if (auto error = boost::system::error_code{}; m_acceptor.listen(128, error))
+ {
+ return error;
+ }
+ else
+ {
+ perform_accept();
+ return error;
+ }
+}
+
+void control_interface::perform_accept()
+{
+ m_acceptor.async_accept(m_socket, [that = shared_from_this(), this](auto const &error) {
+ if (error)
+ {
+ // TODO: Handle error
+ }
+ else
+ {
+ auto [connection, inserted] = m_connections.insert(make_control_connection(std::move(m_socket)));
+ if(inserted)
+ {
+ std::cout << "Accepted a new connection\n";
+ (*connection)->add(shared_from_this());
+ (*connection)->start();
+ }
+ perform_accept();
+ }
+ });
+}
+
+void control_interface::on_received(control_connection::pointer, std::string message)
+{
+ std::cout << "Received '" << message << "'\n";
+}
+
+void control_interface::on_close(control_connection::pointer connection)
+{
+ std::cout << "Connection closed\n";
+ m_connections.erase(connection);
+}
+
+control_interface::pointer make_interface(boost::asio::io_service &service, std::filesystem::path file)
+{
+ if (std::filesystem::exists(file))
+ {
+ std::filesystem::remove(file);
+ }
+ control_interface::protocol::endpoint endpoint{file.string()};
+ return std::make_shared<control_interface>(control_interface::key{}, service, std::move(endpoint));
+}
+
+} // namespace wanda \ No newline at end of file
diff --git a/src/control_interface.hpp b/src/control_interface.hpp
new file mode 100644
index 0000000..f95362c
--- /dev/null
+++ b/src/control_interface.hpp
@@ -0,0 +1,55 @@
+#ifndef WANDA_CONTROL_INTERFACE_HPP
+#define WANDA_CONTROL_INTERFACE_HPP
+
+#include "control_connection.hpp"
+#include "keyed.hpp"
+
+#include <boost/asio.hpp>
+#include <boost/system/error_code.hpp>
+
+#include <filesystem>
+#include <istream>
+#include <memory>
+#include <set>
+#include <string>
+
+namespace wanda
+{
+
+struct socket_deleter
+{
+ ~socket_deleter();
+
+ std::filesystem::path path;
+};
+
+struct control_interface : control_connection::listener, keyed<control_interface>, std::enable_shared_from_this<control_interface>
+{
+ using protocol = boost::asio::local::stream_protocol;
+ using pointer = std::shared_ptr<control_interface>;
+
+ control_interface(key, boost::asio::io_service &service, protocol::endpoint endpoint);
+
+ boost::system::error_code start();
+
+ void on_received(control_connection::pointer connection, std::string message) override;
+ void on_close(control_connection::pointer) override;
+
+ private:
+ void perform_accept();
+
+ friend pointer make_interface(boost::asio::io_service &service, std::filesystem::path file);
+
+ boost::asio::io_service &m_service;
+ protocol::endpoint m_endpoint;
+ protocol::socket m_socket;
+ protocol::acceptor m_acceptor;
+ socket_deleter m_deleter{m_endpoint.path()};
+ std::set<control_connection::pointer> m_connections;
+};
+
+control_interface::pointer make_interface(boost::asio::io_service &service, std::filesystem::path file);
+
+} // namespace wanda
+
+#endif \ No newline at end of file
diff --git a/src/keyed.hpp b/src/keyed.hpp
new file mode 100644
index 0000000..de3500e
--- /dev/null
+++ b/src/keyed.hpp
@@ -0,0 +1,20 @@
+#ifndef WANDA_KEYED_HPP
+#define WANDA_KEYED_HPP
+
+namespace wanda
+{
+
+template <typename Derived>
+struct keyed
+{
+ protected:
+ struct key
+ {
+ };
+
+ explicit keyed(key) {}
+};
+
+} // namespace wanda
+
+#endif \ No newline at end of file
diff --git a/src/main.cpp b/src/main.cpp
index 6d1353d..1d6f2bd 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,6 +1,7 @@
#include "filesystem.hpp"
#include "setting.hpp"
#include "optional.hpp"
+#include "control_interface.hpp"
#include <iostream>
#include <set>
@@ -46,5 +47,12 @@ int main()
auto wallpaper = wanda::random_pick(list);
std::cout << "changing wallpaper to " << wallpaper << '\n';
set_wallpaper(wallpaper);
+
+ auto service = boost::asio::io_service{};
+ auto interface = wanda::make_interface(service, ".wanda_interface");
+ std::cout << interface.use_count() << '\n';
+ auto status = interface->start();
+ std::cout << status << ' ' << status.message() << '\n';
+ service.run();
}) || [] { std::cerr << "Directory does not exist\n"; };
}