diff --git a/CMakeLists.txt b/CMakeLists.txt index 27f91b5..9c561e8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,7 +82,7 @@ target_link_libraries(${PROJECT_NAME} PUBLIC ) set_target_properties(${PROJECT_NAME} PROPERTIES - CXX_CLANG_TIDY "clang-tidy;-header-filter=^${CMAKE_SOURCE_DIR}/src/.*;-checks=*,-some-disabled-checks,misc-const-correctness,modernize-use-nullptr,bugprone-branch-clone,bugprone-use-after-move,performance-unnecessary-value-param,hicpp-no-malloc,cppcoreguidelines-pro-type-const-cast,clang-analyzer-core.NullDereference,-fuchsia-overloaded-operator,-readability-identifier-length,-bugprone-easily-swappable-parameters,-fuchsia-trailing-return,-misc-non-private-member-variables-in-classes,-readability-math-missing-parentheses,-llvmlibc-implementation-in-namespace,-misc-include-cleaner,-modernize-use-std-print,-llvmlibc-restrict-system-libc-headers,-fuchsia-statically-constructed-objects,-cppcoreguidelines-avoid-non-const-global-variables,-llvmlibc-callee-namespace,-misc-use-anonymous-namespace,-cppcoreguidelines-avoid-magic-numbers,-readability-magic-numbers,-hicpp-uppercase-literal-suffix,-readability-uppercase-literal-suffix,-fuchsia-default-arguments-calls,-altera-unroll-loops,-fuchsia-default-arguments-declarations,-readability-function-cognitive-complexity,-altera-struct-pack-align,-altera-id-dependent-backward-branch,-cppcoreguidelines-pro-type-reinterpret-cast,-boost-use-ranges,-cppcoreguidelines-owning-memory,-readability-redundant-declaration,-bugprone-unchecked-optional-access,-readability-const-return-type,-llvm-namespace-comment,-google-readability-namespace-comments,-cert-arr39-c,-cert-con36-c,-cert-con54-cpp,-cert-ctr56-cpp,-cert-dcl03-c,-cert-dcl16-c,-cert-dcl37-c,-cert-dcl51-cpp,-cert-dcl54-cpp,-cert-dcl59-cpp,-cert-err09-cpp,-cert-err61-cpp,-cert-exp42-c,-cert-fio38-c,-cert-flp37-c,-cert-int09-c,-cert-msc24-c,-cert-msc30-c,-cert-msc32-c,-cert-msc33-c,-cert-msc54-cpp,-cert-oop11-cpp,-cert-oop54-cpp,-cert-pos44-c,-cert-pos47-c,-cert-sig30-c,-cert-str34-c,-clang-analyzer-core.BitwiseShift,-clang-analyzer-core.CallAndMessage,-clang-analyzer-core.DivideZero,-clang-analyzer-core.NonNullParamChecker,-clang-analyzer-core.NullDereference,-clang-analyzer-core.StackAddressEscape,-clang-analyzer-core.UndefinedBinaryOperatorResult,-clang-analyzer-core.VLASize,-clang-analyzer-core.uninitialized.ArraySubscript,-clang-analyzer-core.uninitialized.Assign,-clang-analyzer-core.uninitialized.Branch,-clang-analyzer-core.uninitialized.CapturedBlockVariable,-clang-analyzer-core.uninitialized.NewArraySize,-clang-analyzer-core.uninitialized.UndefReturn,-clang-analyzer-cplusplus.ArrayDelete,-clang-analyzer-cplusplus.InnerPointer,-clang-analyzer-cplusplus.Move,-clang-analyzer-cplusplus.NewDelete,-clang-analyzer-cplusplus.NewDeleteLeaks,-clang-analyzer-cplusplus.PlacementNew,-clang-analyzer-cplusplus.SelfAssignment,-clang-analyzer-cplusplus.StringChecker,-clang-analyzer-deadcode.DeadStores,-clang-analyzer-fuchsia.HandleChecker,-clang-analyzer-nullability.NullPassedToNonnull,-clang-analyzer-nullability.NullReturnedFromNonnull,-clang-analyzer-nullability.NullableDereferenced,-clang-analyzer-nullability.NullablePassedToNonnull,-clang-analyzer-nullability.NullableReturnedFromNonnull,-clang-analyzer-optin.core.EnumCastOutOfRange,-clang-analyzer-optin.cplusplus.UninitializedObject,-clang-analyzer-optin.cplusplus.VirtualCall,-clang-analyzer-optin.mpi.MPI-Checker,-clang-analyzer-optin.osx.cocoa.localizability.EmptyLocalizationContextChecker,-clang-analyzer-optin.osx.cocoa.localizability.NonLocalizedStringChecker,-clang-analyzer-optin.performance.GCDAntipattern,-clang-analyzer-optin.performance.Padding,-clang-analyzer-optin.portability.UnixAPI,-clang-analyzer-optin.taint.TaintedAlloc,-clang-analyzer-osx.API,-clang-analyzer-osx.NumberObjectConversion,-clang-analyzer-osx.ObjCProperty,-clang-analyzer-osx.SecKeychainAPI,-clang-analyzer-osx.cocoa.AtSync,-clang-analyzer-osx.cocoa.AutoreleaseWrite,-clang-analyzer-osx.cocoa.ClassRelease,-clang-analyzer-osx.cocoa.Dealloc,-clang-analyzer-osx.cocoa.IncompatibleMethodTypes,-clang-analyzer-osx.cocoa.Loops,-clang-analyzer-osx.cocoa.MissingSuperCall,-clang-analyzer-osx.cocoa.NSAutoreleasePool,-clang-analyzer-osx.cocoa.NSError,-clang-analyzer-osx.cocoa.NilArg,-clang-analyzer-osx.cocoa.NonNilReturnValue,-clang-analyzer-osx.cocoa.ObjCGenerics,-clang-analyzer-osx.cocoa.RetainCount,-clang-analyzer-osx.cocoa.RunLoopAutoreleaseLeak,-clang-analyzer-osx.cocoa.SelfInit,-clang-analyzer-osx.cocoa.SuperDealloc,-clang-analyzer-osx.cocoa.UnusedIvars,-clang-analyzer-osx.cocoa.VariadicMethodTypes,-clang-analyzer-osx.coreFoundation.CFError,-clang-analyzer-osx.coreFoundation.CFNumber,-clang-analyzer-osx.coreFoundation.CFRetainRelease,-clang-analyzer-osx.coreFoundation.containers.OutOfBounds,-clang-analyzer-osx.coreFoundation.containers.PointerSizedValues,-clang-analyzer-security.FloatLoopCounter,-clang-analyzer-security.PutenvStackArray,-clang-analyzer-security.SetgidSetuidOrder,-clang-analyzer-security.cert.env.InvalidPtr,-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,-clang-analyzer-security.insecureAPI.UncheckedReturn,-clang-analyzer-security.insecureAPI.bcmp,-clang-analyzer-security.insecureAPI.bcopy,-clang-analyzer-security.insecureAPI.bzero,-clang-analyzer-security.insecureAPI.decodeValueOfObjCType,-clang-analyzer-security.insecureAPI.getpw,-clang-analyzer-security.insecureAPI.gets,-clang-analyzer-security.insecureAPI.mkstemp,-clang-analyzer-security.insecureAPI.mktemp,-clang-analyzer-security.insecureAPI.rand,-clang-analyzer-security.insecureAPI.strcpy,-clang-analyzer-security.insecureAPI.vfork,-clang-analyzer-unix.API,-clang-analyzer-unix.BlockInCriticalSection,-clang-analyzer-unix.Errno,-clang-analyzer-unix.Malloc,-clang-analyzer-unix.MallocSizeof,-clang-analyzer-unix.MismatchedDeallocator,-clang-analyzer-unix.StdCLibraryFunctions,-clang-analyzer-unix.Stream,-clang-analyzer-unix.Vfork,-clang-analyzer-unix.cstring.BadSizeArg,-clang-analyzer-unix.cstring.NullArg,-clang-analyzer-webkit.NoUncountedMemberChecker,-clang-analyzer-webkit.RefCntblBaseVirtualDtor,-clang-analyzer-webkit.UncountedLambdaCapturesChecker,-cppcoreguidelines-avoid-c-arrays,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-c-copy-assignment-signature,-cppcoreguidelines-explicit-virtual-functions,-cppcoreguidelines-macro-to-enum,-cppcoreguidelines-narrowing-conversions,-cppcoreguidelines-noexcept-destructor,-cppcoreguidelines-noexcept-move-operations,-cppcoreguidelines-noexcept-swap,-cppcoreguidelines-non-private-member-variables-in-classes,-cppcoreguidelines-use-default-member-init,-fuchsia-header-anon-namespaces,-google-readability-braces-around-statements,-google-readability-function-size,-google-readability-namespace-comments,-hicpp-avoid-c-arrays,-hicpp-avoid-goto,-hicpp-braces-around-statements,-hicpp-deprecated-headers,-hicpp-explicit-conversions,-hicpp-function-size,-hicpp-invalid-access-moved,-hicpp-member-init,-hicpp-move-const-arg,-hicpp-named-parameter,-hicpp-new-delete-operators,-hicpp-no-array-decay,-hicpp-no-malloc,-hicpp-noexcept-move,-hicpp-special-member-functions,-hicpp-static-assert,-hicpp-undelegated-constructor,-hicpp-uppercase-literal-suffix,-hicpp-use-auto,-hicpp-use-emplace,-hicpp-use-equals-default,-hicpp-use-equals-delete,-hicpp-use-noexcept,-hicpp-use-nullptr,-hicpp-use-override,-hicpp-vararg,-llvm-else-after-return,-llvm-qualified-auto,-llvm-header-guard" + CXX_CLANG_TIDY "clang-tidy;-header-filter=^${CMAKE_SOURCE_DIR}/src/.*;-checks=*,-some-disabled-checks,misc-const-correctness,modernize-use-nullptr,bugprone-branch-clone,bugprone-use-after-move,performance-unnecessary-value-param,hicpp-no-malloc,cppcoreguidelines-pro-type-const-cast,clang-analyzer-core.NullDereference,-fuchsia-overloaded-operator,-readability-identifier-length,-bugprone-easily-swappable-parameters,-fuchsia-trailing-return,-misc-non-private-member-variables-in-classes,-readability-math-missing-parentheses,-llvmlibc-implementation-in-namespace,-misc-include-cleaner,-modernize-use-std-print,-llvmlibc-restrict-system-libc-headers,-fuchsia-statically-constructed-objects,-cppcoreguidelines-avoid-non-const-global-variables,-llvmlibc-callee-namespace,-misc-use-anonymous-namespace,-cppcoreguidelines-avoid-magic-numbers,-readability-magic-numbers,-hicpp-uppercase-literal-suffix,-readability-uppercase-literal-suffix,-fuchsia-default-arguments-calls,-altera-unroll-loops,-fuchsia-default-arguments-declarations,-readability-function-cognitive-complexity,-altera-struct-pack-align,-altera-id-dependent-backward-branch,-cppcoreguidelines-pro-type-reinterpret-cast,-boost-use-ranges,-cppcoreguidelines-owning-memory,-readability-redundant-declaration,-bugprone-unchecked-optional-access,-readability-const-return-type,-llvm-namespace-comment,-google-readability-namespace-comments,-cert-arr39-c,-cert-con36-c,-cert-con54-cpp,-cert-ctr56-cpp,-cert-dcl03-c,-cert-dcl16-c,-cert-dcl37-c,-cert-dcl51-cpp,-cert-dcl54-cpp,-cert-dcl59-cpp,-cert-err09-cpp,-cert-err61-cpp,-cert-exp42-c,-cert-fio38-c,-cert-flp37-c,-cert-int09-c,-cert-msc24-c,-cert-msc30-c,-cert-msc32-c,-cert-msc33-c,-cert-msc54-cpp,-cert-oop11-cpp,-cert-oop54-cpp,-cert-pos44-c,-cert-pos47-c,-cert-sig30-c,-cert-str34-c,-clang-analyzer-core.BitwiseShift,-clang-analyzer-core.CallAndMessage,-clang-analyzer-core.DivideZero,-clang-analyzer-core.NonNullParamChecker,-clang-analyzer-core.NullDereference,-clang-analyzer-core.StackAddressEscape,-clang-analyzer-core.UndefinedBinaryOperatorResult,-clang-analyzer-core.VLASize,-clang-analyzer-core.uninitialized.ArraySubscript,-clang-analyzer-core.uninitialized.Assign,-clang-analyzer-core.uninitialized.Branch,-clang-analyzer-core.uninitialized.CapturedBlockVariable,-clang-analyzer-core.uninitialized.NewArraySize,-clang-analyzer-core.uninitialized.UndefReturn,-clang-analyzer-cplusplus.ArrayDelete,-clang-analyzer-cplusplus.InnerPointer,-clang-analyzer-cplusplus.Move,-clang-analyzer-cplusplus.NewDelete,-clang-analyzer-cplusplus.NewDeleteLeaks,-clang-analyzer-cplusplus.PlacementNew,-clang-analyzer-cplusplus.SelfAssignment,-clang-analyzer-cplusplus.StringChecker,-clang-analyzer-deadcode.DeadStores,-clang-analyzer-fuchsia.HandleChecker,-clang-analyzer-nullability.NullPassedToNonnull,-clang-analyzer-nullability.NullReturnedFromNonnull,-clang-analyzer-nullability.NullableDereferenced,-clang-analyzer-nullability.NullablePassedToNonnull,-clang-analyzer-nullability.NullableReturnedFromNonnull,-clang-analyzer-optin.core.EnumCastOutOfRange,-clang-analyzer-optin.cplusplus.UninitializedObject,-clang-analyzer-optin.cplusplus.VirtualCall,-clang-analyzer-optin.mpi.MPI-Checker,-clang-analyzer-optin.osx.cocoa.localizability.EmptyLocalizationContextChecker,-clang-analyzer-optin.osx.cocoa.localizability.NonLocalizedStringChecker,-clang-analyzer-optin.performance.GCDAntipattern,-clang-analyzer-optin.performance.Padding,-clang-analyzer-optin.portability.UnixAPI,-clang-analyzer-optin.taint.TaintedAlloc,-clang-analyzer-osx.API,-clang-analyzer-osx.NumberObjectConversion,-clang-analyzer-osx.ObjCProperty,-clang-analyzer-osx.SecKeychainAPI,-clang-analyzer-osx.cocoa.AtSync,-clang-analyzer-osx.cocoa.AutoreleaseWrite,-clang-analyzer-osx.cocoa.ClassRelease,-clang-analyzer-osx.cocoa.Dealloc,-clang-analyzer-osx.cocoa.IncompatibleMethodTypes,-clang-analyzer-osx.cocoa.Loops,-clang-analyzer-osx.cocoa.MissingSuperCall,-clang-analyzer-osx.cocoa.NSAutoreleasePool,-clang-analyzer-osx.cocoa.NSError,-clang-analyzer-osx.cocoa.NilArg,-clang-analyzer-osx.cocoa.NonNilReturnValue,-clang-analyzer-osx.cocoa.ObjCGenerics,-clang-analyzer-osx.cocoa.RetainCount,-clang-analyzer-osx.cocoa.RunLoopAutoreleaseLeak,-clang-analyzer-osx.cocoa.SelfInit,-clang-analyzer-osx.cocoa.SuperDealloc,-clang-analyzer-osx.cocoa.UnusedIvars,-clang-analyzer-osx.cocoa.VariadicMethodTypes,-clang-analyzer-osx.coreFoundation.CFError,-clang-analyzer-osx.coreFoundation.CFNumber,-clang-analyzer-osx.coreFoundation.CFRetainRelease,-clang-analyzer-osx.coreFoundation.containers.OutOfBounds,-clang-analyzer-osx.coreFoundation.containers.PointerSizedValues,-clang-analyzer-security.FloatLoopCounter,-clang-analyzer-security.PutenvStackArray,-clang-analyzer-security.SetgidSetuidOrder,-clang-analyzer-security.cert.env.InvalidPtr,-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,-clang-analyzer-security.insecureAPI.UncheckedReturn,-clang-analyzer-security.insecureAPI.bcmp,-clang-analyzer-security.insecureAPI.bcopy,-clang-analyzer-security.insecureAPI.bzero,-clang-analyzer-security.insecureAPI.decodeValueOfObjCType,-clang-analyzer-security.insecureAPI.getpw,-clang-analyzer-security.insecureAPI.gets,-clang-analyzer-security.insecureAPI.mkstemp,-clang-analyzer-security.insecureAPI.mktemp,-clang-analyzer-security.insecureAPI.rand,-clang-analyzer-security.insecureAPI.strcpy,-clang-analyzer-security.insecureAPI.vfork,-clang-analyzer-unix.API,-clang-analyzer-unix.BlockInCriticalSection,-clang-analyzer-unix.Errno,-clang-analyzer-unix.Malloc,-clang-analyzer-unix.MallocSizeof,-clang-analyzer-unix.MismatchedDeallocator,-clang-analyzer-unix.StdCLibraryFunctions,-clang-analyzer-unix.Stream,-clang-analyzer-unix.Vfork,-clang-analyzer-unix.cstring.BadSizeArg,-clang-analyzer-unix.cstring.NullArg,-clang-analyzer-webkit.NoUncountedMemberChecker,-clang-analyzer-webkit.RefCntblBaseVirtualDtor,-clang-analyzer-webkit.UncountedLambdaCapturesChecker,-cppcoreguidelines-avoid-c-arrays,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-c-copy-assignment-signature,-cppcoreguidelines-explicit-virtual-functions,-cppcoreguidelines-macro-to-enum,-cppcoreguidelines-narrowing-conversions,-cppcoreguidelines-noexcept-destructor,-cppcoreguidelines-noexcept-move-operations,-cppcoreguidelines-noexcept-swap,-cppcoreguidelines-non-private-member-variables-in-classes,-cppcoreguidelines-use-default-member-init,-fuchsia-header-anon-namespaces,-google-readability-braces-around-statements,-google-readability-function-size,-google-readability-namespace-comments,-hicpp-avoid-c-arrays,-hicpp-avoid-goto,-hicpp-braces-around-statements,-hicpp-deprecated-headers,-hicpp-explicit-conversions,-hicpp-function-size,-hicpp-invalid-access-moved,-hicpp-member-init,-hicpp-move-const-arg,-hicpp-named-parameter,-hicpp-new-delete-operators,-hicpp-no-array-decay,-hicpp-no-malloc,-hicpp-noexcept-move,-hicpp-special-member-functions,-hicpp-static-assert,-hicpp-undelegated-constructor,-hicpp-uppercase-literal-suffix,-hicpp-use-auto,-hicpp-use-emplace,-hicpp-use-equals-default,-hicpp-use-equals-delete,-hicpp-use-noexcept,-hicpp-use-nullptr,-hicpp-use-override,-hicpp-vararg,-llvm-else-after-return,-llvm-qualified-auto,-llvm-header-guard,-readability-else-after-return" ) # Wayland protocol codegen diff --git a/lunarwm/lunarwm.dcfg b/lunarwm/lunarwm.dcfg new file mode 100644 index 0000000..c77b06b --- /dev/null +++ b/lunarwm/lunarwm.dcfg @@ -0,0 +1,17 @@ +fn lib = { + input = { + keyboard = { + xkb_options = [ "altwin:swap_lalt_lwin" ] + } + } + + modifier_aliases = { + Main = "Super" + } + + keybindings = [ + { bind = "Main-Escape" action = (lib.quit_compositor) } + { bind = "Main-Shift-R" action = (lib.reload_config) } + ] +} + diff --git a/src/Config.cppm b/src/Config.cppm index 4623de3..fe427c4 100644 --- a/src/Config.cppm +++ b/src/Config.cppm @@ -1,5 +1,11 @@ module; +#include + +extern "C" { +#include +} + #include #include "dhos_config.h" @@ -18,7 +24,8 @@ static auto default_configuration_paths() "/etc/lunarwm/lunarwm.dcfg", }; - if (char const *xdg_dirs = std::getenv("XDG_CONFIG_DIRS")) { + if (char const *xdg_dirs + = std::getenv("XDG_CONFIG_DIRS")) { // NOLINT(concurrency-mt-unsafe) std::string const dirs { xdg_dirs }; std::istringstream ss { dirs }; std::string dir; @@ -31,7 +38,8 @@ static auto default_configuration_paths() } } - if (char const *home = std::getenv("HOME")) { + if (char const *home + = std::getenv("HOME")) { // NOLINT(concurrency-mt-unsafe) std::filesystem::path const home_path(home); paths.push_back(home_path / ".config/lunarwm/lunarwm.dcfg"); } @@ -39,12 +47,43 @@ static auto default_configuration_paths() return paths; } +static inline auto string_to_modifier(std::string_view str) + -> std::optional +{ + if (boost::iequals(str, "shift")) { + return WLR_MODIFIER_SHIFT; + } else if (boost::iequals(str, "ctrl")) { + return WLR_MODIFIER_CTRL; + } else if (boost::iequals(str, "alt")) { + return WLR_MODIFIER_ALT; + } else if (boost::iequals(str, "mod2")) { + return WLR_MODIFIER_MOD2; + } else if (boost::iequals(str, "mod3")) { + return WLR_MODIFIER_MOD3; + } else if (boost::iequals(str, "logo") || boost::iequals(str, "win") + || boost::iequals(str, "super") || boost::iequals(str, "mod4")) { + return WLR_MODIFIER_LOGO; + } else if (boost::iequals(str, "mod5")) { + return WLR_MODIFIER_MOD5; + } + return std::nullopt; +} + export namespace LunarWM::Config { struct Configuration { + dhos::Value e; + + struct KeyCombo { + uint32_t modifiers {}; + xkb_keysym_t sym {}; + + static auto parse(std::string const &str, + std::unordered_map const &modifier_alias) + -> std::optional; + }; struct Keybind { - uint32_t modifiers; - xkb_keysym_t sym; + KeyCombo combo; dhos::Value action; }; @@ -54,31 +93,89 @@ struct Configuration { } keyboard {}; } input {}; + std::unordered_map modifier_aliases; std::vector keybindings; dhos::Object custom_lib; - void execute_keybind(Keybind const &keybind) { + void execute_keybind(Keybind const &keybind) + { dhos::eval(keybind.action, &this->custom_lib); } - void add_to_custom_lib(std::string &name, dhos::Builtin fn) { - this->custom_lib[name] = dhos::Value{dhos::Builtin{fn}}; + void add_to_custom_lib(std::string const &name, dhos::Builtin const &fn) + { + this->custom_lib.insert_or_assign(name, dhos::Value(fn, e.env)); } void load() { this->load(default_configuration_paths()); } void load(std::vector const &paths); }; +auto Configuration::KeyCombo::parse(std::string const &str, + std::unordered_map const &modifier_alias) + -> std::optional +{ + std::istringstream iss(str); + std::string component; + std::vector components; + components.reserve(5); + while (std::getline(iss, component, '-')) { + boost::algorithm::trim(component); + if (component.empty()) { + continue; + } + components.push_back(component); + } + + if (components.size() < 2) { + return std::nullopt; + } + + wlr_keyboard_modifier mods {}; + for (auto const &entry : + std::span(components.begin(), components.end() - 1)) { + auto mod_opt = string_to_modifier(entry); + if (!mod_opt) { + if (modifier_alias.contains(entry)) { + mod_opt = static_cast( + modifier_alias.at(entry)); + } else { + return std::nullopt; + } + } + + mods = static_cast( + static_cast(mods) | static_cast(*mod_opt)); + } + + auto const &key_name = components.back(); + xkb_keysym_t const sym = xkb_keysym_from_name(key_name.c_str(), + static_cast(XKB_KEYSYM_CASE_INSENSITIVE)); + if (sym == XKB_KEY_NoSymbol) { + return std::nullopt; + } + + Configuration::KeyCombo const combo { + .modifiers = mods, + .sym = sym, + }; + return combo; +} + void Configuration::load(std::vector const &paths) { for (auto const &path : paths) { try { - auto e = dhos::eval(dhos::parse_config(path), this->custom_lib); + e = dhos::eval(dhos::parse_config(path), &this->custom_lib); if (!e.is_obj()) { throw std::runtime_error("Top level is not an object!"); } + static dhos::Object base = dhos::make_default_lib(); + auto merged = dhos::merge_lib(base, &this->custom_lib); + dhos::get_env_tbl(&*e.env)["lib"] = dhos::Value(merged, e.env); + if (e["input"].is_obj()) { auto const &input = e["input"]; if (input["keyboard"].is_obj()) { @@ -92,15 +189,80 @@ void Configuration::load(std::vector const &paths) value.str()); } else { // FIXME: Send warning + std::println("Invalid xkb_options type"); } } } } } + + if (e["modifier_aliases"].is_obj()) { + auto const &modifier_aliases = e["modifier_aliases"]; + this->modifier_aliases.clear(); + + for (auto const &[k, v] : modifier_aliases.obj()) { + if (!v.is_str()) { + // FIXME: Send warning + std::println("Invalid modifier aliases value"); + continue; + } else { + auto const to_parse = v.str() + "-a"; + std::println( + "Sending alias value to parse: {}", to_parse); + auto const kc_opt = KeyCombo::parse(to_parse, {}); + if (kc_opt) { + auto const mod_opt = kc_opt->modifiers; + this->modifier_aliases[k] = mod_opt; + } else { + // FIXME: Send warning + std::println( + "Invalid modifier aliases value format"); + } + } + } + } + + if (e["keybindings"].is_arr()) { + auto const &keybindings = e["keybindings"].arr(); + this->keybindings.clear(); + + for (auto const &v : keybindings) { + if (v.is_obj()) { + auto const &o = v.obj(); + if (!o.contains("bind") || !o.contains("action") + || !o.at("bind").is_str() + || !o.at("action").is_call()) { + // FIXME: Send warning + std::println("Invalid keybinding object format"); + continue; + } + + auto const combo_opt = KeyCombo::parse( + o.at("bind").str(), this->modifier_aliases); + if (!combo_opt) { + // FIXME: Send warning + std::println("Invalid keybinding combo"); + continue; + } + + auto const &combo = *combo_opt; + + this->keybindings.push_back({ + .combo = combo, + .action = o.at("action"), + }); + } else { + // FIXME: Send warning + } + } + } + + return; } catch (std::filesystem::filesystem_error const &e) { (void)e; } catch (std::exception &e) { (void)e; + std::println("Exception for config file: {}", e.what()); } } throw std::runtime_error("Unable to find a valid configuration file!"); diff --git a/src/LunarWM.cppm b/src/LunarWM.cppm index 5081f2d..9e0b89c 100644 --- a/src/LunarWM.cppm +++ b/src/LunarWM.cppm @@ -12,6 +12,7 @@ module; #include #include +#include #include #include @@ -38,6 +39,8 @@ extern "C" { #include // NOLINTEND +#include "dhos_config.h" + export module LunarWM.LunarWM; import std; @@ -155,79 +158,10 @@ private: Toplevel(Toplevel &&) = delete; auto operator=(Toplevel &&) -> Toplevel & = delete; - Toplevel(LunarWM *srv, wlr_xdg_toplevel *xdg) - : server(srv) - , xdg_toplevel(xdg) - , surface(xdg->base->surface) - { + Toplevel(LunarWM *srv, wlr_xdg_toplevel *xdg); + ~Toplevel(); - assert(surface); - - commit.notify = [](wl_listener *l, void *) { - auto *tl = wl_container_of( - l, static_cast(nullptr), commit); - - if (tl->xdg_toplevel->base->initial_commit) { - wlr_xdg_toplevel_set_size(tl->xdg_toplevel, 0, 0); - return; - } - tl->update(); - }; - wl_signal_add(&surface->events.commit, &commit); - - destroy.notify = [](wl_listener *l, void *) { - auto *tl = wl_container_of(l, (Toplevel *)nullptr, destroy); - - auto &vec = tl->server->m_wayland.toplevels; - auto const [first, last] = std::ranges::remove_if( - vec, [tl](auto &p) { return p.get() == tl; }); - vec.erase(first, last); - }; - wl_signal_add(&surface->events.destroy, &destroy); - } - - void update() - { - texture = wlr_surface_get_texture(surface); - struct wlr_client_buffer *cl_buf = surface->buffer; - if ((texture == nullptr) || (cl_buf == nullptr)) { - return; - } - - if ((locked_buffer != nullptr) && locked_buffer != &cl_buf->base) { - wlr_buffer_unlock(locked_buffer); - locked_buffer = nullptr; - } - if (locked_buffer == nullptr) { - locked_buffer = wlr_buffer_lock(&cl_buf->base); - } - - wlr_gles2_texture_get_attribs(texture, &attribs); - gles_texture = gles2_get_texture(texture); - if (gles_texture == nullptr) { - return; - } - - rl_texture.id = static_cast(attribs.tex); - rl_texture.width = static_cast(texture->width); - rl_texture.height = static_cast(texture->height); - rl_texture.mipmaps = 1; - rl_texture.format = gles_texture->has_alpha - ? PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 - : PIXELFORMAT_UNCOMPRESSED_R8G8B8; - - SetTextureFilter(rl_texture, TEXTURE_FILTER_BILINEAR); - SetTextureWrap(rl_texture, TEXTURE_WRAP_CLAMP); - } - - ~Toplevel() - { - wl_list_remove(&commit.link); - wl_list_remove(&destroy.link); - if (locked_buffer != nullptr) { - wlr_buffer_unlock(locked_buffer); - } - } + void update(); LunarWM *server {}; @@ -334,7 +268,7 @@ private: bool has_depth {}; }; - struct { + struct Renderer { GLuint fbo {}; RenderTexture2D tmp_rt {}; @@ -347,82 +281,6 @@ private: }; std::array hands {}; - - auto begin_render(GLuint color_tex, GLuint depth_tex, uint32_t w, - uint32_t h, XrView const &view) -> bool - { - if (fbo == 0u) { // create once - glGenFramebuffers(1, &fbo); - } - - rlFramebufferAttach(fbo, color_tex, RL_ATTACHMENT_COLOR_CHANNEL0, - RL_ATTACHMENT_TEXTURE2D, 0); - - assert(rlFramebufferComplete(fbo)); - glBindFramebuffer(GL_FRAMEBUFFER, fbo); - GLenum const fbStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (fbStatus != GL_FRAMEBUFFER_COMPLETE) { - std::println("FBO incomplete: 0x{:04x}", fbStatus); - return false; - } - - tmp_rt = { .id = fbo, - .texture = { color_tex, static_cast(w), - static_cast(h), 1, -1 }, - .depth = { depth_tex, static_cast(w), static_cast(h), - 1, -1 } }; - - BeginTextureMode(tmp_rt); - rlEnableStereoRender(); - - XrFovf const &fov = view.fov; - Matrix const proj = xr_projection_matrix(fov); - Matrix const viewM = MatrixInvert(xr_matrix(view.pose)); - - rlSetMatrixProjectionStereo(proj, proj); - rlSetMatrixViewOffsetStereo(viewM, viewM); - - return true; - } - - static void end_render(LunarWM & /*wm*/, SwapchainInfo & /*color_sc*/, - SwapchainInfo * /*depth_sc*/) - { - rlDisableStereoRender(); - EndTextureMode(); - } - - static void update_camera( - LunarWM &wm, Camera3D &camera, RenderLayerInfo &info) - { - XrTime const time = info.predicted_display_time; - - XrSpaceLocation view_location { XR_TYPE_SPACE_LOCATION }; - XrResult const result = xrLocateSpace( - wm.m_xr.view_space, wm.m_xr.local_space, time, &view_location); - if (result != XR_SUCCESS) { - return; - } - - if ((view_location.locationFlags - & XR_SPACE_LOCATION_POSITION_VALID_BIT) - != 0u) { - auto const &pos = view_location.pose.position; - camera.position = Vector3 { pos.x, pos.y, pos.z }; - } - if ((view_location.locationFlags - & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) - != 0u) { - auto const &rot = view_location.pose.orientation; - auto const forward - = Vector3RotateByQuaternion(Vector3 { 0, 0, -1 }, - Quaternion { rot.x, rot.y, rot.z, rot.w }); - auto const up = Vector3RotateByQuaternion(Vector3 { 0, 1, 0 }, - Quaternion { rot.x, rot.y, rot.z, rot.w }); - camera.target = camera.position + forward; - camera.up = up; - } - } } m_renderer; struct RenderLayerInfo { @@ -437,23 +295,118 @@ private: std::chrono::time_point m_last_tick; - Config::Configuration config {}; + Config::Configuration m_config {}; bool m_running {}; bool m_session_running {}; bool m_session_state {}; }; +LunarWM::Toplevel::Toplevel(LunarWM *srv, wlr_xdg_toplevel *xdg) + : server(srv) + , xdg_toplevel(xdg) + , surface(xdg->base->surface) +{ + + assert(surface); + + commit.notify = [](wl_listener *l, void *) { + auto *tl = wl_container_of(l, static_cast(nullptr), commit); + + if (tl->xdg_toplevel->base->initial_commit) { + wlr_xdg_toplevel_set_size(tl->xdg_toplevel, 0, 0); + return; + } + tl->update(); + }; + wl_signal_add(&surface->events.commit, &commit); + + destroy.notify = [](wl_listener *l, void *) { + auto *tl = wl_container_of(l, (Toplevel *)nullptr, destroy); + + auto &vec = tl->server->m_wayland.toplevels; + auto const [first, last] = std::ranges::remove_if( + vec, [tl](auto &p) { return p.get() == tl; }); + vec.erase(first, last); + }; + wl_signal_add(&surface->events.destroy, &destroy); +} + +LunarWM::Toplevel::~Toplevel() +{ + wl_list_remove(&commit.link); + wl_list_remove(&destroy.link); + if (locked_buffer != nullptr) { + wlr_buffer_unlock(locked_buffer); + } +} + +void LunarWM::Toplevel::update() +{ + texture = wlr_surface_get_texture(surface); + struct wlr_client_buffer *cl_buf = surface->buffer; + if ((texture == nullptr) || (cl_buf == nullptr)) { + return; + } + + if ((locked_buffer != nullptr) && locked_buffer != &cl_buf->base) { + wlr_buffer_unlock(locked_buffer); + locked_buffer = nullptr; + } + if (locked_buffer == nullptr) { + locked_buffer = wlr_buffer_lock(&cl_buf->base); + } + + wlr_gles2_texture_get_attribs(texture, &attribs); + gles_texture = gles2_get_texture(texture); + if (gles_texture == nullptr) { + return; + } + + rl_texture.id = static_cast(attribs.tex); + rl_texture.width = static_cast(texture->width); + rl_texture.height = static_cast(texture->height); + rl_texture.mipmaps = 1; + rl_texture.format = gles_texture->has_alpha + ? PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 + : PIXELFORMAT_UNCOMPRESSED_R8G8B8; + + SetTextureFilter(rl_texture, TEXTURE_FILTER_BILINEAR); + SetTextureWrap(rl_texture, TEXTURE_WRAP_CLAMP); +} + void LunarWM::init() { - // if (getenv("DISPLAY") != nullptr || getenv("WAYLAND_DISPLAY") != nullptr) - // { // NOLINT throw std::runtime_error("This compositor can only be ran in - // DRM mode"); - // } + m_config.add_to_custom_lib( + "reload_config", [&](dhos::Array const &) -> dhos::Value { + m_config.load(); + return dhos::Value {}; + }); + m_config.add_to_custom_lib( + "quit_compositor", [&](dhos::Array const &) -> dhos::Value { + this->terminate(); + return dhos::Value {}; + }); + + m_config.load(); + + std::println("Config.modifier_aliases:"); + for (auto const &[k, v] : m_config.modifier_aliases) { + std::println(" - {}: {}", k, v); + } + + std::println("Config.keybindings:"); + for (auto const &kb : m_config.keybindings) { + std::println(" - {}, {}", kb.combo.modifiers, kb.combo.sym); + } + + if (getenv("DISPLAY") != nullptr // NOLINT + || getenv("WAYLAND_DISPLAY") != nullptr) { // NOLINT + throw std::runtime_error("This compositor can only be ran in DRM mode"); + } this->init_wayland(); - wlr_log(WLR_INFO, "0"); auto *draw = eglGetCurrentSurface(EGL_DRAW); auto *read = eglGetCurrentSurface(EGL_READ); if (eglMakeCurrent(m_wayland.egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, @@ -563,7 +516,12 @@ void LunarWM::init_wayland() keyboard->server = wm; keyboard->wlr_keyboard = wlr_keyboard; - struct xkb_rule_names const rule_names {}; + std::string const options { boost::algorithm::join( + wm->m_config.input.keyboard.xkb_options, ",") }; + + struct xkb_rule_names const rule_names { + .options = options.c_str(), + }; struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); struct xkb_keymap *keymap = xkb_keymap_new_from_names( @@ -610,13 +568,13 @@ void LunarWM::init_wayland() wlr_session_change_vt(server->m_wayland.session, vt); return; } - if ((modifiers & WLR_MODIFIER_LOGO) - && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { - // NOLINTBEGIN - if (syms[XKB_KEY_Escape]) { - kbd->server->terminate(); + if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { + for (auto const &keybind : server->m_config.keybindings) { + if ((modifiers & keybind.combo.modifiers) + && syms[keybind.combo.sym]) { + server->m_config.execute_keybind(keybind); + } } - // NOLINTEND } if (!handled) { @@ -744,16 +702,16 @@ void LunarWM::init_xr() if (xrEnumerateInstanceExtensionProperties( nullptr, 0, &extensionCount, nullptr) != XR_SUCCESS) { - throw std::runtime_error( - "Failed to enumerate OpenXR instance extension properties"); + throw std::runtime_error("Failed to enumerate OpenXR " + "instance extension properties"); } extensionProperties.resize( extensionCount, { XR_TYPE_EXTENSION_PROPERTIES }); if (xrEnumerateInstanceExtensionProperties(nullptr, extensionCount, &extensionCount, extensionProperties.data()) != XR_SUCCESS) { - throw std::runtime_error( - "Failed to enumerate OpenXR instance extension properties"); + throw std::runtime_error("Failed to enumerate OpenXR " + "instance extension properties"); } for (auto &requestedInstanceExtension : instance_extensions) { @@ -910,8 +868,8 @@ void LunarWM::init_xr() if (xrEnumerateViewConfigurations( *m_xr.instance, *m_xr.system_id, 0, &count, nullptr) != XR_SUCCESS) { - throw std::runtime_error( - "Failed to get amount of OpenXR view configurations"); + throw std::runtime_error("Failed to get amount of " + "OpenXR view configurations"); } view_config_types.resize(count); if (xrEnumerateViewConfigurations(*m_xr.instance, *m_xr.system_id, @@ -924,8 +882,8 @@ void LunarWM::init_xr() if (!std::ranges::contains( view_config_types, XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO)) { - throw std::runtime_error( - "XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO not present"); + throw std::runtime_error("XR_VIEW_CONFIGURATION_TYPE_" + "PRIMARY_STEREO not present"); } { @@ -934,7 +892,8 @@ void LunarWM::init_xr() XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, 0, &count, nullptr) != XR_SUCCESS) { throw std::runtime_error( - "Failed to get amount of OpenXR view configuration views"); + "Failed to get amount of OpenXR view configuration " + "views"); } m_xr.view_configuration_views.resize( count, { XR_TYPE_VIEW_CONFIGURATION_VIEW }); @@ -942,8 +901,8 @@ void LunarWM::init_xr() XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, count, &count, m_xr.view_configuration_views.data()) != XR_SUCCESS) { - throw std::runtime_error( - "Failed to enumerate OpenXR view configuration views"); + throw std::runtime_error("Failed to enumerate OpenXR " + "view configuration views"); } } @@ -1164,7 +1123,8 @@ void LunarWM::init_xr() XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, 0, &count, nullptr) != XR_SUCCESS) { throw std::runtime_error( - "Failed to get OpenXR environment blend mode count"); + "Failed to get OpenXR environment blend mode " + "count"); } environment_blend_modes.resize(count); if (xrEnumerateEnvironmentBlendModes(*m_xr.instance, *m_xr.system_id, @@ -1275,11 +1235,13 @@ void LunarWM::poll_events_xr() &event_data) }; wlr_log(WLR_INFO, - "OPENXR: Interaction Profile changed for Session: %p", + "OPENXR: Interaction Profile changed for Session: " + "%p", ipc->session); if (ipc->session != m_xr.session) { wlr_log(WLR_ERROR, - "XrEventDataInteractionProfileChanged for unknown Session"); + "XrEventDataInteractionProfileChanged for " + "unknown Session"); break; } break; @@ -1290,11 +1252,13 @@ void LunarWM::poll_events_xr() &event_data) }; wlr_log(WLR_INFO, - "OPENXR: Reference Space Change pending for Session: %p", + "OPENXR: Reference Space Change pending for " + "Session: %p", scp->session); if (scp->session != m_xr.session) { wlr_log(WLR_ERROR, - "XrEventDataReferenceSpaceChangePending for unknown " + "XrEventDataReferenceSpaceChangePending for " + "unknown " "Session"); break; } @@ -1305,7 +1269,8 @@ void LunarWM::poll_events_xr() &event_data) }; if (sc->session != m_xr.session) { wlr_log(WLR_ERROR, - "XrEventDataSessionStateChanged for unknown Session"); + "XrEventDataSessionStateChanged for unknown " + "Session"); break; } @@ -1531,7 +1496,33 @@ auto LunarWM::render_layer(RenderLayerInfo &info, float dt) -> bool static_cast(eyeH)); ClearBackground({ 0, 0, 10, 255 }); - m_renderer.update_camera(*this, m_renderer.camera, info); + for (int i = 0; i < 1; i++) { + XrTime const time = info.predicted_display_time; + + XrSpaceLocation view_location { XR_TYPE_SPACE_LOCATION }; + XrResult const result = xrLocateSpace( + m_xr.view_space, m_xr.local_space, time, &view_location); + if (result != XR_SUCCESS) { + break; + } + + if ((view_location.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) + != 0u) { + auto const &pos = view_location.pose.position; + m_renderer.camera.position = Vector3 { pos.x, pos.y, pos.z }; + } + if ((view_location.locationFlags + & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) + != 0u) { + auto const &rot = view_location.pose.orientation; + auto const forward = Vector3RotateByQuaternion(Vector3 { 0, 0, -1 }, + Quaternion { rot.x, rot.y, rot.z, rot.w }); + auto const up = Vector3RotateByQuaternion( + Vector3 { 0, 1, 0 }, Quaternion { rot.x, rot.y, rot.z, rot.w }); + m_renderer.camera.target = m_renderer.camera.position + forward; + m_renderer.camera.up = up; + } + } BeginMode3D(m_renderer.camera); { @@ -1813,7 +1804,8 @@ void LunarWM::render_3d(float /*dt*/) DrawSphere(pos, jl.radius, RED); } - // DrawModel(m_renderer.hands.at(h), { 0, 0, 0 }, 1.0f, WHITE); + // DrawModel(m_renderer.hands.at(h), { 0, 0, 0 }, 1.0f, + // WHITE); } } diff --git a/src/dhos_config.cpp b/src/dhos_config.cpp index 9569ec5..344576d 100644 --- a/src/dhos_config.cpp +++ b/src/dhos_config.cpp @@ -3,6 +3,7 @@ // NOLINTBEGIN #include +#include #include #include #include @@ -35,17 +36,6 @@ struct Environment { }; namespace { - -// default lib -Object make_default_lib(); -Object merge_lib(Object const &base, Object const *extra) -{ - Object out = base; - if (extra) - out.insert(extra->begin(), extra->end()); - return out; -} - // env, lexer, parser enum class Tok { @@ -367,7 +357,33 @@ private: adv(); return Value { env }; } - return call(); + { + std::string idn = cur.t; + adv(); + + // make the base AST node + auto node = std::make_shared(); + node->recv = nullptr; // top‐level call + node->name = std::move(idn); + + // consume any “.field” chains + while (acc(Tok::Dot)) { + auto parent = node; + std::string fld = id(); + auto mnode = std::make_shared(); + mnode->recv = std::make_shared( + parent, env); // wrap the old AST + mnode->name = std::move(fld); + node = mnode; + } + + // then any args + while (begins(cur.k)) { + node->args.push_back(val()); + } + + return Value { node, env }; + } case Tok::Num: { std::string s = cur.t; s.erase(std::remove(s.begin(), s.end(), '_'), s.end()); @@ -452,14 +468,6 @@ Value imp(Array const &a) std::istringstream in { b.str() }; return parse_config(in); } -Object make_default_lib() -{ - Object l; - l["join"] = Builtin { join }; - l["list_from_range"] = Builtin { list_rng }; - l["import"] = Builtin { imp }; - return l; -} // serializer void w(std::ostream &o, Value const &v) @@ -513,35 +521,56 @@ void w(std::ostream &o, Value const &v) // public helpers Value eval(Value const &v, Object const *extra_lib) { + static Object base = make_default_lib(); + Object merged = merge_lib(base, extra_lib); + if (v.is_call()) { auto const &c = v.call(); - if (!v.env) - throw std::runtime_error( - std::format("Function call with no environment: {}", c.name)); - Value cal = (*v.env)[c.name]; - Array args; - for (auto &a : c.args) - args.push_back(eval(a, extra_lib)); - if (auto b = std::get_if(&cal.data)) - return (*b)(args); - if (auto fp - = std::get_if>(&cal.data)) { - Object lib; - return *(*fp)->body; + Value fn; + + // receiver or global + if (c.recv && !c.recv->is_nil()) { + Value recv = eval(*c.recv, extra_lib); + fn = recv[c.name]; + } else { + if (auto it = merged.find(c.name); it != merged.end()) + fn = it->second; + else + fn = (*v.env)[c.name]; } - return cal; + + // args + Array argv; + argv.reserve(c.args.size()); + for (auto &a : c.args) + argv.push_back(eval(a, extra_lib)); + + // call + if (auto b = std::get_if(&fn.data)) + return (*b)(argv); + + if (auto fp = std::get_if>(&fn.data)) { + auto child = std::make_shared(fn.env.get()); + for (size_t i = 0; i < (*fp)->params.size(); ++i) + child->tbl[(*fp)->params[i]] + = i < argv.size() ? argv[i] : Value(child); + + Value body = *(*fp)->body; + body.env = child; + return eval(body, extra_lib); + } + + return fn; } + // not a call if (!v.is_fn()) - return v; // already concrete - - static Object base = make_default_lib(); - Object lib = merge_lib(base, extra_lib); + return v; if (auto fp = std::get_if>(&v.data)) return *(*fp)->body; - return v; // Builtin with no args + return v; // builtin with no args } // auto-evaluating operator[] impl @@ -647,6 +676,30 @@ void write_config(Value const &v, std::filesystem::path const &o) w(f, v); } +// default lib +Object make_default_lib(); +Object merge_lib(Object const &base, Object const *extra) +{ + Object out = base; + if (extra) + out.insert(extra->begin(), extra->end()); + return out; +} + +auto get_env_tbl(Environment *env) -> std::unordered_map & +{ + return env->tbl; +} + +Object make_default_lib() +{ + Object l; + l["join"] = Builtin { join }; + l["list_from_range"] = Builtin { list_rng }; + l["import"] = Builtin { imp }; + return l; +} + } // namespace dhos // NOLINTEND diff --git a/src/dhos_config.h b/src/dhos_config.h index 0f6a0e7..141a6c4 100644 --- a/src/dhos_config.h +++ b/src/dhos_config.h @@ -30,6 +30,7 @@ struct Value { struct Call { std::string name; Array args; + std::shared_ptr recv; }; using Data = std::variant, @@ -138,6 +139,11 @@ Value parse_config(std::istream &in); Value parse_config(std::filesystem::path const &f); void write_config(Value const &v, std::filesystem::path const &out); +Object make_default_lib(); +Object merge_lib(Object const &base, Object const *extra); + +auto get_env_tbl(Environment *env) -> std::unordered_map &; + } // namespace dhos // NOLINTEND