diff --git a/include/miral/miral/configuration_data_source.h b/include/miral/miral/configuration_data_source.h new file mode 100644 index 00000000000..c5136296365 --- /dev/null +++ b/include/miral/miral/configuration_data_source.h @@ -0,0 +1,46 @@ +/* + * Copyright © Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 2 or 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#ifndef MIRAL_CONFIGURATION_SOURCE_H +#define MIRAL_CONFIGURATION_SOURCE_H + +#include +#include +#include +#include + +namespace mir { class Server; } + +namespace miral +{ + +/// Used to define another source of configuration data. By default, configuration +/// options are parsed from command line parameters and the Mir configuration file. +/// Using this class, compositor authors can provide another way to set configuration +/// options. For example, one may parse a JSON file to the map and define configuration +/// variable in that way. +class ConfigurationDataSource +{ +public: + explicit ConfigurationDataSource(std::function()> const&); + void operator()(mir::Server& server) const; + + struct Self; + std::shared_ptr self; +}; +} + +#endif //MIRAL_CONFIGURATION_SOURCE_H diff --git a/include/platform/mir/options/default_configuration.h b/include/platform/mir/options/default_configuration.h index cb3c9d3ace5..c7cfe2896cb 100644 --- a/include/platform/mir/options/default_configuration.h +++ b/include/platform/mir/options/default_configuration.h @@ -45,6 +45,8 @@ class DefaultConfiguration : public Configuration // before the first invocation of the_options() - typically during initialization. boost::program_options::options_description_easy_init add_options(); + void set_options_map(std::map const& options); + private: // MUST be the first member to ensure it's destroyed last, lest we attempt to // call destructors in DSOs we've unloaded. @@ -52,6 +54,8 @@ class DefaultConfiguration : public Configuration std::string const config_file; + std::optional> options_map; + void add_platform_options(); // accessed via the base interface, when access to add_options() has been "lost" std::shared_ptr the_options() const override; @@ -70,6 +74,10 @@ class DefaultConfiguration : public Configuration boost::program_options::options_description& desc, ProgramOption& options) const; + virtual void parse_custom( + boost::program_options::options_description& desc, + ProgramOption& options) const; + int const argc; char const** const argv; std::function const unparsed_arguments_handler; diff --git a/include/platform/mir/options/program_option.h b/include/platform/mir/options/program_option.h index dbfe05d0450..73ee51b800f 100644 --- a/include/platform/mir/options/program_option.h +++ b/include/platform/mir/options/program_option.h @@ -45,6 +45,10 @@ class ProgramOption : public Option boost::program_options::options_description const& description, std::string const& filename); + void parse_map( + boost::program_options::options_description const& description, + std::map const& map); + bool is_set(char const* name) const override; bool get(char const* name, bool default_) const override; std::string get(char const*, char const* default_) const override; diff --git a/src/include/server/mir/server.h b/src/include/server/mir/server.h index af588ea65c2..0b2835fcf21 100644 --- a/src/include/server/mir/server.h +++ b/src/include/server/mir/server.h @@ -24,6 +24,7 @@ #include #include #include +#include struct wl_display; @@ -186,6 +187,10 @@ class Server /// 2. $XDG_CONFIG_DIRS (if set, otherwise /etc/xdg) void set_config_filename(std::string const& config_file); + /// Set an alternative source for configuration data. + void set_configuration_data_source( + std::function()> const&); + /// Returns the configuration options. /// This will be null before initialization starts. It will be available /// when the init_callback has been invoked (and thereafter until the server exits). diff --git a/src/miral/CMakeLists.txt b/src/miral/CMakeLists.txt index e9e27d22a2c..664f268e9d8 100644 --- a/src/miral/CMakeLists.txt +++ b/src/miral/CMakeLists.txt @@ -55,6 +55,7 @@ add_library(miral-external OBJECT application_authorizer.cpp ${miral_include}/miral/application_authorizer.h application_info.cpp ${miral_include}/miral/application_info.h canonical_window_manager.cpp ${miral_include}/miral/canonical_window_manager.h + configuration_data_source.cpp ${miral_include}/miral/configuration_data_source.h configuration_option.cpp ${miral_include}/miral/configuration_option.h ${miral_include}/miral/command_line_option.h cursor_theme.cpp ${miral_include}/miral/cursor_theme.h diff --git a/src/miral/configuration_data_source.cpp b/src/miral/configuration_data_source.cpp new file mode 100644 index 00000000000..d628b2d74e7 --- /dev/null +++ b/src/miral/configuration_data_source.cpp @@ -0,0 +1,34 @@ +/* + * Copyright © Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 2 or 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include "miral/configuration_data_source.h" +#include + +struct miral::ConfigurationDataSource::Self +{ + std::function()> get_map; +}; + +miral::ConfigurationDataSource::ConfigurationDataSource( + std::function()> const& get_map) + : self{std::make_shared(get_map)} +{ +} + +void miral::ConfigurationDataSource::operator()(mir::Server& server) const +{ + server.set_configuration_data_source(self->get_map); +} \ No newline at end of file diff --git a/src/miral/symbols.map b/src/miral/symbols.map index 79b4961cfd4..6ec0394d80e 100644 --- a/src/miral/symbols.map +++ b/src/miral/symbols.map @@ -456,6 +456,8 @@ local: *; MIRAL_5.1 { global: extern "C++" { + miral::ConfigurationDataSource::ConfigurationDataSource*; + miral::ConfigurationDataSource::operator*; miral::Decorations::Decorations*; miral::Decorations::always_csd*; miral::Decorations::always_ssd*; @@ -469,7 +471,9 @@ global: miral::IdleListener::on_wake*; miral::IdleListener::operator*; miral::WindowManagerTools::move_cursor_to*; - typeinfo?for?miral::IdleListener; + typeinfo?for?miral::ConfigurationDataSource; typeinfo?for?miral::Decorations; + typeinfo?for?miral::IdleListener; }; } MIRAL_5.0; + diff --git a/src/platform/options/default_configuration.cpp b/src/platform/options/default_configuration.cpp index b2412781c9d..3f8d32fbd72 100644 --- a/src/platform/options/default_configuration.cpp +++ b/src/platform/options/default_configuration.cpp @@ -269,6 +269,11 @@ boost::program_options::options_description_easy_init mo::DefaultConfiguration:: return program_options->add_options(); } +void mo::DefaultConfiguration::set_options_map(std::map const& options) +{ + options_map = options; +} + std::shared_ptr mo::DefaultConfiguration::the_options() const { if (!options) @@ -277,6 +282,7 @@ std::shared_ptr mo::DefaultConfiguration::the_options() const parse_arguments(*program_options, *options, argc, argv); parse_environment(*program_options, *options); parse_config_file(*program_options, *options); + parse_custom(*program_options, *options); this->options = options; } return options; @@ -343,3 +349,11 @@ void mo::DefaultConfiguration::parse_config_file( if (!config_file.empty()) options.parse_file(desc, config_file); } + +void mo::DefaultConfiguration::parse_custom( + boost::program_options::options_description& desc, + mir::options::ProgramOption& options) const +{ + if (options_map) + options.parse_map(desc, options_map.value()); +} \ No newline at end of file diff --git a/src/platform/options/program_option.cpp b/src/platform/options/program_option.cpp index 1cf88974083..41da01c4924 100644 --- a/src/platform/options/program_option.cpp +++ b/src/platform/options/program_option.cpp @@ -119,6 +119,23 @@ void mo::ProgramOption::parse_file( po::notify(options); } +void mo::ProgramOption::parse_map( + po::options_description const& desc, + std::map const& map) +{ + boost::program_options::parsed_options opts(&desc); + for (auto const& [key, val] : map) + { + boost::program_options::basic_option opt; + opt.string_key = key; + opt.value.push_back(val); + opts.options.push_back(opt); + } + + po::store(opts, options); + po::notify(options); +} + bool mo::ProgramOption::is_set(char const* name) const { return options.count(parse_name(name)); diff --git a/src/platform/symbols.map b/src/platform/symbols.map index 80961be7627..59ba505d161 100644 --- a/src/platform/symbols.map +++ b/src/platform/symbols.map @@ -202,3 +202,10 @@ MIR_PLATFORM_2.17 { local: *; }; +MIR_PLATFORM_2.18 { + global: + extern "C++" { + mir::options::DefaultConfiguration::set_options_map*; + }; + local: *; +} MIR_PLATFORM_2.17; \ No newline at end of file diff --git a/src/server/server.cpp b/src/server/server.cpp index 3126ca86b9b..b73df6e6979 100644 --- a/src/server/server.cpp +++ b/src/server/server.cpp @@ -162,6 +162,7 @@ struct mir::Server::Self std::make_shared()}; std::function command_line_hander{}; + std::function()> get_options_map; /// set a callback to introduce additional configuration options. /// this will be invoked by run() before server initialisation starts @@ -244,7 +245,8 @@ std::shared_ptr configuration_options( int argc, char const** argv, std::function const& command_line_hander, - std::string const& config_file) + std::string const& config_file, + std::function()> const& get_options_map) { std::shared_ptr result; @@ -253,6 +255,9 @@ std::shared_ptr configuration_options( else result = std::make_shared(argc, argv, config_file); + if (get_options_map) + result->set_options_map(get_options_map()); + return result; } @@ -342,6 +347,14 @@ void mir::Server::set_config_filename(std::string const& config_file) self->config_file = config_file; } +/// Set an alternative source for configuration data. +void mir::Server::set_configuration_data_source( + std::function()> const& get_map) +{ + verify_setting_allowed(self->server_config); + self->get_options_map = get_map; +} + auto mir::Server::get_options() const -> std::shared_ptr { verify_accessing_allowed(self->server_config); @@ -378,7 +391,8 @@ void mir::Server::apply_settings() { if (self->server_config) return; - auto const options = configuration_options(self->argc, self->argv, self->command_line_hander, self->config_file); + auto const options = configuration_options( + self->argc, self->argv, self->command_line_hander, self->config_file, self->get_options_map); self->add_configuration_options(*options); auto const config = std::make_shared(options, self); diff --git a/src/server/symbols.map b/src/server/symbols.map index 5b6e9aaf91a..b8642934036 100644 --- a/src/server/symbols.map +++ b/src/server/symbols.map @@ -1344,12 +1344,13 @@ local: *; MIR_SERVER_INTERNAL_2.18 { global: extern "C++" { - mir::DefaultServerConfiguration::the_led_observer_registrar*; mir::DecorationStrategy::?DecorationStrategy*; mir::DecorationStrategy::DecorationStrategy*; mir::DecorationStrategy::operator*; mir::DefaultServerConfiguration::set_the_decoration_strategy*; mir::DefaultServerConfiguration::the_decoration_strategy*; + mir::DefaultServerConfiguration::the_led_observer_registrar*; + mir::Server::set_configuration_data_source*; mir::Server::set_the_decoration_strategy*; mir::Server::the_decoration_strategy*; mir::Server::the_idle_handler*; @@ -1369,6 +1370,9 @@ global: mir::input::receiver::XKBMapperRegistrar::xkb_modifiers*; mir::shell::IdleHandlerObserver::?IdleHandlerObserver*; mir::shell::IdleHandlerObserver::IdleHandlerObserver*; + non-virtual?thunk?to?mir::DecorationStrategy::?DecorationStrategy*; + non-virtual?thunk?to?mir::DefaultServerConfiguration::set_the_decoration_strategy*; + non-virtual?thunk?to?mir::DefaultServerConfiguration::the_decoration_strategy*; non-virtual?thunk?to?mir::DefaultServerConfiguration::the_led_observer_registrar*; non-virtual?thunk?to?mir::input::receiver::XKBMapperRegistrar::clear_all_keymaps*; non-virtual?thunk?to?mir::input::receiver::XKBMapperRegistrar::clear_keymap_for_device*; @@ -1382,19 +1386,16 @@ global: non-virtual?thunk?to?mir::input::receiver::XKBMapperRegistrar::unregister_interest*; non-virtual?thunk?to?mir::input::receiver::XKBMapperRegistrar::xkb_modifiers*; non-virtual?thunk?to?mir::shell::IdleHandlerObserver::?IdleHandlerObserver*; - typeinfo?for?mir::input::receiver::XKBMapperRegistrar; - typeinfo?for?mir::shell::IdleHandlerObserver; - vtable?for?mir::input::receiver::XKBMapperRegistrar; - vtable?for?mir::shell::IdleHandlerObserver; - non-virtual?thunk?to?mir::DecorationStrategy::?DecorationStrategy*; - non-virtual?thunk?to?mir::DefaultServerConfiguration::set_the_decoration_strategy*; - non-virtual?thunk?to?mir::DefaultServerConfiguration::the_decoration_strategy*; non-virtual?thunk?to?mir::shell::IdleHandlerObserver::?IdleHandlerObserver*; typeinfo?for?mir::DecorationStrategy; + typeinfo?for?mir::input::receiver::XKBMapperRegistrar; + typeinfo?for?mir::shell::IdleHandlerObserver; typeinfo?for?mir::shell::IdleHandlerObserver; virtual?thunk?to?mir::DefaultServerConfiguration::set_the_decoration_strategy*; virtual?thunk?to?mir::DefaultServerConfiguration::the_decoration_strategy*; vtable?for?mir::DecorationStrategy; + vtable?for?mir::input::receiver::XKBMapperRegistrar; + vtable?for?mir::shell::IdleHandlerObserver; vtable?for?mir::shell::IdleHandlerObserver; }; } MIR_SERVER_INTERNAL_2.17; diff --git a/tools/symbols_map_generator/main.py b/tools/symbols_map_generator/main.py index d0b5376df91..3047925d0a7 100755 --- a/tools/symbols_map_generator/main.py +++ b/tools/symbols_map_generator/main.py @@ -41,8 +41,19 @@ class LibraryInfo(TypedDict): header_directories: list[HeaderDirectory] map_file: str +def get_version_from_library_version_str(version: str) -> str: + """ + Given a string like MIR_SERVER_INTERNAL_5.0.0, returns the version + string (e.g. 5.0.0) + """ + s = version.split("_") + return s[len(s) - 1] + def get_major_version_from_str(version: str) -> int: + """ + Given a string like 5.0.0, returns the major version (e.g. 5). + """ return int(version.split('.')[0]) @@ -482,7 +493,7 @@ def main(): # Remake the stanzas for the previous symbols for symbol in previous_symbols: - major = get_major_version_from_str(symbol.version) + major = get_major_version_from_str(get_version_from_library_version_str(symbol.version)) # If we are going up by a major version, then we should add # all existing symbols to the new stanza