Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package wf-config for openSUSE:Factory checked in at 2026-08-18 16:38:01 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/wf-config (Old) and /work/SRC/openSUSE:Factory/.wf-config.new.1258 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "wf-config" Tue Aug 18 16:38:01 2026 rev:3 rq:1371738 version:0.11.0 Changes: -------- --- /work/SRC/openSUSE:Factory/wf-config/wf-config.changes 2025-09-04 18:01:43.404626237 +0200 +++ /work/SRC/openSUSE:Factory/.wf-config.new.1258/wf-config.changes 2026-08-18 16:38:49.201948332 +0200 @@ -1,0 +2,14 @@ +Tue Jul 28 04:36:45 UTC 2026 - Dead Mozay <[email protected]> + +- Update to version 0.11.0: + * Refactor load_xml_files: suppress per-file logging, summarize + loaded …. + * ini-parser: emit errors for options not part of the XML spec. + * option-wrapper: add option to load via section+option name. + * support cubic-bezier as easing. + * Don't copy accessed values in getters. + * Add extra AUTO options. + * files: add support for reloading XMLs files after building + the initial configuration. + +------------------------------------------------------------------- Old: ---- wf-config-0.10.0.tar.xz wf-config-0.10.0.tar.xz.sha256sum New: ---- wf-config-0.11.0.tar.xz wf-config-0.11.0.tar.xz.sha256sum ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ wf-config.spec ++++++ --- /var/tmp/diff_new_pack.sgkVfQ/_old 2026-08-18 16:38:49.857971806 +0200 +++ /var/tmp/diff_new_pack.sgkVfQ/_new 2026-08-18 16:38:49.859971878 +0200 @@ -1,7 +1,7 @@ # # spec file for package wf-config # -# Copyright (c) 2025 SUSE LLC and contributors +# Copyright (c) 2026 SUSE LLC and contributors # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -16,10 +16,10 @@ # -%define so_ver 0.10 +%define so_ver 0.11 %define libname libwf-config1 Name: wf-config -Version: 0.10.0 +Version: 0.11.0 Release: 0 Summary: A library for managing configuration files License: MIT @@ -33,7 +33,7 @@ BuildRequires: pkgconfig(glm) BuildRequires: pkgconfig(libevdev) BuildRequires: pkgconfig(libxml-2.0) -BuildRequires: pkgconfig(wlroots) >= 0.17.0 +BuildRequires: pkgconfig(wlroots-0.20) %description A library for managing configuration files, written for wayfire. ++++++ wf-config-0.10.0.tar.xz -> wf-config-0.11.0.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wf-config-0.10.0/.github/workflows/ci.yaml new/wf-config-0.11.0/.github/workflows/ci.yaml --- old/wf-config-0.10.0/.github/workflows/ci.yaml 2025-06-22 08:30:31.000000000 +0200 +++ new/wf-config-0.11.0/.github/workflows/ci.yaml 2026-07-20 21:40:27.000000000 +0200 @@ -12,7 +12,9 @@ - run: git clone http://github.com/ammen99/uncrustify - run: cd uncrustify && mkdir build && cd build && cmake ../ && make && cd ../../ - run: curl https://raw.githubusercontent.com/WayfireWM/wayfire/master/uncrustify.ini > uncrustify.ini - - run: git ls-files | grep "hpp$\|cpp$" | xargs ./uncrustify/build/uncrustify -c uncrustify.ini --check + - run: git ls-files | grep "hpp$\|cpp$" | xargs ./uncrustify/build/uncrustify -c uncrustify.ini --no-backup --replace + - run: git diff + - run: git diff | diff - /dev/null &> /dev/null run_tests: name: "Check that tests do not break" runs-on: ubuntu-latest diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wf-config-0.10.0/.gitignore new/wf-config-0.11.0/.gitignore --- old/wf-config-0.10.0/.gitignore 1970-01-01 01:00:00.000000000 +0100 +++ new/wf-config-0.11.0/.gitignore 2026-07-20 21:40:27.000000000 +0200 @@ -0,0 +1 @@ +uncrustify \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wf-config-0.10.0/include/wayfire/config/compound-option.hpp new/wf-config-0.11.0/include/wayfire/config/compound-option.hpp --- old/wf-config-0.10.0/include/wayfire/config/compound-option.hpp 2025-06-22 08:30:31.000000000 +0200 +++ new/wf-config-0.11.0/include/wayfire/config/compound-option.hpp 2026-07-20 21:40:27.000000000 +0200 @@ -47,19 +47,19 @@ virtual ~compound_option_entry_base_t() = default; /** @return The prefix of the tuple entry. */ - std::string get_prefix() const + const std::string& get_prefix() const WF_LIFETIMEBOUND { return prefix; } /** @return The name of the tuple entry. */ - std::string get_name() const + const std::string& get_name() const WF_LIFETIMEBOUND { return name; } /** @return The untyped default value of the tuple entry. */ - std::optional<std::string> get_default_value() const + const std::optional<std::string>& get_default_value() const WF_LIFETIMEBOUND { return default_value; } @@ -224,12 +224,12 @@ /** * Get the type information about entries in the option. */ - const entries_t& get_entries() const; + const entries_t& get_entries() const WF_LIFETIMEBOUND; /** * Check if this compound option has named tuples. */ - std::string get_type_hint() const + const std::string& get_type_hint() const WF_LIFETIMEBOUND { return list_type_hint; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wf-config-0.10.0/include/wayfire/config/file.hpp new/wf-config-0.11.0/include/wayfire/config/file.hpp --- old/wf-config-0.10.0/include/wayfire/config/file.hpp 2025-06-22 08:30:31.000000000 +0200 +++ new/wf-config-0.11.0/include/wayfire/config/file.hpp 2026-07-20 21:40:27.000000000 +0200 @@ -86,5 +86,13 @@ */ config_manager_t build_configuration(const std::vector<std::string>& xmldirs, const std::string& sysconf, const std::string& userconf); + +/** + * Load XML option metadata from @xmldirs and merge any newly discovered + * sections/options into @manager. Existing options are left untouched so that + * active option wrappers keep their values and callbacks. + */ +void load_configuration_options_from_xml_dirs(config_manager_t& manager, + const std::vector<std::string>& xmldirs); } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wf-config-0.10.0/include/wayfire/config/option-wrapper.hpp new/wf-config-0.11.0/include/wayfire/config/option-wrapper.hpp --- old/wf-config-0.10.0/include/wayfire/config/option-wrapper.hpp 2025-06-22 08:30:31.000000000 +0200 +++ new/wf-config-0.11.0/include/wayfire/config/option-wrapper.hpp 2026-07-20 21:40:27.000000000 +0200 @@ -2,6 +2,7 @@ #include <stdexcept> #include <wayfire/config/option.hpp> +#include <wayfire/config/section.hpp> #include <wayfire/config/compound-option.hpp> namespace wf @@ -77,25 +78,12 @@ */ void load_option(const std::string& name) { - if (raw_option) - { - throw std::logic_error( - "Loading an option into option wrapper twice!"); - } - - auto untyped_option = load_raw_option(name); - if (untyped_option == nullptr) - { - throw std::runtime_error("No such option: " + std::string(name)); - } - - raw_option = std::dynamic_pointer_cast<OptionType>(untyped_option); - if (raw_option == nullptr) - { - throw std::runtime_error("Bad option type: " + std::string(name)); - } + _load_option(load_raw_option(name), name); + } - raw_option->add_updated_handler(&option_update_listener); + void load_option(std::shared_ptr<wf::config::section_t> section, const std::string& name) + { + _load_option(section->get_option_or(name), section->get_name() + "/" + name); } virtual ~base_option_wrapper_t() @@ -107,12 +95,12 @@ } /** Implicitly convertible to the value of the option */ - operator Type() const + operator decltype(auto)() const WF_LIFETIMEBOUND { return this->value(); } - Type value() const + decltype(auto) value() const WF_LIFETIMEBOUND { if constexpr (is_std_vector<Type>::value) { @@ -162,5 +150,27 @@ */ virtual std::shared_ptr<wf::config::option_base_t> load_raw_option( const std::string& name) = 0; + + void _load_option(std::shared_ptr<wf::config::option_base_t> _option, const std::string& name) + { + if (raw_option) + { + throw std::logic_error( + "Loading an option into option wrapper twice!"); + } + + if (_option == nullptr) + { + throw std::runtime_error("No such option: " + std::string(name)); + } + + raw_option = std::dynamic_pointer_cast<OptionType>(_option); + if (raw_option == nullptr) + { + throw std::runtime_error("Bad option type: " + name); + } + + raw_option->add_updated_handler(&option_update_listener); + } }; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wf-config-0.10.0/include/wayfire/config/option.hpp new/wf-config-0.11.0/include/wayfire/config/option.hpp --- old/wf-config-0.10.0/include/wayfire/config/option.hpp 2025-06-22 08:30:31.000000000 +0200 +++ new/wf-config-0.11.0/include/wayfire/config/option.hpp 2026-07-20 21:40:27.000000000 +0200 @@ -6,6 +6,12 @@ #include <memory> +#ifdef __clang__ + #define WF_LIFETIMEBOUND [[clang::lifetimebound]] +#else + #define WF_LIFETIMEBOUND +#endif + namespace wf { namespace config @@ -21,7 +27,7 @@ option_base_t& operator =(const option_base_t& other) = delete; /** @return The name of the option */ - std::string get_name() const; + const std::string& get_name() const WF_LIFETIMEBOUND; /** @return A copy of the option */ virtual std::shared_ptr<option_base_t> clone_option() const = 0; @@ -253,12 +259,12 @@ } } - Type get_value() const + const Type& get_value() const WF_LIFETIMEBOUND { return value; } - Type get_default_value() const + const Type& get_default_value() const WF_LIFETIMEBOUND { return default_value; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wf-config-0.10.0/include/wayfire/config/types.hpp new/wf-config-0.11.0/include/wayfire/config/types.hpp --- old/wf-config-0.10.0/include/wayfire/config/types.hpp 2025-06-22 08:30:31.000000000 +0200 +++ new/wf-config-0.11.0/include/wayfire/config/types.hpp 2026-07-20 21:40:27.000000000 +0200 @@ -487,6 +487,10 @@ { /** Output was configured in automatic mode. */ MODE_AUTO, + /** Output was configured with the biggest refresh rate. */ + MODE_HIGHRR, + /** Output was configured with the biggest resolution. */ + MODE_HIGHRES, /** Output was configured to be turned off. */ MODE_OFF, /** Output was configured with a given resolution. */ @@ -502,11 +506,13 @@ struct mode_t { /** - * Initialize an OFF or AUTO mode. + * Initialize a mode. * - * @param auto_on If true, the created mode will be an AUTO mode. + * @param mode One of: MODE_AUTO, MODE_HIGHRR, MODE_HIGHRES, MODE_OFF. MODE_HIGHRR prioritizes refresh + * rate, MODE_HIGHRES prioritises resolution and MODE_AUTO chooses whatever the display tells it to use. + * @throws std::invalid_argument if the mode isn't MODE_AUTO, MODEHIGHRR, MODEHIGHRES or MODE_OFF. */ - mode_t(bool auto_on = false); + mode_t(output_config::mode_type_t mode); /** * Initialize the mode with source self. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wf-config-0.10.0/include/wayfire/util/duration.hpp new/wf-config-0.11.0/include/wayfire/util/duration.hpp --- old/wf-config-0.10.0/include/wayfire/util/duration.hpp 2025-06-22 08:30:31.000000000 +0200 +++ new/wf-config-0.11.0/include/wayfire/util/duration.hpp 2026-07-20 21:40:27.000000000 +0200 @@ -11,9 +11,8 @@ namespace smoothing { /** - * A smooth function is a function which takes a double in [0, 1] and returns - * another double in R. Both ranges represent percentage of a progress of - * an animation. + * A smooth function is a function which takes a double in [0, 1] and returns another double in R. Both ranges + * represent percentage of a progress of an animation. */ using smooth_function = std::function<double (double)>; @@ -23,6 +22,8 @@ extern smooth_function circle; /** "sigmoid" smoothing function, i.e x -> 1.0 / (1 + exp(-12 * x + 6)) */ extern smooth_function sigmoid; +/** custom cubic-bezier as in CSS */ +extern smooth_function get_cubic_bezier(double x1, double y1, double x2, double y2); std::vector<std::string> get_available_smooth_functions(); } @@ -34,10 +35,7 @@ animation::smoothing::smooth_function easing; std::string easing_name; - bool operator ==(const animation_description_t& other) const - { - return (length_ms == other.length_ms) && (easing_name == other.easing_name); - } + bool operator ==(const animation_description_t& other) const; }; namespace option_type @@ -66,15 +64,13 @@ }; /** - * duration_t is a class which can be used to track progress over a specific - * time interval. + * duration_t is a class which can be used to track progress over a specific time interval. */ class duration_t { public: /** - * Construct a new duration. - * Initially, the duration is not running and its progress is 1. + * Construct a new duration. Initially, the duration is not running and its progress is 1. * * @param length The length of the duration in milliseconds. * @param smooth The smoothing function for transitions. @@ -95,40 +91,36 @@ duration_t& operator =(duration_t&& other) = default; /** - * Start the duration. - * This means that the progress will get reset to 0. + * Start the duration. This means that the progress will get reset to 0. */ void start(); /** - * Get the progress of the duration in percentage. - * The progress will be smoothed using the smoothing function. + * Get the progress of the duration in percentage. The progress will be smoothed using the smoothing + * function. * - * @return The current progress after smoothing. It is guaranteed that when - * the duration starts, progress will be close to 0, and when it is - * finished, it will be close to 1. + * @return The current progress after smoothing. It is guaranteed that when the duration starts, progress + * will be close to 0, and when it is finished, it will be close to 1. */ double progress() const; /** - * Check if the duration is still running. - * Note that even when the duration first finishes, this function will - * still return that the function is running one time. + * Check if the duration is still running. Note that even when the duration first finishes, this function + * will still return that the function is running one time. * * @return Whether the duration still has not elapsed. */ bool running(); /** - * Reverse the duration. The progress will remain the same but the - * direction will reverse toward the opposite start or end point. + * Reverse the duration. The progress will remain the same but the direction will reverse toward the + * opposite start or end point. */ void reverse(); /** * Get duration direction. - * 0: reverse - * 1: forward + * 0: reverse 1: forward */ int get_direction(); @@ -138,17 +130,14 @@ }; /** - * A timed transition is a transition between two states which happens - * over a period of time. + * A timed transition is a transition between two states which happens over a period of time. * - * During the transition, the current state is smoothly interpolated between - * start and end. + * During the transition, the current state is smoothly interpolated between start and end. */ struct timed_transition_t : public transition_t { /** - * Construct a new timed transition using the given duration to measure - * progress. + * Construct a new timed transition using the given duration to measure progress. * * @duration The duration to use for time measurement * @start The start state. @@ -204,14 +193,12 @@ void animate(double start, double end); /** - * Animate from the current progress to the given end, and start the - * duration. + * Animate from the current progress to the given end, and start the duration. */ void animate(double end); /** - * Animate from the current progress to the current end, and start the - * duration. + * Animate from the current progress to the current end, and start the duration. */ void animate(); }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wf-config-0.10.0/meson.build new/wf-config-0.11.0/meson.build --- old/wf-config-0.10.0/meson.build 2025-06-22 08:30:31.000000000 +0200 +++ new/wf-config-0.11.0/meson.build 2026-07-20 21:40:27.000000000 +0200 @@ -1,7 +1,7 @@ project( 'wf-config', 'cpp', - version: '0.10.0', + version: '0.11.0', license: 'MIT', meson_version: '>=0.47.0', default_options: [ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wf-config-0.10.0/src/compound-option.cpp new/wf-config-0.11.0/src/compound-option.cpp --- old/wf-config-0.10.0/src/compound-option.cpp 2025-06-22 08:30:31.000000000 +0200 +++ new/wf-config-0.11.0/src/compound-option.cpp 2026-07-20 21:40:27.000000000 +0200 @@ -60,10 +60,10 @@ value[0] = suffix; for (size_t i = 0; i < entries.size(); ++i) { - if (const auto & entry_option = - section->get_option_or(entries[i]->get_prefix() + suffix); + if (const auto & entry_option = section->get_option_or(entries[i]->get_prefix() + suffix); entry_option && !should_ignore_option(entry_option)) { + entry_option->priv->could_be_compound = true; if (entries[i]->is_parsable(entry_option->get_value_str())) { value[i + 1] = entry_option->get_value_str(); @@ -83,9 +83,6 @@ value[i + 1] = *default_value; } else { - LOGE("The option ", - section->get_name() + "/" + entries[i]->get_prefix() + suffix, - " is neither specified nor has a default value"); value.clear(); break; } @@ -94,6 +91,14 @@ if (!value.empty()) { stored_value.push_back(std::move(value)); + for (size_t i = 0; i < entries.size(); ++i) + { + if (auto entry_option = section->get_option_or(entries[i]->get_prefix() + suffix)) + { + // The option was used as part of the compound option, do not issue warning for it! + entry_option->priv->is_part_compound = true; + } + } } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wf-config-0.10.0/src/duration.cpp new/wf-config-0.11.0/src/duration.cpp --- old/wf-config-0.10.0/src/duration.cpp 2025-06-22 08:30:31.000000000 +0200 +++ new/wf-config-0.11.0/src/duration.cpp 2026-07-20 21:40:27.000000000 +0200 @@ -4,7 +4,20 @@ #include <algorithm> #include <chrono> #include <cmath> +#include <limits> #include <map> +#include <sstream> + +double bezier_helper(double t, double p0, double p1, double p2, double p3) +{ + const double u = 1 - t; + return u * u * u * p0 + 3 * u * u * t * p1 + 3 * u * t * t * p2 + t * t * t * p3; +} + +inline bool epsilon_comparison(double a, double b) +{ + return std::fabs(a - b) <= std::numeric_limits<double>::epsilon() * std::fabs(a + b); +} namespace wf { @@ -20,10 +33,59 @@ const double sigmoid_max = 1 + std::exp(-6); smooth_function sigmoid = [] (double x) -> double { return sigmoid_max / (1 + exp(-12 * x + 6)); }; + +smooth_function get_cubic_bezier(double x1, double y1, double x2, double y2) +{ + // https://en.wikipedia.org/wiki/Newton%27s_method + return [=] (double x) + { + double t = x; + for (int i = 0; i < 10; ++i) + { + const double f = bezier_helper(t, 0, x1, x2, 1) - x; + const double df = 3 * (1 - t) * (1 - t) * x1 + 6 * (1 - t) * t * (x2 - x1) + 3 * t * t * (1 - x2); + if (std::abs(f) < 1e-6) + { + break; + } + + t -= f / df; + } + + return bezier_helper(t, 0, y1, y2, 1); + }; +} } } // namespace animation } +bool wf::animation_description_t::operator ==(const animation_description_t & other) const +{ + if (easing_name == other.easing_name) + { + return (length_ms == other.length_ms); + } + + // Cubic-bezier easings need parsing to handle epsilon + std::stringstream easing_a(easing_name); + std::stringstream easing_b(easing_name); + std::string easing_type_a, easing_type_b; + easing_a >> easing_type_a; + easing_b >> easing_type_b; + if ((easing_type_a != "cubic-bezier") || (easing_type_b != "cubic-bezier")) + { + return false; + } + + double x1_a, y1_a, x2_a, y2_a, x1_b, y1_b, x2_b, y2_b; + easing_a >> x1_a >> y1_a >> x2_a >> y2_a; + easing_b >> x1_b >> y1_b >> x2_b >> y2_b; + return epsilon_comparison(x1_a, x1_b) && + epsilon_comparison(y1_a, y1_b) && + epsilon_comparison(x2_a, x2_b) && + epsilon_comparison(y2_b, y2_b); +} + class wf::animation::duration_t::impl { public: @@ -297,7 +359,7 @@ return animation_description_t{ .length_ms = *val, .easing = animation::smoothing::circle, - .easing_name = "circle", + .easing_name = "circle" }; } @@ -321,7 +383,20 @@ result.easing_name = "circle"; } - if (!animation::smoothing::easing_map.count(result.easing_name)) + if (animation::smoothing::easing_map.count(result.easing_name)) + { + result.easing = animation::smoothing::easing_map.at(result.easing_name); + } else if (result.easing_name == "cubic-bezier") + { + double x1 = 0, y1 = 0, x2 = 1, y2 = 1; + stream >> x1 >> y1 >> x2 >> y2; + result.easing = animation::smoothing::get_cubic_bezier(x1, y1, x2, y2); + result.easing_name = "cubic-bezier " + + to_string(x1) + + " " + to_string(y1) + + " " + to_string(x2) + + " " + to_string(y2); + } else { return {}; } @@ -333,7 +408,6 @@ return {}; } - result.easing = animation::smoothing::easing_map.at(result.easing_name); if (suffix == "s") { result.length_ms = N * 1000; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wf-config-0.10.0/src/file.cpp new/wf-config-0.11.0/src/file.cpp --- old/wf-config-0.10.0/src/file.cpp 2025-06-22 08:30:31.000000000 +0200 +++ new/wf-config-0.11.0/src/file.cpp 2026-07-20 21:40:27.000000000 +0200 @@ -40,6 +40,7 @@ size_t source_line_number; }; + using lines_t = std::vector<line_t>; static lines_t split_to_lines(const std::string& source) @@ -214,9 +215,8 @@ }; /** - * Try to parse an option line. - * If the option line is valid, the corresponding option is modified or added - * to @current_section, and the option is added to @reloaded. + * Try to parse an option line. If the option line is valid, the corresponding option is modified or added to + * @current_section, and the option is added to @reloaded. * * @return The parse status of the line. */ @@ -252,9 +252,8 @@ } /** - * Check whether the @line is a valid section start. - * If yes, it will either return the section in @config with the same name, or - * create a new section and register it in config. + * Check whether the @line is a valid section start. If yes, it will either return the section in @config with + * the same name, or create a new section and register it in config. * * @return nullptr if line is not a valid section, the section otherwise. */ @@ -354,6 +353,10 @@ for (auto opt : section->get_registered_options()) { opt->priv->option_in_config_file = (reloaded.count(opt) > 0); + + opt->priv->is_part_compound = false; // will be re-set when updating compound options + opt->priv->could_be_compound = false; // will be re-set when updating compound options + if (!opt->priv->option_in_config_file && !opt->is_locked()) { opt->reset_to_default(); @@ -374,6 +377,27 @@ } } } + + for (auto section : config.get_all_sections()) + { + for (auto opt : section->get_registered_options()) + { + if (!opt->priv->xml && !opt->priv->is_part_compound) + { + if (opt->priv->could_be_compound) + { + LOGW("Option ", section->get_name(), "/", opt->get_name(), + " could not be parsed as part of a compound option: missing entries or wrong type!"); + } else + { + LOGW("Loaded option ", section->get_name(), "/", opt->get_name(), + ", which does not belong to any registered plugin, nor could be parsed as a part of ", + "a compound list option. Make sure all the relevant XML files are installed and " + "that the option name is spelled correctly!"); + } + } + } + } } std::string wf::config::save_configuration_options_to_string( @@ -518,16 +542,13 @@ flock(fd, LOCK_UN); close(fd); - /* Modify the file one last time. Now programs waiting for updates can - * acquire a shared lock. */ + /* Modify the file one last time. Now programs waiting for updates can acquire a shared lock. */ fout << std::endl; } static void process_xml_file(wf::config::config_manager_t& manager, const std::string & filename) { - LOGI("Reading XML configuration options from file ", filename); - /* Parse the XML file. */ auto doc = xmlParseFile(filename.c_str()); if (!doc) @@ -562,23 +583,23 @@ // xmlFreeDoc(doc); - May clear the XML nodes before they are used } -static wf::config::config_manager_t load_xml_files( - const std::vector<std::string>& xmldirs) +static wf::config::config_manager_t load_xml_files(const std::vector<std::string>& xmldirs) { wf::config::config_manager_t manager; for (auto& xmldir : xmldirs) { auto xmld = opendir(xmldir.c_str()); - if (NULL == xmld) + if (!xmld) { LOGW("Failed to open XML directory ", xmldir); continue; } - LOGI("Reading XML configuration options from directory ", xmldir); + std::vector<std::string> loaded_files; + struct dirent *entry; - while ((entry = readdir(xmld)) != NULL) + while ((entry = readdir(xmld)) != nullptr) { if ((entry->d_type != DT_LNK) && (entry->d_type != DT_REG) && (entry->d_type != DT_UNKNOWN)) @@ -591,10 +612,29 @@ (filename.rfind(".xml") == filename.length() - 4)) { process_xml_file(manager, filename); + loaded_files.push_back(entry->d_name); } } closedir(xmld); + + if (!loaded_files.empty()) + { + LOGI("Loaded XML configuration options from ", loaded_files.size(), + " files in ", xmldir, ":"); + + std::string list; + for (size_t i = 0; i < loaded_files.size(); ++i) + { + list += loaded_files[i]; + if (i + 1 != loaded_files.size()) + { + list += ", "; + } + } + + LOGI(list); + } } return manager; @@ -643,3 +683,32 @@ load_configuration_options_from_file(manager, userconf); return manager; } + +void wf::config::load_configuration_options_from_xml_dirs( + wf::config::config_manager_t& manager, + const std::vector<std::string>& xmldirs) +{ + auto loaded = load_xml_files(xmldirs); + for (auto& section : loaded.get_all_sections()) + { + auto existing = manager.get_section(section->get_name()); + if (!existing) + { + manager.merge_section(section); + continue; + } + + for (auto& option : section->get_registered_options()) + { + auto existing_option = existing->get_option_or(option->get_name()); + if (!existing_option) + { + existing->register_new_option(option); + } else if (!xml::get_option_xml_node(existing_option)) + { + option->set_value_str(existing_option->get_value_str()); + existing->register_new_option(option); + } + } + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wf-config-0.10.0/src/option-impl.hpp new/wf-config-0.11.0/src/option-impl.hpp --- old/wf-config-0.10.0/src/option-impl.hpp 2025-06-22 08:30:31.000000000 +0200 +++ new/wf-config-0.11.0/src/option-impl.hpp 2026-07-20 21:40:27.000000000 +0200 @@ -6,21 +6,6 @@ #include <libxml/tree.h> #include <stdint.h> -struct wf::config::option_base_t::impl -{ - std::string name; - wf::safe_list_t<updated_callback_t*> updated_handlers; - - // Number of times the option has been locked - int32_t lock_count = 0; - - // Associated XML node - xmlNode *xml; - - // Is option in config file? - bool option_in_config_file = false; -}; - namespace wf { namespace config @@ -37,3 +22,23 @@ const std::shared_ptr<section_t>& section); } } + +struct wf::config::option_base_t::impl +{ + std::string name; + wf::safe_list_t<updated_callback_t*> updated_handlers; + + // Number of times the option has been locked + int32_t lock_count = 0; + + // Associated XML node + xmlNode *xml = nullptr; + + // Is option in config file? + bool option_in_config_file = false; + + // Is option part of a successfully parsed compound option? + bool is_part_compound = false; + // Does this option match a compound option in part at least? + bool could_be_compound = false; +}; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wf-config-0.10.0/src/option.cpp new/wf-config-0.11.0/src/option.cpp --- old/wf-config-0.10.0/src/option.cpp 2025-06-22 08:30:31.000000000 +0200 +++ new/wf-config-0.11.0/src/option.cpp 2026-07-20 21:40:27.000000000 +0200 @@ -5,7 +5,7 @@ #include "option-impl.hpp" #include "wayfire/util/log.hpp" -std::string wf::config::option_base_t::get_name() const +const std::string& wf::config::option_base_t::get_name() const { return this->priv->name; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wf-config-0.10.0/src/types.cpp new/wf-config-0.11.0/src/types.cpp --- old/wf-config-0.10.0/src/types.cpp 2025-06-22 08:30:31.000000000 +0200 +++ new/wf-config-0.11.0/src/types.cpp 2026-07-20 21:40:27.000000000 +0200 @@ -1067,9 +1067,14 @@ } /* ------------------------- Output config types ---------------------------- */ -wf::output_config::mode_t::mode_t(bool auto_on) +wf::output_config::mode_t::mode_t(output_config::mode_type_t mode) { - this->type = auto_on ? MODE_AUTO : MODE_OFF; + if ((mode == MODE_RESOLUTION) || (mode == MODE_MIRROR)) + { + throw std::invalid_argument("Invalid mode definition"); + } + + this->type = mode; } wf::output_config::mode_t::mode_t(int32_t width, int32_t height, int32_t refresh) @@ -1132,6 +1137,8 @@ return mirror_from == other.mirror_from; case MODE_AUTO: + case MODE_HIGHRR: + case MODE_HIGHRES: case MODE_OFF: return true; } @@ -1145,12 +1152,22 @@ { if (string == "off") { - return wf::output_config::mode_t{false}; + return wf::output_config::mode_t{wf::output_config::mode_type_t::MODE_OFF}; } if ((string == "auto") || (string == "default")) { - return wf::output_config::mode_t{true}; + return wf::output_config::mode_t{wf::output_config::mode_type_t::MODE_AUTO}; + } + + if ((string == "highres")) + { + return wf::output_config::mode_t{wf::output_config::mode_type_t::MODE_HIGHRES}; + } + + if ((string == "highrr")) + { + return wf::output_config::mode_t{wf::output_config::mode_type_t::MODE_HIGHRR}; } if (string.substr(0, 6) == "mirror") @@ -1204,6 +1221,12 @@ case output_config::MODE_AUTO: return "auto"; + case output_config::MODE_HIGHRR: + return "highrr"; + + case output_config::MODE_HIGHRES: + return "highres"; + case output_config::MODE_OFF: return "off"; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wf-config-0.10.0/test/file_test.cpp new/wf-config-0.11.0/test/file_test.cpp --- old/wf-config-0.10.0/test/file_test.cpp 2025-06-22 08:30:31.000000000 +0200 +++ new/wf-config-0.11.0/test/file_test.cpp 2026-07-20 21:40:27.000000000 +0200 @@ -94,6 +94,10 @@ EXPECT_LINE(log, "Error in file test:5"); EXPECT_LINE(log, "Error in file test:20"); EXPECT_LINE(log, "Error in file test:21"); + + // reset logging state for subsequent tests + wf::log::initialize_logging(std::cout, wf::log::LOG_LEVEL_DEBUG, + wf::log::LOG_COLOR_MODE_OFF); } TEST_CASE("wf::config::load_configuration_options_from_string - " diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wf-config-0.10.0/test/types_test.cpp new/wf-config-0.11.0/test/types_test.cpp --- old/wf-config-0.10.0/test/types_test.cpp 2025-06-22 08:30:31.000000000 +0200 +++ new/wf-config-0.11.0/test/types_test.cpp 2026-07-20 21:40:27.000000000 +0200 @@ -416,9 +416,11 @@ using namespace wf::output_config; std::vector<mt> modes = { - mt{true}, - mt{true}, - mt{false}, + mt{MODE_HIGHRES}, + mt{MODE_HIGHRR}, + mt{MODE_AUTO}, + mt{MODE_AUTO}, + mt{MODE_OFF}, mt{1920, 1080, 0}, mt{1920, 1080, 59000}, mt{1920, 1080, 59000}, @@ -426,6 +428,8 @@ }; std::vector<mode_type_t> types = { + MODE_HIGHRES, + MODE_HIGHRR, MODE_AUTO, MODE_AUTO, MODE_OFF, @@ -436,6 +440,8 @@ }; std::vector<std::string> desc = { + "highres", + "highrr", "auto", "default", "off", @@ -452,10 +458,10 @@ "1920 1080", }; - CHECK(modes[3].get_refresh() == 0); - CHECK(modes[4].get_refresh() == 59000); - CHECK(modes[5].get_refresh() == 59000); - CHECK(modes[6].get_mirror_from() == "eDP-1"); + CHECK(modes[5].get_refresh() == 0); + CHECK(modes[6].get_refresh() == 59000); + CHECK(modes[7].get_refresh() == 59000); + CHECK(modes[8].get_mirror_from() == "eDP-1"); for (size_t i = 0; i < modes.size(); i++) { @@ -536,9 +542,26 @@ }; std::string sigmoid250ms_str = "250ms sigmoid"; + adt custom4s = { + .length_ms = 4000, + .easing = wf::animation::smoothing::get_cubic_bezier(0.25, 0.6, 0.75, 0.4), + .easing_name = "cubic-bezier 0.25 0.6 0.75 0.4", + }; + std::string custom4s_str = "4s cubic-bezier 0.25 0.6 0.75 0.4"; + + adt custom333ms = { + .length_ms = 333, + .easing = wf::animation::smoothing::get_cubic_bezier(0.16, 1, 0.3, 1), + .easing_name = "cubic-bezier 0.1600 1.0000 0.3000 1.0000", + }; + std::string custom333ms_str = "333ms cubic-bezier 0.16 1 0.3 1"; + CHECK(from_string<adt>(circle100_str) == circle100); CHECK(from_string<adt>(circle100_str_2) == circle100); CHECK(from_string<adt>(linear8s_str) == linear8s); CHECK(from_string<adt>(sigmoid250ms_str) == sigmoid250ms); + CHECK(from_string<adt>(custom4s_str) == custom4s); + CHECK(from_string<adt>(custom333ms_str) == custom333ms); + CHECK(from_string<adt>(to_string<adt>(custom333ms)) == from_string<adt>(custom333ms_str)); CHECK(to_string<adt>(sigmoid250ms) == sigmoid250ms_str); } ++++++ wf-config-0.10.0.tar.xz.sha256sum -> wf-config-0.11.0.tar.xz.sha256sum ++++++ --- /work/SRC/openSUSE:Factory/wf-config/wf-config-0.10.0.tar.xz.sha256sum 2025-09-04 18:01:43.388625562 +0200 +++ /work/SRC/openSUSE:Factory/.wf-config.new.1258/wf-config-0.11.0.tar.xz.sha256sum 2026-08-18 16:38:49.192948009 +0200 @@ -1 +1 @@ -9676f08248aaf83b91ecce5c953326c4341084b6efa00d3757a936617a51e487 *wf-config-0.10.0.tar.xz +b7721326ade8d42b25ecd2d572e5deb853b1327672608472854c6473bc8d2514 *wf-config-0.11.0.tar.xz
