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:
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;
}