Files
yabridge/src/common/configuration.cpp
T
Robbert van der Helm 5613248cda Gate the new resizing behavior behind new option
This interferes with resizing plugins using resize handles, and since it
only helps with two buggy plugins this seems like the best solution
here.
2021-08-16 22:39:13 +02:00

165 lines
7.0 KiB
C++

// yabridge: a Wine VST bridge
// Copyright (C) 2020-2021 Robbert van der Helm
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "configuration.h"
// tomlplusplus recently got some Windows fixes, but they cause compilation
// errors and we don't need them so we'll just disable them outright
#define TOML_WINDOWS_COMPAT 0
#include <fnmatch.h>
#include <toml++/toml.h>
#include <fstream>
#include "utils.h"
namespace fs = boost::filesystem;
Configuration::Configuration() noexcept {}
Configuration::Configuration(const fs::path& config_path,
const fs::path& yabridge_path)
: Configuration() {
// Will throw a `toml::parsing_error` if the file cannot be parsed. Better
// to throw here rather than failing silently since syntax errors would
// otherwise be impossible to spot. We'll also have to sort all tables by
// the location in the file since tomlplusplus internally uses ordered maps
// so otherwise we'll get the tables sorted by key instead.
toml::table table = toml::parse_file(config_path.string());
// I wasn't able to wade through the template soup to come up with a better
// way to sort this by location, so please feel free to correct this if you
// know of a better way! The source locations has to be stored inside of the
// vector itself because the `node.source()` on the copies stored in this
// vector won't contain the proper location after we've iterated through
// `table`.
std::vector<std::tuple<std::string, toml::source_region, toml::table>>
sorted_tables{};
for (auto [pattern, node] : table) {
if (const toml::table* config = node.as_table()) {
sorted_tables.push_back(
std::make_tuple(pattern, config->source(), *config));
}
}
std::sort(sorted_tables.begin(), sorted_tables.end(),
[](const auto& a, const auto& b) {
const auto& [a_pattern, a_source, a_table] = a;
const auto& [b_pattern, b_source, b_table] = b;
return a_source.begin.line < b_source.begin.line;
});
const fs::path relative_path =
yabridge_path.lexically_relative(config_path.parent_path());
for (const auto& [pattern, source, table] : sorted_tables) {
// First try to match the glob pattern, allow matching an entire
// directory for ease of use. If none of the patterns in the file match
// the plugin path then everything will be left at the defaults.
if (fnmatch(pattern.c_str(), relative_path.c_str(),
FNM_PATHNAME | FNM_LEADING_DIR) != 0) {
continue;
}
matched_file = config_path;
matched_pattern = pattern;
// If the table is missing some fields then they will simply be left at
// their defaults. At this point I'd really wish C++ could do pattern
// matching.
for (const auto& [key, value] : table) {
if (key == "group") {
if (const auto parsed_value = value.as_string()) {
group = parsed_value->get();
} else {
invalid_options.push_back(key);
}
} else if (key == "disable_pipes") {
// This option can be either enabled or disable with a boolean,
// or it can be set to an absolute path
if (const auto parsed_value = value.as_boolean()) {
if (*parsed_value) {
disable_pipes = get_temporary_directory() /
"yabridge-plugin-output.log";
} else {
disable_pipes = std::nullopt;
}
} else if (const auto parsed_value = value.as_string()) {
disable_pipes = parsed_value->get();
} else {
invalid_options.push_back(key);
}
} else if (key == "editor_coordinate_hack") {
if (const auto parsed_value = value.as_boolean()) {
editor_coordinate_hack = parsed_value->get();
} else {
invalid_options.push_back(key);
}
} else if (key == "editor_force_dnd") {
if (const auto parsed_value = value.as_boolean()) {
editor_force_dnd = parsed_value->get();
} else {
invalid_options.push_back(key);
}
} else if (key == "editor_xembed") {
if (const auto parsed_value = value.as_boolean()) {
editor_xembed = parsed_value->get();
} else {
invalid_options.push_back(key);
}
} else if (key == "frame_rate") {
if (const auto parsed_value = value.as_floating_point()) {
frame_rate = parsed_value->get();
} else if (const auto parsed_value = value.as_integer()) {
// For usability's sake we want to be a bit more lax than a
// normal TOML file would be and accept both floating point
// values and integers here
frame_rate = parsed_value->get();
} else {
invalid_options.push_back(key);
}
} else if (key == "hide_daw") {
if (const auto parsed_value = value.as_boolean()) {
hide_daw = parsed_value->get();
} else {
invalid_options.push_back(key);
}
} else if (key == "vst3_no_scaling") {
if (const auto parsed_value = value.as_boolean()) {
vst3_no_scaling = parsed_value->get();
} else {
invalid_options.push_back(key);
}
} else if (key == "vst3_prefer_32bit") {
if (const auto parsed_value = value.as_boolean()) {
vst3_prefer_32bit = parsed_value->get();
} else {
invalid_options.push_back(key);
}
} else {
unknown_options.push_back(key);
}
}
break;
}
}
std::chrono::steady_clock::duration Configuration::event_loop_interval()
const noexcept {
return std::chrono::duration_cast<std::chrono::steady_clock::duration>(
std::chrono::milliseconds(1000) / frame_rate.value_or(60.0));
}