aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@gmail.com>2018-11-29 18:29:34 +0100
committerFelix Morgner <felix.morgner@gmail.com>2018-11-29 18:29:34 +0100
commitca992f4f76d09965e4e62c805daa02b23266a224 (patch)
tree07eaeae4e288832306c353dd46cd3c2e52b8b5db
parentd018603b1ff8e93902e8c9c904199f54076154c4 (diff)
downloadwanda-ca992f4f76d09965e4e62c805daa02b23266a224.tar.xz
wanda-ca992f4f76d09965e4e62c805daa02b23266a224.zip
control: begin control interface implementation
-rw-r--r--.vscode/settings.json50
-rw-r--r--CMakeLists.txt8
-rw-r--r--cmake/Modules/SystemDependencies.cmake6
-rw-r--r--conanfile.py1
-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
10 files changed, 414 insertions, 2 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 7192bf1..4b30768 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -11,6 +11,54 @@
"string": "cpp",
"variant": "cpp",
"string_view": "cpp",
- "ostream": "cpp"
+ "ostream": "cpp",
+ "cctype": "cpp",
+ "clocale": "cpp",
+ "cmath": "cpp",
+ "csignal": "cpp",
+ "cstdarg": "cpp",
+ "cstddef": "cpp",
+ "cstdio": "cpp",
+ "cstdlib": "cpp",
+ "cstring": "cpp",
+ "ctime": "cpp",
+ "cwchar": "cpp",
+ "cwctype": "cpp",
+ "array": "cpp",
+ "atomic": "cpp",
+ "*.tcc": "cpp",
+ "bitset": "cpp",
+ "chrono": "cpp",
+ "codecvt": "cpp",
+ "complex": "cpp",
+ "condition_variable": "cpp",
+ "cstdint": "cpp",
+ "deque": "cpp",
+ "list": "cpp",
+ "unordered_map": "cpp",
+ "vector": "cpp",
+ "fstream": "cpp",
+ "functional": "cpp",
+ "future": "cpp",
+ "iomanip": "cpp",
+ "iosfwd": "cpp",
+ "iostream": "cpp",
+ "istream": "cpp",
+ "limits": "cpp",
+ "memory": "cpp",
+ "mutex": "cpp",
+ "new": "cpp",
+ "numeric": "cpp",
+ "ratio": "cpp",
+ "sstream": "cpp",
+ "stdexcept": "cpp",
+ "streambuf": "cpp",
+ "system_error": "cpp",
+ "thread": "cpp",
+ "tuple": "cpp",
+ "typeindex": "cpp",
+ "typeinfo": "cpp",
+ "__config": "cpp",
+ "__nullptr": "cpp"
}
} \ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e82b59e..d90fecc 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -24,9 +24,14 @@ add_executable("wanda"
"src/main.cpp"
# Components
+ "src/control_connection.cpp"
+ "src/control_connection.hpp"
+ "src/control_interface.cpp"
+ "src/control_interface.hpp"
"src/deferred_failure.hpp"
"src/filesystem.cpp"
"src/filesystem.hpp"
+ "src/keyed.hpp"
"src/optional.hpp"
"src/setting.cpp"
"src/setting.hpp"
@@ -34,7 +39,10 @@ add_executable("wanda"
)
target_link_libraries("wanda"
+ "CONAN_PKG::boost_asio"
"CONAN_PKG::boost_config"
+ "CONAN_PKG::boost_program_options"
"SYSTEM::C++FS"
"SYSTEM::GIO"
+ "Threads::Threads"
)
diff --git a/cmake/Modules/SystemDependencies.cmake b/cmake/Modules/SystemDependencies.cmake
index 8200824..c970bfe 100644
--- a/cmake/Modules/SystemDependencies.cmake
+++ b/cmake/Modules/SystemDependencies.cmake
@@ -11,4 +11,8 @@ pkg_check_modules("GIO" "gio-2.0" REQUIRED)
add_library("SYSTEM::GIO" INTERFACE IMPORTED)
set_property(TARGET "SYSTEM::GIO" PROPERTY INTERFACE_LINK_LIBRARIES ${GIO_LIBRARIES})
set_property(TARGET "SYSTEM::GIO" PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${GIO_INCLUDE_DIRS})
-set_property(TARGET "SYSTEM::GIO" PROPERTY INTERFACE_COMPILE_OPTIONS ${GIO_CFLAGS} ${GIO_CFLAGS_OTHER}) \ No newline at end of file
+set_property(TARGET "SYSTEM::GIO" PROPERTY INTERFACE_COMPILE_OPTIONS ${GIO_CFLAGS} ${GIO_CFLAGS_OTHER})
+
+set(CMAKE_THREAD_PREFER_PTHREAD ON)
+set(THREADS_PREFER_PTHREAD_FLAG ON)
+find_package("Threads") \ No newline at end of file
diff --git a/conanfile.py b/conanfile.py
index a77ff14..26b6e47 100644
--- a/conanfile.py
+++ b/conanfile.py
@@ -25,6 +25,7 @@ class Wanda(ConanFile):
requires = (
"boost_program_options/1.67.0@bincrafters/stable",
"boost_iterator/1.67.0@bincrafters/stable",
+ "boost_asio/1.67.0@bincrafters/stable",
)
def build(self):
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"; };
}