kranewl

A wlroots-based dynamic Wayland compositor, written in C++, configurable with Lua
git clone git://git.deurzen.net/kranewl
Log | Files | Refs | LICENSE

commit 7c18b07f8557d5bbbde2c5e00ce21fc7e01d993a
parent c8d6fb9fe79f78755fd1b06d9852355935adff36
Author: deurzen <m.deurzen@tum.de>
Date:   Tue, 17 May 2022 10:43:19 +0200

adds initial configuration handling routines

Diffstat:
Ainclude/kranewl/bindings.hh | 24++++++++++++++++++++++++
Ainclude/kranewl/conf/config.hh | 45+++++++++++++++++++++++++++++++++++++++++++++
Minclude/kranewl/conf/options.hh | 15++++++++++++---
Ainclude/kranewl/exec.hh | 5+++++
Ainclude/kranewl/geometry.hh | 145+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dinclude/kranewl/input/binding.hh | 3---
Minclude/kranewl/model.hh | 26+++++++++++++++++++++++---
Minclude/kranewl/server.hh | 2--
Minclude/version.hh | 4++--
Asrc/kranewl/conf/config.cc | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/kranewl/conf/options.cc | 83++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
Asrc/kranewl/exec.cc | 12++++++++++++
Asrc/kranewl/geometry.cc | 3+++
Dsrc/kranewl/input/binding.cc | 3---
Msrc/kranewl/main.cc | 11+++++++----
15 files changed, 428 insertions(+), 31 deletions(-)

diff --git a/include/kranewl/bindings.hh b/include/kranewl/bindings.hh @@ -0,0 +1,24 @@ +#pragma once + +#include <functional> +#include <optional> +#include <cstdint> + +class Model; +typedef class Client* Client_ptr; + +typedef + std::function<void(Model&)> + KeyAction; + +typedef + std::function<bool(Model&, Client_ptr)> + MouseAction; + +typedef + std::unordered_map<uint32_t, KeyAction> + KeyBindings; + +typedef + std::unordered_map<uint32_t, MouseAction> + MouseBindings; diff --git a/include/kranewl/conf/config.hh b/include/kranewl/conf/config.hh @@ -0,0 +1,45 @@ +#pragma once + +#include <kranewl/bindings.hh> + +#include <string> +#include <memory> + +extern "C" { +#include <lua5.4/lua.h> +#include <lua5.4/lauxlib.h> +#include <lua5.4/lualib.h> +} + +struct Config final { + KeyBindings key_bindings; + MouseBindings mouse_bindings; +}; + +class ConfigParser final { +public: + ConfigParser(std::string const& config_path) + : m_state(luaL_newstate()), + m_config_path(config_path) + { + luaL_openlibs(m_state.get()); + } + + Config generate_config() const noexcept; + +private: + struct lua_State_deleter { + void operator()(lua_State* L) const { + lua_close(L); + } + }; + + bool parse_decorations(Config&) const noexcept; + bool parse_outputs(Config&) const noexcept; + bool parse_commands(Config&) const noexcept; + bool parse_bindings(Config&) const noexcept; + + std::unique_ptr<lua_State, lua_State_deleter> m_state; + std::string const& m_config_path; + +}; diff --git a/include/kranewl/conf/options.hh b/include/kranewl/conf/options.hh @@ -1,10 +1,19 @@ #pragma once +#include <optional> #include <string> -struct Options { +struct Options final { + Options( + std::string const&& _config_path, + std::optional<std::string> _autostart_path + ) + : config_path(_config_path), + autostart_path(_autostart_path) + {} + std::string config_path; - std::string autostart; + std::optional<std::string> autostart_path; }; -Options parse_options(int, char **); +Options parse_options(int, char**) noexcept; diff --git a/include/kranewl/exec.hh b/include/kranewl/exec.hh @@ -0,0 +1,5 @@ +#pragma once + +#include <string> + +void exec_external(std::string& command); diff --git a/include/kranewl/geometry.hh b/include/kranewl/geometry.hh @@ -0,0 +1,145 @@ +#pragma once + +#include <ostream> + +enum class Edge { + Left, Right, Top, Bottom +}; + +enum class Corner { + TopLeft, TopRight, BottomLeft, BottomRight +}; + +enum class Direction { + Forward, Backward +}; + +struct Dim final { + int w; + int h; +}; + +inline bool +operator==(Dim const& lhs, Dim const& rhs) +{ + return lhs.w == rhs.w && lhs.h == rhs.h; +} + +inline std::ostream& +operator<<(std::ostream& os, Dim const& dim) { + return os << "(" << dim.w << "×" << dim.h << ")"; +} + +struct Pos final +{ + int x; + int y; + + static Pos + from_center_of_dim(Dim dim) noexcept + { + return Pos { + static_cast<int>(static_cast<float>(dim.w) / 2.f), + static_cast<int>(static_cast<float>(dim.h) / 2.f) + }; + } + + static bool + is_at_origin(Pos& pos) noexcept + { + return pos.x == 0 && pos.y == 0; + } +}; + +inline bool +operator==(Pos const& lhs, Pos const& rhs) +{ + return lhs.x == rhs.x && lhs.y == rhs.y; +} + +inline std::ostream& +operator<<(std::ostream& os, Pos const& pos) { + return os << "(" << pos.x << ", " << pos.y << ")"; +} + +inline Pos +operator+(Pos const& pos1, Pos const& pos2) +{ + return Pos{ + pos1.x + pos2.x, + pos1.y + pos2.y + }; +} + +inline Pos +operator-(Pos const& pos1, Pos const& pos2) +{ + return Pos{ + pos1.x - pos2.x, + pos1.y - pos2.y + }; +} + +inline Pos +operator+(Pos const& pos, Dim const& dim) +{ + return Pos{ + pos.x + dim.w, + pos.y + dim.h + }; +} + +inline Pos +operator-(Pos const& pos, Dim const& dim) +{ + return Pos{ + pos.x - dim.w, + pos.y - dim.h + }; +} + +struct Padding final +{ + int left; + int right; + int top; + int bottom; +}; + +typedef Padding Extents; + +inline std::ostream& +operator<<(std::ostream& os, Padding const& padding) { + return os << "[" << padding.left + << "; " << padding.top + << "; " << padding.right + << "; " << padding.bottom << "]"; +} + +struct Region final +{ + Pos pos; + Dim dim; +}; + +inline bool +operator==(Region const& lhs, Region const& rhs) +{ + return lhs.pos == rhs.pos && lhs.dim == rhs.dim; +} + +inline std::ostream& +operator<<(std::ostream& os, Region const& region) { + return os << "[" << region.pos << " " << region.dim << "]"; +} + +struct Distance final +{ + int dx; + int dy; +}; + +inline std::ostream& +operator<<(std::ostream& os, Distance const& dist) { + return os << "𝛿(" << dist.dx << ", " << dist.dy << ")"; +} diff --git a/include/kranewl/input/binding.hh b/include/kranewl/input/binding.hh @@ -1,3 +0,0 @@ -#pragma once - - diff --git a/include/kranewl/model.hh b/include/kranewl/model.hh @@ -1,13 +1,32 @@ #pragma once +#include <kranewl/conf/config.hh> +#include <kranewl/exec.hh> #include <kranewl/server.hh> +#include <spdlog/spdlog.h> + +#include <optional> +#include <string> + class Model final { public: - Model(Server& server) - : m_server(server) - {} + Model( + Server& server, + Config const& config, + [[maybe_unused]] std::optional<std::string> autostart + ) + : m_server(server), + m_config(config) + { +#ifdef NDEBUG + if (autostart) { + spdlog::info("Executing autostart at " + config_path); + exec_external(*autostart); + } +#endif + } ~Model() {} @@ -15,5 +34,6 @@ public: private: Server& m_server; + Config const& m_config; }; diff --git a/include/kranewl/server.hh b/include/kranewl/server.hh @@ -18,6 +18,4 @@ public: private: std::string m_wm_name; - - }; diff --git a/include/version.hh b/include/version.hh @@ -1 +1 @@ -#define VERSION "master/b9e55e2+" -\ No newline at end of file +#define VERSION "master/c8d6fb9+" +\ No newline at end of file diff --git a/src/kranewl/conf/config.cc b/src/kranewl/conf/config.cc @@ -0,0 +1,78 @@ +#include <kranewl/conf/config.hh> + +#include <spdlog/spdlog.h> + +extern "C" { +#include <lua5.4/lua.h> +#include <lua5.4/lauxlib.h> +#include <lua5.4/lualib.h> +} + +Config +ConfigParser::generate_config() const noexcept +{ + Config config; + spdlog::info("Generating config from " + m_config_path); + + if (luaL_dofile(m_state.get(), m_config_path.c_str()) != LUA_OK) { + std::string error_msg = lua_tostring(m_state.get(), -1); + spdlog::error("Error generating config: " + error_msg); + spdlog::warn("Falling back to default configuration"); + return config; + } + + parse_decorations(config); + parse_outputs(config); + parse_commands(config); + parse_bindings(config); + + return config; +} + +bool +ConfigParser::parse_decorations(Config& config) const noexcept +{ + static_cast<void>(config); + + lua_getglobal(m_state.get(), "decorations"); + if (lua_istable(m_state.get(), -1)) + ; // TODO + + return true; +} + +bool +ConfigParser::parse_outputs(Config& config) const noexcept +{ + static_cast<void>(config); + + lua_getglobal(m_state.get(), "outputs"); + if (lua_istable(m_state.get(), -1)) + ; // TODO + + return true; +} + +bool +ConfigParser::parse_commands(Config& config) const noexcept +{ + static_cast<void>(config); + + lua_getglobal(m_state.get(), "commands"); + if (lua_istable(m_state.get(), -1)) + ; // TODO + + return true; +} + +bool +ConfigParser::parse_bindings(Config& config) const noexcept +{ + static_cast<void>(config); + + lua_getglobal(m_state.get(), "bindings"); + if (lua_istable(m_state.get(), -1)) + ; // TODO + + return true; +} diff --git a/src/kranewl/conf/options.cc b/src/kranewl/conf/options.cc @@ -2,30 +2,88 @@ #include <kranewl/conf/options.hh> +#include <cassert> +#include <optional> #include <iostream> +#include <cstdlib> +#include <sys/stat.h> #include <unistd.h> -const std::string usage = "Usage: kranewl [...options]\n\n" - "Options: \n" - " -x <autostart_file> Path to an executable autostart file.\n" +static const std::string CONFIG_FILE = "kranewlrc.lua"; +static const std::string DEFAULT_CONFIG = "/etc/kranewl/" + CONFIG_FILE; +static const std::string USAGE = "usage: kranewl [...options]\n\n" + "options: \n" + " -a <autostart_file> Path to an executable autostart file.\n" " -c <config_file> Path to a configuration file.\n" " -h Prints this message.\n" " -v Prints the version."; +static std::string +default_user_path(std::string const& path) noexcept +{ + if (const char* prefix = std::getenv("XDG_CONFIG_HOME")) + return std::string{prefix} + + std::string{"/kranewl/"} + + path; + else + return std::string{std::getenv("HOME")} + + std::string{"/.config/kranewl/"} + + path; +} + +static bool +assert_permissions(std::string const& path, int mode) noexcept +{ + struct stat s; + return stat(path.c_str(), &s) == 0 + && S_ISREG(s.st_mode) + && !access(path.c_str(), mode); +} + +static std::string const& +resolve_config_path(std::string& path) noexcept +{ + if (!path.empty()) + if (assert_permissions(path, R_OK)) + return path; + + path.assign(default_user_path(CONFIG_FILE)); + if (assert_permissions(path, R_OK)) + return path; + + return DEFAULT_CONFIG; +} + +static std::optional<std::string> +resolve_autostart_path(std::string& path) noexcept +{ + const int mode = R_OK | X_OK; + + if (!path.empty()) + if (assert_permissions(path, mode)) + return {path}; + + path.assign(default_user_path("autostart")); + if (assert_permissions(path, mode)) + return {path}; + + return {}; +} + Options -parse_options(int argc, char **argv) +parse_options(int argc, char** argv) noexcept { - Options options; + std::string autostart_path, config_path; int opt; - while ((opt = getopt(argc, argv, "vhx:c:")) != -1) { + while ((opt = getopt(argc, argv, "vha:c:")) != -1) { switch (opt) { - case 'x': - options.autostart = optarg; + case 'a': + autostart_path = optarg; break; case 'c': - options.config_path = optarg; + config_path = optarg; break; case 'v': @@ -36,11 +94,14 @@ parse_options(int argc, char **argv) case '?': case 'h': default: - std::cout << usage << std::endl << std::flush; + std::cout << USAGE << std::endl << std::flush; exit(EXIT_SUCCESS); break; } } - return options; + return Options( + std::move(resolve_config_path(config_path)), + resolve_autostart_path(autostart_path) + ); } diff --git a/src/kranewl/exec.cc b/src/kranewl/exec.cc @@ -0,0 +1,12 @@ +#include <unistd.h> +#include <cstdlib> +#include <string> + +void +exec_external(std::string& command) { + if (!fork()) { + setsid(); + execl("/bin/sh", "/bin/sh", "-c", ("exec " + command).c_str(), NULL); + exit(EXIT_SUCCESS); + } +} diff --git a/src/kranewl/geometry.cc b/src/kranewl/geometry.cc @@ -0,0 +1,3 @@ +#include <kranewl/geometry.hh> + + diff --git a/src/kranewl/input/binding.cc b/src/kranewl/input/binding.cc @@ -1,3 +0,0 @@ -#include <kranewl/input/binding.hh> - - diff --git a/src/kranewl/main.cc b/src/kranewl/main.cc @@ -1,12 +1,13 @@ -#ifdef DEBUG +#ifndef NDEBUG #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG #endif #include <version.hh> +#include <kranewl/conf/config.hh> +#include <kranewl/conf/options.hh> #include <kranewl/model.hh> #include <kranewl/server.hh> -#include <kranewl/conf/options.hh> #include <spdlog/spdlog.h> @@ -15,12 +16,14 @@ int main(int argc, char** argv) { - Options options = parse_options(argc, argv); + const Options options = parse_options(argc, argv); spdlog::info("Initializing kranewl-" VERSION); + const ConfigParser config_parser{options.config_path}; + const Config config = config_parser.generate_config(); Server server{"kranewl"}; - Model{server}.run(); + Model{server, config, options.autostart_path}.run(); return EXIT_SUCCESS; }