diff --git a/CMakeLists.txt b/CMakeLists.txt index 75abd4a..717275f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -84,13 +84,21 @@ FetchContent_Declare( FetchContent_MakeAvailable(SQLiteCpp) FetchContent_Declare( - cpptrace - GIT_REPOSITORY https://github.com/jeremy-rifkin/cpptrace.git - GIT_TAG "v1.0.1" + cpptrace + GIT_REPOSITORY https://github.com/jeremy-rifkin/cpptrace.git + GIT_TAG "v1.0.1" GIT_SHALLOW 1 ) FetchContent_MakeAvailable(cpptrace) +FetchContent_Declare( + tomlplusplus + GIT_REPOSITORY https://github.com/marzer/tomlplusplus + GIT_TAG "v3.4.0" + GIT_SHALLOW 1 +) +FetchContent_MakeAvailable(tomlplusplus) + add_subdirectory(vendor) find_program(WAYLAND_SCANNER wayland-scanner REQUIRED) @@ -189,6 +197,7 @@ add_custom_target(generate_protocols ALL add_executable(waylight ${GEN_C_PRIVATES} + ${CMAKE_CURRENT_SOURCE_DIR}/src/Config.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/IconRegistry.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Cache.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/InotifyWatcher.cpp @@ -215,6 +224,7 @@ target_link_libraries(waylight PRIVATE PkgConfig::FONTCONFIG PkgConfig::HARFBUZZ + tomlplusplus::tomlplusplus cpptrace::cpptrace tinyfiledialogs mINI diff --git a/flake.nix b/flake.nix index f6a1a93..da208a8 100644 --- a/flake.nix +++ b/flake.nix @@ -29,8 +29,8 @@ fontconfig harfbuzz sqlite - zenity - boost + zenity + boost ]; buildInputs = with pkgs; [ cmake diff --git a/src/App.cpp b/src/App.cpp index 8d7cb78..bbf1301 100644 --- a/src/App.cpp +++ b/src/App.cpp @@ -8,7 +8,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -159,6 +161,25 @@ App::App() std::filesystem::create_directories(m_data_home_dir); } + { + auto const env = getenv("XDG_CONFIG_HOME"); + if (env && *env) { + if (std::filesystem::exists(env)) { + m_config_home_dir = env; + } + } + if (m_config_home_dir.empty()) { + auto const home = getenv("HOME"); + assert(home && *home); + m_config_home_dir = std::filesystem::path(home) / ".config"; + std::filesystem::create_directories(m_config_home_dir); + } + m_config_home_dir /= "waylight"; + std::filesystem::create_directories(m_config_home_dir); + + m_config = Config::load(m_config_home_dir); + } + m_db = std::make_shared(m_data_home_dir / "data.db", SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE); @@ -1243,4 +1264,65 @@ auto App::clipboard(std::string_view const &str) -> void wl_display_flush(m_wayland.display); } +void App::execute_command(bool terminal, std::string_view const command) +{ + constexpr auto resolve_cmdline { [](std::string_view const cmdline, + std::vector &out) { + std::ranges::copy(cmdline | std::views::split(' ') + | std::views::transform( + [](auto &&s) { return std::string(s.begin(), s.end()); }), + std::back_inserter(out)); + } }; + + std::vector args; + if (terminal) { + resolve_cmdline(m_config.terminal_cmdline, args); + } else { + args.push_back("/bin/sh"); + args.push_back("-c"); + } + + args.push_back(std::string(command)); + if (auto const &exe { args.at(0) }; exe.at(0) != '/') { + auto const *path_env { getenv("PATH") }; + if (!(path_env && *path_env)) { + path_env = "/bin"; + } + auto const path { std::string(path_env) }; + + for (auto const &dir : + path | std::views::split(':') | std::views::transform([](auto &&s) { + return std::filesystem::path(s.begin(), s.end()); + }) | std::views::filter([](auto &&p) { + return std::filesystem::is_directory(p); + })) { + auto const path = dir / exe; + if (std::filesystem::is_regular_file(path)) { + args[0] = path.string(); + } + } + } + + std::print("Final args: "); + for (auto const &arg : args) { + std::print("{} ", arg); + } + std::println(""); + + std::vector cargs; + std::transform(args.begin(), args.end(), std::back_inserter(cargs), + [](auto &&s) { return s.c_str(); }); + cargs.push_back(nullptr); + auto cargsc { const_cast(cargs.data()) }; + + auto const pid = fork(); + if (pid == 0) { + setsid(); + + execv(args.at(0).c_str(), cargsc); + } else if (pid < 0) { + throw std::runtime_error("Failed to fork process"); + } +} + } // namespace Waylight diff --git a/src/App.hpp b/src/App.hpp index f256681..eb6dbc9 100644 --- a/src/App.hpp +++ b/src/App.hpp @@ -24,6 +24,7 @@ extern "C" { #include #include "Cache.hpp" +#include "Config.hpp" #include "IconRegistry.hpp" #include "ImGui.hpp" #include "TextRenderer.hpp" @@ -201,9 +202,6 @@ private: bool surrounding_dirty { false }; } m_ime; - // NOTE: Canonicalize first! - std::unordered_map m_textures; - auto get_texture(std::filesystem::path const &path) -> Texture2D const & { if (m_textures.contains(path)) { @@ -218,10 +216,16 @@ private: return m_textures[path]; } + void execute_command(bool terminal, std::string_view const command); + + // NOTE: Canonicalize first! + std::unordered_map m_textures; + enum_array m_themes { make_default_themes() }; Theme m_active_theme { Theme::Light }; IconRegistry m_ir; std::optional m_cache; + Config m_config; int m_win_w { 800 }; int m_win_h { 600 }; @@ -231,6 +235,7 @@ private: Color m_accent_color { 127, 127, 255, 255 }; std::filesystem::path m_data_home_dir {}; + std::filesystem::path m_config_home_dir {}; std::shared_ptr m_db {}; int m_sfd { -1 }; }; diff --git a/src/Config.cpp b/src/Config.cpp new file mode 100644 index 0000000..4d867f3 --- /dev/null +++ b/src/Config.cpp @@ -0,0 +1,47 @@ +#include "Config.hpp" + +#include +#include +#include + +#include + +namespace Waylight { + +void Config::write(std::filesystem::path const &path) +{ + std::ofstream f(path); + if (!f) { + throw std::runtime_error("Failed to open config file for writing"); + } + std::println(f, "[settings]"); + std::println(f, "terminal_cmdline=\"{}\"", terminal_cmdline); +} + +auto Config::load(std::filesystem::path const &config_dir_path) -> Config const +{ + if (!std::filesystem::is_directory(config_dir_path)) + throw std::runtime_error("Provided path is not a directory!"); + + Config cfg {}; + + std::filesystem::path path_config { config_dir_path / "config.toml" }; + if (!std::filesystem::is_regular_file(path_config)) { + try { + std::filesystem::remove_all(path_config); + } catch (std::exception const &e) { + } + cfg.write(path_config); + } + + std::println("Config file: {}", path_config.string()); + auto const tbl { toml::parse_file(path_config.string()) }; + auto const terminal_cmdline { tbl["settings"]["terminal_cmdline"].value_or( + "kitty -c") }; + + cfg.terminal_cmdline = terminal_cmdline; + + return cfg; +} + +} // namespace Waylight diff --git a/src/Config.hpp b/src/Config.hpp new file mode 100644 index 0000000..e2585c2 --- /dev/null +++ b/src/Config.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +namespace Waylight { + +struct Config { + std::string terminal_cmdline { "kitty" }; + + void write(std::filesystem::path const &path); + + static auto load(std::filesystem::path const &config_dir_path) + -> Config const; +}; + +} // namespace Waylight diff --git a/src/Tick.cpp b/src/Tick.cpp index f92d24b..0abf8b6 100644 --- a/src/Tick.cpp +++ b/src/Tick.cpp @@ -66,6 +66,12 @@ auto App::tick() -> void result.test(1)) { m_ime.surrounding_dirty = true; } else if (result.test(0)) { + if (text_input_data == "kitty") { + execute_command(false, "kitty"); + } else if (text_input_data == "nvim") { + execute_command(true, "nvim"); + } + text_input_data = ""; }