From 13f6f0b923ff3ec94a3bec886c28b90402ceef91 Mon Sep 17 00:00:00 2001 From: Vaxry <43317083+vaxerski@users.noreply.github.com> Date: Sun, 18 Feb 2024 15:00:34 +0000 Subject: Migrate the config to hyprlang (#4656) * Migrate to hyprlang * pop up errors * fix swapped args * Meson & Nix: build with hyprlang * CI: add hyprlang to setup action * add infra for plugin stuff * fix hyprctl getoption * fix hyprctl getoption with json * format * fix post parse logic * fix autogen config * oops missed exec-once * fmt * fix ws rules * require 0.3.0 for hyprlang * nix: flaek * minor type fixes * fix cfg usages in swipe * use cvarlist for ws rules * fix throw in addPluginConfigVar * Nix: update hyprlang * minor fixes * fix disableLogs * mention hyprlang docs * bump hyprlang dep in cmake * Meson: bump min hyprlang version Nix: update hyprlang * minor fix * Nix: update meson patch --------- Co-authored-by: Mihai Fufezan --- src/config/ConfigManager.cpp | 3752 ++++++++++++++++++++---------------------- 1 file changed, 1760 insertions(+), 1992 deletions(-) (limited to 'src/config/ConfigManager.cpp') diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index f1326c2a..4d3e6358 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -15,35 +15,545 @@ #include #include -extern "C" char** environ; +extern "C" char** environ; -CConfigManager::CConfigManager() { - configValues["general:col.active_border"].data = std::make_shared(0xffffffff); - configValues["general:col.inactive_border"].data = std::make_shared(0xff444444); - configValues["general:col.nogroup_border"].data = std::make_shared(0xffffaaff); - configValues["general:col.nogroup_border_active"].data = std::make_shared(0xffff00ff); +static Hyprlang::CParseResult configHandleGradientSet(const char* VALUE, void** data) { + std::string V = VALUE; - configValues["group:col.border_active"].data = std::make_shared(0x66ffff00); - configValues["group:col.border_inactive"].data = std::make_shared(0x66777700); - configValues["group:col.border_locked_active"].data = std::make_shared(0x66ff5500); - configValues["group:col.border_locked_inactive"].data = std::make_shared(0x66775500); + if (!*data) + *data = new CGradientValueData(); - configValues["group:groupbar:col.active"].data = std::make_shared(0x66ffff00); - configValues["group:groupbar:col.inactive"].data = std::make_shared(0x66777700); - configValues["group:groupbar:col.locked_active"].data = std::make_shared(0x66ff5500); - configValues["group:groupbar:col.locked_inactive"].data = std::make_shared(0x66775500); + const auto DATA = reinterpret_cast(*data); - Debug::log(LOG, "NOTE: further logs to stdout / logfile are disabled by default. Use debug:disable_logs and debug:enable_stdout_logs to override this."); + CVarList varlist(V, 0, ' '); + DATA->m_vColors.clear(); - setDefaultVars(); - setDefaultAnimationVars(); + std::string parseError = ""; + + for (auto& var : varlist) { + if (var.find("deg") != std::string::npos) { + // last arg + try { + DATA->m_fAngle = std::stoi(var.substr(0, var.find("deg"))) * (PI / 180.0); // radians + } catch (...) { + Debug::log(WARN, "Error parsing gradient {}", V); + parseError = "Error parsing gradient " + V; + } + + break; + } + + if (DATA->m_vColors.size() >= 10) { + Debug::log(WARN, "Error parsing gradient {}: max colors is 10.", V); + parseError = "Error parsing gradient " + V + ": max colors is 10."; + break; + } + + try { + DATA->m_vColors.push_back(CColor(configStringToInt(var))); + } catch (std::exception& e) { + Debug::log(WARN, "Error parsing gradient {}", V); + parseError = "Error parsing gradient " + V + ": " + e.what(); + } + } + + if (DATA->m_vColors.size() == 0) { + Debug::log(WARN, "Error parsing gradient {}", V); + parseError = "Error parsing gradient " + V + ": No colors?"; + + DATA->m_vColors.push_back(0); // transparent + } + + Hyprlang::CParseResult result; + if (!parseError.empty()) + result.setError(parseError.c_str()); + + return result; +} + +static void configHandleGradientDestroy(void** data) { + if (*data) + delete reinterpret_cast(*data); +} + +static Hyprlang::CParseResult handleRawExec(const char* c, const char* v) { + const std::string VALUE = v; + const std::string COMMAND = c; + + const auto RESULT = g_pConfigManager->handleRawExec(COMMAND, VALUE); + + Hyprlang::CParseResult result; + if (RESULT.has_value()) + result.setError(RESULT.value().c_str()); + return result; +} + +static Hyprlang::CParseResult handleExecOnce(const char* c, const char* v) { + const std::string VALUE = v; + const std::string COMMAND = c; + + const auto RESULT = g_pConfigManager->handleExecOnce(COMMAND, VALUE); + + Hyprlang::CParseResult result; + if (RESULT.has_value()) + result.setError(RESULT.value().c_str()); + return result; +} + +static Hyprlang::CParseResult handleMonitor(const char* c, const char* v) { + const std::string VALUE = v; + const std::string COMMAND = c; + + const auto RESULT = g_pConfigManager->handleMonitor(COMMAND, VALUE); + + Hyprlang::CParseResult result; + if (RESULT.has_value()) + result.setError(RESULT.value().c_str()); + return result; +} + +static Hyprlang::CParseResult handleBezier(const char* c, const char* v) { + const std::string VALUE = v; + const std::string COMMAND = c; + + const auto RESULT = g_pConfigManager->handleBezier(COMMAND, VALUE); + + Hyprlang::CParseResult result; + if (RESULT.has_value()) + result.setError(RESULT.value().c_str()); + return result; +} + +static Hyprlang::CParseResult handleAnimation(const char* c, const char* v) { + const std::string VALUE = v; + const std::string COMMAND = c; + + const auto RESULT = g_pConfigManager->handleAnimation(COMMAND, VALUE); + + Hyprlang::CParseResult result; + if (RESULT.has_value()) + result.setError(RESULT.value().c_str()); + return result; +} + +static Hyprlang::CParseResult handleBind(const char* c, const char* v) { + const std::string VALUE = v; + const std::string COMMAND = c; + + const auto RESULT = g_pConfigManager->handleBind(COMMAND, VALUE); + + Hyprlang::CParseResult result; + if (RESULT.has_value()) + result.setError(RESULT.value().c_str()); + return result; +} + +static Hyprlang::CParseResult handleUnbind(const char* c, const char* v) { + const std::string VALUE = v; + const std::string COMMAND = c; + + const auto RESULT = g_pConfigManager->handleUnbind(COMMAND, VALUE); + + Hyprlang::CParseResult result; + if (RESULT.has_value()) + result.setError(RESULT.value().c_str()); + return result; +} + +static Hyprlang::CParseResult handleWindowRule(const char* c, const char* v) { + const std::string VALUE = v; + const std::string COMMAND = c; + + const auto RESULT = g_pConfigManager->handleWindowRule(COMMAND, VALUE); + + Hyprlang::CParseResult result; + if (RESULT.has_value()) + result.setError(RESULT.value().c_str()); + return result; +} + +static Hyprlang::CParseResult handleLayerRule(const char* c, const char* v) { + const std::string VALUE = v; + const std::string COMMAND = c; + + const auto RESULT = g_pConfigManager->handleLayerRule(COMMAND, VALUE); + + Hyprlang::CParseResult result; + if (RESULT.has_value()) + result.setError(RESULT.value().c_str()); + return result; +} + +static Hyprlang::CParseResult handleWindowRuleV2(const char* c, const char* v) { + const std::string VALUE = v; + const std::string COMMAND = c; + + const auto RESULT = g_pConfigManager->handleWindowRuleV2(COMMAND, VALUE); + + Hyprlang::CParseResult result; + if (RESULT.has_value()) + result.setError(RESULT.value().c_str()); + return result; +} + +static Hyprlang::CParseResult handleBlurLS(const char* c, const char* v) { + const std::string VALUE = v; + const std::string COMMAND = c; + + const auto RESULT = g_pConfigManager->handleBlurLS(COMMAND, VALUE); + + Hyprlang::CParseResult result; + if (RESULT.has_value()) + result.setError(RESULT.value().c_str()); + return result; +} + +static Hyprlang::CParseResult handleWorkspaceRules(const char* c, const char* v) { + const std::string VALUE = v; + const std::string COMMAND = c; + + const auto RESULT = g_pConfigManager->handleWorkspaceRules(COMMAND, VALUE); + + Hyprlang::CParseResult result; + if (RESULT.has_value()) + result.setError(RESULT.value().c_str()); + return result; +} + +static Hyprlang::CParseResult handleSubmap(const char* c, const char* v) { + const std::string VALUE = v; + const std::string COMMAND = c; + + const auto RESULT = g_pConfigManager->handleSubmap(COMMAND, VALUE); + + Hyprlang::CParseResult result; + if (RESULT.has_value()) + result.setError(RESULT.value().c_str()); + return result; +} + +static Hyprlang::CParseResult handleSource(const char* c, const char* v) { + const std::string VALUE = v; + const std::string COMMAND = c; + + const auto RESULT = g_pConfigManager->handleSource(COMMAND, VALUE); + + Hyprlang::CParseResult result; + if (RESULT.has_value()) + result.setError(RESULT.value().c_str()); + return result; +} + +static Hyprlang::CParseResult handleEnv(const char* c, const char* v) { + const std::string VALUE = v; + const std::string COMMAND = c; + + const auto RESULT = g_pConfigManager->handleEnv(COMMAND, VALUE); + + Hyprlang::CParseResult result; + if (RESULT.has_value()) + result.setError(RESULT.value().c_str()); + return result; +} + +static Hyprlang::CParseResult handlePlugin(const char* c, const char* v) { + const std::string VALUE = v; + const std::string COMMAND = c; + + const auto RESULT = g_pConfigManager->handlePlugin(COMMAND, VALUE); + + Hyprlang::CParseResult result; + if (RESULT.has_value()) + result.setError(RESULT.value().c_str()); + return result; +} + +CConfigManager::CConfigManager() { + const auto ERR = verifyConfigExists(); configPaths.emplace_back(getMainConfigPath()); + m_pConfig = std::make_unique(configPaths.begin()->c_str(), Hyprlang::SConfigOptions{.throwAllErrors = true, .allowMissingConfig = true}); + + m_pConfig->addConfigValue("general:sensitivity", {1.0f}); + m_pConfig->addConfigValue("general:apply_sens_to_raw", {0L}); + m_pConfig->addConfigValue("general:border_size", {1L}); + m_pConfig->addConfigValue("general:no_border_on_floating", {0L}); + m_pConfig->addConfigValue("general:border_part_of_window", {1L}); + m_pConfig->addConfigValue("general:gaps_in", {5L}); + m_pConfig->addConfigValue("general:gaps_out", {20L}); + m_pConfig->addConfigValue("general:gaps_workspaces", {0L}); + m_pConfig->addConfigValue("general:cursor_inactive_timeout", {0L}); + m_pConfig->addConfigValue("general:no_cursor_warps", {0L}); + m_pConfig->addConfigValue("general:no_focus_fallback", {0L}); + m_pConfig->addConfigValue("general:resize_on_border", {0L}); + m_pConfig->addConfigValue("general:extend_border_grab_area", {15L}); + m_pConfig->addConfigValue("general:hover_icon_on_border", {1L}); + m_pConfig->addConfigValue("general:layout", {"dwindle"}); + m_pConfig->addConfigValue("general:allow_tearing", {0L}); + + m_pConfig->addConfigValue("misc:disable_hyprland_logo", {0L}); + m_pConfig->addConfigValue("misc:disable_splash_rendering", {0L}); + m_pConfig->addConfigValue("misc:force_default_wallpaper", {-1L}); + m_pConfig->addConfigValue("misc:vfr", {1L}); + m_pConfig->addConfigValue("misc:vrr", {0L}); + m_pConfig->addConfigValue("misc:mouse_move_enables_dpms", {0L}); + m_pConfig->addConfigValue("misc:key_press_enables_dpms", {0L}); + m_pConfig->addConfigValue("misc:always_follow_on_dnd", {1L}); + m_pConfig->addConfigValue("misc:layers_hog_keyboard_focus", {1L}); + m_pConfig->addConfigValue("misc:animate_manual_resizes", {0L}); + m_pConfig->addConfigValue("misc:animate_mouse_windowdragging", {0L}); + m_pConfig->addConfigValue("misc:disable_autoreload", {0L}); + m_pConfig->addConfigValue("misc:enable_swallow", {0L}); + m_pConfig->addConfigValue("misc:swallow_regex", {STRVAL_EMPTY}); + m_pConfig->addConfigValue("misc:swallow_exception_regex", {STRVAL_EMPTY}); + m_pConfig->addConfigValue("misc:focus_on_activate", {0L}); + m_pConfig->addConfigValue("misc:no_direct_scanout", {1L}); + m_pConfig->addConfigValue("misc:hide_cursor_on_touch", {1L}); + m_pConfig->addConfigValue("misc:mouse_move_focuses_monitor", {1L}); + m_pConfig->addConfigValue("misc:render_ahead_of_time", {0L}); + m_pConfig->addConfigValue("misc:render_ahead_safezone", {1L}); + m_pConfig->addConfigValue("misc:cursor_zoom_factor", {1.f}); + m_pConfig->addConfigValue("misc:cursor_zoom_rigid", {0L}); + m_pConfig->addConfigValue("misc:allow_session_lock_restore", {0L}); + m_pConfig->addConfigValue("misc:close_special_on_empty", {1L}); + m_pConfig->addConfigValue("misc:background_color", {0xff111111L}); + m_pConfig->addConfigValue("misc:new_window_takes_over_fullscreen", {0L}); + + m_pConfig->addConfigValue("group:insert_after_current", {1L}); + m_pConfig->addConfigValue("group:focus_removed_window", {1L}); + m_pConfig->addConfigValue("group:groupbar:enabled", {1L}); + m_pConfig->addConfigValue("group:groupbar:font_family", {"Sans"}); + m_pConfig->addConfigValue("group:groupbar:font_size", {8L}); + m_pConfig->addConfigValue("group:groupbar:gradients", {1L}); + m_pConfig->addConfigValue("group:groupbar:height", {14L}); + m_pConfig->addConfigValue("group:groupbar:priority", {3L}); + m_pConfig->addConfigValue("group:groupbar:render_titles", {1L}); + m_pConfig->addConfigValue("group:groupbar:scrolling", {1L}); + m_pConfig->addConfigValue("group:groupbar:text_color", {0xffffffffL}); + + m_pConfig->addConfigValue("debug:int", {0L}); + m_pConfig->addConfigValue("debug:log_damage", {0L}); + m_pConfig->addConfigValue("debug:overlay", {0L}); + m_pConfig->addConfigValue("debug:damage_blink", {0L}); + m_pConfig->addConfigValue("debug:disable_logs", {1L}); + m_pConfig->addConfigValue("debug:disable_time", {1L}); + m_pConfig->addConfigValue("debug:enable_stdout_logs", {0L}); + m_pConfig->addConfigValue("debug:damage_tracking", {(Hyprlang::INT)DAMAGE_TRACKING_FULL}); + m_pConfig->addConfigValue("debug:manual_crash", {0L}); + m_pConfig->addConfigValue("debug:suppress_errors", {0L}); + m_pConfig->addConfigValue("debug:watchdog_timeout", {5L}); + m_pConfig->addConfigValue("debug:disable_scale_checks", {0L}); + + m_pConfig->addConfigValue("decoration:rounding", {0L}); + m_pConfig->addConfigValue("decoration:blur:enabled", {1L}); + m_pConfig->addConfigValue("decoration:blur:size", {8L}); + m_pConfig->addConfigValue("decoration:blur:passes", {1L}); + m_pConfig->addConfigValue("decoration:blur:ignore_opacity", {0L}); + m_pConfig->addConfigValue("decoration:blur:new_optimizations", {1L}); + m_pConfig->addConfigValue("decoration:blur:xray", {0L}); + m_pConfig->addConfigValue("decoration:blur:contrast", {0.8916F}); + m_pConfig->addConfigValue("decoration:blur:brightness", {1.0F}); + m_pConfig->addConfigValue("decoration:blur:vibrancy", {0.1696F}); + m_pConfig->addConfigValue("decoration:blur:vibrancy_darkness", {0.0F}); + m_pConfig->addConfigValue("decoration:blur:noise", {0.0117F}); + m_pConfig->addConfigValue("decoration:blur:special", {0L}); + m_pConfig->addConfigValue("decoration:blur:popups", {0L}); + m_pConfig->addConfigValue("decoration:blur:popups_ignorealpha", {0.2F}); + m_pConfig->addConfigValue("decoration:active_opacity", {1.F}); + m_pConfig->addConfigValue("decoration:inactive_opacity", {1.F}); + m_pConfig->addConfigValue("decoration:fullscreen_opacity", {1.F}); + m_pConfig->addConfigValue("decoration:no_blur_on_oversized", {0L}); + m_pConfig->addConfigValue("decoration:drop_shadow", {1L}); + m_pConfig->addConfigValue("decoration:shadow_range", {4L}); + m_pConfig->addConfigValue("decoration:shadow_render_power", {3L}); + m_pConfig->addConfigValue("decoration:shadow_ignore_window", {1L}); + m_pConfig->addConfigValue("decoration:shadow_offset", Hyprlang::VEC2{0, 0}); + m_pConfig->addConfigValue("decoration:shadow_scale", {1.f}); + m_pConfig->addConfigValue("decoration:col.shadow", {0xee1a1a1aL}); + m_pConfig->addConfigValue("decoration:col.shadow_inactive", {(Hyprlang::INT)INT_MAX}); + m_pConfig->addConfigValue("decoration:dim_inactive", {0L}); + m_pConfig->addConfigValue("decoration:dim_strength", {0.5f}); + m_pConfig->addConfigValue("decoration:dim_special", {0.2f}); + m_pConfig->addConfigValue("decoration:dim_around", {0.4f}); + m_pConfig->addConfigValue("decoration:screen_shader", {STRVAL_EMPTY}); + + m_pConfig->addConfigValue("dwindle:pseudotile", {0L}); + m_pConfig->addConfigValue("dwindle:force_split", {0L}); + m_pConfig->addConfigValue("dwindle:permanent_direction_override", {0L}); + m_pConfig->addConfigValue("dwindle:preserve_split", {0L}); + m_pConfig->addConfigValue("dwindle:special_scale_factor", {1.f}); + m_pConfig->addConfigValue("dwindle:split_width_multiplier", {1.0f}); + m_pConfig->addConfigValue("dwindle:no_gaps_when_only", {0L}); + m_pConfig->addConfigValue("dwindle:use_active_for_splits", {1L}); + m_pConfig->addConfigValue("dwindle:default_split_ratio", {1.f}); + m_pConfig->addConfigValue("dwindle:smart_split", {0L}); + m_pConfig->addConfigValue("dwindle:smart_resizing", {1L}); + + m_pConfig->addConfigValue("master:special_scale_factor", {1.f}); + m_pConfig->addConfigValue("master:mfact", {0.55f}); + m_pConfig->addConfigValue("master:new_is_master", {1L}); + m_pConfig->addConfigValue("master:always_center_master", {0L}); + m_pConfig->addConfigValue("master:new_on_top", {0L}); + m_pConfig->addConfigValue("master:no_gaps_when_only", {0L}); + m_pConfig->addConfigValue("master:orientation", {"left"}); + m_pConfig->addConfigValue("master:inherit_fullscreen", {1L}); + m_pConfig->addConfigValue("master:allow_small_split", {0L}); + m_pConfig->addConfigValue("master:smart_resizing", {1L}); + m_pConfig->addConfigValue("master:drop_at_cursor", {1L}); + + m_pConfig->addConfigValue("animations:enabled", {1L}); + m_pConfig->addConfigValue("animations:first_launch_animation", {1L}); + + m_pConfig->addConfigValue("input:follow_mouse", {1L}); + m_pConfig->addConfigValue("input:mouse_refocus", {1L}); + m_pConfig->addConfigValue("input:special_fallthrough", {0L}); + m_pConfig->addConfigValue("input:sensitivity", {0.f}); + m_pConfig->addConfigValue("input:accel_profile", {STRVAL_EMPTY}); + m_pConfig->addConfigValue("input:kb_file", {STRVAL_EMPTY}); + m_pConfig->addConfigValue("input:kb_layout", {"us"}); + m_pConfig->addConfigValue("input:kb_variant", {STRVAL_EMPTY}); + m_pConfig->addConfigValue("input:kb_options", {STRVAL_EMPTY}); + m_pConfig->addConfigValue("input:kb_rules", {STRVAL_EMPTY}); + m_pConfig->addConfigValue("input:kb_model", {STRVAL_EMPTY}); + m_pConfig->addConfigValue("input:repeat_rate", {25L}); + m_pConfig->addConfigValue("input:repeat_delay", {600L}); + m_pConfig->addConfigValue("input:natural_scroll", {0L}); + m_pConfig->addConfigValue("input:numlock_by_default", {0L}); + m_pConfig->addConfigValue("input:force_no_accel", {0L}); + m_pConfig->addConfigValue("input:float_switch_override_focus", {1L}); + m_pConfig->addConfigValue("input:left_handed", {0L}); + m_pConfig->addConfigValue("input:scroll_method", {STRVAL_EMPTY}); + m_pConfig->addConfigValue("input:scroll_button", {0L}); + m_pConfig->addConfigValue("input:scroll_button_lock", {0L}); + m_pConfig->addConfigValue("input:scroll_points", {STRVAL_EMPTY}); + m_pConfig->addConfigValue("input:touchpad:natural_scroll", {0L}); + m_pConfig->addConfigValue("input:touchpad:disable_while_typing", {1L}); + m_pConfig->addConfigValue("input:touchpad:clickfinger_behavior", {0L}); + m_pConfig->addConfigValue("input:touchpad:tap_button_map", {STRVAL_EMPTY}); + m_pConfig->addConfigValue("input:touchpad:middle_button_emulation", {0L}); + m_pConfig->addConfigValue("input:touchpad:tap-to-click", {1L}); + m_pConfig->addConfigValue("input:touchpad:tap-and-drag", {1L}); + m_pConfig->addConfigValue("input:touchpad:drag_lock", {0L}); + m_pConfig->addConfigValue("input:touchpad:scroll_factor", {1.f}); + m_pConfig->addConfigValue("input:touchdevice:transform", {0L}); + m_pConfig->addConfigValue("input:touchdevice:output", {STRVAL_EMPTY}); + m_pConfig->addConfigValue("input:touchdevice:enabled", {1L}); + m_pConfig->addConfigValue("input:tablet:transform", {0L}); + m_pConfig->addConfigValue("input:tablet:output", {STRVAL_EMPTY}); + m_pConfig->addConfigValue("input:tablet:region_position", Hyprlang::VEC2{0, 0}); + m_pConfig->addConfigValue("input:tablet:region_size", Hyprlang::VEC2{0, 0}); + m_pConfig->addConfigValue("input:tablet:relative_input", {0L}); + + m_pConfig->addConfigValue("binds:pass_mouse_when_bound", {0L}); + m_pConfig->addConfigValue("binds:scroll_event_delay", {300L}); + m_pConfig->addConfigValue("binds:workspace_back_and_forth", {0L}); + m_pConfig->addConfigValue("binds:allow_workspace_cycles", {0L}); + m_pConfig->addConfigValue("binds:workspace_center_on", {1L}); + m_pConfig->addConfigValue("binds:focus_preferred_method", {0L}); + m_pConfig->addConfigValue("binds:ignore_group_lock", {0L}); + m_pConfig->addConfigValue("binds:movefocus_cycles_fullscreen", {1L}); + + m_pConfig->addConfigValue("gestures:workspace_swipe", {0L}); + m_pConfig->addConfigValue("gestures:workspace_swipe_fingers", {3L}); + m_pConfig->addConfigValue("gestures:workspace_swipe_distance", {300L}); + m_pConfig->addConfigValue("gestures:workspace_swipe_invert", {1L}); + m_pConfig->addConfigValue("gestures:workspace_swipe_min_speed_to_force", {30L}); + m_pConfig->addConfigValue("gestures:workspace_swipe_cancel_ratio", {0.5f}); + m_pConfig->addConfigValue("gestures:workspace_swipe_create_new", {1L}); + m_pConfig->addConfigValue("gestures:workspace_swipe_direction_lock", {1L}); + m_pConfig->addConfigValue("gestures:workspace_swipe_direction_lock_threshold", {10L}); + m_pConfig->addConfigValue("gestures:workspace_swipe_forever", {0L}); + m_pConfig->addConfigValue("gestures:workspace_swipe_numbered", {0L}); + m_pConfig->addConfigValue("gestures:workspace_swipe_use_r", {0L}); + + m_pConfig->addConfigValue("xwayland:use_nearest_neighbor", {1L}); + m_pConfig->addConfigValue("xwayland:force_zero_scaling", {0L}); + + m_pConfig->addConfigValue("opengl:nvidia_anti_flicker", {1L}); + + m_pConfig->addConfigValue("autogenerated", {0L}); + + m_pConfig->addConfigValue("general:col.active_border", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0xffffffff"}); + m_pConfig->addConfigValue("general:col.inactive_border", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0xff444444"}); + m_pConfig->addConfigValue("general:col.nogroup_border", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0xffffaaff"}); + m_pConfig->addConfigValue("general:col.nogroup_border_active", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0xffff00ff"}); + + m_pConfig->addConfigValue("group:col.border_active", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0x66ffff00"}); + m_pConfig->addConfigValue("group:col.border_inactive", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0x66777700"}); + m_pConfig->addConfigValue("group:col.border_locked_active", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0x66ff5500"}); + m_pConfig->addConfigValue("group:col.border_locked_inactive", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0x66775500"}); + + m_pConfig->addConfigValue("group:groupbar:col.active", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0x66ffff00"}); + m_pConfig->addConfigValue("group:groupbar:col.inactive", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0x66777700"}); + m_pConfig->addConfigValue("group:groupbar:col.locked_active", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0x66ff5500"}); + m_pConfig->addConfigValue("group:groupbar:col.locked_inactive", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0x66775500"}); + + // devices + m_pConfig->addSpecialCategory("device", {"name"}); + m_pConfig->addSpecialConfigValue("device", "sensitivity", {0.F}); + m_pConfig->addSpecialConfigValue("device", "accel_profile", {STRVAL_EMPTY}); + m_pConfig->addSpecialConfigValue("device", "kb_file", {STRVAL_EMPTY}); + m_pConfig->addSpecialConfigValue("device", "kb_layout", {"us"}); + m_pConfig->addSpecialConfigValue("device", "kb_variant", {STRVAL_EMPTY}); + m_pConfig->addSpecialConfigValue("device", "kb_options", {STRVAL_EMPTY}); + m_pConfig->addSpecialConfigValue("device", "kb_rules", {STRVAL_EMPTY}); + m_pConfig->addSpecialConfigValue("device", "kb_model", {STRVAL_EMPTY}); + m_pConfig->addSpecialConfigValue("device", "repeat_rate", {25L}); + m_pConfig->addSpecialConfigValue("device", "repeat_delay", {600L}); + m_pConfig->addSpecialConfigValue("device", "natural_scroll", {0L}); + m_pConfig->addSpecialConfigValue("device", "tap_button_map", {STRVAL_EMPTY}); + m_pConfig->addSpecialConfigValue("device", "numlock_by_default", {0L}); + m_pConfig->addSpecialConfigValue("device", "disable_while_typing", {1L}); + m_pConfig->addSpecialConfigValue("device", "clickfinger_behavior", {0L}); + m_pConfig->addSpecialConfigValue("device", "middle_button_emulation", {0L}); + m_pConfig->addSpecialConfigValue("device", "tap-to-click", {1L}); + m_pConfig->addSpecialConfigValue("device", "tap-and-drag", {1L}); + m_pConfig->addSpecialConfigValue("device", "drag_lock", {0L}); + m_pConfig->addSpecialConfigValue("device", "left_handed", {0L}); + m_pConfig->addSpecialConfigValue("device", "scroll_method", {STRVAL_EMPTY}); + m_pConfig->addSpecialConfigValue("device", "scroll_button", {0L}); + m_pConfig->addSpecialConfigValue("device", "scroll_button_lock", {0L}); + m_pConfig->addSpecialConfigValue("device", "scroll_points", {STRVAL_EMPTY}); + m_pConfig->addSpecialConfigValue("device", "transform", {0L}); + m_pConfig->addSpecialConfigValue("device", "output", {STRVAL_EMPTY}); + m_pConfig->addSpecialConfigValue("device", "enabled", {1L}); // only for mice, touchpads, and touchdevices + m_pConfig->addSpecialConfigValue("device", "region_position", Hyprlang::VEC2{0, 0}); // only for tablets + m_pConfig->addSpecialConfigValue("device", "region_size", Hyprlang::VEC2{0, 0}); // only for tablets + m_pConfig->addSpecialConfigValue("device", "relative_input", {0L}); // only for tablets + + // keywords + m_pConfig->registerHandler(&::handleRawExec, "exec", {false}); + m_pConfig->registerHandler(&::handleExecOnce, "exec-once", {false}); + m_pConfig->registerHandler(&::handleMonitor, "monitor", {false}); + m_pConfig->registerHandler(&::handleBind, "bind", {true}); + m_pConfig->registerHandler(&::handleUnbind, "unbind", {false}); + m_pConfig->registerHandler(&::handleWorkspaceRules, "workspace", {false}); + m_pConfig->registerHandler(&::handleWindowRule, "windowrule", {false}); + m_pConfig->registerHandler(&::handleLayerRule, "layerrule", {false}); + m_pConfig->registerHandler(&::handleWindowRuleV2, "windowrulev2", {false}); + m_pConfig->registerHandler(&::handleBezier, "bezier", {false}); + m_pConfig->registerHandler(&::handleAnimation, "animation", {false}); + m_pConfig->registerHandler(&::handleSource, "source", {false}); + m_pConfig->registerHandler(&::handleSubmap, "submap", {false}); + m_pConfig->registerHandler(&::handleBlurLS, "blurls", {false}); + m_pConfig->registerHandler(&::handlePlugin, "plugin", {false}); + m_pConfig->registerHandler(&::handleEnv, "env", {true}); + + // pluginza + m_pConfig->addSpecialCategory("plugin", {nullptr, true}); + + m_pConfig->commence(); + + Debug::log(LOG, "NOTE: further logs to stdout / logfile are disabled by default. Use debug:disable_logs and debug:enable_stdout_logs to override this."); + + setDefaultAnimationVars(); + resetHLConfig(); - Debug::disableLogs = &configValues["debug:disable_logs"].intValue; - Debug::disableTime = &configValues["debug:disable_time"].intValue; + Debug::disableLogs = reinterpret_cast(m_pConfig->getConfigValuePtr("debug:disable_logs")->getDataStaticPtr()); + Debug::disableTime = reinterpret_cast(m_pConfig->getConfigValuePtr("debug:disable_time")->getDataStaticPtr()); - populateEnvironment(); + if (ERR.has_value()) + g_pHyprError->queueCreate(ERR.value(), CColor{1.0, 0.1, 0.1, 1.0}); } std::string CConfigManager::getConfigDir() { @@ -62,268 +572,12 @@ std::string CConfigManager::getMainConfigPath() { return getConfigDir() + "/hypr/" + (ISDEBUG ? "hyprlandd.conf" : "hyprland.conf"); } -void CConfigManager::populateEnvironment() { - environmentVariables.clear(); - for (char** env = environ; *env; ++env) { - const std::string ENVVAR = *env; - const auto VARIABLE = ENVVAR.substr(0, ENVVAR.find_first_of('=')); - const auto VALUE = ENVVAR.substr(ENVVAR.find_first_of('=') + 1); - environmentVariables.emplace_back(std::make_pair<>(VARIABLE, VALUE)); - } - - std::sort(environmentVariables.begin(), environmentVariables.end(), [&](const auto& a, const auto& b) { return a.first.length() > b.first.length(); }); -} - -void CConfigManager::setDefaultVars() { - configValues["general:max_fps"].intValue = 60; - configValues["general:sensitivity"].floatValue = 1.0f; - configValues["general:apply_sens_to_raw"].intValue = 0; - configValues["general:border_size"].intValue = 1; - configValues["general:no_border_on_floating"].intValue = 0; - configValues["general:border_part_of_window"].intValue = 1; - configValues["general:gaps_in"].intValue = 5; - configValues["general:gaps_out"].intValue = 20; - configValues["general:gaps_workspaces"].intValue = 0; - ((CGradientValueData*)configValues["general:col.active_border"].data.get())->reset(0xffffffff); - ((CGradientValueData*)configValues["general:col.inactive_border"].data.get())->reset(0xff444444); - ((CGradientValueData*)configValues["general:col.nogroup_border"].data.get())->reset(0xff444444); - ((CGradientValueData*)configValues["general:col.nogroup_border_active"].data.get())->reset(0xffff00ff); - configValues["general:cursor_inactive_timeout"].intValue = 0; - configValues["general:no_cursor_warps"].intValue = 0; - configValues["general:no_focus_fallback"].intValue = 0; - configValues["general:resize_on_border"].intValue = 0; - configValues["general:extend_border_grab_area"].intValue = 15; - configValues["general:hover_icon_on_border"].intValue = 1; - configValues["general:layout"].strValue = "dwindle"; - configValues["general:allow_tearing"].intValue = 0; - - configValues["misc:disable_hyprland_logo"].intValue = 0; - configValues["misc:disable_splash_rendering"].intValue = 0; - configValues["misc:force_default_wallpaper"].intValue = -1; - configValues["misc:vfr"].intValue = 1; - configValues["misc:vrr"].intValue = 0; - configValues["misc:mouse_move_enables_dpms"].intValue = 0; - configValues["misc:key_press_enables_dpms"].intValue = 0; - configValues["misc:always_follow_on_dnd"].intValue = 1; - configValues["misc:layers_hog_keyboard_focus"].intValue = 1; - configValues["misc:animate_manual_resizes"].intValue = 0; - configValues["misc:animate_mouse_windowdragging"].intValue = 0; - configValues["misc:disable_autoreload"].intValue = 0; - configValues["misc:enable_swallow"].intValue = 0; - configValues["misc:swallow_regex"].strValue = STRVAL_EMPTY; - configValues["misc:swallow_exception_regex"].strValue = STRVAL_EMPTY; - configValues["misc:focus_on_activate"].intValue = 0; - configValues["misc:no_direct_scanout"].intValue = 1; - configValues["misc:hide_cursor_on_touch"].intValue = 1; - configValues["misc:mouse_move_focuses_monitor"].intValue = 1; - configValues["misc:render_ahead_of_time"].intValue = 0; - configValues["misc:render_ahead_safezone"].intValue = 1; - configValues["misc:cursor_zoom_factor"].floatValue = 1.f; - configValues["misc:cursor_zoom_rigid"].intValue = 0; - configValues["misc:allow_session_lock_restore"].intValue = 0; - configValues["misc:close_special_on_empty"].intValue = 1; - configValues["misc:background_color"].intValue = 0xff111111; - configValues["misc:new_window_takes_over_fullscreen"].intValue = 0; - - ((CGradientValueData*)configValues["group:col.border_active"].data.get())->reset(0x66ffff00); - ((CGradientValueData*)configValues["group:col.border_inactive"].data.get())->reset(0x66777700); - ((CGradientValueData*)configValues["group:col.border_locked_active"].data.get())->reset(0x66ff5500); - ((CGradientValueData*)configValues["group:col.border_locked_inactive"].data.get())->reset(0x66775500); - - configValues["group:insert_after_current"].intValue = 1; - configValues["group:focus_removed_window"].intValue = 1; - - configValues["group:groupbar:enabled"].intValue = 1; - configValues["group:groupbar:font_family"].strValue = "Sans"; - configValues["group:groupbar:font_size"].intValue = 8; - configValues["group:groupbar:gradients"].intValue = 1; - configValues["group:groupbar:height"].intValue = 14; - configValues["group:groupbar:priority"].intValue = 3; - configValues["group:groupbar:render_titles"].intValue = 1; - configValues["group:groupbar:scrolling"].intValue = 1; - configValues["group:groupbar:text_color"].intValue = 0xffffffff; - - ((CGradientValueData*)configValues["group:groupbar:col.active"].data.get())->reset(0x66ffff00); - ((CGradientValueData*)configValues["group:groupbar:col.inactive"].data.get())->reset(0x66777700); - ((CGradientValueData*)configValues["group:groupbar:col.locked_active"].data.get())->reset(0x66ff5500); - ((CGradientValueData*)configValues["group:groupbar:col.locked_inactive"].data.get())->reset(0x66775500); - - configValues["debug:int"].intValue = 0; - configValues["debug:log_damage"].intValue = 0; - configValues["debug:overlay"].intValue = 0; - configValues["debug:damage_blink"].intValue = 0; - configValues["debug:disable_logs"].intValue = 1; - configValues["debug:disable_time"].intValue = 1; - configValues["debug:enable_stdout_logs"].intValue = 0; - configValues["debug:damage_tracking"].intValue = DAMAGE_TRACKING_FULL; - configValues["debug:manual_crash"].intValue = 0; - configValues["debug:suppress_errors"].intValue = 0; - configValues["debug:watchdog_timeout"].intValue = 5; - configValues["debug:disable_scale_checks"].intValue = 0; - - configValues["decoration:rounding"].intValue = 0; - configValues["decoration:blur:enabled"].intValue = 1; - configValues["decoration:blur:size"].intValue = 8; - configValues["decoration:blur:passes"].intValue = 1; - configValues["decoration:blur:ignore_opacity"].intValue = 0; - configValues["decoration:blur:new_optimizations"].intValue = 1; - configValues["decoration:blur:xray"].intValue = 0; - configValues["decoration:blur:contrast"].floatValue = 0.8916; - configValues["decoration:blur:brightness"].floatValue = 1.0; - configValues["decoration:blur:vibrancy"].floatValue = 0.1696; - configValues["decoration:blur:vibrancy_darkness"].floatValue = 0.0; - configValues["decoration:blur:noise"].floatValue = 0.0117; - configValues["decoration:blur:special"].intValue = 0; - configValues["decoration:blur:popups"].intValue = 0; - configValues["decoration:blur:popups_ignorealpha"].floatValue = 0.2; - configValues["decoration:active_opacity"].floatValue = 1; - configValues["decoration:inactive_opacity"].floatValue = 1; - configValues["decoration:fullscreen_opacity"].floatValue = 1; - configValues["decoration:no_blur_on_oversized"].intValue = 0; - configValues["decoration:drop_shadow"].intValue = 1; - configValues["decoration:shadow_range"].intValue = 4; - configValues["decoration:shadow_render_power"].intValue = 3; - configValues["decoration:shadow_ignore_window"].intValue = 1; - configValues["decoration:shadow_offset"].vecValue = Vector2D(); - configValues["decoration:shadow_scale"].floatValue = 1.f; - configValues["decoration:col.shadow"].intValue = 0xee1a1a1a; - configValues["decoration:col.shadow_inactive"].intValue = INT_MAX; - configValues["decoration:dim_inactive"].intValue = 0; - configValues["decoration:dim_strength"].floatValue = 0.5f; - configValues["decoration:dim_special"].floatValue = 0.2f; - configValues["decoration:dim_around"].floatValue = 0.4f; - configValues["decoration:screen_shader"].strValue = STRVAL_EMPTY; - - configValues["dwindle:pseudotile"].intValue = 0; - configValues["dwindle:force_split"].intValue = 0; - configValues["dwindle:permanent_direction_override"].intValue = 0; - configValues["dwindle:preserve_split"].intValue = 0; - configValues["dwindle:special_scale_factor"].floatValue = 1.f; - configValues["dwindle:split_width_multiplier"].floatValue = 1.0f; - configValues["dwindle:no_gaps_when_only"].intValue = 0; - configValues["dwindle:use_active_for_splits"].intValue = 1; - configValues["dwindle:default_split_ratio"].floatValue = 1.f; - configValues["dwindle:smart_split"].intValue = 0; - configValues["dwindle:smart_resizing"].intValue = 1; - - configValues["master:special_scale_factor"].floatValue = 1.f; - configValues["master:mfact"].floatValue = 0.55f; - configValues["master:new_is_master"].intValue = 1; - configValues["master:always_center_master"].intValue = 0; - configValues["master:new_on_top"].intValue = 0; - configValues["master:no_gaps_when_only"].intValue = 0; - configValues["master:orientation"].strValue = "left"; - configValues["master:inherit_fullscreen"].intValue = 1; - configValues["master:allow_small_split"].intValue = 0; - configValues["master:smart_resizing"].intValue = 1; - configValues["master:drop_at_cursor"].intValue = 1; - - configValues["animations:enabled"].intValue = 1; - configValues["animations:first_launch_animation"].intValue = 1; - - configValues["input:follow_mouse"].intValue = 1; - configValues["input:mouse_refocus"].intValue = 1; - configValues["input:special_fallthrough"].intValue = 0; - configValues["input:sensitivity"].floatValue = 0.f; - configValues["input:accel_profile"].strValue = STRVAL_EMPTY; - configValues["input:kb_file"].strValue = STRVAL_EMPTY; - configValues["input:kb_layout"].strValue = "us"; - configValues["input:kb_variant"].strValue = STRVAL_EMPTY; - configValues["input:kb_options"].strValue = STRVAL_EMPTY; - configValues["input:kb_rules"].strValue = STRVAL_EMPTY; - configValues["input:kb_model"].strValue = STRVAL_EMPTY; - configValues["input:repeat_rate"].intValue = 25; - configValues["input:repeat_delay"].intValue = 600; - configValues["input:natural_scroll"].intValue = 0; - configValues["input:numlock_by_default"].intValue = 0; - configValues["input:force_no_accel"].intValue = 0; - configValues["input:float_switch_override_focus"].intValue = 1; - configValues["input:left_handed"].intValue = 0; - configValues["input:scroll_method"].strValue = STRVAL_EMPTY; - configValues["input:scroll_button"].intValue = 0; - configValues["input:scroll_button_lock"].intValue = 0; - configValues["input:scroll_points"].strValue = STRVAL_EMPTY; - configValues["input:touchpad:natural_scroll"].intValue = 0; - configValues["input:touchpad:disable_while_typing"].intValue = 1; - configValues["input:touchpad:clickfinger_behavior"].intValue = 0; - configValues["input:touchpad:tap_button_map"].strValue = STRVAL_EMPTY; - configValues["input:touchpad:middle_button_emulation"].intValue = 0; - configValues["input:touchpad:tap-to-click"].intValue = 1; - configValues["input:touchpad:tap-and-drag"].intValue = 1; - configValues["input:touchpad:drag_lock"].intValue = 0; - configValues["input:touchpad:scroll_factor"].floatValue = 1.f; - configValues["input:touchdevice:transform"].intValue = 0; - configValues["input:touchdevice:output"].strValue = STRVAL_EMPTY; - configValues["input:touchdevice:enabled"].intValue = 1; - configValues["input:tablet:transform"].intValue = 0; - configValues["input:tablet:output"].strValue = STRVAL_EMPTY; - configValues["input:tablet:region_position"].vecValue = Vector2D(); - configValues["input:tablet:region_size"].vecValue = Vector2D(); - configValues["input:tablet:relative_input"].intValue = 0; - - configValues["binds:pass_mouse_when_bound"].intValue = 0; - configValues["binds:scroll_event_delay"].intValue = 300; - configValues["binds:workspace_back_and_forth"].intValue = 0; - configValues["binds:allow_workspace_cycles"].intValue = 0; - configValues["binds:workspace_center_on"].intValue = 1; - configValues["binds:focus_preferred_method"].intValue = 0; - configValues["binds:ignore_group_lock"].intValue = 0; - configValues["binds:movefocus_cycles_fullscreen"].intValue = 1; - - configValues["gestures:workspace_swipe"].intValue = 0; - configValues["gestures:workspace_swipe_fingers"].intValue = 3; - configValues["gestures:workspace_swipe_distance"].intValue = 300; - configValues["gestures:workspace_swipe_invert"].intValue = 1; - configValues["gestures:workspace_swipe_min_speed_to_force"].intValue = 30; - configValues["gestures:workspace_swipe_cancel_ratio"].floatValue = 0.5f; - configValues["gestures:workspace_swipe_create_new"].intValue = 1; - configValues["gestures:workspace_swipe_direction_lock"].intValue = 1; - configValues["gestures:workspace_swipe_direction_lock_threshold"].intValue = 10; - configValues["gestures:workspace_swipe_forever"].intValue = 0; - configValues["gestures:workspace_swipe_numbered"].intValue = 0; - configValues["gestures:workspace_swipe_use_r"].intValue = 0; - - configValues["xwayland:use_nearest_neighbor"].intValue = 1; - configValues["xwayland:force_zero_scaling"].intValue = 0; - - configValues["opengl:nvidia_anti_flicker"].intValue = 1; - - configValues["autogenerated"].intValue = 0; -} - -void CConfigManager::setDeviceDefaultVars(const std::string& dev) { - auto& cfgValues = deviceConfigs[dev]; - - cfgValues["sensitivity"].floatValue = 0.f; - cfgValues["accel_profile"].strValue = STRVAL_EMPTY; - cfgValues["kb_file"].strValue = STRVAL_EMPTY; - cfgValues["kb_layout"].strValue = "us"; - cfgValues["kb_variant"].strValue = STRVAL_EMPTY; - cfgValues["kb_options"].strValue = STRVAL_EMPTY; - cfgValues["kb_rules"].strValue = STRVAL_EMPTY; - cfgValues["kb_model"].strValue = STRVAL_EMPTY; - cfgValues["repeat_rate"].intValue = 25; - cfgValues["repeat_delay"].intValue = 600; - cfgValues["natural_scroll"].intValue = 0; - cfgValues["tap_button_map"].strValue = STRVAL_EMPTY; - cfgValues["numlock_by_default"].intValue = 0; - cfgValues["disable_while_typing"].intValue = 1; - cfgValues["clickfinger_behavior"].intValue = 0; - cfgValues["middle_button_emulation"].intValue = 0; - cfgValues["tap-to-click"].intValue = 1; - cfgValues["tap-and-drag"].intValue = 1; - cfgValues["drag_lock"].intValue = 0; - cfgValues["left_handed"].intValue = 0; - cfgValues["scroll_method"].strValue = STRVAL_EMPTY; - cfgValues["scroll_button"].intValue = 0; - cfgValues["scroll_button_lock"].intValue = 0; - cfgValues["scroll_points"].strValue = STRVAL_EMPTY; - cfgValues["transform"].intValue = 0; - cfgValues["output"].strValue = STRVAL_EMPTY; - cfgValues["enabled"].intValue = 1; // only for mice, touchpads, and touchdevices - cfgValues["region_position"].vecValue = Vector2D(); // only for tablets - cfgValues["region_size"].vecValue = Vector2D(); // only for tablets - cfgValues["relative_input"].intValue = 0; // only for tablets +void CConfigManager::reload() { + setDefaultAnimationVars(); + resetHLConfig(); + configCurrentPath = getMainConfigPath(); + const auto ERR = m_pConfig->parse(); + postConfigReload(ERR); } void CConfigManager::setDefaultAnimationVars() { @@ -375,2124 +629,1638 @@ void CConfigManager::setDefaultAnimationVars() { CREATEANIMCFG("specialWorkspace", "workspaces"); } -void CConfigManager::init() { +std::optional CConfigManager::verifyConfigExists() { + std::string mainConfigPath = getMainConfigPath(); - loadConfigLoadVars(); + if (g_pCompositor->explicitConfigPath.empty() && !std::filesystem::exists(mainConfigPath)) { + std::string configPath = std::filesystem::path(mainConfigPath).parent_path(); - const std::string CONFIGPATH = getMainConfigPath(); + if (!std::filesystem::is_directory(configPath)) { + Debug::log(WARN, "Creating config home directory"); + try { + std::filesystem::create_directories(configPath); + } catch (...) { return "Broken config file! (Could not create config directory)"; } + } - struct stat fileStat; - int err = stat(CONFIGPATH.c_str(), &fileStat); - if (err != 0) { - Debug::log(WARN, "Error at statting config, error {}", errno); + Debug::log(WARN, "No config file found; attempting to generate."); + std::ofstream ofs; + ofs.open(mainConfigPath, std::ios::trunc); + ofs << AUTOCONFIG; + ofs.close(); } - configModifyTimes[CONFIGPATH] = fileStat.st_mtime; + if (!std::filesystem::exists(mainConfigPath)) + return "broken config dir?"; - isFirstLaunch = false; + return {}; } -void CConfigManager::configSetValueSafe(const std::string& COMMAND, const std::string& VALUE) { - if (!configValues.contains(COMMAND)) { - if (!COMMAND.starts_with("device:") /* devices parsed later */ && !COMMAND.starts_with("plugin:") /* plugins parsed later */) { - if (COMMAND[0] == '$') { - // register a dynamic var - bool found = false; - for (auto& [var, val] : configDynamicVars) { - if (var == COMMAND.substr(1)) { - Debug::log(LOG, "Registered new value for dynamic var \"{}\" -> {}", COMMAND, VALUE); - val = VALUE; - found = true; - } - } +std::optional CConfigManager::resetHLConfig() { + m_dMonitorRules.clear(); + m_dWindowRules.clear(); + g_pKeybindManager->clearKeybinds(); + g_pAnimationManager->removeAllBeziers(); + m_mAdditionalReservedAreas.clear(); + m_dBlurLSNamespaces.clear(); + m_dWorkspaceRules.clear(); + setDefaultAnimationVars(); // reset anims + m_vDeclaredPlugins.clear(); + m_dLayerRules.clear(); + m_vFailedPluginConfigValues.clear(); - if (!found) { - Debug::log(LOG, "Registered dynamic var \"{}\" -> {}", COMMAND, VALUE); - configDynamicVars.emplace_back(std::make_pair<>(COMMAND.substr(1), VALUE)); - std::sort(configDynamicVars.begin(), configDynamicVars.end(), [&](const auto& a, const auto& b) { return a.first.length() > b.first.length(); }); - } - } else { - parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">: No such field."; - } + // paths + configPaths.clear(); + std::string mainConfigPath = getMainConfigPath(); + Debug::log(LOG, "Using config: {}", mainConfigPath); + configPaths.push_back(mainConfigPath); - return; - } - } + const auto RET = verifyConfigExists(); - SConfigValue* CONFIGENTRY = nullptr; + return RET; +} - if (COMMAND.starts_with("device:")) { - const auto DEVICE = COMMAND.substr(7).substr(0, COMMAND.find_last_of(':') - 7); - const auto CONFIGVAR = COMMAND.substr(COMMAND.find_last_of(':') + 1); +void CConfigManager::postConfigReload(const Hyprlang::CParseResult& result) { + for (auto& w : g_pCompositor->m_vWindows) { + w->uncacheWindowDecos(); + } - if (!deviceConfigExists(DEVICE)) - setDeviceDefaultVars(DEVICE); + for (auto& m : g_pCompositor->m_vMonitors) + g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->ID); - auto it = deviceConfigs.find(DEVICE); + // Update the keyboard layout to the cfg'd one if this is not the first launch + if (!isFirstLaunch) { + g_pInputManager->setKeyboardLayout(); + g_pInputManager->setPointerConfigs(); + g_pInputManager->setTouchDeviceConfigs(); + g_pInputManager->setTabletConfigs(); + } - if (it->second.find(CONFIGVAR) == it->second.end()) { - if (it->second.contains("touch_output") || it->second.contains("touch_transform")) { - parseError = "touch_output and touch_transform have been changed to output and transform respectively"; - return; - } + if (!isFirstLaunch) + g_pHyprOpenGL->m_bReloadScreenShader = true; - parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">: No such field."; - return; - } + // parseError will be displayed next frame + if (result.error && !std::any_cast(m_pConfig->getConfigValue("debug:suppress_errors"))) + g_pHyprError->queueCreate(result.getError(), CColor(1.0, 50.0 / 255.0, 50.0 / 255.0, 1.0)); + else if (std::any_cast(m_pConfig->getConfigValue("autogenerated")) == 1) + g_pHyprError->queueCreate("Warning: You're using an autogenerated config! (config file: " + getMainConfigPath() + " )\nSUPER+Q -> kitty\nSUPER+M -> exit Hyprland", + CColor(1.0, 1.0, 70.0 / 255.0, 1.0)); + else + g_pHyprError->destroy(); - CONFIGENTRY = &it->second.at(CONFIGVAR); - } else if (COMMAND.starts_with("plugin:")) { - for (auto& [handle, pMap] : pluginConfigs) { - auto it = std::find_if(pMap->begin(), pMap->end(), [&](const auto& other) { return other.first == COMMAND; }); - if (it == pMap->end()) { - continue; // May be in another plugin - } + // Set the modes for all monitors as we configured them + // not on first launch because monitors might not exist yet + // and they'll be taken care of in the newMonitor event + // ignore if nomonitorreload is set + if (!isFirstLaunch && !m_bNoMonitorReload) { + // check + performMonitorReload(); + ensureMonitorStatus(); + ensureVRR(); + } - CONFIGENTRY = &it->second; - } + if (!isFirstLaunch && !g_pCompositor->m_bUnsafeState) + refreshGroupBarGradients(); - if (!CONFIGENTRY) { - m_vFailedPluginConfigValues.emplace_back(std::make_pair<>(COMMAND, VALUE)); - return; // silent ignore - } - } else { - CONFIGENTRY = &configValues.at(COMMAND); + // Updates dynamic window and workspace rules + for (auto& w : g_pCompositor->m_vWindows) { + if (!w->m_bIsMapped) + continue; + + w->updateDynamicRules(); + w->updateSpecialRenderData(); } - CONFIGENTRY->set = true; + // Update window border colors + g_pCompositor->updateAllWindowsAnimatedDecorationValues(); - if (CONFIGENTRY->intValue != -INT64_MAX) { - try { - CONFIGENTRY->intValue = configStringToInt(VALUE); - } catch (std::exception& e) { - Debug::log(WARN, "Error reading value of {}", COMMAND); - parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">. " + e.what(); - } - } else if (CONFIGENTRY->floatValue != -__FLT_MAX__) { - try { - CONFIGENTRY->floatValue = stof(VALUE); - } catch (...) { - Debug::log(WARN, "Error reading value of {}", COMMAND); - parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">."; - } - } else if (CONFIGENTRY->strValue != "") { - try { - CONFIGENTRY->strValue = VALUE; - } catch (...) { - Debug::log(WARN, "Error reading value of {}", COMMAND); - parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">."; - } - } else if (CONFIGENTRY->vecValue != Vector2D(-__FLT_MAX__, -__FLT_MAX__)) { - try { - if (const auto SPACEPOS = VALUE.find(' '); SPACEPOS != std::string::npos) { - const auto X = VALUE.substr(0, SPACEPOS); - const auto Y = VALUE.substr(SPACEPOS + 1); + // update layout + g_pLayoutManager->switchToLayout(std::any_cast(m_pConfig->getConfigValue("general:layout"))); - if (isNumber(X, true) && isNumber(Y, true)) { - CONFIGENTRY->vecValue = Vector2D(std::stof(X), std::stof(Y)); - } - } else { - Debug::log(WARN, "Error reading value of {}", COMMAND); - parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">."; - } - } catch (...) { - Debug::log(WARN, "Error reading value of {}", COMMAND); - parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">."; - } - } else if (CONFIGENTRY->data.get() != nullptr) { + // manual crash + if (std::any_cast(m_pConfig->getConfigValue("debug:manual_crash")) && !m_bManualCrashInitiated) { + m_bManualCrashInitiated = true; + g_pHyprNotificationOverlay->addNotification("Manual crash has been set up. Set debug:manual_crash back to 0 in order to crash the compositor.", CColor(0), 5000, ICON_INFO); + } else if (m_bManualCrashInitiated && !std::any_cast(m_pConfig->getConfigValue("debug:manual_crash"))) { + // cowabunga it is + g_pHyprRenderer->initiateManualCrash(); + } - switch (CONFIGENTRY->data->getDataType()) { - case CVD_TYPE_GRADIENT: { + Debug::disableStdout = !std::any_cast(m_pConfig->getConfigValue("debug:enable_stdout_logs")); + if (Debug::disableStdout && isFirstLaunch) + Debug::log(LOG, "Disabling stdout logs! Check the log for further logs."); - CVarList varlist(VALUE, 0, ' '); + for (auto& m : g_pCompositor->m_vMonitors) { + // mark blur dirty + g_pHyprOpenGL->markBlurDirtyForMonitor(m.get()); - CGradientValueData* data = (CGradientValueData*)CONFIGENTRY->data.get(); - data->m_vColors.clear(); + g_pCompositor->scheduleFrameForMonitor(m.get()); - for (auto& var : varlist) { - if (var.find("deg") != std::string::npos) { - // last arg - try { - data->m_fAngle = std::stoi(var.substr(0, var.find("deg"))) * (PI / 180.0); // radians - } catch (...) { - Debug::log(WARN, "Error reading value of {}", COMMAND); - parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">."; - } + // Force the compositor to fully re-render all monitors + m->forceFullFrames = 2; + } - break; - } + // Reset no monitor reload + m_bNoMonitorReload = false; - if (data->m_vColors.size() >= 10) { - Debug::log(WARN, "Error reading value of {}", COMMAND); - parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">. Max colors in a gradient is 10."; - break; - } + // update plugins + handlePluginLoads(); - try { - data->m_vColors.push_back(CColor(configStringToInt(var))); - } catch (std::exception& e) { - Debug::log(WARN, "Error reading value of {}", COMMAND); - parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">. " + e.what(); - } - } + EMIT_HOOK_EVENT("configReloaded", nullptr); + if (g_pEventManager) + g_pEventManager->postEvent(SHyprIPCEvent{"configreloaded", ""}); +} - if (data->m_vColors.size() == 0) { - Debug::log(WARN, "Error reading value of {}", COMMAND); - parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">. No colors provided."; +void CConfigManager::init() { - data->m_vColors.push_back(0); // transparent - } + const std::string CONFIGPATH = getMainConfigPath(); + reload(); - break; - } - default: { - UNREACHABLE(); - } - } + struct stat fileStat; + int err = stat(CONFIGPATH.c_str(), &fileStat); + if (err != 0) { + Debug::log(WARN, "Error at statting config, error {}", errno); } - if (COMMAND == "decoration:screen_shader" && VALUE != STRVAL_EMPTY) { - const auto PATH = absolutePath(VALUE, configCurrentPath); + configModifyTimes[CONFIGPATH] = fileStat.st_mtime; - configPaths.push_back(PATH); + isFirstLaunch = false; +} - struct stat fileStat; - int err = stat(PATH.c_str(), &fileStat); - if (err != 0) { - Debug::log(WARN, "Error at ticking config at {}, error {}: {}", PATH, err, strerror(err)); - return; +std::string CConfigManager::parseKeyword(const std::string& COMMAND, const std::string& VALUE) { + int needsLayoutRecalc = COMMAND == "monitor"; // 0 - no, 1 - yes, 2 - maybe + + const auto RET = m_pConfig->parseDynamic(COMMAND.c_str(), VALUE.c_str()); + + // invalidate layouts if they changed + if (needsLayoutRecalc) { + if (needsLayoutRecalc == 1 || COMMAND.contains("gaps_") || COMMAND.starts_with("dwindle:") || COMMAND.starts_with("master:")) { + for (auto& m : g_pCompositor->m_vMonitors) + g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->ID); } + } - configModifyTimes[PATH] = fileStat.st_mtime; + // Update window border colors + g_pCompositor->updateAllWindowsAnimatedDecorationValues(); + + // manual crash + if (std::any_cast(m_pConfig->getConfigValue("debug:manual_crash")) && !m_bManualCrashInitiated) { + m_bManualCrashInitiated = true; + if (g_pHyprNotificationOverlay) { + g_pHyprNotificationOverlay->addNotification("Manual crash has been set up. Set debug:manual_crash back to 0 in order to crash the compositor.", CColor(0), 5000, + ICON_INFO); + } + } else if (m_bManualCrashInitiated && !std::any_cast(m_pConfig->getConfigValue("debug:manual_crash"))) { + // cowabunga it is + g_pHyprRenderer->initiateManualCrash(); } -} -void CConfigManager::handleRawExec(const std::string& command, const std::string& args) { - // Exec in the background dont wait for it. - g_pKeybindManager->spawn(args); + return RET.error ? RET.getError() : ""; } -static bool parseModeLine(const std::string& modeline, drmModeModeInfo& mode) { - auto args = CVarList(modeline, 0, 's'); +void CConfigManager::tick() { + std::string CONFIGPATH = getMainConfigPath(); + if (!std::filesystem::exists(CONFIGPATH)) { + Debug::log(ERR, "Config doesn't exist??"); + return; + } - auto keyword = args[0]; - std::transform(keyword.begin(), keyword.end(), keyword.begin(), ::tolower); + bool parse = false; - if (keyword != "modeline") - return false; + for (auto& cf : configPaths) { + struct stat fileStat; + int err = stat(cf.c_str(), &fileStat); + if (err != 0) { + Debug::log(WARN, "Error at ticking config at {}, error {}: {}", cf, err, strerror(err)); + continue; + } - if (args.size() < 10) { - Debug::log(ERR, "modeline parse error: expected at least 9 arguments, got {}", args.size() - 1); - return false; + // check if we need to reload cfg + if (fileStat.st_mtime != configModifyTimes[cf] || m_bForceReload) { + parse = true; + configModifyTimes[cf] = fileStat.st_mtime; + } } - int argno = 1; - - mode.type = DRM_MODE_TYPE_USERDEF; - mode.clock = std::stof(args[argno++]) * 1000; - mode.hdisplay = std::stoi(args[argno++]); - mode.hsync_start = std::stoi(args[argno++]); - mode.hsync_end = std::stoi(args[argno++]); - mode.htotal = std::stoi(args[argno++]); - mode.vdisplay = std::stoi(args[argno++]); - mode.vsync_start = std::stoi(args[argno++]); - mode.vsync_end = std::stoi(args[argno++]); - mode.vtotal = std::stoi(args[argno++]); - mode.vrefresh = mode.clock * 1000.0 * 1000.0 / mode.htotal / mode.vtotal; + if (parse) { + m_bForceReload = false; - // clang-format off - static std::unordered_map flagsmap = { - {"+hsync", DRM_MODE_FLAG_PHSYNC}, - {"-hsync", DRM_MODE_FLAG_NHSYNC}, - {"+vsync", DRM_MODE_FLAG_PVSYNC}, - {"-vsync", DRM_MODE_FLAG_NVSYNC}, - {"Interlace", DRM_MODE_FLAG_INTERLACE}, - }; - // clang-format on + reload(); + } +} - for (; argno < static_cast(args.size()); argno++) { - auto key = args[argno]; - std::transform(key.begin(), key.end(), key.begin(), ::tolower); +Hyprlang::CConfigValue* CConfigManager::getConfigValueSafeDevice(const std::string& dev, const std::string& val, const std::string& fallback) { - auto it = flagsmap.find(key); + const auto VAL = m_pConfig->getSpecialConfigValuePtr("device", val.c_str(), dev.c_str()); - if (it != flagsmap.end()) - mode.flags |= it->second; - else - Debug::log(ERR, "invalid flag {} in modeline", it->first); + if ((!VAL || !VAL->m_bSetByUser) && !fallback.empty()) { + return m_pConfig->getConfigValuePtr(fallback.c_str()); } - snprintf(mode.name, sizeof(mode.name), "%dx%d@%d", mode.hdisplay, mode.vdisplay, mode.vrefresh / 1000); - - return true; + return VAL; } -void CConfigManager::handleMonitor(const std::string& command, const std::string& args) { - - // get the monitor config - SMonitorRule newrule; - - const auto ARGS = CVarList(args); +int CConfigManager::getDeviceInt(const std::string& dev, const std::string& v, const std::string& fallback) { + return std::any_cast(getConfigValueSafeDevice(dev, v, fallback)->getValue()); +} - newrule.name = ARGS[0]; +float CConfigManager::getDeviceFloat(const std::string& dev, const std::string& v, const std::string& fallback) { + return std::any_cast(getConfigValueSafeDevice(dev, v, fallback)->getValue()); +} - if (ARGS[1] == "disable" || ARGS[1] == "disabled" || ARGS[1] == "addreserved" || ARGS[1] == "transform") { - if (ARGS[1] == "disable" || ARGS[1] == "disabled") - newrule.disabled = true; - else if (ARGS[1] == "transform") { - const auto TSF = std::stoi(ARGS[2]); - if (std::clamp(TSF, 0, 7) != TSF) { - Debug::log(ERR, "invalid transform {} in monitor", TSF); - parseError = "invalid transform"; - return; - } +Vector2D CConfigManager::getDeviceVec(const std::string& dev, const std::string& v, const std::string& fallback) { + return std::any_cast(getConfigValueSafeDevice(dev, v, fallback)->getValue()); +} - const auto TRANSFORM = (wl_output_transform)TSF; +std::string CConfigManager::getDeviceString(const std::string& dev, const std::string& v, const std::string& fallback) { + const auto VAL = std::string{std::any_cast(getConfigValueSafeDevice(dev, v, fallback)->getValue())}; - // overwrite if exists - for (auto& r : m_dMonitorRules) { - if (r.name == newrule.name) { - r.transform = TRANSFORM; - return; - } - } + if (VAL == STRVAL_EMPTY) + return ""; - return; - } else if (ARGS[1] == "addreserved") { - int top = std::stoi(ARGS[2]); + return VAL; +} - int bottom = std::stoi(ARGS[3]); +SMonitorRule CConfigManager::getMonitorRuleFor(const std::string& name, const std::string& displayName) { + SMonitorRule* found = nullptr; - int left = std::stoi(ARGS[4]); + for (auto& r : m_dMonitorRules) { + if (r.name == name || + (r.name.starts_with("desc:") && + (r.name.substr(5) == displayName || r.name.substr(5) == removeBeginEndSpacesTabs(displayName.substr(0, displayName.find_first_of('(')))))) { + found = &r; + break; + } + } - int right = std::stoi(ARGS[5]); + if (found) + return *found; - m_mAdditionalReservedAreas[newrule.name] = {top, bottom, left, right}; + Debug::log(WARN, "No rule found for {}, trying to use the first.", name); - return; // this is not a rule, ignore - } else { - Debug::log(ERR, "ConfigManager parseMonitor, curitem bogus???"); - return; + for (auto& r : m_dMonitorRules) { + if (r.name == "") { + found = &r; + break; } + } - std::erase_if(m_dMonitorRules, [&](const auto& other) { return other.name == newrule.name; }); + if (found) + return *found; - m_dMonitorRules.push_back(newrule); + Debug::log(WARN, "No rules configured. Using the default hardcoded one."); - return; - } + return SMonitorRule{.name = "", .resolution = Vector2D(0, 0), .offset = Vector2D(-INT32_MAX, -INT32_MAX), .scale = -1}; // 0, 0 is preferred and -1, -1 is auto +} - if (ARGS[1].starts_with("pref")) { - newrule.resolution = Vector2D(); - } else if (ARGS[1].starts_with("highrr")) { - newrule.resolution = Vector2D(-1, -1); - } else if (ARGS[1].starts_with("highres")) { - newrule.resolution = Vector2D(-1, -2); - } else if (parseModeLine(ARGS[1], newrule.drmMode)) { - newrule.resolution = Vector2D(newrule.drmMode.hdisplay, newrule.drmMode.vdisplay); - newrule.refreshRate = newrule.drmMode.vrefresh / 1000; - } else { - newrule.resolution.x = stoi(ARGS[1].substr(0, ARGS[1].find_first_of('x'))); - newrule.resolution.y = stoi(ARGS[1].substr(ARGS[1].find_first_of('x') + 1, ARGS[1].find_first_of('@'))); +SWorkspaceRule CConfigManager::getWorkspaceRuleFor(CWorkspace* pWorkspace) { + const auto WORKSPACEIDSTR = std::to_string(pWorkspace->m_iID); + const auto IT = std::find_if(m_dWorkspaceRules.begin(), m_dWorkspaceRules.end(), [&](const auto& other) { + return other.workspaceName == pWorkspace->m_szName /* name matches */ + || (pWorkspace->m_bIsSpecialWorkspace && other.workspaceName.starts_with("special:") && + other.workspaceName.substr(8) == pWorkspace->m_szName) /* special and special:name */ + || (pWorkspace->m_iID > 0 && WORKSPACEIDSTR == other.workspaceName); /* id matches and workspace is numerical */ + }); + if (IT == m_dWorkspaceRules.end()) + return SWorkspaceRule{}; + return *IT; +} - if (ARGS[1].contains("@")) - newrule.refreshRate = stof(ARGS[1].substr(ARGS[1].find_first_of('@') + 1)); - } +std::vector CConfigManager::getMatchingRules(CWindow* pWindow, bool dynamic) { + if (!g_pCompositor->windowValidMapped(pWindow)) + return std::vector(); - if (ARGS[2].starts_with("auto")) { - newrule.offset = Vector2D(-INT32_MAX, -INT32_MAX); - } else { - newrule.offset.x = stoi(ARGS[2].substr(0, ARGS[2].find_first_of('x'))); - newrule.offset.y = stoi(ARGS[2].substr(ARGS[2].find_first_of('x') + 1)); - } + std::vector returns; - if (ARGS[3].starts_with("auto")) { - newrule.scale = -1; - } else { - newrule.scale = stof(ARGS[3]); + std::string title = g_pXWaylandManager->getTitle(pWindow); + std::string appidclass = g_pXWaylandManager->getAppIDClass(pWindow); - if (newrule.scale < 0.25f) { - parseError = "not a valid scale."; - newrule.scale = 1; - } - } + Debug::log(LOG, "Searching for matching rules for {} (title: {})", appidclass, title); - int argno = 4; + // since some rules will be applied later, we need to store some flags + bool hasFloating = pWindow->m_bIsFloating; + bool hasFullscreen = pWindow->m_bIsFullscreen; - while (ARGS[argno] != "") { - if (ARGS[argno] == "mirror") { - newrule.mirrorOf = ARGS[argno + 1]; - argno++; - } else if (ARGS[argno] == "bitdepth") { - newrule.enable10bit = ARGS[argno + 1] == "10"; - argno++; - } else if (ARGS[argno] == "transform") { - newrule.transform = (wl_output_transform)std::stoi(ARGS[argno + 1]); - argno++; - } else if (ARGS[argno] == "vrr") { - newrule.vrr = std::stoi(ARGS[argno + 1]); - argno++; - } else if (ARGS[argno] == "workspace") { - std::string name = ""; - int wsId = getWorkspaceIDFromString(ARGS[argno + 1], name); + for (auto& rule : m_dWindowRules) { + // check if we have a matching rule + if (!rule.v2) { + try { + if (rule.szValue.starts_with("title:")) { + // we have a title rule. + std::regex RULECHECK(rule.szValue.substr(6)); - SWorkspaceRule wsRule; - wsRule.monitor = newrule.name; - wsRule.workspaceString = ARGS[argno + 1]; - wsRule.workspaceName = name; - wsRule.workspaceId = wsId; + if (!std::regex_search(title, RULECHECK)) + continue; + } else { + std::regex classCheck(rule.szValue); - m_dWorkspaceRules.emplace_back(wsRule); - argno++; + if (!std::regex_search(appidclass, classCheck)) + continue; + } + } catch (...) { + Debug::log(ERR, "Regex error at {}", rule.szValue); + continue; + } } else { - Debug::log(ERR, "Config error: invalid monitor syntax"); - parseError = "invalid syntax at \"" + ARGS[argno] + "\""; - return; - } + try { + if (rule.szClass != "") { + std::regex RULECHECK(rule.szClass); - argno++; - } + if (!std::regex_search(appidclass, RULECHECK)) + continue; + } - std::erase_if(m_dMonitorRules, [&](const auto& other) { return other.name == newrule.name; }); + if (rule.szTitle != "") { + std::regex RULECHECK(rule.szTitle); - m_dMonitorRules.push_back(newrule); -} + if (!std::regex_search(title, RULECHECK)) + continue; + } -void CConfigManager::handleBezier(const std::string& command, const std::string& args) { - const auto ARGS = CVarList(args); + if (rule.szInitialTitle != "") { + std::regex RULECHECK(rule.szInitialTitle); - std::string bezierName = ARGS[0]; + if (!std::regex_search(pWindow->m_szInitialTitle, RULECHECK)) + continue; + } - if (ARGS[1] == "") - parseError = "too few arguments"; - float p1x = std::stof(ARGS[1]); + if (rule.szInitialClass != "") { + std::regex RULECHECK(rule.szInitialClass); - if (ARGS[2] == "") - parseError = "too few arguments"; - float p1y = std::stof(ARGS[2]); - - if (ARGS[3] == "") - parseError = "too few arguments"; - float p2x = std::stof(ARGS[3]); - - if (ARGS[4] == "") - parseError = "too few arguments"; - float p2y = std::stof(ARGS[4]); - - if (ARGS[5] != "") - parseError = "too many arguments"; - - g_pAnimationManager->addBezierWithName(bezierName, Vector2D(p1x, p1y), Vector2D(p2x, p2y)); -} - -void CConfigManager::setAnimForChildren(SAnimationPropertyConfig* const ANIM) { - for (auto& [name, anim] : animationConfig) { - if (anim.pParentAnimation == ANIM && !anim.overridden) { - // if a child isnt overridden, set the values of the parent - anim.pValues = ANIM->pValues; + if (!std::regex_search(pWindow->m_szInitialClass, RULECHECK)) + continue; + } - setAnimForChildren(&anim); - } - } -}; + if (rule.bX11 != -1) { + if (pWindow->m_bIsX11 != rule.bX11) + continue; + } -void CConfigManager::handleAnimation(const std::string& command, const std::string& args) { - const auto ARGS = CVarList(args); + if (rule.bFloating != -1) { + if (hasFloating != rule.bFloating) + continue; + } - // Master on/off + if (rule.bFullscreen != -1) { + if (hasFullscreen != rule.bFullscreen) + continue; + } - // anim name - const auto ANIMNAME = ARGS[0]; + if (rule.bPinned != -1) { + if (pWindow->m_bPinned != rule.bPinned) + continue; + } - const auto PANIM = animationConfig.find(ANIMNAME); + if (rule.bFocus != -1) { + if (rule.bFocus != (g_pCompositor->m_pLastWindow == pWindow)) + continue; + } - if (PANIM == animationConfig.end()) { - parseError = "no such animation"; - return; - } + if (rule.iOnWorkspace != -1) { + if (rule.iOnWorkspace != g_pCompositor->getWindowsOnWorkspace(pWindow->m_iWorkspaceID)) + continue; + } - PANIM->second.overridden = true; - PANIM->second.pValues = &PANIM->second; + if (!rule.szWorkspace.empty()) { + const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(pWindow->m_iWorkspaceID); - // on/off - PANIM->second.internalEnabled = ARGS[1] == "1"; + if (!PWORKSPACE) + continue; - if (ARGS[1] != "0" && ARGS[1] != "1") { - parseError = "invalid animation on/off state"; - } + if (rule.szWorkspace.starts_with("name:")) { + if (PWORKSPACE->m_szName != rule.szWorkspace.substr(5)) + continue; + } else { + // number + if (!isNumber(rule.szWorkspace)) + throw std::runtime_error("szWorkspace not name: or number"); - if (PANIM->second.internalEnabled) { - // speed - if (isNumber(ARGS[2], true)) { - PANIM->second.internalSpeed = std::stof(ARGS[2]); + const int64_t ID = std::stoll(rule.szWorkspace); - if (PANIM->second.internalSpeed <= 0) { - parseError = "invalid speed"; - PANIM->second.internalSpeed = 1.f; + if (PWORKSPACE->m_iID != ID) + continue; + } + } + } catch (std::exception& e) { + Debug::log(ERR, "Regex error at {} ({})", rule.szValue, e.what()); + continue; } - } else { - PANIM->second.internalSpeed = 10.f; - parseError = "invalid speed"; } - // curve - PANIM->second.internalBezier = ARGS[3]; - - if (!g_pAnimationManager->bezierExists(ARGS[3])) { - parseError = "no such bezier"; - PANIM->second.internalBezier = "default"; - } + // applies. Read the rule and behave accordingly + Debug::log(LOG, "Window rule {} -> {} matched {}", rule.szRule, rule.szValue, pWindow); - // style - PANIM->second.internalStyle = ARGS[4]; + returns.push_back(rule); - if (ARGS[4] != "") { - const auto ERR = g_pAnimationManager->styleValidInConfigVar(ANIMNAME, ARGS[4]); + if (dynamic) + continue; - if (ERR != "") - parseError = ERR; - } + if (rule.szRule == "float") + hasFloating = true; + else if (rule.szRule == "fullscreen") + hasFullscreen = true; } - // now, check for children, recursively - setAnimForChildren(&PANIM->second); -} - -void CConfigManager::handleBind(const std::string& command, const std::string& value) { - // example: - // bind[fl]=SUPER,G,exec,dmenu_run + std::vector PIDs = {(uint64_t)pWindow->getPID()}; + while (getPPIDof(PIDs.back()) > 10) + PIDs.push_back(getPPIDof(PIDs.back())); - // flags - bool locked = false; - bool release = false; - bool repeat = false; - bool mouse = false; - bool nonConsuming = false; - bool transparent = false; - bool ignoreMods = false; - const auto BINDARGS = command.substr(4); + bool anyExecFound = false; - for (auto& arg : BINDARGS) { - if (arg == 'l') { - locked = true; - } else if (arg == 'r') { - release = true; - } else if (arg == 'e') { - repeat = true; - } else if (arg == 'm') { - mouse = true; - } else if (arg == 'n') { - nonConsuming = true; - } else if (arg == 't') { - transparent = true; - } else if (arg == 'i') { - ignoreMods = true; - } else { - parseError = "bind: invalid flag"; - return; + for (auto& er : execRequestedRules) { + if (std::ranges::any_of(PIDs, [&](const auto& pid) { return pid == er.iPid; })) { + returns.push_back({er.szRule, "execRule"}); + anyExecFound = true; } } - if (release && repeat) { - parseError = "flags r and e are mutually exclusive"; - return; - } - - if (mouse && (repeat || release || locked)) { - parseError = "flag m is exclusive"; - return; - } - - const auto ARGS = CVarList(value, 4); - - if ((ARGS.size() < 3 && !mouse) || (ARGS.size() < 3 && mouse)) { - parseError = "bind: too few args"; - return; - } else if ((ARGS.size() > 4 && !mouse) || (ARGS.size() > 3 && mouse)) { - parseError = "bind: too many args"; - return; - } - - const auto MOD = g_pKeybindManager->stringToModMask(ARGS[0]); - const auto MODSTR = ARGS[0]; - - const auto KEY = ARGS[1]; - - auto HANDLER = ARGS[2]; - - const auto COMMAND = mouse ? HANDLER : ARGS[3]; + if (anyExecFound) // remove exec rules to unclog searches in the future, why have the garbage here. + execRequestedRules.erase(std::remove_if(execRequestedRules.begin(), execRequestedRules.end(), + [&](const SExecRequestedRule& other) { return std::ranges::any_of(PIDs, [&](const auto& pid) { return pid == other.iPid; }); })); - if (mouse) - HANDLER = "mouse"; + return returns; +} - // to lower - std::transform(HANDLER.begin(), HANDLER.end(), HANDLER.begin(), ::tolower); +std::vector CConfigManager::getMatchingRules(SLayerSurface* pLS) { + std::vector returns; - const auto DISPATCHER = g_pKeybindManager->m_mDispatchers.find(HANDLER); + if (!pLS->layerSurface || pLS->fadingOut) + return returns; - if (DISPATCHER == g_pKeybindManager->m_mDispatchers.end()) { - Debug::log(ERR, "Invalid dispatcher!"); - parseError = "Invalid dispatcher, requested \"" + HANDLER + "\" does not exist"; - return; - } + for (auto& lr : m_dLayerRules) { + if (lr.targetNamespace.starts_with("address:0x")) { + if (std::format("address:0x{:x}", (uintptr_t)pLS) != lr.targetNamespace) + continue; + } else { + std::regex NSCHECK(lr.targetNamespace); - if (MOD == 0 && MODSTR != "") { - Debug::log(ERR, "Invalid mod!"); - parseError = "Invalid mod, requested mod \"" + MODSTR + "\" is not a valid mod."; - return; - } + if (!pLS->layerSurface->_namespace || !std::regex_search(pLS->layerSurface->_namespace, NSCHECK)) + continue; + } - if (KEY != "") { - if (isNumber(KEY) && std::stoi(KEY) > 9) - g_pKeybindManager->addKeybind( - SKeybind{"", std::stoi(KEY), MOD, HANDLER, COMMAND, locked, m_szCurrentSubmap, release, repeat, mouse, nonConsuming, transparent, ignoreMods}); - else if (KEY.starts_with("code:") && isNumber(KEY.substr(5))) - g_pKeybindManager->addKeybind( - SKeybind{"", std::stoi(KEY.substr(5)), MOD, HANDLER, COMMAND, locked, m_szCurrentSubmap, release, repeat, mouse, nonConsuming, transparent, ignoreMods}); - else - g_pKeybindManager->addKeybind(SKeybind{KEY, 0, MOD, HANDLER, COMMAND, locked, m_szCurrentSubmap, release, repeat, mouse, nonConsuming, transparent, ignoreMods}); + // hit + returns.push_back(lr); } -} - -void CConfigManager::handleUnbind(const std::string& command, const std::string& value) { - const auto ARGS = CVarList(value); - - const auto MOD = g_pKeybindManager->stringToModMask(ARGS[0]); - - const auto KEY = ARGS[1]; - - g_pKeybindManager->removeKeybind(MOD, KEY); -} -bool windowRuleValid(const std::string& RULE) { - return RULE == "float" || RULE == "tile" || RULE.starts_with("opacity") || RULE.starts_with("move") || RULE.starts_with("size") || RULE.starts_with("minsize") || - RULE.starts_with("maxsize") || RULE.starts_with("pseudo") || RULE.starts_with("monitor") || RULE.starts_with("idleinhibit") || RULE == "nofocus" || RULE == "noblur" || - RULE == "noshadow" || RULE == "nodim" || RULE == "noborder" || RULE == "opaque" || RULE == "forceinput" || RULE == "fullscreen" || RULE == "fakefullscreen" || - RULE == "nomaxsize" || RULE == "pin" || RULE == "noanim" || RULE == "dimaround" || RULE == "windowdance" || RULE == "maximize" || RULE == "keepaspectratio" || - RULE.starts_with("animation") || RULE.starts_with("rounding") || RULE.starts_with("workspace") || RULE.starts_with("bordercolor") || RULE == "forcergbx" || - RULE == "noinitialfocus" || RULE == "stayfocused" || RULE.starts_with("bordersize") || RULE.starts_with("xray") || RULE.starts_with("center") || - RULE.starts_with("group") || RULE == "immediate" || RULE == "nearestneighbor" || RULE.starts_with("suppressevent"); -} + if (pLS->layerSurface->_namespace && shouldBlurLS(pLS->layerSurface->_namespace)) + returns.push_back({pLS->layerSurface->_namespace, "blur"}); -bool layerRuleValid(const std::string& RULE) { - return RULE == "noanim" || RULE == "blur" || RULE.starts_with("ignorealpha") || RULE.starts_with("ignorezero") || RULE.starts_with("xray"); + return returns; } -void CConfigManager::handleWindowRule(const std::string& command, const std::string& value) { - const auto RULE = removeBeginEndSpacesTabs(value.substr(0, value.find_first_of(','))); - const auto VALUE = removeBeginEndSpacesTabs(value.substr(value.find_first_of(',') + 1)); - - // check rule and value - if (RULE == "" || VALUE == "") { +void CConfigManager::dispatchExecOnce() { + if (firstExecDispatched || isFirstLaunch) return; - } - if (RULE == "unset") { - std::erase_if(m_dWindowRules, [&](const SWindowRule& other) { return other.szValue == VALUE; }); - return; - } + // update dbus env + if (g_pCompositor->m_sWLRSession) + handleRawExec("", +#ifdef USES_SYSTEMD + "systemctl --user import-environment DISPLAY WAYLAND_DISPLAY HYPRLAND_INSTANCE_SIGNATURE XDG_CURRENT_DESKTOP QT_QPA_PLATFORMTHEME && hash " + "dbus-update-activation-environment 2>/dev/null && " +#endif + "dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP HYPRLAND_INSTANCE_SIGNATURE QT_QPA_PLATFORMTHEME"); - // verify we support a rule - if (!windowRuleValid(RULE)) { - Debug::log(ERR, "Invalid rule found: {}", RULE); - parseError = "Invalid rule found: " + RULE; - return; - } - - if (RULE.starts_with("size") || RULE.starts_with("maxsize") || RULE.starts_with("minsize")) - m_dWindowRules.push_front({RULE, VALUE}); - else - m_dWindowRules.push_back({RULE, VALUE}); -} - -void CConfigManager::handleLayerRule(const std::string& command, const std::string& value) { - const auto RULE = removeBeginEndSpacesTabs(value.substr(0, value.find_first_of(','))); - const auto VALUE = removeBeginEndSpacesTabs(value.substr(value.find_first_of(',') + 1)); - - // check rule and value - if (RULE == "" || VALUE == "") - return; - - if (RULE == "unset") { - std::erase_if(m_dLayerRules, [&](const SLayerRule& other) { return other.targetNamespace == VALUE; }); - return; - } - - if (!layerRuleValid(RULE)) { - Debug::log(ERR, "Invalid rule found: {}", RULE); - parseError = "Invalid rule found: " + RULE; - return; - } - - m_dLayerRules.push_back({VALUE, RULE}); - - for (auto& m : g_pCompositor->m_vMonitors) - for (auto& lsl : m->m_aLayerSurfaceLayers) - for (auto& ls : lsl) - ls->applyRules(); -} - -void CConfigManager::handleWindowRuleV2(const std::string& command, const std::string& value) { - const auto RULE = removeBeginEndSpacesTabs(value.substr(0, value.find_first_of(','))); - const auto VALUE = value.substr(value.find_first_of(',') + 1); - - if (!windowRuleValid(RULE) && RULE != "unset") { - Debug::log(ERR, "Invalid rulev2 found: {}", RULE); - parseError = "Invalid rulev2 found: " + RULE; - return; - } - - // now we estract shit from the value - SWindowRule rule; - rule.v2 = true; - rule.szRule = RULE; - rule.szValue = VALUE; - - const auto TITLEPOS = VALUE.find("title:"); - const auto CLASSPOS = VALUE.find("class:"); - const auto INITIALTITLEPOS = VALUE.find("initialTitle:"); - const auto INITIALCLASSPOS = VALUE.find("initialClass:"); - const auto X11POS = VALUE.find("xwayland:"); - const auto FLOATPOS = VALUE.find("floating:"); - const auto FULLSCREENPOS = VALUE.find("fullscreen:"); - const auto PINNEDPOS = VALUE.find("pinned:"); - const auto FOCUSPOS = VALUE.find("focus:"); - const auto ONWORKSPACEPOS = VALUE.find("onworkspace:"); - - // find workspacepos that isn't onworkspacepos - size_t WORKSPACEPOS = std::string::npos; - size_t currentPos = VALUE.find("workspace:"); - while (currentPos != std::string::npos) { - if (currentPos == 0 || VALUE[currentPos - 1] != 'n') { - WORKSPACEPOS = currentPos; - break; - } - currentPos = VALUE.find("workspace:", currentPos + 1); - } - - if (TITLEPOS == std::string::npos && CLASSPOS == std::string::npos && INITIALTITLEPOS == std::string::npos && INITIALCLASSPOS == std::string::npos && - X11POS == std::string::npos && FLOATPOS == std::string::npos && FULLSCREENPOS == std::string::npos && PINNEDPOS == std::string::npos && WORKSPACEPOS == std::string::npos && - FOCUSPOS == std::string::npos && ONWORKSPACEPOS == std::string::npos) { - Debug::log(ERR, "Invalid rulev2 syntax: {}", VALUE); - parseError = "Invalid rulev2 syntax: " + VALUE; - return; - } - - auto extract = [&](size_t pos) -> std::string { - std::string result; - result = VALUE.substr(pos); - - size_t min = 999999; - if (TITLEPOS > pos && TITLEPOS < min) - min = TITLEPOS; - if (CLASSPOS > pos && CLASSPOS < min) - min = CLASSPOS; - if (INITIALTITLEPOS > pos && INITIALTITLEPOS < min) - min = INITIALTITLEPOS; - if (INITIALCLASSPOS > pos && INITIALCLASSPOS < min) - min = INITIALCLASSPOS; - if (X11POS > pos && X11POS < min) - min = X11POS; - if (FLOATPOS > pos && FLOATPOS < min) - min = FLOATPOS; - if (FULLSCREENPOS > pos && FULLSCREENPOS < min) - min = FULLSCREENPOS; - if (PINNEDPOS > pos && PINNEDPOS < min) - min = PINNEDPOS; - if (ONWORKSPACEPOS > pos && ONWORKSPACEPOS < min) - min = ONWORKSPACEPOS; - if (WORKSPACEPOS > pos && WORKSPACEPOS < min) - min = WORKSPACEPOS; - if (FOCUSPOS > pos && FOCUSPOS < min) - min = FOCUSPOS; - - result = result.substr(0, min - pos); - - result = removeBeginEndSpacesTabs(result); - - if (result.back() == ',') - result.pop_back(); - - return result; - }; - - if (CLASSPOS != std::string::npos) - rule.szClass = extract(CLASSPOS + 6); - - if (TITLEPOS != std::string::npos) - rule.szTitle = extract(TITLEPOS + 6); - - if (INITIALCLASSPOS != std::string::npos) - rule.szInitialClass = extract(INITIALCLASSPOS + 13); - - if (INITIALTITLEPOS != std::string::npos) - rule.szInitialTitle = extract(INITIALTITLEPOS + 13); - - if (X11POS != std::string::npos) - rule.bX11 = extract(X11POS + 9) == "1" ? 1 : 0; - - if (FLOATPOS != std::string::npos) - rule.bFloating = extract(FLOATPOS + 9) == "1" ? 1 : 0; - - if (FULLSCREENPOS != std::string::npos) - rule.bFullscreen = extract(FULLSCREENPOS + 11) == "1" ? 1 : 0; - - if (PINNEDPOS != std::string::npos) - rule.bPinned = extract(PINNEDPOS + 7) == "1" ? 1 : 0; - - if (WORKSPACEPOS != std::string::npos) - rule.szWorkspace = extract(WORKSPACEPOS + 10); - - if (FOCUSPOS != std::string::npos) - rule.bFocus = extract(FOCUSPOS + 6) == "1" ? 1 : 0; - - if (ONWORKSPACEPOS != std::string::npos) - rule.iOnWorkspace = configStringToInt(extract(ONWORKSPACEPOS + 12)); - - if (RULE == "unset") { - std::erase_if(m_dWindowRules, [&](const SWindowRule& other) { - if (!other.v2) { - return other.szClass == rule.szClass && !rule.szClass.empty(); - } else { - if (!rule.szClass.empty() && rule.szClass != other.szClass) - return false; - - if (!rule.szTitle.empty() && rule.szTitle != other.szTitle) - return false; - - if (!rule.szInitialClass.empty() && rule.szInitialClass != other.szInitialClass) - return false; - - if (!rule.szInitialTitle.empty() && rule.szInitialTitle != other.szInitialTitle) - return false; - - if (rule.bX11 != -1 && rule.bX11 != other.bX11) - return false; - - if (rule.bFloating != -1 && rule.bFloating != other.bFloating) - return false; - - if (rule.bFullscreen != -1 && rule.bFullscreen != other.bFullscreen) - return false; - - if (rule.bPinned != -1 && rule.bPinned != other.bPinned) - return false; - - if (!rule.szWorkspace.empty() && rule.szWorkspace != other.szWorkspace) - return false; - - if (rule.bFocus != -1 && rule.bFocus != other.bFocus) - return false; - - if (rule.iOnWorkspace != -1 && rule.iOnWorkspace != other.iOnWorkspace) - return false; - - return true; - } - }); - return; - } - - if (RULE.starts_with("size") || RULE.starts_with("maxsize") || RULE.starts_with("minsize")) - m_dWindowRules.push_front(rule); - else - m_dWindowRules.push_back(rule); -} - -void CConfigManager::updateBlurredLS(const std::string& name, const bool forceBlur) { - const bool BYADDRESS = name.starts_with("address:"); - std::string matchName = name; - - if (BYADDRESS) { - matchName = matchName.substr(8); - } - - for (auto& m : g_pCompositor->m_vMonitors) { - for (auto& lsl : m->m_aLayerSurfaceLayers) { - for (auto& ls : lsl) { - if (BYADDRESS) { - if (std::format("0x{:x}", (uintptr_t)ls.get()) == matchName) - ls->forceBlur = forceBlur; - } else if (ls->szNamespace == matchName) - ls->forceBlur = forceBlur; - } - } - } -} - -void CConfigManager::handleBlurLS(const std::string& command, const std::string& value) { - if (value.starts_with("remove,")) { - const auto TOREMOVE = removeBeginEndSpacesTabs(value.substr(7)); - if (std::erase_if(m_dBlurLSNamespaces, [&](const auto& other) { return other == TOREMOVE; })) - updateBlurredLS(TOREMOVE, false); - return; - } - - m_dBlurLSNamespaces.emplace_back(value); - updateBlurredLS(value, true); -} - -void CConfigManager::handleWorkspaceRules(const std::string& command, const std::string& value) { - // This can either be the monitor or the workspace identifier - const auto FIRST_DELIM = value.find_first_of(','); - - std::string name = ""; - auto first_ident = removeBeginEndSpacesTabs(value.substr(0, FIRST_DELIM)); - int id = getWorkspaceIDFromString(first_ident, name); - - auto rules = value.substr(FIRST_DELIM + 1); - SWorkspaceRule wsRule; - wsRule.workspaceString = first_ident; - if (id == WORKSPACE_INVALID) { - // it could be the monitor. If so, second value MUST be - // the workspace. - const auto WORKSPACE_DELIM = value.find_first_of(',', FIRST_DELIM + 1); - auto wsIdent = removeBeginEndSpacesTabs(value.substr(FIRST_DELIM + 1, (WORKSPACE_DELIM - FIRST_DELIM - 1))); - id = getWorkspaceIDFromString(wsIdent, name); - if (id == WORKSPACE_INVALID) { - Debug::log(ERR, "Invalid workspace identifier found: {}", wsIdent); - parseError = "Invalid workspace identifier found: " + wsIdent; - return; - } - wsRule.monitor = first_ident; - wsRule.workspaceString = wsIdent; - wsRule.isDefault = true; // backwards compat - rules = value.substr(WORKSPACE_DELIM + 1); - } - - const static std::string ruleOnCreatedEmtpy = "on-created-empty:"; - const static int ruleOnCreatedEmtpyLen = ruleOnCreatedEmtpy.length(); - - auto assignRule = [&](std::string rule) { - size_t delim = std::string::npos; - if ((delim = rule.find("gapsin:")) != std::string::npos) - wsRule.gapsIn = std::stoi(rule.substr(delim + 7)); - else if ((delim = rule.find("gapsout:")) != std::string::npos) - wsRule.gapsOut = std::stoi(rule.substr(delim + 8)); - else if ((delim = rule.find("bordersize:")) != std::string::npos) - wsRule.borderSize = std::stoi(rule.substr(delim + 11)); - else if ((delim = rule.find("border:")) != std::string::npos) - wsRule.border = configStringToInt(rule.substr(delim + 7)); - else if ((delim = rule.find("shadow:")) != std::string::npos) - wsRule.shadow = configStringToInt(rule.substr(delim + 7)); - else if ((delim = rule.find("rounding:")) != std::string::npos) - wsRule.rounding = configStringToInt(rule.substr(delim + 9)); - else if ((delim = rule.find("decorate:")) != std::string::npos) - wsRule.decorate = configStringToInt(rule.substr(delim + 9)); - else if ((delim = rule.find("monitor:")) != std::string::npos) - wsRule.monitor = rule.substr(delim + 8); - else if ((delim = rule.find("default:")) != std::string::npos) - wsRule.isDefault = configStringToInt(rule.substr(delim + 8)); - else if ((delim = rule.find("persistent:")) != std::string::npos) - wsRule.isPersistent = configStringToInt(rule.substr(delim + 11)); - else if ((delim = rule.find(ruleOnCreatedEmtpy)) != std::string::npos) - wsRule.onCreatedEmptyRunCmd = cleanCmdForWorkspace(name, rule.substr(delim + ruleOnCreatedEmtpyLen)); - else if ((delim = rule.find("layoutopt:")) != std::string::npos) { - std::string opt = rule.substr(delim + 10); - if (!opt.contains(":")) { - // invalid - Debug::log(ERR, "Invalid workspace rule found: {}", rule); - parseError = "Invalid workspace rule found: " + rule; - return; - } - - std::string val = opt.substr(opt.find(":") + 1); - opt = opt.substr(0, opt.find(":")); - - wsRule.layoutopts[opt] = val; - } - }; - - size_t pos = 0; - std::string rule; - while ((pos = rules.find(',')) != std::string::npos) { - rule = rules.substr(0, pos); - assignRule(rule); - rules.erase(0, pos + 1); - } - assignRule(rules); // match remaining rule - - wsRule.workspaceId = id; - wsRule.workspaceName = name; - - const auto IT = std::find_if(m_dWorkspaceRules.begin(), m_dWorkspaceRules.end(), [&](const auto& other) { return other.workspaceString == wsRule.workspaceString; }); - - if (IT == m_dWorkspaceRules.end()) - m_dWorkspaceRules.emplace_back(wsRule); - else - *IT = wsRule; -} - -void CConfigManager::handleSubmap(const std::string& command, const std::string& submap) { - if (submap == "reset") - m_szCurrentSubmap = ""; - else - m_szCurrentSubmap = submap; -} - -void CConfigManager::handleSource(const std::string& command, const std::string& rawpath) { - if (rawpath.length() < 2) { - Debug::log(ERR, "source= path garbage"); - parseError = "source path " + rawpath + " bogus!"; - return; - } - std::unique_ptr glob_buf{new glob_t, [](glob_t* g) { globfree(g); }}; - memset(glob_buf.get(), 0, sizeof(glob_t)); - - if (auto r = glob(absolutePath(rawpath, configCurrentPath).c_str(), GLOB_TILDE, nullptr, glob_buf.get()); r != 0) { - parseError = std::format("source= globbing error: {}", r == GLOB_NOMATCH ? "found no match" : GLOB_ABORTED ? "read error" : "out of memory"); - Debug::log(ERR, "{}", parseError); - return; - } - - for (size_t i = 0; i < glob_buf->gl_pathc; i++) { - auto value = absolutePath(glob_buf->gl_pathv[i], configCurrentPath); - - if (!std::filesystem::is_regular_file(value)) { - if (std::filesystem::exists(value)) { - Debug::log(WARN, "source= skipping non-file {}", value); - continue; - } - - Debug::log(ERR, "source= file doesnt exist"); - parseError = "source file " + value + " doesn't exist!"; - return; - } - configPaths.push_back(value); - - struct stat fileStat; - int err = stat(value.c_str(), &fileStat); - if (err != 0) { - Debug::log(WARN, "Error at ticking config at {}, error {}: {}", value, err, strerror(err)); - return; - } + firstExecDispatched = true; - configModifyTimes[value] = fileStat.st_mtime; + for (auto& c : firstExecRequests) { + handleRawExec("", c); + } - std::ifstream ifs; - ifs.open(value); + firstExecRequests.clear(); // free some kb of memory :P - std::string line = ""; - int linenum = 1; - if (ifs.is_open()) { - auto configCurrentPathBackup = configCurrentPath; + // set input, fixes some certain issues + g_pInputManager->setKeyboardLayout(); + g_pInputManager->setPointerConfigs(); + g_pInputManager->setTouchDeviceConfigs(); + g_pInputManager->setTabletConfigs(); - while (std::getline(ifs, line)) { - // Read line by line. - try { - configCurrentPath = value; - parseLine(line); - } catch (...) { - Debug::log(ERR, "Error reading line from config. Line:"); - Debug::log(NONE, "{}", line.c_str()); + // check for user's possible errors with their setup and notify them if needed + g_pCompositor->performUserChecks(); +} - parseError += "Config error at line " + std::to_string(linenum) + " (" + configCurrentPath + "): Line parsing error."; - } +void CConfigManager::performMonitorReload() { - if (parseError != "" && !parseError.starts_with("Config error at line")) { - parseError = "Config error at line " + std::to_string(linenum) + " (" + configCurrentPath + "): " + parseError; - } + bool overAgain = false; - ++linenum; - } + for (auto& m : g_pCompositor->m_vRealMonitors) { + if (!m->output || m->isUnsafeFallback) + continue; - ifs.close(); + auto rule = getMonitorRuleFor(m->szName, m->szDescription); - configCurrentPath = configCurrentPathBackup; + if (!g_pHyprRenderer->applyMonitorRule(m.get(), &rule)) { + overAgain = true; + break; } + + // ensure mirror + m->setMirror(rule.mirrorOf); + + g_pHyprRenderer->arrangeLayersForMonitor(m->ID); } -} -void CConfigManager::handleBindWS(const std::string& command, const std::string& value) { - parseError = "bindws has been deprecated in favor of workspace rules, see the wiki -> workspace rules"; + if (overAgain) + performMonitorReload(); + + m_bWantsMonitorReload = false; + + EMIT_HOOK_EVENT("monitorLayoutChanged", nullptr); } -void CConfigManager::handleEnv(const std::string& command, const std::string& value) { - if (!isFirstLaunch) - return; +void* const* CConfigManager::getConfigValuePtr(const std::string& val) { + const auto VAL = m_pConfig->getConfigValuePtr(val.c_str()); + if (!VAL) + return nullptr; + return VAL->getDataStaticPtr(); +} - const auto ARGS = CVarList(value, 2); +Hyprlang::CConfigValue* CConfigManager::getHyprlangConfigValuePtr(const std::string& name, const std::string& specialCat) { + if (!specialCat.empty()) + return m_pConfig->getSpecialConfigValuePtr(specialCat.c_str(), name.c_str(), nullptr); - if (ARGS[0].empty()) { - parseError = "env empty"; - return; - } + return m_pConfig->getConfigValuePtr(name.c_str()); +} - setenv(ARGS[0].c_str(), ARGS[1].c_str(), 1); +bool CConfigManager::deviceConfigExists(const std::string& dev) { + auto copy = dev; + std::replace(copy.begin(), copy.end(), ' ', '-'); - if (command.back() == 'd') { - // dbus - const auto CMD = -#ifdef USES_SYSTEMD - "systemctl --user import-environment " + ARGS[0] + - " && hash dbus-update-activation-environment 2>/dev/null && " -#endif - "dbus-update-activation-environment --systemd " + - ARGS[0]; - handleRawExec("", CMD); - } + return m_pConfig->specialCategoryExistsForKey("device", copy.c_str()); } -void CConfigManager::handlePlugin(const std::string& command, const std::string& path) { - if (std::find(m_vDeclaredPlugins.begin(), m_vDeclaredPlugins.end(), path) != m_vDeclaredPlugins.end()) { - parseError = "plugin '" + path + "' declared twice"; - return; +bool CConfigManager::shouldBlurLS(const std::string& ns) { + for (auto& bls : m_dBlurLSNamespaces) { + if (bls == ns) { + return true; + } } - m_vDeclaredPlugins.push_back(path); + return false; } -std::string CConfigManager::parseKeyword(const std::string& COMMAND, const std::string& VALUE, bool dynamic) { - if (dynamic) { - parseError = ""; - currentCategory = ""; - } +void CConfigManager::ensureMonitorStatus() { + for (auto& rm : g_pCompositor->m_vRealMonitors) { + if (!rm->output || rm->isUnsafeFallback) + continue; - int needsLayoutRecalc = COMMAND == "monitor"; // 0 - no, 1 - yes, 2 - maybe + auto rule = getMonitorRuleFor(rm->szName, rm->szDescription); - if (COMMAND == "exec") { - if (isFirstLaunch) { - firstExecRequests.push_back(VALUE); - } else { - handleRawExec(COMMAND, VALUE); - } - } else if (COMMAND == "exec-once") { - if (isFirstLaunch) { - firstExecRequests.push_back(VALUE); - } - } else if (COMMAND == "monitor") - handleMonitor(COMMAND, VALUE); - else if (COMMAND.starts_with("bind")) - handleBind(COMMAND, VALUE); - else if (COMMAND == "unbind") - handleUnbind(COMMAND, VALUE); - else if (COMMAND == "workspace") - handleWorkspaceRules(COMMAND, VALUE); - else if (COMMAND == "windowrule") - handleWindowRule(COMMAND, VALUE); - else if (COMMAND == "windowrulev2") - handleWindowRuleV2(COMMAND, VALUE); - else if (COMMAND == "layerrule") - handleLayerRule(COMMAND, VALUE); - else if (COMMAND == "bezier") - handleBezier(COMMAND, VALUE); - else if (COMMAND == "animation") - handleAnimation(COMMAND, VALUE); - else if (COMMAND == "source") - handleSource(COMMAND, VALUE); - else if (COMMAND == "submap") - handleSubmap(COMMAND, VALUE); - else if (COMMAND == "blurls") - handleBlurLS(COMMAND, VALUE); - else if (COMMAND == "wsbind") - handleBindWS(COMMAND, VALUE); - else if (COMMAND == "plugin") - handlePlugin(COMMAND, VALUE); - else if (COMMAND.starts_with("env")) - handleEnv(COMMAND, VALUE); - else { - // try config - const auto IT = std::find_if(pluginKeywords.begin(), pluginKeywords.end(), [&](const auto& other) { return other.name == COMMAND; }); - - if (IT != pluginKeywords.end()) { - IT->fn(COMMAND, VALUE); - } else { - configSetValueSafe(currentCategory + (currentCategory == "" ? "" : ":") + COMMAND, VALUE); - needsLayoutRecalc = 2; - } + if (rule.disabled == rm->m_bEnabled) + g_pHyprRenderer->applyMonitorRule(rm.get(), &rule); } +} + +void CConfigManager::ensureVRR(CMonitor* pMonitor) { + static auto* const PVRR = reinterpret_cast(getConfigValuePtr("misc:vrr")); - if (dynamic) { - std::string retval = parseError; - parseError = ""; + static auto ensureVRRForDisplay = [&](CMonitor* m) -> void { + if (!m->output || m->createdByUser) + return; - // invalidate layouts if they changed - if (needsLayoutRecalc) { - if (needsLayoutRecalc == 1 || COMMAND.contains("gaps_") || COMMAND.starts_with("dwindle:") || COMMAND.starts_with("master:")) { - for (auto& m : g_pCompositor->m_vMonitors) - g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->ID); - } - } + const auto USEVRR = m->activeMonitorRule.vrr.has_value() ? m->activeMonitorRule.vrr.value() : **PVRR; - // Update window border colors - g_pCompositor->updateAllWindowsAnimatedDecorationValues(); + if (USEVRR == 0) { + if (m->vrrActive) { + wlr_output_state_set_adaptive_sync_enabled(m->state.wlr(), 0); - // manual crash - if (configValues["debug:manual_crash"].intValue && !m_bManualCrashInitiated) { - m_bManualCrashInitiated = true; - if (g_pHyprNotificationOverlay) { - g_pHyprNotificationOverlay->addNotification("Manual crash has been set up. Set debug:manual_crash back to 0 in order to crash the compositor.", CColor(0), 5000, - ICON_INFO); + if (!m->state.commit()) + Debug::log(ERR, "Couldn't commit output {} in ensureVRR -> false", m->output->name); } - } else if (m_bManualCrashInitiated && !configValues["debug:manual_crash"].intValue) { - // cowabunga it is - g_pHyprRenderer->initiateManualCrash(); - } + m->vrrActive = false; + return; + } else if (USEVRR == 1) { + if (!m->vrrActive) { + wlr_output_state_set_adaptive_sync_enabled(m->state.wlr(), 1); - return retval; - } + if (!m->state.test()) { + Debug::log(LOG, "Pending output {} does not accept VRR.", m->output->name); + wlr_output_state_set_adaptive_sync_enabled(m->state.wlr(), 0); + } - return parseError; -} + if (!m->state.commit()) + Debug::log(ERR, "Couldn't commit output {} in ensureVRR -> true", m->output->name); + } + m->vrrActive = true; + return; + } else if (USEVRR == 2) { + /* fullscreen */ + m->vrrActive = true; -void CConfigManager::applyUserDefinedVars(std::string& line, const size_t equalsPlace) { - auto dollarPlace = line.find_first_of('$', equalsPlace); + const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(m->activeWorkspace); - int times = 0; + if (!PWORKSPACE) + return; // ??? - while (dollarPlace != std::string::npos) { - times++; + const auto WORKSPACEFULL = PWORKSPACE->m_bHasFullscreenWindow && PWORKSPACE->m_efFullscreenMode == FULLSCREEN_FULL; - const auto STRAFTERDOLLAR = line.substr(dollarPlace + 1); - bool found = false; - for (auto& [var, value] : configDynamicVars) { - if (STRAFTERDOLLAR.starts_with(var)) { - line.replace(dollarPlace, var.length() + 1, value); - found = true; - break; - } - } + if (WORKSPACEFULL && m->output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_DISABLED) { + wlr_output_state_set_adaptive_sync_enabled(m->state.wlr(), 1); - if (!found) { - // maybe env? - for (auto& [var, value] : environmentVariables) { - if (STRAFTERDOLLAR.starts_with(var)) { - line.replace(dollarPlace, var.length() + 1, value); - break; + if (!m->state.test()) { + Debug::log(LOG, "Pending output {} does not accept VRR.", m->output->name); + wlr_output_state_set_adaptive_sync_enabled(m->state.wlr(), 0); } + + if (!m->state.commit()) + Debug::log(ERR, "Couldn't commit output {} in ensureVRR -> true", m->output->name); + + } else if (!WORKSPACEFULL && m->output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED) { + wlr_output_state_set_adaptive_sync_enabled(m->state.wlr(), 0); + + if (!m->state.commit()) + Debug::log(ERR, "Couldn't commit output {} in ensureVRR -> false", m->output->name); } } + }; - dollarPlace = line.find_first_of('$', dollarPlace + 1); + if (pMonitor) { + ensureVRRForDisplay(pMonitor); + return; + } - if (times > 256 /* arbitrary limit */) { - line = ""; - parseError = "Maximum variable recursion limit hit. Evaluating the line led to too many variable substitutions."; - Debug::log(ERR, "Variable recursion limit hit in configmanager"); - break; - } + for (auto& m : g_pCompositor->m_vMonitors) { + ensureVRRForDisplay(m.get()); } } -void CConfigManager::parseLine(std::string& line) { - // first check if its not a comment - if (line[0] == '#') - return; +SAnimationPropertyConfig* CConfigManager::getAnimationPropertyConfig(const std::string& name) { + return &animationConfig[name]; +} - // now, cut the comment off. ## is an escape. - for (long unsigned int i = 1; i < line.length(); ++i) { - if (line[i] == '#') { - if (i + 1 < line.length() && line[i + 1] != '#') { - line = line.substr(0, i); - break; // no need to parse more - } +void CConfigManager::addParseError(const std::string& err) { + g_pHyprError->queueCreate(err + "\nHyprland may not work correctly.", CColor(1.0, 50.0 / 255.0, 50.0 / 255.0, 1.0)); +} + +CMonitor* CConfigManager::getBoundMonitorForWS(const std::string& wsname) { + auto monitor = getBoundMonitorStringForWS(wsname); + if (monitor.substr(0, 5) == "desc:") + return g_pCompositor->getMonitorFromDesc(monitor.substr(5)); + else + return g_pCompositor->getMonitorFromName(monitor); +} + +std::string CConfigManager::getBoundMonitorStringForWS(const std::string& wsname) { + for (auto& wr : m_dWorkspaceRules) { + const auto WSNAME = wr.workspaceName.starts_with("name:") ? wr.workspaceName.substr(5) : wr.workspaceName; - i++; + if (WSNAME == wsname) { + return wr.monitor; } } - size_t startPos = 0; - while ((startPos = line.find("##", startPos)) != std::string::npos && startPos < line.length() - 1 && startPos > 0) { - line.replace(startPos, 2, "#"); - startPos++; - } + return ""; +} - line = removeBeginEndSpacesTabs(line); +const std::deque& CConfigManager::getAllWorkspaceRules() { + return m_dWorkspaceRules; +} - if (line.contains(" {")) { - auto cat = line.substr(0, line.find(" {")); - transform(cat.begin(), cat.end(), cat.begin(), ::tolower); - std::replace(cat.begin(), cat.end(), ' ', '-'); - if (currentCategory.length() != 0) { - currentCategory.push_back(':'); - currentCategory.append(cat); - } else { - currentCategory = cat; - } +void CConfigManager::addExecRule(const SExecRequestedRule& rule) { + execRequestedRules.push_back(rule); +} +void CConfigManager::handlePluginLoads() { + if (g_pPluginSystem == nullptr) return; - } - if (line.contains("}") && currentCategory != "") { + bool pluginsChanged = false; + auto failedPlugins = g_pPluginSystem->updateConfigPlugins(m_vDeclaredPlugins, pluginsChanged); - const auto LASTSEP = currentCategory.find_last_of(':'); + if (!failedPlugins.empty()) { + std::stringstream error; + error << "Failed to load the following plugins:"; - if (LASTSEP == std::string::npos || currentCategory.starts_with("device")) - currentCategory = ""; - else - currentCategory = currentCategory.substr(0, LASTSEP); + for (auto path : failedPlugins) { + error << "\n" << path; + } - return; + g_pHyprError->queueCreate(error.str(), CColor(1.0, 50.0 / 255.0, 50.0 / 255.0, 1.0)); } - // And parse - // check if command - const auto EQUALSPLACE = line.find_first_of('='); + if (pluginsChanged) { + g_pHyprError->destroy(); + m_bForceReload = true; + tick(); + } +} - // apply vars - applyUserDefinedVars(line, EQUALSPLACE); +ICustomConfigValueData::~ICustomConfigValueData() { + ; // empty +} - if (EQUALSPLACE == std::string::npos) - return; +std::unordered_map CConfigManager::getAnimationConfig() { + return animationConfig; +} - const auto COMMAND = removeBeginEndSpacesTabs(line.substr(0, EQUALSPLACE)); - const auto VALUE = removeBeginEndSpacesTabs(line.substr(EQUALSPLACE + 1)); +void onPluginLoadUnload(const std::string& name, bool load) { // - - parseKeyword(COMMAND, VALUE); } -void CConfigManager::loadConfigLoadVars() { - EMIT_HOOK_EVENT("preConfigReload", nullptr); - - Debug::log(LOG, "Reloading the config!"); - parseError = ""; // reset the error - currentCategory = ""; // reset the category +void CConfigManager::addPluginConfigVar(HANDLE handle, const std::string& name, const Hyprlang::CConfigValue& value) { + if (!name.starts_with("plugin:")) + return; - // reset all vars before loading - setDefaultVars(); - m_dMonitorRules.clear(); - m_dWindowRules.clear(); - g_pKeybindManager->clearKeybinds(); - g_pAnimationManager->removeAllBeziers(); - m_mAdditionalReservedAreas.clear(); - configDynamicVars.clear(); - deviceConfigs.clear(); - m_dBlurLSNamespaces.clear(); - m_dWorkspaceRules.clear(); - setDefaultAnimationVars(); // reset anims - m_vDeclaredPlugins.clear(); - m_dLayerRules.clear(); - m_vFailedPluginConfigValues.clear(); + std::string field = name.substr(7); - // paths - configPaths.clear(); - std::string mainConfigPath = getMainConfigPath(); - Debug::log(LOG, "Using config: {}", mainConfigPath); - configPaths.push_back(mainConfigPath); + m_pConfig->addSpecialConfigValue("plugin", field.c_str(), value); + pluginVariables.push_back({handle, field}); +} - if (g_pCompositor->explicitConfigPath.empty() && !std::filesystem::exists(mainConfigPath)) { - std::string configPath = std::filesystem::path(mainConfigPath).parent_path(); +void CConfigManager::addPluginKeyword(HANDLE handle, const std::string& name, Hyprlang::PCONFIGHANDLERFUNC fn, Hyprlang::SHandlerOptions opts) { + pluginKeywords.emplace_back(SPluginKeyword{handle, name, fn}); + m_pConfig->registerHandler(fn, name.c_str(), opts); +} - if (!std::filesystem::is_directory(configPath)) { - Debug::log(WARN, "Creating config home directory"); - try { - std::filesystem::create_directories(configPath); - } catch (...) { - parseError = "Broken config file! (Could not create config directory)"; - return; - } - } +void CConfigManager::removePluginConfig(HANDLE handle) { + for (auto& k : pluginKeywords) { + if (k.handle != handle) + continue; - Debug::log(WARN, "No config file found; attempting to generate."); - std::ofstream ofs; - ofs.open(mainConfigPath, std::ios::trunc); - ofs << AUTOCONFIG; - ofs.close(); + m_pConfig->unregisterHandler(k.name.c_str()); } - std::ifstream ifs; - ifs.open(mainConfigPath); - - if (!ifs.good()) { - ifs.close(); - - if (!g_pCompositor->explicitConfigPath.empty()) { - Debug::log(WARN, "Config reading error!"); - parseError = "Broken config file! (Could not read)"; - return; - } - - Debug::log(WARN, "Config reading error. Attempting to generate, backing up old one if exists"); - - if (std::filesystem::exists(mainConfigPath)) - std::filesystem::rename(mainConfigPath, mainConfigPath + ".backup"); + std::erase_if(pluginKeywords, [&](const auto& other) { return other.handle == handle; }); + for (auto& [h, n] : pluginVariables) { + if (h != handle) + continue; - // Create default config - std::ofstream ofs; - ofs.open(mainConfigPath, std::ios::trunc); - ofs << AUTOCONFIG; - ofs.close(); + m_pConfig->removeSpecialConfigValue("plugin", n.c_str()); + } + std::erase_if(pluginVariables, [handle](const auto& other) { return other.handle == handle; }); +} - // Try to re-open - ifs.open(mainConfigPath); - if (!ifs.good()) { - parseError = "Broken config file! (Could not open)"; - return; +std::string CConfigManager::getDefaultWorkspaceFor(const std::string& name) { + for (auto other = m_dWorkspaceRules.begin(); other != m_dWorkspaceRules.end(); ++other) { + if (other->isDefault) { + if (other->monitor == name) + return other->workspaceString; + if (other->monitor.substr(0, 5) == "desc:") { + auto monitor = g_pCompositor->getMonitorFromDesc(other->monitor.substr(5)); + if (monitor && monitor->szName == name) + return other->workspaceString; + } } } + return ""; +} - std::string line = ""; - int linenum = 1; - if (ifs.is_open()) { - while (std::getline(ifs, line)) { - // Read line by line. - try { - configCurrentPath = mainConfigPath; - parseLine(line); - } catch (...) { - Debug::log(ERR, "Error reading line from config. Line:"); - Debug::log(NONE, "{}", line); +std::optional CConfigManager::handleRawExec(const std::string& command, const std::string& args) { + if (isFirstLaunch) { + firstExecRequests.push_back(args); + return {}; + } - parseError += "Config error at line " + std::to_string(linenum) + " (" + mainConfigPath + "): Line parsing error."; - } + g_pKeybindManager->spawn(args); + return {}; +} - if (parseError != "" && !parseError.starts_with("Config error at line")) { - parseError = "Config error at line " + std::to_string(linenum) + " (" + mainConfigPath + "): " + parseError; - } +std::optional CConfigManager::handleExecOnce(const std::string& command, const std::string& args) { + if (isFirstLaunch) + firstExecRequests.push_back(args); - ++linenum; - } + return {}; +} - ifs.close(); - } +static bool parseModeLine(const std::string& modeline, drmModeModeInfo& mode) { + auto args = CVarList(modeline, 0, 's'); - for (auto& w : g_pCompositor->m_vWindows) { - w->uncacheWindowDecos(); - } + auto keyword = args[0]; + std::transform(keyword.begin(), keyword.end(), keyword.begin(), ::tolower); - for (auto& m : g_pCompositor->m_vMonitors) - g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->ID); + if (keyword != "modeline") + return false; - // Update the keyboard layout to the cfg'd one if this is not the first launch - if (!isFirstLaunch) { - g_pInputManager->setKeyboardLayout(); - g_pInputManager->setPointerConfigs(); - g_pInputManager->setTouchDeviceConfigs(); - g_pInputManager->setTabletConfigs(); + if (args.size() < 10) { + Debug::log(ERR, "modeline parse error: expected at least 9 arguments, got {}", args.size() - 1); + return false; } - if (!isFirstLaunch) - g_pHyprOpenGL->m_bReloadScreenShader = true; + int argno = 1; - // parseError will be displayed next frame - if (parseError != "" && !configValues["debug:suppress_errors"].intValue) - g_pHyprError->queueCreate(parseError + "\nHyprland may not work correctly.", CColor(1.0, 50.0 / 255.0, 50.0 / 255.0, 1.0)); - else if (configValues["autogenerated"].intValue == 1) - g_pHyprError->queueCreate("Warning: You're using an autogenerated config! (config file: " + mainConfigPath + " )\nSUPER+Q -> kitty\nSUPER+M -> exit Hyprland", - CColor(1.0, 1.0, 70.0 / 255.0, 1.0)); - else - g_pHyprError->destroy(); + mode.type = DRM_MODE_TYPE_USERDEF; + mode.clock = std::stof(args[argno++]) * 1000; + mode.hdisplay = std::stoi(args[argno++]); + mode.hsync_start = std::stoi(args[argno++]); + mode.hsync_end = std::stoi(args[argno++]); + mode.htotal = std::stoi(args[argno++]); + mode.vdisplay = std::stoi(args[argno++]); + mode.vsync_start = std::stoi(args[argno++]); + mode.vsync_end = std::stoi(args[argno++]); + mode.vtotal = std::stoi(args[argno++]); + mode.vrefresh = mode.clock * 1000.0 * 1000.0 / mode.htotal / mode.vtotal; - // Set the modes for all monitors as we configured them - // not on first launch because monitors might not exist yet - // and they'll be taken care of in the newMonitor event - // ignore if nomonitorreload is set - if (!isFirstLaunch && !m_bNoMonitorReload) { - // check - performMonitorReload(); - ensureMonitorStatus(); - ensureVRR(); - } + // clang-format off + static std::unordered_map flagsmap = { + {"+hsync", DRM_MODE_FLAG_PHSYNC}, + {"-hsync", DRM_MODE_FLAG_NHSYNC}, + {"+vsync", DRM_MODE_FLAG_PVSYNC}, + {"-vsync", DRM_MODE_FLAG_NVSYNC}, + {"Interlace", DRM_MODE_FLAG_INTERLACE}, + }; + // clang-format on - if (!isFirstLaunch && !g_pCompositor->m_bUnsafeState) - refreshGroupBarGradients(); + for (; argno < static_cast(args.size()); argno++) { + auto key = args[argno]; + std::transform(key.begin(), key.end(), key.begin(), ::tolower); - // Updates dynamic window and workspace rules - for (auto& w : g_pCompositor->m_vWindows) { - if (!w->m_bIsMapped) - continue; + auto it = flagsmap.find(key); - w->updateDynamicRules(); - w->updateSpecialRenderData(); + if (it != flagsmap.end()) + mode.flags |= it->second; + else + Debug::log(ERR, "invalid flag {} in modeline", it->first); } - // Update window border colors - g_pCompositor->updateAllWindowsAnimatedDecorationValues(); + snprintf(mode.name, sizeof(mode.name), "%dx%d@%d", mode.hdisplay, mode.vdisplay, mode.vrefresh / 1000); - // update layout - g_pLayoutManager->switchToLayout(configValues["general:layout"].strValue); + return true; +} - // manual crash - if (configValues["debug:manual_crash"].intValue && !m_bManualCrashInitiated) { - m_bManualCrashInitiated = true; - g_pHyprNotificationOverlay->addNotification("Manual crash has been set up. Set debug:manual_crash back to 0 in order to crash the compositor.", CColor(0), 5000, ICON_INFO); - } else if (m_bManualCrashInitiated && !configValues["debug:manual_crash"].intValue) { - // cowabunga it is - g_pHyprRenderer->initiateManualCrash(); - } +std::optional CConfigManager::handleMonitor(const std::string& command, const std::string& args) { - Debug::disableStdout = !configValues["debug:enable_stdout_logs"].intValue; - if (Debug::disableStdout && isFirstLaunch) - Debug::log(LOG, "Disabling stdout logs! Check the log for further logs."); + // get the monitor config + SMonitorRule newrule; - for (auto& m : g_pCompositor->m_vMonitors) { - // mark blur dirty - g_pHyprOpenGL->markBlurDirtyForMonitor(m.get()); + const auto ARGS = CVarList(args); - g_pCompositor->scheduleFrameForMonitor(m.get()); + newrule.name = ARGS[0]; - // Force the compositor to fully re-render all monitors - m->forceFullFrames = 2; - } + if (ARGS[1] == "disable" || ARGS[1] == "disabled" || ARGS[1] == "addreserved" || ARGS[1] == "transform") { + if (ARGS[1] == "disable" || ARGS[1] == "disabled") + newrule.disabled = true; + else if (ARGS[1] == "transform") { + const auto TSF = std::stoi(ARGS[2]); + if (std::clamp(TSF, 0, 7) != TSF) { + Debug::log(ERR, "invalid transform {} in monitor", TSF); + return "invalid transform"; + } - // Reset no monitor reload - m_bNoMonitorReload = false; + const auto TRANSFORM = (wl_output_transform)TSF; - // update plugins - handlePluginLoads(); + // overwrite if exists + for (auto& r : m_dMonitorRules) { + if (r.name == newrule.name) { + r.transform = TRANSFORM; + return {}; + } + } - EMIT_HOOK_EVENT("configReloaded", nullptr); - if (g_pEventManager) - g_pEventManager->postEvent(SHyprIPCEvent{"configreloaded", ""}); -} + return {}; + } else if (ARGS[1] == "addreserved") { + int top = std::stoi(ARGS[2]); -void CConfigManager::tick() { - std::string CONFIGPATH = getMainConfigPath(); - if (!std::filesystem::exists(CONFIGPATH)) { - Debug::log(ERR, "Config doesn't exist??"); - return; - } + int bottom = std::stoi(ARGS[3]); - bool parse = false; + int left = std::stoi(ARGS[4]); - for (auto& cf : configPaths) { - struct stat fileStat; - int err = stat(cf.c_str(), &fileStat); - if (err != 0) { - Debug::log(WARN, "Error at ticking config at {}, error {}: {}", cf, err, strerror(err)); - continue; - } + int right = std::stoi(ARGS[5]); - // check if we need to reload cfg - if (fileStat.st_mtime != configModifyTimes[cf] || m_bForceReload) { - parse = true; - configModifyTimes[cf] = fileStat.st_mtime; + m_mAdditionalReservedAreas[newrule.name] = {top, bottom, left, right}; + + return {}; + } else { + Debug::log(ERR, "ConfigManager parseMonitor, curitem bogus???"); + return "parse error: curitem bogus"; } - } - if (parse) { - m_bForceReload = false; + std::erase_if(m_dMonitorRules, [&](const auto& other) { return other.name == newrule.name; }); + + m_dMonitorRules.push_back(newrule); - loadConfigLoadVars(); + return {}; } -} -std::mutex configmtx; -SConfigValue CConfigManager::getConfigValueSafe(const std::string& val) { - std::lock_guard lg(configmtx); + if (ARGS[1].starts_with("pref")) { + newrule.resolution = Vector2D(); + } else if (ARGS[1].starts_with("highrr")) { + newrule.resolution = Vector2D(-1, -1); + } else if (ARGS[1].starts_with("highres")) { + newrule.resolution = Vector2D(-1, -2); + } else if (parseModeLine(ARGS[1], newrule.drmMode)) { + newrule.resolution = Vector2D(newrule.drmMode.hdisplay, newrule.drmMode.vdisplay); + newrule.refreshRate = newrule.drmMode.vrefresh / 1000; + } else { + newrule.resolution.x = stoi(ARGS[1].substr(0, ARGS[1].find_first_of('x'))); + newrule.resolution.y = stoi(ARGS[1].substr(ARGS[1].find_first_of('x') + 1, ARGS[1].find_first_of('@'))); - SConfigValue copy = configValues[val]; + if (ARGS[1].contains("@")) + newrule.refreshRate = stof(ARGS[1].substr(ARGS[1].find_first_of('@') + 1)); + } - return copy; -} + if (ARGS[2].starts_with("auto")) { + newrule.offset = Vector2D(-INT32_MAX, -INT32_MAX); + } else { + newrule.offset.x = stoi(ARGS[2].substr(0, ARGS[2].find_first_of('x'))); + newrule.offset.y = stoi(ARGS[2].substr(ARGS[2].find_first_of('x') + 1)); + } -SConfigValue CConfigManager::getConfigValueSafeDevice(const std::string& dev, const std::string& val, const std::string& fallback) { - std::lock_guard lg(configmtx); + std::string error = ""; - const auto it = deviceConfigs.find(dev); + if (ARGS[3].starts_with("auto")) { + newrule.scale = -1; + } else { + newrule.scale = stof(ARGS[3]); - if (it == deviceConfigs.end()) { - if (fallback.empty()) { - Debug::log(ERR, "getConfigValueSafeDevice: No device config for {} found???", dev); - return SConfigValue(); + if (newrule.scale < 0.25f) { + error = "invalid scale"; + newrule.scale = 1; } - return configValues[fallback]; - } - - const SConfigValue DEVICECONFIG = it->second[val]; - - if (!DEVICECONFIG.set && !fallback.empty()) { - return configValues[fallback]; } - return DEVICECONFIG; -} + int argno = 4; -int CConfigManager::getInt(const std::string& v) { - return getConfigValueSafe(v).intValue; -} + while (ARGS[argno] != "") { + if (ARGS[argno] == "mirror") { + newrule.mirrorOf = ARGS[argno + 1]; + argno++; + } else if (ARGS[argno] == "bitdepth") { + newrule.enable10bit = ARGS[argno + 1] == "10"; + argno++; + } else if (ARGS[argno] == "transform") { + newrule.transform = (wl_output_transform)std::stoi(ARGS[argno + 1]); + argno++; + } else if (ARGS[argno] == "vrr") { + newrule.vrr = std::stoi(ARGS[argno + 1]); + argno++; + } else if (ARGS[argno] == "workspace") { + std::string name = ""; + int wsId = getWorkspaceIDFromString(ARGS[argno + 1], name); -float CConfigManager::getFloat(const std::string& v) { - return getConfigValueSafe(v).floatValue; -} + SWorkspaceRule wsRule; + wsRule.monitor = newrule.name; + wsRule.workspaceString = ARGS[argno + 1]; + wsRule.workspaceName = name; + wsRule.workspaceId = wsId; -Vector2D CConfigManager::getVec(const std::string& v) { - return getConfigValueSafe(v).vecValue; -} + m_dWorkspaceRules.emplace_back(wsRule); + argno++; + } else { + Debug::log(ERR, "Config error: invalid monitor syntax"); + return "invalid syntax at \"" + ARGS[argno] + "\""; + } -std::string CConfigManager::getString(const std::string& v) { - auto VAL = getConfigValueSafe(v).strValue; + argno++; + } - if (VAL == STRVAL_EMPTY) - return ""; + std::erase_if(m_dMonitorRules, [&](const auto& other) { return other.name == newrule.name; }); - return VAL; -} + m_dMonitorRules.push_back(newrule); -int CConfigManager::getDeviceInt(const std::string& dev, const std::string& v, const std::string& fallback) { - return getConfigValueSafeDevice(dev, v, fallback).intValue; + if (error.empty()) + return {}; + return error; } -float CConfigManager::getDeviceFloat(const std::string& dev, const std::string& v, const std::string& fallback) { - return getConfigValueSafeDevice(dev, v, fallback).floatValue; -} +std::optional CConfigManager::handleBezier(const std::string& command, const std::string& args) { + const auto ARGS = CVarList(args); -Vector2D CConfigManager::getDeviceVec(const std::string& dev, const std::string& v, const std::string& fallback) { - return getConfigValueSafeDevice(dev, v, fallback).vecValue; -} + std::string bezierName = ARGS[0]; -std::string CConfigManager::getDeviceString(const std::string& dev, const std::string& v, const std::string& fallback) { - auto VAL = getConfigValueSafeDevice(dev, v, fallback).strValue; + if (ARGS[1] == "") + return "too few arguments"; + float p1x = std::stof(ARGS[1]); - if (VAL == STRVAL_EMPTY) - return ""; + if (ARGS[2] == "") + return "too few arguments"; + float p1y = std::stof(ARGS[2]); - return VAL; -} + if (ARGS[3] == "") + return "too few arguments"; + float p2x = std::stof(ARGS[3]); -void CConfigManager::setInt(const std::string& v, int val) { - configValues[v].intValue = val; -} + if (ARGS[4] == "") + return "too few arguments"; + float p2y = std::stof(ARGS[4]); -void CConfigManager::setFloat(const std::string& v, float val) { - configValues[v].floatValue = val; -} + if (ARGS[5] != "") + return "too many arguments"; -void CConfigManager::setVec(const std::string& v, Vector2D val) { - configValues[v].vecValue = val; -} + g_pAnimationManager->addBezierWithName(bezierName, Vector2D(p1x, p1y), Vector2D(p2x, p2y)); -void CConfigManager::setString(const std::string& v, const std::string& val) { - configValues[v].strValue = val; + return {}; } -SMonitorRule CConfigManager::getMonitorRuleFor(const std::string& name, const std::string& displayName) { - SMonitorRule* found = nullptr; +void CConfigManager::setAnimForChildren(SAnimationPropertyConfig* const ANIM) { + for (auto& [name, anim] : animationConfig) { + if (anim.pParentAnimation == ANIM && !anim.overridden) { + // if a child isnt overridden, set the values of the parent + anim.pValues = ANIM->pValues; - for (auto& r : m_dMonitorRules) { - if (r.name == name || - (r.name.starts_with("desc:") && - (r.name.substr(5) == displayName || r.name.substr(5) == removeBeginEndSpacesTabs(displayName.substr(0, displayName.find_first_of('(')))))) { - found = &r; - break; + setAnimForChildren(&anim); } } +}; - if (found) - return *found; - - Debug::log(WARN, "No rule found for {}, trying to use the first.", name); +std::optional CConfigManager::handleAnimation(const std::string& command, const std::string& args) { + const auto ARGS = CVarList(args); - for (auto& r : m_dMonitorRules) { - if (r.name == "") { - found = &r; - break; - } - } + // Master on/off - if (found) - return *found; + // anim name + const auto ANIMNAME = ARGS[0]; - Debug::log(WARN, "No rules configured. Using the default hardcoded one."); + const auto PANIM = animationConfig.find(ANIMNAME); - return SMonitorRule{.name = "", .resolution = Vector2D(0, 0), .offset = Vector2D(-INT32_MAX, -INT32_MAX), .scale = -1}; // 0, 0 is preferred and -1, -1 is auto -} + if (PANIM == animationConfig.end()) + return "no such animation"; -SWorkspaceRule CConfigManager::getWorkspaceRuleFor(CWorkspace* pWorkspace) { - const auto WORKSPACEIDSTR = std::to_string(pWorkspace->m_iID); - const auto IT = std::find_if(m_dWorkspaceRules.begin(), m_dWorkspaceRules.end(), [&](const auto& other) { - return other.workspaceName == pWorkspace->m_szName /* name matches */ - || (pWorkspace->m_bIsSpecialWorkspace && other.workspaceName.starts_with("special:") && - other.workspaceName.substr(8) == pWorkspace->m_szName) /* special and special:name */ - || (pWorkspace->m_iID > 0 && WORKSPACEIDSTR == other.workspaceName); /* id matches and workspace is numerical */ - }); - if (IT == m_dWorkspaceRules.end()) - return SWorkspaceRule{}; - return *IT; -} + PANIM->second.overridden = true; + PANIM->second.pValues = &PANIM->second; -std::vector CConfigManager::getMatchingRules(CWindow* pWindow, bool dynamic) { - if (!g_pCompositor->windowValidMapped(pWindow)) - return std::vector(); + // on/off + PANIM->second.internalEnabled = ARGS[1] == "1"; - std::vector returns; + if (ARGS[1] != "0" && ARGS[1] != "1") + return "invalid animation on/off state"; - std::string title = g_pXWaylandManager->getTitle(pWindow); - std::string appidclass = g_pXWaylandManager->getAppIDClass(pWindow); + if (PANIM->second.internalEnabled) { + // speed + if (isNumber(ARGS[2], true)) { + PANIM->second.internalSpeed = std::stof(ARGS[2]); - Debug::log(LOG, "Searching for matching rules for {} (title: {})", appidclass, title); + if (PANIM->second.internalSpeed <= 0) { + PANIM->second.internalSpeed = 1.f; + return "invalid speed"; + } + } else { + PANIM->second.internalSpeed = 10.f; + return "invalid speed"; + } - // since some rules will be applied later, we need to store some flags - bool hasFloating = pWindow->m_bIsFloating; - bool hasFullscreen = pWindow->m_bIsFullscreen; + // curve + PANIM->second.internalBezier = ARGS[3]; - for (auto& rule : m_dWindowRules) { - // check if we have a matching rule - if (!rule.v2) { - try { - if (rule.szValue.starts_with("title:")) { - // we have a title rule. - std::regex RULECHECK(rule.szValue.substr(6)); + if (!g_pAnimationManager->bezierExists(ARGS[3])) { + PANIM->second.internalBezier = "default"; + return "no such bezier"; + } - if (!std::regex_search(title, RULECHECK)) - continue; - } else { - std::regex classCheck(rule.szValue); + // style + PANIM->second.internalStyle = ARGS[4]; - if (!std::regex_search(appidclass, classCheck)) - continue; - } - } catch (...) { - Debug::log(ERR, "Regex error at {}", rule.szValue); - continue; - } - } else { - try { - if (rule.szClass != "") { - std::regex RULECHECK(rule.szClass); + if (ARGS[4] != "") { + const auto ERR = g_pAnimationManager->styleValidInConfigVar(ANIMNAME, ARGS[4]); - if (!std::regex_search(appidclass, RULECHECK)) - continue; - } + if (ERR != "") + return ERR; + } + } - if (rule.szTitle != "") { - std::regex RULECHECK(rule.szTitle); + // now, check for children, recursively + setAnimForChildren(&PANIM->second); - if (!std::regex_search(title, RULECHECK)) - continue; - } + return {}; +} - if (rule.szInitialTitle != "") { - std::regex RULECHECK(rule.szInitialTitle); +std::optional CConfigManager::handleBind(const std::string& command, const std::string& value) { + // example: + // bind[fl]=SUPER,G,exec,dmenu_run - if (!std::regex_search(pWindow->m_szInitialTitle, RULECHECK)) - continue; - } + // flags + bool locked = false; + bool release = false; + bool repeat = false; + bool mouse = false; + bool nonConsuming = false; + bool transparent = false; + bool ignoreMods = false; + const auto BINDARGS = command.substr(4); - if (rule.szInitialClass != "") { - std::regex RULECHECK(rule.szInitialClass); + for (auto& arg : BINDARGS) { + if (arg == 'l') { + locked = true; + } else if (arg == 'r') { + release = true; + } else if (arg == 'e') { + repeat = true; + } else if (arg == 'm') { + mouse = true; + } else if (arg == 'n') { + nonConsuming = true; + } else if (arg == 't') { + transparent = true; + } else if (arg == 'i') { + ignoreMods = true; + } else { + return "bind: invalid flag"; + } + } - if (!std::regex_search(pWindow->m_szInitialClass, RULECHECK)) - continue; - } + if (release && repeat) + return "flags r and e are mutually exclusive"; - if (rule.bX11 != -1) { - if (pWindow->m_bIsX11 != rule.bX11) - continue; - } + if (mouse && (repeat || release || locked)) + return "flag m is exclusive"; - if (rule.bFloating != -1) { - if (hasFloating != rule.bFloating) - continue; - } + const auto ARGS = CVarList(value, 4); - if (rule.bFullscreen != -1) { - if (hasFullscreen != rule.bFullscreen) - continue; - } + if ((ARGS.size() < 3 && !mouse) || (ARGS.size() < 3 && mouse)) + return "bind: too few args"; + else if ((ARGS.size() > 4 && !mouse) || (ARGS.size() > 3 && mouse)) + return "bind: too many args"; - if (rule.bPinned != -1) { - if (pWindow->m_bPinned != rule.bPinned) - continue; - } + const auto MOD = g_pKeybindManager->stringToModMask(ARGS[0]); + const auto MODSTR = ARGS[0]; - if (rule.bFocus != -1) { - if (rule.bFocus != (g_pCompositor->m_pLastWindow == pWindow)) - continue; - } + const auto KEY = ARGS[1]; - if (rule.iOnWorkspace != -1) { - if (rule.iOnWorkspace != g_pCompositor->getWindowsOnWorkspace(pWindow->m_iWorkspaceID)) - continue; - } + auto HANDLER = ARGS[2]; - if (!rule.szWorkspace.empty()) { - const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(pWindow->m_iWorkspaceID); + const auto COMMAND = mouse ? HANDLER : ARGS[3]; - if (!PWORKSPACE) - continue; + if (mouse) + HANDLER = "mouse"; - if (rule.szWorkspace.starts_with("name:")) { - if (PWORKSPACE->m_szName != rule.szWorkspace.substr(5)) - continue; - } else { - // number - if (!isNumber(rule.szWorkspace)) - throw std::runtime_error("szWorkspace not name: or number"); + // to lower + std::transform(HANDLER.begin(), HANDLER.end(), HANDLER.begin(), ::tolower); - const int64_t ID = std::stoll(rule.szWorkspace); + const auto DISPATCHER = g_pKeybindManager->m_mDispatchers.find(HANDLER); - if (PWORKSPACE->m_iID != ID) - continue; - } - } - } catch (std::exception& e) { - Debug::log(ERR, "Regex error at {} ({})", rule.szValue, e.what()); - continue; - } - } + if (DISPATCHER == g_pKeybindManager->m_mDispatchers.end()) { + Debug::log(ERR, "Invalid dispatcher!"); + return "Invalid dispatcher, requested \"" + HANDLER + "\" does not exist"; + } - // applies. Read the rule and behave accordingly - Debug::log(LOG, "Window rule {} -> {} matched {}", rule.szRule, rule.szValue, pWindow); + if (MOD == 0 && MODSTR != "") { + Debug::log(ERR, "Invalid mod!"); + return "Invalid mod, requested mod \"" + MODSTR + "\" is not a valid mod."; + } - returns.push_back(rule); + if (KEY != "") { + if (isNumber(KEY) && std::stoi(KEY) > 9) + g_pKeybindManager->addKeybind( + SKeybind{"", std::stoi(KEY), MOD, HANDLER, COMMAND, locked, m_szCurrentSubmap, release, repeat, mouse, nonConsuming, transparent, ignoreMods}); + else if (KEY.starts_with("code:") && isNumber(KEY.substr(5))) + g_pKeybindManager->addKeybind( + SKeybind{"", std::stoi(KEY.substr(5)), MOD, HANDLER, COMMAND, locked, m_szCurrentSubmap, release, repeat, mouse, nonConsuming, transparent, ignoreMods}); + else + g_pKeybindManager->addKeybind(SKeybind{KEY, 0, MOD, HANDLER, COMMAND, locked, m_szCurrentSubmap, release, repeat, mouse, nonConsuming, transparent, ignoreMods}); + } - if (dynamic) - continue; + return {}; +} - if (rule.szRule == "float") - hasFloating = true; - else if (rule.szRule == "fullscreen") - hasFullscreen = true; - } +std::optional CConfigManager::handleUnbind(const std::string& command, const std::string& value) { + const auto ARGS = CVarList(value); - std::vector PIDs = {(uint64_t)pWindow->getPID()}; - while (getPPIDof(PIDs.back()) > 10) - PIDs.push_back(getPPIDof(PIDs.back())); + const auto MOD = g_pKeybindManager->stringToModMask(ARGS[0]); - bool anyExecFound = false; + const auto KEY = ARGS[1]; - for (auto& er : execRequestedRules) { - if (std::ranges::any_of(PIDs, [&](const auto& pid) { return pid == er.iPid; })) { - returns.push_back({er.szRule, "execRule"}); - anyExecFound = true; - } - } + g_pKeybindManager->removeKeybind(MOD, KEY); - if (anyExecFound) // remove exec rules to unclog searches in the future, why have the garbage here. - execRequestedRules.erase(std::remove_if(execRequestedRules.begin(), execRequestedRules.end(), - [&](const SExecRequestedRule& other) { return std::ranges::any_of(PIDs, [&](const auto& pid) { return pid == other.iPid; }); })); + return {}; +} - return returns; +bool windowRuleValid(const std::string& RULE) { + return RULE == "float" || RULE == "tile" || RULE.starts_with("opacity") || RULE.starts_with("move") || RULE.starts_with("size") || RULE.starts_with("minsize") || + RULE.starts_with("maxsize") || RULE.starts_with("pseudo") || RULE.starts_with("monitor") || RULE.starts_with("idleinhibit") || RULE == "nofocus" || RULE == "noblur" || + RULE == "noshadow" || RULE == "nodim" || RULE == "noborder" || RULE == "opaque" || RULE == "forceinput" || RULE == "fullscreen" || RULE == "fakefullscreen" || + RULE == "nomaxsize" || RULE == "pin" || RULE == "noanim" || RULE == "dimaround" || RULE == "windowdance" || RULE == "maximize" || RULE == "keepaspectratio" || + RULE.starts_with("animation") || RULE.starts_with("rounding") || RULE.starts_with("workspace") || RULE.starts_with("bordercolor") || RULE == "forcergbx" || + RULE == "noinitialfocus" || RULE == "stayfocused" || RULE.starts_with("bordersize") || RULE.starts_with("xray") || RULE.starts_with("center") || + RULE.starts_with("group") || RULE == "immediate" || RULE == "nearestneighbor" || RULE.starts_with("suppressevent"); } -std::vector CConfigManager::getMatchingRules(SLayerSurface* pLS) { - std::vector returns; +bool layerRuleValid(const std::string& RULE) { + return RULE == "noanim" || RULE == "blur" || RULE.starts_with("ignorealpha") || RULE.starts_with("ignorezero") || RULE.starts_with("xray"); +} - if (!pLS->layerSurface || pLS->fadingOut) - return returns; +std::optional CConfigManager::handleWindowRule(const std::string& command, const std::string& value) { + const auto RULE = removeBeginEndSpacesTabs(value.substr(0, value.find_first_of(','))); + const auto VALUE = removeBeginEndSpacesTabs(value.substr(value.find_first_of(',') + 1)); - for (auto& lr : m_dLayerRules) { - if (lr.targetNamespace.starts_with("address:0x")) { - if (std::format("address:0x{:x}", (uintptr_t)pLS) != lr.targetNamespace) - continue; - } else { - std::regex NSCHECK(lr.targetNamespace); + // check rule and value + if (RULE == "" || VALUE == "") + return "empty rule?"; - if (!pLS->layerSurface->_namespace || !std::regex_search(pLS->layerSurface->_namespace, NSCHECK)) - continue; - } + if (RULE == "unset") { + std::erase_if(m_dWindowRules, [&](const SWindowRule& other) { return other.szValue == VALUE; }); + return {}; + } - // hit - returns.push_back(lr); + // verify we support a rule + if (!windowRuleValid(RULE)) { + Debug::log(ERR, "Invalid rule found: {}", RULE); + return "Invalid rule: " + RULE; } - if (pLS->layerSurface->_namespace && shouldBlurLS(pLS->layerSurface->_namespace)) - returns.push_back({pLS->layerSurface->_namespace, "blur"}); + if (RULE.starts_with("size") || RULE.starts_with("maxsize") || RULE.starts_with("minsize")) + m_dWindowRules.push_front({RULE, VALUE}); + else + m_dWindowRules.push_back({RULE, VALUE}); - return returns; + return {}; } -void CConfigManager::dispatchExecOnce() { - if (firstExecDispatched || isFirstLaunch) - return; +std::optional CConfigManager::handleLayerRule(const std::string& command, const std::string& value) { + const auto RULE = removeBeginEndSpacesTabs(value.substr(0, value.find_first_of(','))); + const auto VALUE = removeBeginEndSpacesTabs(value.substr(value.find_first_of(',') + 1)); - // update dbus env - if (g_pCompositor->m_sWLRSession) - handleRawExec("", -#ifdef USES_SYSTEMD - "systemctl --user import-environment DISPLAY WAYLAND_DISPLAY HYPRLAND_INSTANCE_SIGNATURE XDG_CURRENT_DESKTOP QT_QPA_PLATFORMTHEME && hash " - "dbus-update-activation-environment 2>/dev/null && " -#endif - "dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP HYPRLAND_INSTANCE_SIGNATURE QT_QPA_PLATFORMTHEME"); + // check rule and value + if (RULE == "" || VALUE == "") + return "empty rule?"; - firstExecDispatched = true; + if (RULE == "unset") { + std::erase_if(m_dLayerRules, [&](const SLayerRule& other) { return other.targetNamespace == VALUE; }); + return {}; + } - for (auto& c : firstExecRequests) { - handleRawExec("", c); + if (!layerRuleValid(RULE)) { + Debug::log(ERR, "Invalid rule found: {}", RULE); + return "Invalid rule found: " + RULE; } - firstExecRequests.clear(); // free some kb of memory :P + m_dLayerRules.push_back({VALUE, RULE}); - // set input, fixes some certain issues - g_pInputManager->setKeyboardLayout(); - g_pInputManager->setPointerConfigs(); - g_pInputManager->setTouchDeviceConfigs(); - g_pInputManager->setTabletConfigs(); + for (auto& m : g_pCompositor->m_vMonitors) + for (auto& lsl : m->m_aLayerSurfaceLayers) + for (auto& ls : lsl) + ls->applyRules(); - // check for user's possible errors with their setup and notify them if needed - g_pCompositor->performUserChecks(); + return {}; } -void CConfigManager::performMonitorReload() { +std::optional CConfigManager::handleWindowRuleV2(const std::string& command, const std::string& value) { + const auto RULE = removeBeginEndSpacesTabs(value.substr(0, value.find_first_of(','))); + const auto VALUE = value.substr(value.find_first_of(',') + 1); - bool overAgain = false; + if (!windowRuleValid(RULE) && RULE != "unset") { + Debug::log(ERR, "Invalid rulev2 found: {}", RULE); + return "Invalid rulev2 found: " + RULE; + } - for (auto& m : g_pCompositor->m_vRealMonitors) { - if (!m->output || m->isUnsafeFallback) - continue; + // now we estract shit from the value + SWindowRule rule; + rule.v2 = true; + rule.szRule = RULE; + rule.szValue = VALUE; - auto rule = getMonitorRuleFor(m->szName, m->szDescription); + const auto TITLEPOS = VALUE.find("title:"); + const auto CLASSPOS = VALUE.find("class:"); + const auto INITIALTITLEPOS = VALUE.find("initialTitle:"); + const auto INITIALCLASSPOS = VALUE.find("initialClass:"); + const auto X11POS = VALUE.find("xwayland:"); + const auto FLOATPOS = VALUE.find("floating:"); + const auto FULLSCREENPOS = VALUE.find("fullscreen:"); + const auto PINNEDPOS = VALUE.find("pinned:"); + const auto FOCUSPOS = VALUE.find("focus:"); + const auto ONWORKSPACEPOS = VALUE.find("onworkspace:"); - if (!g_pHyprRenderer->applyMonitorRule(m.get(), &rule)) { - overAgain = true; + // find workspacepos that isn't onworkspacepos + size_t WORKSPACEPOS = std::string::npos; + size_t currentPos = VALUE.find("workspace:"); + while (currentPos != std::string::npos) { + if (currentPos == 0 || VALUE[currentPos - 1] != 'n') { + WORKSPACEPOS = currentPos; break; } + currentPos = VALUE.find("workspace:", currentPos + 1); + } - // ensure mirror - m->setMirror(rule.mirrorOf); - - g_pHyprRenderer->arrangeLayersForMonitor(m->ID); + if (TITLEPOS == std::string::npos && CLASSPOS == std::string::npos && INITIALTITLEPOS == std::string::npos && INITIALCLASSPOS == std::string::npos && + X11POS == std::string::npos && FLOATPOS == std::string::npos && FULLSCREENPOS == std::string::npos && PINNEDPOS == std::string::npos && WORKSPACEPOS == std::string::npos && + FOCUSPOS == std::string::npos && ONWORKSPACEPOS == std::string::npos) { + Debug::log(ERR, "Invalid rulev2 syntax: {}", VALUE); + return "Invalid rulev2 syntax: " + VALUE; } - if (overAgain) - performMonitorReload(); + auto extract = [&](size_t pos) -> std::string { + std::string result; + result = VALUE.substr(pos); - m_bWantsMonitorReload = false; + size_t min = 999999; + if (TITLEPOS > pos && TITLEPOS < min) + min = TITLEPOS; + if (CLASSPOS > pos && CLASSPOS < min) + min = CLASSPOS; + if (INITIALTITLEPOS > pos && INITIALTITLEPOS < min) + min = INITIALTITLEPOS; + if (INITIALCLASSPOS > pos && INITIALCLASSPOS < min) + min = INITIALCLASSPOS; + if (X11POS > pos && X11POS < min) + min = X11POS; + if (FLOATPOS > pos && FLOATPOS < min) + min = FLOATPOS; + if (FULLSCREENPOS > pos && FULLSCREENPOS < min) + min = FULLSCREENPOS; + if (PINNEDPOS > pos && PINNEDPOS < min) + min = PINNEDPOS; + if (ONWORKSPACEPOS > pos && ONWORKSPACEPOS < min) + min = ONWORKSPACEPOS; + if (WORKSPACEPOS > pos && WORKSPACEPOS < min) + min = WORKSPACEPOS; + if (FOCUSPOS > pos && FOCUSPOS < min) + min = FOCUSPOS; - EMIT_HOOK_EVENT("monitorLayoutChanged", nullptr); -} + result = result.substr(0, min - pos); -SConfigValue* CConfigManager::getConfigValuePtr(const std::string& val) { - return &configValues[val]; -} + result = removeBeginEndSpacesTabs(result); -SConfigValue* CConfigManager::getConfigValuePtrSafe(const std::string& val) { - if (val.starts_with("device:")) { - const auto DEVICE = val.substr(7, val.find_last_of(':') - 7); - const auto CONFIGVAR = val.substr(val.find_last_of(':') + 1); + if (result.back() == ',') + result.pop_back(); - const auto DEVICECONF = deviceConfigs.find(DEVICE); - if (DEVICECONF == deviceConfigs.end()) - return nullptr; + return result; + }; - const auto IT = DEVICECONF->second.find(CONFIGVAR); + if (CLASSPOS != std::string::npos) + rule.szClass = extract(CLASSPOS + 6); - if (IT == DEVICECONF->second.end()) - return nullptr; + if (TITLEPOS != std::string::npos) + rule.szTitle = extract(TITLEPOS + 6); - return &IT->second; - } else if (val.starts_with("plugin:")) { - for (auto& [pl, pMap] : pluginConfigs) { - const auto IT = pMap->find(val); + if (INITIALCLASSPOS != std::string::npos) + rule.szInitialClass = extract(INITIALCLASSPOS + 13); - if (IT != pMap->end()) - return &IT->second; - } + if (INITIALTITLEPOS != std::string::npos) + rule.szInitialTitle = extract(INITIALTITLEPOS + 13); - return nullptr; - } + if (X11POS != std::string::npos) + rule.bX11 = extract(X11POS + 9) == "1" ? 1 : 0; - const auto IT = configValues.find(val); + if (FLOATPOS != std::string::npos) + rule.bFloating = extract(FLOATPOS + 9) == "1" ? 1 : 0; - if (IT == configValues.end()) - return nullptr; + if (FULLSCREENPOS != std::string::npos) + rule.bFullscreen = extract(FULLSCREENPOS + 11) == "1" ? 1 : 0; - return &(IT->second); -} + if (PINNEDPOS != std::string::npos) + rule.bPinned = extract(PINNEDPOS + 7) == "1" ? 1 : 0; -bool CConfigManager::deviceConfigExists(const std::string& dev) { - auto copy = dev; - std::replace(copy.begin(), copy.end(), ' ', '-'); + if (WORKSPACEPOS != std::string::npos) + rule.szWorkspace = extract(WORKSPACEPOS + 10); - return deviceConfigs.contains(copy); -} + if (FOCUSPOS != std::string::npos) + rule.bFocus = extract(FOCUSPOS + 6) == "1" ? 1 : 0; -bool CConfigManager::shouldBlurLS(const std::string& ns) { - for (auto& bls : m_dBlurLSNamespaces) { - if (bls == ns) { - return true; - } - } + if (ONWORKSPACEPOS != std::string::npos) + rule.iOnWorkspace = configStringToInt(extract(ONWORKSPACEPOS + 12)); - return false; -} + if (RULE == "unset") { + std::erase_if(m_dWindowRules, [&](const SWindowRule& other) { + if (!other.v2) { + return other.szClass == rule.szClass && !rule.szClass.empty(); + } else { + if (!rule.szClass.empty() && rule.szClass != other.szClass) + return false; -void CConfigManager::ensureMonitorStatus() { - for (auto& rm : g_pCompositor->m_vRealMonitors) { - if (!rm->output || rm->isUnsafeFallback) - continue; + if (!rule.szTitle.empty() && rule.szTitle != other.szTitle) + return false; - auto rule = getMonitorRuleFor(rm->szName, rm->szDescription); + if (!rule.szInitialClass.empty() && rule.szInitialClass != other.szInitialClass) + return false; - if (rule.disabled == rm->m_bEnabled) - g_pHyprRenderer->applyMonitorRule(rm.get(), &rule); + if (!rule.szInitialTitle.empty() && rule.szInitialTitle != other.szInitialTitle) + return false; + + if (rule.bX11 != -1 && rule.bX11 != other.bX11) + return false; + + if (rule.bFloating != -1 && rule.bFloating != other.bFloating) + return false; + + if (rule.bFullscreen != -1 && rule.bFullscreen != other.bFullscreen) + return false; + + if (rule.bPinned != -1 && rule.bPinned != other.bPinned) + return false; + + if (!rule.szWorkspace.empty() && rule.szWorkspace != other.szWorkspace) + return false; + + if (rule.bFocus != -1 && rule.bFocus != other.bFocus) + return false; + + if (rule.iOnWorkspace != -1 && rule.iOnWorkspace != other.iOnWorkspace) + return false; + + return true; + } + }); + return {}; } -} -void CConfigManager::ensureVRR(CMonitor* pMonitor) { - static auto* const PVRR = &getConfigValuePtr("misc:vrr")->intValue; + if (RULE.starts_with("size") || RULE.starts_with("maxsize") || RULE.starts_with("minsize")) + m_dWindowRules.push_front(rule); + else + m_dWindowRules.push_back(rule); - static auto ensureVRRForDisplay = [&](CMonitor* m) -> void { - if (!m->output || m->createdByUser) - return; + return {}; +} - const auto USEVRR = m->activeMonitorRule.vrr.has_value() ? m->activeMonitorRule.vrr.value() : *PVRR; +void CConfigManager::updateBlurredLS(const std::string& name, const bool forceBlur) { + const bool BYADDRESS = name.starts_with("address:"); + std::string matchName = name; - if (USEVRR == 0) { - if (m->vrrActive) { - wlr_output_state_set_adaptive_sync_enabled(m->state.wlr(), 0); + if (BYADDRESS) { + matchName = matchName.substr(8); + } - if (!m->state.commit()) - Debug::log(ERR, "Couldn't commit output {} in ensureVRR -> false", m->output->name); + for (auto& m : g_pCompositor->m_vMonitors) { + for (auto& lsl : m->m_aLayerSurfaceLayers) { + for (auto& ls : lsl) { + if (BYADDRESS) { + if (std::format("0x{:x}", (uintptr_t)ls.get()) == matchName) + ls->forceBlur = forceBlur; + } else if (ls->szNamespace == matchName) + ls->forceBlur = forceBlur; } - m->vrrActive = false; - return; - } else if (USEVRR == 1) { - if (!m->vrrActive) { - wlr_output_state_set_adaptive_sync_enabled(m->state.wlr(), 1); + } + } +} - if (!m->state.test()) { - Debug::log(LOG, "Pending output {} does not accept VRR.", m->output->name); - wlr_output_state_set_adaptive_sync_enabled(m->state.wlr(), 0); - } +std::optional CConfigManager::handleBlurLS(const std::string& command, const std::string& value) { + if (value.starts_with("remove,")) { + const auto TOREMOVE = removeBeginEndSpacesTabs(value.substr(7)); + if (std::erase_if(m_dBlurLSNamespaces, [&](const auto& other) { return other == TOREMOVE; })) + updateBlurredLS(TOREMOVE, false); + return {}; + } - if (!m->state.commit()) - Debug::log(ERR, "Couldn't commit output {} in ensureVRR -> true", m->output->name); - } - m->vrrActive = true; - return; - } else if (USEVRR == 2) { - /* fullscreen */ - m->vrrActive = true; + m_dBlurLSNamespaces.emplace_back(value); + updateBlurredLS(value, true); - const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(m->activeWorkspace); + return {}; +} - if (!PWORKSPACE) - return; // ??? +std::optional CConfigManager::handleWorkspaceRules(const std::string& command, const std::string& value) { + // This can either be the monitor or the workspace identifier + const auto FIRST_DELIM = value.find_first_of(','); - const auto WORKSPACEFULL = PWORKSPACE->m_bHasFullscreenWindow && PWORKSPACE->m_efFullscreenMode == FULLSCREEN_FULL; + std::string name = ""; + auto first_ident = removeBeginEndSpacesTabs(value.substr(0, FIRST_DELIM)); + int id = getWorkspaceIDFromString(first_ident, name); - if (WORKSPACEFULL && m->output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_DISABLED) { - wlr_output_state_set_adaptive_sync_enabled(m->state.wlr(), 1); + auto rules = value.substr(FIRST_DELIM + 1); + SWorkspaceRule wsRule; + wsRule.workspaceString = first_ident; + if (id == WORKSPACE_INVALID) { + // it could be the monitor. If so, second value MUST be + // the workspace. + const auto WORKSPACE_DELIM = value.find_first_of(',', FIRST_DELIM + 1); + auto wsIdent = removeBeginEndSpacesTabs(value.substr(FIRST_DELIM + 1, (WORKSPACE_DELIM - FIRST_DELIM - 1))); + id = getWorkspaceIDFromString(wsIdent, name); + if (id == WORKSPACE_INVALID) { + Debug::log(ERR, "Invalid workspace identifier found: {}", wsIdent); + return "Invalid workspace identifier found: " + wsIdent; + } + wsRule.monitor = first_ident; + wsRule.workspaceString = wsIdent; + wsRule.isDefault = true; // backwards compat + rules = value.substr(WORKSPACE_DELIM + 1); + } - if (!m->state.test()) { - Debug::log(LOG, "Pending output {} does not accept VRR.", m->output->name); - wlr_output_state_set_adaptive_sync_enabled(m->state.wlr(), 0); - } + const static std::string ruleOnCreatedEmtpy = "on-created-empty:"; + const static int ruleOnCreatedEmtpyLen = ruleOnCreatedEmtpy.length(); - if (!m->state.commit()) - Debug::log(ERR, "Couldn't commit output {} in ensureVRR -> true", m->output->name); + auto assignRule = [&](std::string rule) -> std::optional { + size_t delim = std::string::npos; + if ((delim = rule.find("gapsin:")) != std::string::npos) + wsRule.gapsIn = std::stoi(rule.substr(delim + 7)); + else if ((delim = rule.find("gapsout:")) != std::string::npos) + wsRule.gapsOut = std::stoi(rule.substr(delim + 8)); + else if ((delim = rule.find("bordersize:")) != std::string::npos) + wsRule.borderSize = std::stoi(rule.substr(delim + 11)); + else if ((delim = rule.find("border:")) != std::string::npos) + wsRule.border = configStringToInt(rule.substr(delim + 7)); + else if ((delim = rule.find("shadow:")) != std::string::npos) + wsRule.shadow = configStringToInt(rule.substr(delim + 7)); + else if ((delim = rule.find("rounding:")) != std::string::npos) + wsRule.rounding = configStringToInt(rule.substr(delim + 9)); + else if ((delim = rule.find("decorate:")) != std::string::npos) + wsRule.decorate = configStringToInt(rule.substr(delim + 9)); + else if ((delim = rule.find("monitor:")) != std::string::npos) + wsRule.monitor = rule.substr(delim + 8); + else if ((delim = rule.find("default:")) != std::string::npos) + wsRule.isDefault = configStringToInt(rule.substr(delim + 8)); + else if ((delim = rule.find("persistent:")) != std::string::npos) + wsRule.isPersistent = configStringToInt(rule.substr(delim + 11)); + else if ((delim = rule.find(ruleOnCreatedEmtpy)) != std::string::npos) + wsRule.onCreatedEmptyRunCmd = cleanCmdForWorkspace(name, rule.substr(delim + ruleOnCreatedEmtpyLen)); + else if ((delim = rule.find("layoutopt:")) != std::string::npos) { + std::string opt = rule.substr(delim + 10); + if (!opt.contains(":")) { + // invalid + Debug::log(ERR, "Invalid workspace rule found: {}", rule); + return "Invalid workspace rule found: " + rule; + } - } else if (!WORKSPACEFULL && m->output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED) { - wlr_output_state_set_adaptive_sync_enabled(m->state.wlr(), 0); + std::string val = opt.substr(opt.find(":") + 1); + opt = opt.substr(0, opt.find(":")); - if (!m->state.commit()) - Debug::log(ERR, "Couldn't commit output {} in ensureVRR -> false", m->output->name); - } + wsRule.layoutopts[opt] = val; } + + return {}; }; - if (pMonitor) { - ensureVRRForDisplay(pMonitor); - return; + CVarList rulesList{rules, 0, ',', true}; + for (auto& r : rulesList) { + const auto R = assignRule(r); + if (R.has_value()) + return R; } - for (auto& m : g_pCompositor->m_vMonitors) { - ensureVRRForDisplay(m.get()); - } -} + wsRule.workspaceId = id; + wsRule.workspaceName = name; -SAnimationPropertyConfig* CConfigManager::getAnimationPropertyConfig(const std::string& name) { - return &animationConfig[name]; -} + const auto IT = std::find_if(m_dWorkspaceRules.begin(), m_dWorkspaceRules.end(), [&](const auto& other) { return other.workspaceString == wsRule.workspaceString; }); -void CConfigManager::addParseError(const std::string& err) { - if (parseError == "") - parseError = err; + if (IT == m_dWorkspaceRules.end()) + m_dWorkspaceRules.emplace_back(wsRule); + else + *IT = wsRule; - g_pHyprError->queueCreate(parseError + "\nHyprland may not work correctly.", CColor(1.0, 50.0 / 255.0, 50.0 / 255.0, 1.0)); + return {}; } -CMonitor* CConfigManager::getBoundMonitorForWS(const std::string& wsname) { - auto monitor = getBoundMonitorStringForWS(wsname); - if (monitor.substr(0, 5) == "desc:") - return g_pCompositor->getMonitorFromDesc(monitor.substr(5)); +std::optional CConfigManager::handleSubmap(const std::string& command, const std::string& submap) { + if (submap == "reset") + m_szCurrentSubmap = ""; else - return g_pCompositor->getMonitorFromName(monitor); -} - -std::string CConfigManager::getBoundMonitorStringForWS(const std::string& wsname) { - for (auto& wr : m_dWorkspaceRules) { - const auto WSNAME = wr.workspaceName.starts_with("name:") ? wr.workspaceName.substr(5) : wr.workspaceName; - - if (WSNAME == wsname) { - return wr.monitor; - } - } + m_szCurrentSubmap = submap; - return ""; + return {}; } -const std::deque& CConfigManager::getAllWorkspaceRules() { - return m_dWorkspaceRules; -} +std::optional CConfigManager::handleSource(const std::string& command, const std::string& rawpath) { + if (rawpath.length() < 2) { + Debug::log(ERR, "source= path garbage"); + return "source path " + rawpath + " bogus!"; + } + std::unique_ptr glob_buf{new glob_t, [](glob_t* g) { globfree(g); }}; + memset(glob_buf.get(), 0, sizeof(glob_t)); -void CConfigManager::addExecRule(const SExecRequestedRule& rule) { - execRequestedRules.push_back(rule); -} + if (auto r = glob(absolutePath(rawpath, configCurrentPath).c_str(), GLOB_TILDE, nullptr, glob_buf.get()); r != 0) { + std::string err = std::format("source= globbing error: {}", r == GLOB_NOMATCH ? "found no match" : GLOB_ABORTED ? "read error" : "out of memory"); + Debug::log(ERR, "{}", err); + return err; + } -void CConfigManager::handlePluginLoads() { - if (g_pPluginSystem == nullptr) - return; + for (size_t i = 0; i < glob_buf->gl_pathc; i++) { + auto value = absolutePath(glob_buf->gl_pathv[i], configCurrentPath); - bool pluginsChanged = false; - auto failedPlugins = g_pPluginSystem->updateConfigPlugins(m_vDeclaredPlugins, pluginsChanged); + if (!std::filesystem::is_regular_file(value)) { + if (std::filesystem::exists(value)) { + Debug::log(WARN, "source= skipping non-file {}", value); + continue; + } - if (!failedPlugins.empty()) { - std::stringstream error; - error << "Failed to load the following plugins:"; + Debug::log(ERR, "source= file doesnt exist"); + return "source file " + value + " doesn't exist!"; + } + configPaths.push_back(value); - for (auto path : failedPlugins) { - error << "\n" << path; + struct stat fileStat; + int err = stat(value.c_str(), &fileStat); + if (err != 0) { + Debug::log(WARN, "Error at ticking config at {}, error {}: {}", value, err, strerror(err)); + return {}; } - g_pHyprError->queueCreate(error.str(), CColor(1.0, 50.0 / 255.0, 50.0 / 255.0, 1.0)); - } + configModifyTimes[value] = fileStat.st_mtime; + auto configCurrentPathBackup = configCurrentPath; + configCurrentPath = value; - if (pluginsChanged) { - g_pHyprError->destroy(); - m_bForceReload = true; - tick(); + m_pConfig->parseFile(value.c_str()); + + configCurrentPath = configCurrentPathBackup; } -} -ICustomConfigValueData::~ICustomConfigValueData() { - ; // empty + return {}; } -std::unordered_map CConfigManager::getAnimationConfig() { - return animationConfig; -} +std::optional CConfigManager::handleEnv(const std::string& command, const std::string& value) { + if (!isFirstLaunch) + return {}; -void CConfigManager::addPluginConfigVar(HANDLE handle, const std::string& name, const SConfigValue& value) { - auto CONFIGMAPIT = std::find_if(pluginConfigs.begin(), pluginConfigs.end(), [&](const auto& other) { return other.first == handle; }); + const auto ARGS = CVarList(value, 2); - if (CONFIGMAPIT == pluginConfigs.end()) { - pluginConfigs.emplace( - std::pair>>(handle, std::make_unique>())); - CONFIGMAPIT = std::find_if(pluginConfigs.begin(), pluginConfigs.end(), [&](const auto& other) { return other.first == handle; }); - } + if (ARGS[0].empty()) + return "env empty"; - (*CONFIGMAPIT->second)[name] = value; + setenv(ARGS[0].c_str(), ARGS[1].c_str(), 1); - if (const auto IT = std::find_if(m_vFailedPluginConfigValues.begin(), m_vFailedPluginConfigValues.end(), [&](const auto& other) { return other.first == name; }); - IT != m_vFailedPluginConfigValues.end()) { - configSetValueSafe(IT->first, IT->second); + if (command.back() == 'd') { + // dbus + const auto CMD = +#ifdef USES_SYSTEMD + "systemctl --user import-environment " + ARGS[0] + + " && hash dbus-update-activation-environment 2>/dev/null && " +#endif + "dbus-update-activation-environment --systemd " + + ARGS[0]; + handleRawExec("", CMD); } -} -void CConfigManager::addPluginKeyword(HANDLE handle, const std::string& name, std::function fn) { - pluginKeywords.emplace_back(SPluginKeyword{handle, name, fn}); + return {}; } -void CConfigManager::removePluginConfig(HANDLE handle) { - std::erase_if(pluginConfigs, [&](const auto& other) { return other.first == handle; }); - std::erase_if(pluginKeywords, [&](const auto& other) { return other.handle == handle; }); -} +std::optional CConfigManager::handlePlugin(const std::string& command, const std::string& path) { + if (std::find(m_vDeclaredPlugins.begin(), m_vDeclaredPlugins.end(), path) != m_vDeclaredPlugins.end()) + return "plugin '" + path + "' declared twice"; -std::string CConfigManager::getDefaultWorkspaceFor(const std::string& name) { - for (auto other = m_dWorkspaceRules.begin(); other != m_dWorkspaceRules.end(); ++other) { - if (other->isDefault) { - if (other->monitor == name) - return other->workspaceString; - if (other->monitor.substr(0, 5) == "desc:") { - auto monitor = g_pCompositor->getMonitorFromDesc(other->monitor.substr(5)); - if (monitor && monitor->szName == name) - return other->workspaceString; - } - } - } - return ""; -} + m_vDeclaredPlugins.push_back(path); + + return {}; +} \ No newline at end of file -- cgit v1.2.3