summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorStanislaw Halik <sthalik@misaki.pl>2016-08-17 21:28:45 +0200
committerStanislaw Halik <sthalik@misaki.pl>2016-08-17 21:34:53 +0200
commitcb33be1c50b68d6022f344ddac923c7aac3a11d5 (patch)
tree1840e3f9829aa69f10f81804f655e97be3f2eca8
parent41ba8aa5329c16a797140dc23650ef45f42753a3 (diff)
move options framework into its own library
- adjust usages - add support for QList signals and metatype
-rwxr-xr-xCMakeLists.txt1
-rw-r--r--api/CMakeLists.txt2
-rw-r--r--api/plugin-support.hpp2
-rw-r--r--cmake/opentrack-boilerplate.cmake2
-rw-r--r--compat/options.cpp269
-rw-r--r--compat/options.hpp491
-rw-r--r--compat/singleton.cpp0
-rw-r--r--compat/singleton.hpp0
-rw-r--r--filter-accela/ftnoir_filter_accela.h2
-rw-r--r--filter-ewma2/ftnoir_filter_ewma2.h2
-rw-r--r--filter-kalman/kalman.h2
-rw-r--r--gui/main-window.cpp2
-rw-r--r--gui/main-window.hpp2
-rw-r--r--gui/main.cpp2
-rw-r--r--gui/new_file_dialog.h2
-rw-r--r--gui/process_detector.h2
-rw-r--r--logic/main-settings.hpp2
-rw-r--r--logic/mappings.hpp3
-rw-r--r--logic/shortcuts.h2
-rw-r--r--logic/state.hpp2
-rw-r--r--logic/tracker.h2
-rw-r--r--logic/tracklogger.hpp2
-rw-r--r--options/CMakeLists.txt4
-rw-r--r--options/bundle.cpp140
-rw-r--r--options/bundle.hpp84
-rw-r--r--options/defs.hpp5
-rw-r--r--options/export.hpp27
-rw-r--r--options/group.cpp114
-rw-r--r--options/group.hpp42
-rw-r--r--options/metatype.cpp23
-rw-r--r--options/metatype.hpp36
-rw-r--r--options/options.hpp16
-rw-r--r--options/scoped.cpp14
-rw-r--r--options/scoped.hpp19
-rw-r--r--options/slider.cpp (renamed from compat/slider.cpp)8
-rw-r--r--options/slider.hpp (renamed from compat/slider.hpp)29
-rw-r--r--options/tie.hpp212
-rw-r--r--options/value.hpp167
-rw-r--r--proto-fg/ftnoir_protocol_fg.h2
-rw-r--r--proto-fsuipc/ftnoir_protocol_fsuipc.h2
-rw-r--r--proto-ft/ftnoir_protocol_ft.h2
-rw-r--r--proto-ftn/ftnoir_protocol_ftn.h2
-rw-r--r--proto-mouse/ftnoir_protocol_mouse.h2
-rw-r--r--proto-sc/ftnoir_protocol_sc.h2
-rw-r--r--spline-widget/CMakeLists.txt2
-rw-r--r--tracker-aruco/ftnoir_tracker_aruco.h2
-rw-r--r--tracker-freepie-udp/ftnoir_tracker_freepie-udp.h2
-rw-r--r--tracker-hatire/ftnoir_tracker_hat_settings.h2
-rw-r--r--tracker-ht/ftnoir_tracker_ht.h2
-rw-r--r--tracker-hydra/ftnoir_tracker_hydra.h2
-rw-r--r--tracker-joystick/ftnoir_tracker_joystick.h2
-rw-r--r--tracker-pt/ftnoir_tracker_pt_settings.h2
-rw-r--r--tracker-rift-025/ftnoir_tracker_rift_025.h2
-rw-r--r--tracker-rift-042/ftnoir_tracker_rift_042.h2
-rw-r--r--tracker-rift-080/ftnoir_tracker_rift_080.h2
-rw-r--r--tracker-udp/ftnoir_tracker_udp.h2
56 files changed, 962 insertions, 810 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index bd42afa3..25220fe5 100755
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -19,6 +19,7 @@ file(GLOB opentrack-subprojects
"tracker-*/${C}"
"proto-*/${C}"
"filter-*/${C}"
+ "options/${C}"
"api/${C}"
"compat/${C}"
"logic/${C}"
diff --git a/api/CMakeLists.txt b/api/CMakeLists.txt
index 5151cbf7..0be2c242 100644
--- a/api/CMakeLists.txt
+++ b/api/CMakeLists.txt
@@ -1,3 +1,3 @@
opentrack_boilerplate(opentrack-api NO-COMPAT BIN)
-target_link_libraries(opentrack-api opentrack-compat)
+target_link_libraries(opentrack-api opentrack-options opentrack-compat)
target_include_directories(opentrack-api PUBLIC ${CMAKE_BINARY_DIR})
diff --git a/api/plugin-support.hpp b/api/plugin-support.hpp
index 072c8da7..8b20d211 100644
--- a/api/plugin-support.hpp
+++ b/api/plugin-support.hpp
@@ -8,7 +8,7 @@
#pragma once
#include "plugin-api.hpp"
-#include "compat/options.hpp"
+#include "options/options.hpp"
#include <QWidget>
#include <QDebug>
diff --git a/cmake/opentrack-boilerplate.cmake b/cmake/opentrack-boilerplate.cmake
index a731f3f0..41af076c 100644
--- a/cmake/opentrack-boilerplate.cmake
+++ b/cmake/opentrack-boilerplate.cmake
@@ -87,7 +87,7 @@ function(opentrack_boilerplate__ n files_ no-library_ static_ no-compat_ compile
opentrack_compat(${n})
endif()
if(NOT no-compat_)
- target_link_libraries(${n} opentrack-api opentrack-compat)
+ target_link_libraries(${n} opentrack-api opentrack-options opentrack-compat)
endif()
target_link_libraries(${n} ${MY_QT_LIBS})
if(CMAKE_COMPILER_IS_GNUCXX)
diff --git a/compat/options.cpp b/compat/options.cpp
deleted file mode 100644
index 33c1cf0d..00000000
--- a/compat/options.cpp
+++ /dev/null
@@ -1,269 +0,0 @@
-#include "options.hpp"
-
-namespace options
-{
-
-group::group(const QString& name) : name(name)
-{
- auto conf = ini_file();
- conf->beginGroup(name);
- for (auto& k_ : conf->childKeys())
- {
- auto tmp = k_.toUtf8();
- QString k(tmp);
- kvs[k] = conf->value(k_);
- }
- conf->endGroup();
-}
-
-void group::save() const
-{
- auto s = ini_file();
- s->beginGroup(name);
- for (auto& i : kvs)
- s->setValue(i.first, i.second);
- s->endGroup();
-}
-
-void group::put(const QString &s, const QVariant &d)
-{
- kvs[s] = d;
-}
-
-bool group::contains(const QString &s) const
-{
- return kvs.count(s) != 0;
-}
-
-QString group::ini_directory()
-{
- const auto dirs = QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation);
- if (dirs.size() == 0)
- return "";
- if (QDir(dirs[0]).mkpath(OPENTRACK_ORG))
- return dirs[0] + "/" OPENTRACK_ORG;
- return "";
-}
-
-QString group::ini_filename()
-{
- QSettings settings(OPENTRACK_ORG);
- return settings.value(OPENTRACK_CONFIG_FILENAME_KEY, OPENTRACK_DEFAULT_CONFIG).toString();
-}
-
-QString group::ini_pathname()
-{
- const auto dir = ini_directory();
- if (dir == "")
- return "";
- return dir + "/" + ini_filename();
-}
-
-const QStringList group::ini_list()
-{
- const auto dirname = ini_directory();
- if (dirname == "")
- return QStringList();
- QDir settings_dir(dirname);
- QStringList list = settings_dir.entryList( QStringList { "*.ini" } , QDir::Files, QDir::Name );
- std::sort(list.begin(), list.end());
- return list;
-}
-
-const mem<QSettings> group::ini_file()
-{
- const auto pathname = ini_pathname();
- if (pathname != "")
- return std::make_shared<QSettings>(ini_pathname(), QSettings::IniFormat);
- return std::make_shared<QSettings>();
-}
-
-bool group::operator==(const group& other) const
-{
- for (const auto& kv : kvs)
- {
- const QVariant val = other.get<QVariant>(kv.first);
- if (!other.contains(kv.first) || kv.second != val)
- {
- qDebug() << "bundle" << name << "modified" << "key" << kv.first << "-" << val << "<>" << kv.second;
- return false;
- }
- }
-
- for (const auto& kv : other.kvs)
- {
- const QVariant val = get<QVariant>(kv.first);
- if (!contains(kv.first) || kv.second != val)
- {
- qDebug() << "bundle" << name << "modified" << "key" << kv.first << "-" << kv.second << "<>" << val;
- return false;
- }
- }
- return true;
-}
-
-impl_bundle::impl_bundle(const QString& group_name)
- :
- mtx(QMutex::Recursive),
- group_name(group_name),
- saved(group_name),
- transient(saved)
-{
-}
-
-void impl_bundle::reload()
-{
- {
- QMutexLocker l(&mtx);
- saved = group(group_name);
- transient = saved;
- }
- emit reloading();
-}
-
-void impl_bundle::store_kv(const QString& name, const QVariant& datum)
-{
- QMutexLocker l(&mtx);
-
- transient.put(name, datum);
-}
-
-bool impl_bundle::contains(const QString &name) const
-{
- QMutexLocker l(const_cast<QMutex*>(&mtx));
- return transient.contains(name);
-}
-
-void impl_bundle::save()
-{
- bool modified_ = false;
-
- {
- QMutexLocker l(&mtx);
- if (saved != transient)
- {
- qDebug() << "bundle" << group_name << "changed, saving";
- modified_ = true;
- saved = transient;
- saved.save();
- }
- }
-
- if (modified_)
- emit saving();
-}
-
-bool impl_bundle::modifiedp() const // XXX unused
-{
- QMutexLocker l(const_cast<QMutex*>(&mtx));
- return transient != saved;
-}
-
-base_value::base_value(pbundle b, const QString &name) :
- b(b),
- self_name(name)
-{
-}
-
-opts::~opts()
-{
- b->reload();
-}
-
-opts::opts(const QString &name) : b(bundle(name))
-{
-}
-
-detail::custom_type_initializer::custom_type_initializer()
-{
- qRegisterMetaTypeStreamOperators<slider_value>("slider_value");
- QMetaType::registerDebugStreamOperator<slider_value>();
-}
-
-detail::custom_type_initializer detail::custom_type_initializer::singleton;
-
-namespace detail {
-
-opt_bundle::opt_bundle(const QString& group_name)
- : impl_bundle(group_name)
-{
-}
-
-opt_bundle::~opt_bundle()
-{
- detail::singleton().bundle_decf(group_name);
-}
-
-void opt_singleton::bundle_decf(const opt_singleton::k& key)
-{
- QMutexLocker l(&implsgl_mtx);
-
- if (--std::get<0>(implsgl_data[key]) == 0)
- {
- qDebug() << "bundle -" << key;
-
- implsgl_data.erase(key);
- }
-}
-
-void opt_singleton::after_profile_changed_()
-{
- QMutexLocker l(&implsgl_mtx);
-
- for (auto& kv : implsgl_data)
- {
- tt& tuple = kv.second;
- std::weak_ptr<v>& bundle = std::get<1>(tuple);
-
- mem<v> bundle_ = bundle.lock();
- if (bundle_)
- {
- qDebug() << "bundle: reverting" << kv.first << "due to profile change";
- bundle_->reload();
- }
- }
-}
-
-void opt_singleton::refresh_all_bundles()
-{
- singleton().after_profile_changed_();
-}
-
-opt_singleton::opt_singleton() : implsgl_mtx(QMutex::Recursive)
-{
-}
-
-opt_singleton::~opt_singleton()
-{
- qDebug() << "exit: bundle singleton";
-}
-
-pbundle opt_singleton::bundle(const opt_singleton::k &key)
-{
- QMutexLocker l(&implsgl_mtx);
-
- if (implsgl_data.count(key) != 0)
- {
- auto shared = std::get<1>(implsgl_data[key]).lock();
- if (shared != nullptr)
- return shared;
- }
-
- qDebug() << "bundle +" << key;
-
- auto shr = std::make_shared<v>(key);
- implsgl_data[key] = tt(1, shr);
- return shr;
-}
-
-OPENTRACK_COMPAT_EXPORT opt_singleton& singleton()
-{
- static opt_singleton ret;
- return ret;
-}
-
-
-} // end options::detail
-
-} // end options
-
diff --git a/compat/options.hpp b/compat/options.hpp
deleted file mode 100644
index 7b004ba6..00000000
--- a/compat/options.hpp
+++ /dev/null
@@ -1,491 +0,0 @@
-/* Copyright (c) 2013-2016 Stanislaw Halik
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- */
-
-#pragma once
-
-#include <memory>
-#include <tuple>
-#include <map>
-#include <cinttypes>
-#include <vector>
-#include <memory>
-
-#include <QObject>
-#include <QSettings>
-#include <QString>
-#include <QVariant>
-#include <QMutex>
-#include <QMutexLocker>
-#include <QWidget>
-#include <QComboBox>
-#include <QCheckBox>
-#include <QDoubleSpinBox>
-#include <QSpinBox>
-#include <QSlider>
-#include <QLineEdit>
-#include <QLabel>
-#include <QTabWidget>
-#include <QCoreApplication>
-#include <QFileInfo>
-#include <QDir>
-#include <QStandardPaths>
-#include <QApplication>
-
-#include <QMetaType>
-#include <QDataStream>
-
-#include <QDebug>
-
-#include "export.hpp"
-#include "slider.hpp"
-
-#include "util.hpp"
-
-#define OPENTRACK_CONFIG_FILENAME_KEY "settings-filename"
-#define OPENTRACK_DEFAULT_CONFIG "default.ini"
-#define OPENTRACK_ORG "opentrack-2.3"
-
-namespace options {
-namespace detail {
-class custom_type_initializer
-{
- custom_type_initializer();
- static custom_type_initializer singleton;
-};
-}
-
-template<typename k, typename v> using map = std::map<k, v>;
-
-// snapshot of qsettings group at given time
-class OPENTRACK_COMPAT_EXPORT group
-{
-private:
- map<QString, QVariant> kvs;
- QString name;
-public:
- group(const QString& name);
- void save() const;
- void put(const QString& s, const QVariant& d);
- bool contains(const QString& s) const;
- static QString ini_directory();
- static QString ini_filename();
- static QString ini_pathname();
- static const QStringList ini_list();
- static const mem<QSettings> ini_file();
- bool operator==(const group& other) const;
- bool operator!=(const group& other) const { return !(*this == other); }
-
- template<typename t>
- t get(const QString& k) const
- {
- auto value = kvs.find(k);
- if (value != kvs.cend())
- return value->second.value<t>();
- return t();
- }
-};
-
-class OPENTRACK_COMPAT_EXPORT impl_bundle : public QObject
-{
- Q_OBJECT
-protected:
- QMutex mtx;
- const QString group_name;
- group saved;
- group transient;
- impl_bundle(const impl_bundle&) = delete;
- impl_bundle& operator=(const impl_bundle&) = delete;
-signals:
- void reloading();
- void saving() const;
-public:
- impl_bundle(const QString& group_name);
- QString name() { return group_name; }
- void reload();
- void store_kv(const QString& name, const QVariant& datum);
- bool contains(const QString& name) const;
- void save();
- bool modifiedp() const;
-
- template<typename t>
- t get(const QString& name) const
- {
- QMutexLocker l(const_cast<QMutex*>(&mtx));
- return transient.get<t>(name);
- }
-};
-
-namespace detail
-{
-class OPENTRACK_COMPAT_EXPORT opt_bundle final : public impl_bundle
-{
-public:
- opt_bundle(const QString& group_name);
- ~opt_bundle();
-};
-
-struct OPENTRACK_COMPAT_EXPORT opt_singleton
-{
-public:
- using k = QString;
- using v = opt_bundle;
- using cnt = int;
- using pbundle = std::shared_ptr<v>;
- using tt = std::tuple<cnt, std::weak_ptr<v>>;
-private:
- QMutex implsgl_mtx;
- map<k, tt> implsgl_data;
- void after_profile_changed_();
-public:
- opt_singleton();
- ~opt_singleton();
- pbundle bundle(const k& key);
- void bundle_decf(const k& key);
- static void refresh_all_bundles();
-};
-
-OPENTRACK_COMPAT_EXPORT opt_singleton& singleton();
-}
-
-using pbundle = std::shared_ptr<detail::opt_bundle>;
-
-inline pbundle bundle(const QString& name)
-{
- return detail::singleton().bundle(name);
-}
-
-#define OPENTRACK_DEFINE_SLOT(t) void setValue(t datum) { store(datum); }
-#define OPENTRACK_DEFINE_SIGNAL(t) void valueChanged(t)
-
-namespace detail {
-template<typename t> struct value_type_traits { using type = t;};
-template<> struct value_type_traits<QString> { using type = const QString&; };
-template<> struct value_type_traits<slider_value> { using type = const slider_value&; };
-template<typename t> using value_type_t = typename value_type_traits<t>::type;
-}
-
-class OPENTRACK_COMPAT_EXPORT base_value : public QObject
-{
- Q_OBJECT
-public:
- QString name() const { return self_name; }
- base_value(pbundle b, const QString& name);
-signals:
- OPENTRACK_DEFINE_SIGNAL(double);
- OPENTRACK_DEFINE_SIGNAL(float);
- OPENTRACK_DEFINE_SIGNAL(int);
- OPENTRACK_DEFINE_SIGNAL(bool);
- OPENTRACK_DEFINE_SIGNAL(const QString&);
- OPENTRACK_DEFINE_SIGNAL(const slider_value&);
-protected:
- pbundle b;
- QString self_name;
-
- template<typename t>
- void store(const t& datum)
- {
- b->store_kv(self_name, QVariant::fromValue(datum));
- emit valueChanged(static_cast<detail::value_type_t<t>>(datum));
- }
- void store(float datum)
- {
- store(double(datum));
- }
-
-public slots:
- OPENTRACK_DEFINE_SLOT(double)
- OPENTRACK_DEFINE_SLOT(int)
- OPENTRACK_DEFINE_SLOT(bool)
- OPENTRACK_DEFINE_SLOT(const QString&)
- OPENTRACK_DEFINE_SLOT(const slider_value&)
- public slots:
- virtual void reload() = 0;
-};
-
-namespace detail {
-template<typename t>
-struct value_get_traits
-{
- static inline t get(const t& val, const t&)
- {
- return val;
- }
-};
-
-template<>
-struct value_get_traits<slider_value>
-{
- using t = slider_value;
- static inline t get(const t& val, const t& def)
- {
- return t(val.cur(), def.min(), def.max());
- }
-};
-}
-
-template<typename t_>
-class value : public base_value
-{
- template<typename t__, typename Enable = void>
- struct get_t
- { using t = t__; };
-
- // Qt uses int a lot in slots so use it for all enums
- template<typename t__>
- struct get_t<t__, typename std::enable_if<std::is_enum<t__>::value>::type>
- //{ using t = typename std::underlying_type<t__>::type; };
- { using t = int; };
-
- using t = t_;
-public:
- using underlying_t = typename get_t<t_>::t;
-
- t operator=(const t& datum)
- {
- store(static_cast<underlying_t>(datum));
- return datum;
- }
-
- static constexpr const Qt::ConnectionType DIRECT_CONNTYPE = Qt::AutoConnection;
- static constexpr const Qt::ConnectionType SAFE_CONNTYPE = Qt::QueuedConnection;
-
- value(pbundle b, const QString& name, t def) : base_value(b, name), def(def)
- {
- QObject::connect(b.get(), SIGNAL(reloading()),
- this, SLOT(reload()),
- DIRECT_CONNTYPE);
- if (!b->contains(name) || b->get<QVariant>(name).type() == QVariant::Invalid)
- *this = def;
- }
-
- value(pbundle b, const char* name, t def) : value(b, QString(name), def)
- {
- }
-
- t get() const
- {
- t val = b->contains(self_name)
- ? static_cast<t>(b->get<underlying_t>(self_name))
- : def;
- return detail::value_get_traits<t>::get(val, def);
- }
-
- operator t() const { return get(); }
-
- void reload() override
- {
- *this = static_cast<t>(*this);
- }
-
-private:
- t def;
-};
-
-struct OPENTRACK_COMPAT_EXPORT opts
-{
- pbundle b;
- opts(const QString& name);
- opts& operator=(const opts&) = delete;
- opts(const opts&) = delete;
- ~opts();
-};
-
-template<typename t, typename q>
-inline void tie_setting(value<t>&, q*);
-
-template<typename t>
-inline
-typename std::enable_if<std::is_enum<t>::value>::type
-tie_setting(value<t>& v, QComboBox* cb)
-{
- cb->setCurrentIndex(cb->findData((unsigned)static_cast<t>(v)));
- v = static_cast<t>(cb->currentData().toInt());
-
- // QObject::connect plays badly with std::bind of std::shared_ptr. Data seems to get freed.
- // Direct accesses of cb->currentData within arbitrary thread context cause crashes as well.
- // Hence we go for a verbose implementation.
-
- std::vector<int> enum_cases;
- enum_cases.reserve(unsigned(cb->count()));
-
- for (int i = 0; i < cb->count(); i++)
- enum_cases.push_back(cb->itemData(i).toInt());
-
- struct fn1
- {
- value<t>& v;
- QComboBox* cb;
- std::vector<int> enum_cases;
-
- fn1(value<t>& v, QComboBox* cb, const std::vector<int>& enum_cases) :
- v(v),
- cb(cb),
- enum_cases(enum_cases)
- {
- }
-
- void operator()(int idx)
- {
- if (idx < 0 || idx >= (int)enum_cases.size())
- v = static_cast<t>(-1);
- else
- v = static_cast<t>(t(std::intptr_t(enum_cases[idx])));
- }
- };
-
- struct fn2
- {
- value<t>& v;
- QComboBox* cb;
- std::vector<int> enum_cases;
-
- fn2(value<t>& v, QComboBox* cb, const std::vector<int>& enum_cases) :
- v(v),
- cb(cb),
- enum_cases(enum_cases)
- {
- }
-
- void operator()(int val)
- {
- for (unsigned i = 0; i < enum_cases.size(); i++)
- {
- if (val == enum_cases[i])
- {
- cb->setCurrentIndex(i);
- return;
- }
- }
- cb->setCurrentIndex(-1);
- }
- };
-
- base_value::connect(cb,
- static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
- &v,
- fn1(v, cb, enum_cases),
- v.DIRECT_CONNTYPE);
- base_value::connect(&v,
- static_cast<void (base_value::*)(int)>(&base_value::valueChanged),
- cb,
- fn2(v, cb, enum_cases),
- v.DIRECT_CONNTYPE);
-}
-
-template<>
-inline void tie_setting(value<int>& v, QComboBox* cb)
-{
- cb->setCurrentIndex(v);
- v = cb->currentIndex();
- base_value::connect(cb, SIGNAL(currentIndexChanged(int)), &v, SLOT(setValue(int)), v.DIRECT_CONNTYPE);
- base_value::connect(&v, SIGNAL(valueChanged(int)), cb, SLOT(setCurrentIndex(int)), v.SAFE_CONNTYPE);
-}
-
-template<>
-inline void tie_setting(value<QString>& v, QComboBox* cb)
-{
- cb->setCurrentText(v);
- v = cb->currentText();
- base_value::connect(cb, SIGNAL(currentTextChanged(QString)), &v, SLOT(setValue(QString)), v.DIRECT_CONNTYPE);
- base_value::connect(&v, SIGNAL(valueChanged(QString)), cb, SLOT(setCurrentText(QString)), v.SAFE_CONNTYPE);
-}
-
-template<>
-inline void tie_setting(value<bool>& v, QCheckBox* cb)
-{
- cb->setChecked(v);
- base_value::connect(cb, SIGNAL(toggled(bool)), &v, SLOT(setValue(bool)), v.DIRECT_CONNTYPE);
- base_value::connect(&v, SIGNAL(valueChanged(bool)), cb, SLOT(setChecked(bool)), v.SAFE_CONNTYPE);
-}
-
-template<>
-inline void tie_setting(value<double>& v, QDoubleSpinBox* dsb)
-{
- dsb->setValue(v);
- base_value::connect(dsb, SIGNAL(valueChanged(double)), &v, SLOT(setValue(double)), v.DIRECT_CONNTYPE);
- base_value::connect(&v, SIGNAL(valueChanged(double)), dsb, SLOT(setValue(double)), v.SAFE_CONNTYPE);
-}
-
-template<>
-inline void tie_setting(value<int>& v, QSpinBox* sb)
-{
- sb->setValue(v);
- base_value::connect(sb, SIGNAL(valueChanged(int)), &v, SLOT(setValue(int)), v.DIRECT_CONNTYPE);
- base_value::connect(&v, SIGNAL(valueChanged(int)), sb, SLOT(setValue(int)), v.SAFE_CONNTYPE);
-}
-
-template<>
-inline void tie_setting(value<int>& v, QSlider* sl)
-{
- sl->setValue(v);
- v = sl->value();
- base_value::connect(sl, SIGNAL(valueChanged(int)), &v, SLOT(setValue(int)), v.DIRECT_CONNTYPE);
- base_value::connect(&v, SIGNAL(valueChanged(int)), sl, SLOT(setValue(int)), v.SAFE_CONNTYPE);
-}
-
-template<>
-inline void tie_setting(value<QString>& v, QLineEdit* le)
-{
- le->setText(v);
- base_value::connect(le, SIGNAL(textChanged(QString)), &v, SLOT(setValue(QString)), v.DIRECT_CONNTYPE);
- base_value::connect(&v, SIGNAL(valueChanged(QString)),le, SLOT(setText(QString)), v.SAFE_CONNTYPE);
-}
-
-template<>
-inline void tie_setting(value<QString>& v, QLabel* lb)
-{
- lb->setText(v);
- base_value::connect(&v, SIGNAL(valueChanged(QString)), lb, SLOT(setText(QString)), v.DIRECT_CONNTYPE);
-}
-
-template<>
-inline void tie_setting(value<int>& v, QTabWidget* t)
-{
- t->setCurrentIndex(v);
- base_value::connect(t, SIGNAL(currentChanged(int)), &v, SLOT(setValue(int)), v.DIRECT_CONNTYPE);
- base_value::connect(&v, SIGNAL(valueChanged(int)), t, SLOT(setCurrentIndex(int)), v.SAFE_CONNTYPE);
-}
-
-template<>
-inline void tie_setting(value<slider_value>& v, QSlider* w)
-{
- // we can't get these at runtime since signals cross threads
- const int q_min = w->minimum();
- const int q_max = w->maximum();
- const int q_diff = q_max - q_min;
-
- slider_value sv(v);
-
- const double sv_max = sv.max();
- const double sv_min = sv.min();
- const double sv_c = sv_max - sv_min;
-
- w->setValue(int((sv.cur() - sv_min) / sv_c * q_diff + q_min));
- v = slider_value(q_diff <= 0 ? 0 : (w->value() - q_min) * sv_c / (double)q_diff + sv_min, sv_min, sv_max);
-
- base_value::connect(w,
- &QSlider::valueChanged,
- &v,
- [=, &v](int pos) -> void
- {
- if (q_diff <= 0 || pos <= 0)
- v = slider_value(sv_min, sv_min, sv_max);
- else
- v = slider_value((pos - q_min) * sv_c / (double)q_diff + sv_min, sv_min, sv_max);
- },
- v.DIRECT_CONNTYPE);
- base_value::connect(&v,
- static_cast<void(base_value::*)(double)>(&base_value::valueChanged),
- w,
- [=](double value) -> void
- {
- w->setValue(int(value * q_diff) + q_min);
- },
- v.SAFE_CONNTYPE);
-}
-}
-
diff --git a/compat/singleton.cpp b/compat/singleton.cpp
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/compat/singleton.cpp
diff --git a/compat/singleton.hpp b/compat/singleton.hpp
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/compat/singleton.hpp
diff --git a/filter-accela/ftnoir_filter_accela.h b/filter-accela/ftnoir_filter_accela.h
index 828b78a0..d3156429 100644
--- a/filter-accela/ftnoir_filter_accela.h
+++ b/filter-accela/ftnoir_filter_accela.h
@@ -12,7 +12,7 @@
#include <QMutex>
#include <QTimer>
-#include "compat/options.hpp"
+#include "options/options.hpp"
using namespace options;
#include "compat/timer.hpp"
diff --git a/filter-ewma2/ftnoir_filter_ewma2.h b/filter-ewma2/ftnoir_filter_ewma2.h
index e7fdd49e..8680971b 100644
--- a/filter-ewma2/ftnoir_filter_ewma2.h
+++ b/filter-ewma2/ftnoir_filter_ewma2.h
@@ -4,7 +4,7 @@
#include "ui_ftnoir_ewma_filtercontrols.h"
#include <QWidget>
#include <QMutex>
-#include "compat/options.hpp"
+#include "options/options.hpp"
#include "compat/timer.hpp"
using namespace options;
diff --git a/filter-kalman/kalman.h b/filter-kalman/kalman.h
index d28557c6..d2bfaee5 100644
--- a/filter-kalman/kalman.h
+++ b/filter-kalman/kalman.h
@@ -8,7 +8,7 @@
#include "ui_ftnoir_kalman_filtercontrols.h"
#include "api/plugin-api.hpp"
-#include "compat/options.hpp"
+#include "options/options.hpp"
using namespace options;
#include "compat/timer.hpp"
diff --git a/gui/main-window.cpp b/gui/main-window.cpp
index 2f856b11..9cad0ef4 100644
--- a/gui/main-window.cpp
+++ b/gui/main-window.cpp
@@ -8,7 +8,7 @@
#include "main-window.hpp"
#include "logic/tracker.h"
-#include "compat/options.hpp"
+#include "options/options.hpp"
#include "opentrack-library-path.h"
#include "new_file_dialog.h"
#include <QFile>
diff --git a/gui/main-window.hpp b/gui/main-window.hpp
index 42e8425c..a74575c9 100644
--- a/gui/main-window.hpp
+++ b/gui/main-window.hpp
@@ -17,7 +17,7 @@
#include "logic/shortcuts.h"
#include "logic/work.hpp"
#include "logic/state.hpp"
-#include "compat/options.hpp"
+#include "options/options.hpp"
#include <QMainWindow>
#include <QKeySequence>
diff --git a/gui/main.cpp b/gui/main.cpp
index 43fd4539..fd41cbd3 100644
--- a/gui/main.cpp
+++ b/gui/main.cpp
@@ -9,7 +9,7 @@
#endif
#include "main-window.hpp"
-#include "compat/options.hpp"
+#include "options/options.hpp"
#include "compat/win32-com.hpp"
using namespace options;
#include <QApplication>
diff --git a/gui/new_file_dialog.h b/gui/new_file_dialog.h
index 8cb971c3..6ba3ede3 100644
--- a/gui/new_file_dialog.h
+++ b/gui/new_file_dialog.h
@@ -1,7 +1,7 @@
#pragma once
#include "ui_new_config.h"
-#include "compat/options.hpp"
+#include "options/options.hpp"
#include <QFile>
#include <QRegExp>
#include <QString>
diff --git a/gui/process_detector.h b/gui/process_detector.h
index e081fa27..a659c178 100644
--- a/gui/process_detector.h
+++ b/gui/process_detector.h
@@ -13,7 +13,7 @@
#include <QTableWidget>
#include <QResizeEvent>
-#include "compat/options.hpp"
+#include "options/options.hpp"
using namespace options;
class FancyTable : public QTableWidget
diff --git a/logic/main-settings.hpp b/logic/main-settings.hpp
index 26313a26..d96a833b 100644
--- a/logic/main-settings.hpp
+++ b/logic/main-settings.hpp
@@ -9,7 +9,7 @@
#pragma once
#include <QString>
-#include "compat/options.hpp"
+#include "options/options.hpp"
#include "api/plugin-api.hpp"
using namespace options;
diff --git a/logic/mappings.hpp b/logic/mappings.hpp
index 0b9373c0..038b7560 100644
--- a/logic/mappings.hpp
+++ b/logic/mappings.hpp
@@ -7,8 +7,7 @@
#pragma once
-#include <QSettings>
-#include "compat/options.hpp"
+#include "options/options.hpp"
using namespace options;
#include "spline-widget/spline.hpp"
#include "main-settings.hpp"
diff --git a/logic/shortcuts.h b/logic/shortcuts.h
index c81b01f6..52b9979a 100644
--- a/logic/shortcuts.h
+++ b/logic/shortcuts.h
@@ -15,7 +15,7 @@
#include "export.hpp"
#include "qxt-mini/QxtGlobalShortcut"
-#include "compat/options.hpp"
+#include "options/options.hpp"
#include "main-settings.hpp"
#ifdef _WIN32
diff --git a/logic/state.hpp b/logic/state.hpp
index 4c3bb7a0..4e021949 100644
--- a/logic/state.hpp
+++ b/logic/state.hpp
@@ -8,7 +8,7 @@
#pragma once
-#include "compat/options.hpp"
+#include "options/options.hpp"
using namespace options;
#include "api/plugin-support.hpp"
#include "main-settings.hpp"
diff --git a/logic/tracker.h b/logic/tracker.h
index 15fb9701..d3c2660b 100644
--- a/logic/tracker.h
+++ b/logic/tracker.h
@@ -19,7 +19,7 @@
#include "spline-widget/spline.hpp"
#include "main-settings.hpp"
-#include "compat/options.hpp"
+#include "options/options.hpp"
#include "tracklogger.hpp"
#include <QMutex>
diff --git a/logic/tracklogger.hpp b/logic/tracklogger.hpp
index 52ab35bc..19e16fef 100644
--- a/logic/tracklogger.hpp
+++ b/logic/tracklogger.hpp
@@ -1,6 +1,6 @@
#pragma once
#include "main-settings.hpp"
-#include "compat/options.hpp"
+#include "options/options.hpp"
#include "compat/timer.hpp"
#include <fstream>
diff --git a/options/CMakeLists.txt b/options/CMakeLists.txt
new file mode 100644
index 00000000..25749bf5
--- /dev/null
+++ b/options/CMakeLists.txt
@@ -0,0 +1,4 @@
+opentrack_boilerplate(opentrack-options NO-COMPAT BIN)
+if(NOT WIN32 AND NOT APPLE)
+ target_link_libraries(opentrack-options rt)
+endif()
diff --git a/options/bundle.cpp b/options/bundle.cpp
new file mode 100644
index 00000000..a60810c5
--- /dev/null
+++ b/options/bundle.cpp
@@ -0,0 +1,140 @@
+#include "bundle.hpp"
+
+namespace options
+{
+
+namespace detail {
+
+bundle::bundle(const QString& group_name)
+ :
+ mtx(QMutex::Recursive),
+ group_name(group_name),
+ saved(group_name),
+ transient(saved)
+{
+}
+
+void bundle::reload()
+{
+ {
+ QMutexLocker l(&mtx);
+ saved = group(group_name);
+ transient = saved;
+ }
+ emit reloading();
+}
+
+void bundle::store_kv(const QString& name, const QVariant& datum)
+{
+ QMutexLocker l(&mtx);
+
+ transient.put(name, datum);
+}
+
+bool bundle::contains(const QString &name) const
+{
+ QMutexLocker l(const_cast<QMutex*>(&mtx));
+ return transient.contains(name);
+}
+
+void bundle::save()
+{
+ if (group_name == "")
+ return;
+
+ bool modified_ = false;
+
+ {
+ QMutexLocker l(&mtx);
+ if (saved != transient)
+ {
+ qDebug() << "bundle" << group_name << "changed, saving";
+ modified_ = true;
+ saved = transient;
+ saved.save();
+ }
+ }
+
+ if (modified_)
+ emit saving();
+}
+
+bool bundle::modifiedp() const // XXX unused
+{
+ QMutexLocker l(const_cast<QMutex*>(&mtx));
+ return transient != saved;
+}
+
+void bundler::bundle_decf(const bundler::k& key)
+{
+ QMutexLocker l(&implsgl_mtx);
+
+ if (--std::get<0>(implsgl_data[key]) == 0)
+ {
+ qDebug() << "bundle -" << key;
+
+ implsgl_data.erase(key);
+ }
+}
+
+void bundler::after_profile_changed_()
+{
+ QMutexLocker l(&implsgl_mtx);
+
+ for (auto& kv : implsgl_data)
+ {
+ tt& tuple = kv.second;
+ std::weak_ptr<v>& bundle = std::get<1>(tuple);
+
+ mem<v> bundle_ = bundle.lock();
+ if (bundle_)
+ {
+ qDebug() << "bundle: reverting" << kv.first << "due to profile change";
+ bundle_->reload();
+ }
+ }
+}
+
+void bundler::refresh_all_bundles()
+{
+ singleton().after_profile_changed_();
+}
+
+bundler::bundler() : implsgl_mtx(QMutex::Recursive)
+{
+}
+
+bundler::~bundler()
+{
+ qDebug() << "exit: bundle singleton";
+}
+
+std::shared_ptr<bundler::v> bundler::make_bundle(const bundler::k &key)
+{
+ QMutexLocker l(&implsgl_mtx);
+
+ if (implsgl_data.count(key) != 0)
+ {
+ auto shared = std::get<1>(implsgl_data[key]).lock();
+ if (shared != nullptr)
+ return shared;
+ }
+
+ qDebug() << "bundle +" << key;
+
+ std::shared_ptr<v> shr(new v(key), [this](v* val) { bundle_decf(val->name()); });
+
+ implsgl_data[key] = tt(1, shr);
+ return shr;
+}
+
+OPENTRACK_OPTIONS_EXPORT bundler& singleton()
+{
+ static bundler ret;
+ return ret;
+}
+
+} // end options::detail
+
+} // end options
+
diff --git a/options/bundle.hpp b/options/bundle.hpp
new file mode 100644
index 00000000..56d48f60
--- /dev/null
+++ b/options/bundle.hpp
@@ -0,0 +1,84 @@
+#pragma once
+
+#include <memory>
+#include <tuple>
+#include <map>
+#include <memory>
+
+#include <QObject>
+#include <QString>
+#include <QVariant>
+#include <QMutex>
+#include <QMutexLocker>
+
+#include <QDebug>
+
+#include "group.hpp"
+#include "compat/util.hpp"
+#include "export.hpp"
+
+namespace options {
+
+namespace detail {
+
+class OPENTRACK_OPTIONS_EXPORT bundle final : public QObject
+{
+ Q_OBJECT
+protected:
+ QMutex mtx;
+ const QString group_name;
+ group saved;
+ group transient;
+ bundle(const bundle&) = delete;
+ bundle& operator=(const bundle&) = delete;
+signals:
+ void reloading();
+ void saving() const;
+public:
+ bundle(const QString& group_name);
+ QString name() { return group_name; }
+ void reload();
+ void store_kv(const QString& name, const QVariant& datum);
+ bool contains(const QString& name) const;
+ void save();
+ bool modifiedp() const;
+
+ template<typename t>
+ t get(const QString& name) const
+ {
+ QMutexLocker l(const_cast<QMutex*>(&mtx));
+ return transient.get<t>(name);
+ }
+};
+
+struct OPENTRACK_OPTIONS_EXPORT bundler
+{
+public:
+ using k = QString;
+ using v = bundle;
+ using cnt = int;
+ using tt = std::tuple<cnt, std::weak_ptr<v>>;
+private:
+ QMutex implsgl_mtx;
+ std::map<k, tt> implsgl_data;
+ void after_profile_changed_();
+public:
+ bundler();
+ ~bundler();
+ std::shared_ptr<v> make_bundle(const k& key);
+ void bundle_decf(const k& key);
+ static void refresh_all_bundles();
+};
+
+OPENTRACK_OPTIONS_EXPORT bundler& singleton();
+}
+
+using bundle_type = detail::bundle;
+using bundle = std::shared_ptr<bundle_type>;
+
+inline bundle make_bundle(const QString& name)
+{
+ return detail::singleton().make_bundle(name);
+}
+
+}
diff --git a/options/defs.hpp b/options/defs.hpp
new file mode 100644
index 00000000..8464c784
--- /dev/null
+++ b/options/defs.hpp
@@ -0,0 +1,5 @@
+#pragma once
+
+#define OPENTRACK_CONFIG_FILENAME_KEY "settings-filename"
+#define OPENTRACK_DEFAULT_CONFIG "default.ini"
+#define OPENTRACK_ORG "opentrack-2.3"
diff --git a/options/export.hpp b/options/export.hpp
new file mode 100644
index 00000000..c430540f
--- /dev/null
+++ b/options/export.hpp
@@ -0,0 +1,27 @@
+#pragma once
+
+#ifdef BUILD_options
+# ifdef _WIN32
+# define OPENTRACK_OPTIONS_LINKAGE __declspec(dllexport)
+# else
+# define OPENTRACK_OPTIONS_LINKAGE
+# endif
+
+# ifndef _MSC_VER
+# define OPENTRACK_OPTIONS_EXPORT __attribute__ ((visibility ("default"))) OPENTRACK_OPTIONS_LINKAGE
+# else
+# define OPENTRACK_OPTIONS_EXPORT OPENTRACK_OPTIONS_LINKAGE
+# endif
+#else
+ #ifdef _WIN32
+ # define OPENTRACK_OPTIONS_LINKAGE __declspec(dllimport)
+ #else
+ # define OPENTRACK_OPTIONS_LINKAGE
+ #endif
+
+ #ifndef _MSC_VER
+ # define OPENTRACK_OPTIONS_EXPORT __attribute__ ((visibility ("default"))) OPENTRACK_OPTIONS_LINKAGE
+ #else
+ # define OPENTRACK_OPTIONS_EXPORT OPENTRACK_OPTIONS_LINKAGE
+ #endif
+#endif
diff --git a/options/group.cpp b/options/group.cpp
new file mode 100644
index 00000000..8aa381fc
--- /dev/null
+++ b/options/group.cpp
@@ -0,0 +1,114 @@
+#include "group.hpp"
+#include "defs.hpp"
+#include <QStandardPaths>
+#include <QDir>
+#include <QDebug>
+
+namespace options {
+
+group::group(const QString& name) : name(name)
+{
+ if (name == "")
+ return;
+
+ auto conf = ini_file();
+ conf->beginGroup(name);
+ for (auto& k_ : conf->childKeys())
+ {
+ auto tmp = k_.toUtf8();
+ QString k(tmp);
+ kvs[k] = conf->value(k_);
+ }
+ conf->endGroup();
+}
+
+void group::save() const
+{
+ if (name == "")
+ return;
+
+ auto s = ini_file();
+ s->beginGroup(name);
+ for (auto& i : kvs)
+ s->setValue(i.first, i.second);
+ s->endGroup();
+}
+
+void group::put(const QString &s, const QVariant &d)
+{
+ kvs[s] = d;
+}
+
+bool group::contains(const QString &s) const
+{
+ return kvs.count(s) != 0;
+}
+
+QString group::ini_directory()
+{
+ const auto dirs = QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation);
+ if (dirs.size() == 0)
+ return "";
+ if (QDir(dirs[0]).mkpath(OPENTRACK_ORG))
+ return dirs[0] + "/" OPENTRACK_ORG;
+ return "";
+}
+
+QString group::ini_filename()
+{
+ QSettings settings(OPENTRACK_ORG);
+ return settings.value(OPENTRACK_CONFIG_FILENAME_KEY, OPENTRACK_DEFAULT_CONFIG).toString();
+}
+
+QString group::ini_pathname()
+{
+ const auto dir = ini_directory();
+ if (dir == "")
+ return "";
+ return dir + "/" + ini_filename();
+}
+
+const QStringList group::ini_list()
+{
+ const auto dirname = ini_directory();
+ if (dirname == "")
+ return QStringList();
+ QDir settings_dir(dirname);
+ QStringList list = settings_dir.entryList( QStringList { "*.ini" } , QDir::Files, QDir::Name );
+ std::sort(list.begin(), list.end());
+ return list;
+}
+
+const std::shared_ptr<QSettings> group::ini_file()
+{
+ const auto pathname = ini_pathname();
+ if (pathname != "")
+ return std::make_shared<QSettings>(ini_pathname(), QSettings::IniFormat);
+ return std::make_shared<QSettings>();
+}
+
+bool group::operator==(const group& other) const
+{
+ for (const auto& kv : kvs)
+ {
+ const QVariant val = other.get<QVariant>(kv.first);
+ if (!other.contains(kv.first) || kv.second != val)
+ {
+ qDebug() << "bundle" << name << "modified" << "key" << kv.first << "-" << val << "<>" << kv.second;
+ return false;
+ }
+ }
+
+ for (const auto& kv : other.kvs)
+ {
+ const QVariant val = get<QVariant>(kv.first);
+ if (!contains(kv.first) || kv.second != val)
+ {
+ qDebug() << "bundle" << name << "modified" << "key" << kv.first << "-" << kv.second << "<>" << val;
+ return false;
+ }
+ }
+ return true;
+}
+
+}
diff --git a/options/group.hpp b/options/group.hpp
new file mode 100644
index 00000000..0deddfa8
--- /dev/null
+++ b/options/group.hpp
@@ -0,0 +1,42 @@
+#pragma once
+
+#include "export.hpp"
+#include <map>
+#include <memory>
+#include <QString>
+#include <QList>
+#include <QVariant>
+#include <QSettings>
+
+namespace options {
+
+// snapshot of qsettings group at given time
+class OPENTRACK_OPTIONS_EXPORT group final
+{
+private:
+ std::map<QString, QVariant> kvs;
+ QString name;
+public:
+ group(const QString& name);
+ void save() const;
+ void put(const QString& s, const QVariant& d);
+ bool contains(const QString& s) const;
+ static QString ini_directory();
+ static QString ini_filename();
+ static QString ini_pathname();
+ static const QStringList ini_list();
+ static const std::shared_ptr<QSettings> ini_file();
+ bool operator==(const group& other) const;
+ bool operator!=(const group& other) const { return !(*this == other); }
+
+ template<typename t>
+ t get(const QString& k) const
+ {
+ auto value = kvs.find(k);
+ if (value != kvs.cend())
+ return value->second.value<t>();
+ return t();
+ }
+};
+
+}
diff --git a/options/metatype.cpp b/options/metatype.cpp
new file mode 100644
index 00000000..8f92f385
--- /dev/null
+++ b/options/metatype.cpp
@@ -0,0 +1,23 @@
+#include "slider.hpp"
+#include "metatype.hpp"
+
+#define OPENTRACK_REGISTER_METATYPE(t) ::options::detail::custom_type_initializer::declare_for_type<t>(#t)
+
+namespace options {
+namespace detail {
+
+custom_type_initializer::custom_type_initializer()
+{
+ OPENTRACK_REGISTER_METATYPE(slider_value);
+ OPENTRACK_REGISTER_METATYPE(QList<double>);
+ OPENTRACK_REGISTER_METATYPE(QList<float>);
+ OPENTRACK_REGISTER_METATYPE(QList<int>);
+ OPENTRACK_REGISTER_METATYPE(QList<bool>);
+ OPENTRACK_REGISTER_METATYPE(QList<QString>);
+ OPENTRACK_REGISTER_METATYPE(QList<QPointF>);
+}
+
+const custom_type_initializer custom_type_initializer::singleton;
+
+}
+}
diff --git a/options/metatype.hpp b/options/metatype.hpp
new file mode 100644
index 00000000..68480b55
--- /dev/null
+++ b/options/metatype.hpp
@@ -0,0 +1,36 @@
+#pragma once
+
+#include <QMetaType>
+#include <QList>
+#include <QString>
+#include <QPointF>
+#include <QDataStream>
+#include <QDebug>
+
+#include "export.hpp"
+
+Q_DECLARE_METATYPE(QList<double>)
+Q_DECLARE_METATYPE(QList<float>)
+Q_DECLARE_METATYPE(QList<int>)
+Q_DECLARE_METATYPE(QList<bool>)
+Q_DECLARE_METATYPE(QList<QString>)
+Q_DECLARE_METATYPE(QList<QPointF>)
+
+namespace options {
+namespace detail {
+
+struct custom_type_initializer final
+{
+ static const custom_type_initializer singleton;
+
+ custom_type_initializer();
+
+ template<typename t> static inline void declare_for_type(const char* str)
+ {
+ qRegisterMetaType<t>(str);
+ qRegisterMetaTypeStreamOperators<t>();
+ }
+};
+
+}
+}
diff --git a/options/options.hpp b/options/options.hpp
new file mode 100644
index 00000000..ebc480f3
--- /dev/null
+++ b/options/options.hpp
@@ -0,0 +1,16 @@
+/* Copyright (c) 2013-2016 Stanislaw Halik
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ */
+#pragma once
+
+#include "defs.hpp"
+#include "bundle.hpp"
+#include "group.hpp"
+#include "slider.hpp"
+#include "value.hpp"
+#include "tie.hpp"
+#include "metatype.hpp"
+#include "scoped.hpp"
diff --git a/options/scoped.cpp b/options/scoped.cpp
new file mode 100644
index 00000000..e1ddbae2
--- /dev/null
+++ b/options/scoped.cpp
@@ -0,0 +1,14 @@
+#include "scoped.hpp"
+
+namespace options {
+
+opts::~opts()
+{
+ b->reload();
+}
+
+opts::opts(const QString &name) : b(make_bundle(name))
+{
+}
+
+}
diff --git a/options/scoped.hpp b/options/scoped.hpp
new file mode 100644
index 00000000..52ae383d
--- /dev/null
+++ b/options/scoped.hpp
@@ -0,0 +1,19 @@
+#pragma once
+
+#include "bundle.hpp"
+#include <QString>
+
+#include "export.hpp"
+
+namespace options {
+
+struct OPENTRACK_OPTIONS_EXPORT opts
+{
+ bundle b;
+ opts(const QString& name);
+ opts& operator=(const opts&) = delete;
+ opts(const opts&) = delete;
+ ~opts();
+};
+
+}
diff --git a/compat/slider.cpp b/options/slider.cpp
index be1aaeb8..8d46d51b 100644
--- a/compat/slider.cpp
+++ b/options/slider.cpp
@@ -3,7 +3,7 @@
namespace options {
-slider_value::slider_value(double cur, double min, double max) :
+slider_value::slider_value(float cur, float min, float max) :
cur_(cur),
min_(min),
max_(max)
@@ -20,7 +20,9 @@ slider_value::slider_value(const slider_value& v) : slider_value(v.cur(), v.min(
{
}
-slider_value::slider_value() : slider_value(0, 0, 0)
+
+
+slider_value::slider_value() : slider_value(0.f, 0.f, 0.f)
{
}
@@ -38,7 +40,7 @@ bool slider_value::operator==(const slider_value& v) const
{
using std::fabs;
- static constexpr double eps = 1e-3;
+ static constexpr float eps = 1e-3f;
return (fabs(v.cur_ - cur_) < eps &&
fabs(v.min_ - min_) < eps &&
diff --git a/compat/slider.hpp b/options/slider.hpp
index 7d54c650..1d02f17f 100644
--- a/compat/slider.hpp
+++ b/options/slider.hpp
@@ -14,19 +14,26 @@
namespace options
{
- class OPENTRACK_COMPAT_EXPORT slider_value final
+ class OPENTRACK_OPTIONS_EXPORT slider_value final
{
- double cur_, min_, max_;
+ float cur_, min_, max_;
public:
- slider_value(double cur, double min, double max);
+ slider_value(float cur, float min, float max);
+
+ template<typename t, typename u, typename v> slider_value(t cur, u min, v max) :
+ cur_(float(cur)),
+ min_(float(min)),
+ max_(float(max))
+ {}
+
slider_value(const slider_value& v);
slider_value();
slider_value& operator=(const slider_value& v);
bool operator==(const slider_value& v) const;
- operator double() const { return cur_; }
- double cur() const { return cur_; }
- double min() const { return min_; }
- double max() const { return max_; }
+ operator float() const { return cur_; }
+ double cur() const { return double(cur_); }
+ double min() const { return double(min_); }
+ double max() const { return double(max_); }
slider_value update_from_slider(int pos, int q_min, int q_max) const;
int to_slider_pos(int q_min, int q_max) const;
};
@@ -41,15 +48,15 @@ inline QDebug operator << (QDebug dbg, const options::slider_value& val)
inline QDataStream& operator << (QDataStream& out, const options::slider_value& v)
{
- out << v.cur()
- << v.min()
- << v.max();
+ out << float(v.cur())
+ << float(v.min())
+ << float(v.max());
return out;
}
inline QDataStream& operator >> (QDataStream& in, options::slider_value& v)
{
- double cur, min, max;
+ float cur, min, max;
in >> cur;
in >> min;
in >> max;
diff --git a/options/tie.hpp b/options/tie.hpp
new file mode 100644
index 00000000..2969f8d4
--- /dev/null
+++ b/options/tie.hpp
@@ -0,0 +1,212 @@
+#pragma once
+
+#include "value.hpp"
+
+#include <QComboBox>
+#include <QCheckBox>
+#include <QDoubleSpinBox>
+#include <QSpinBox>
+#include <QSlider>
+#include <QLineEdit>
+#include <QLabel>
+#include <QTabWidget>
+
+#include <cmath>
+
+namespace options {
+
+template<typename t, typename q>
+inline void tie_setting(value<t>&, q*);
+
+template<typename t>
+inline
+typename std::enable_if<std::is_enum<t>::value>::type
+tie_setting(value<t>& v, QComboBox* cb)
+{
+ cb->setCurrentIndex(cb->findData((unsigned)static_cast<t>(v)));
+ v = static_cast<t>(cb->currentData().toInt());
+
+ // QObject::connect plays badly with std::bind of std::shared_ptr. Data seems to get freed.
+ // Direct accesses of cb->currentData within arbitrary thread context cause crashes as well.
+ // Hence we go for a verbose implementation.
+
+ std::vector<int> enum_cases;
+ enum_cases.reserve(unsigned(cb->count()));
+
+ for (int i = 0; i < cb->count(); i++)
+ enum_cases.push_back(cb->itemData(i).toInt());
+
+ struct fn1
+ {
+ value<t>& v;
+ QComboBox* cb;
+ std::vector<int> enum_cases;
+
+ fn1(value<t>& v, QComboBox* cb, const std::vector<int>& enum_cases) :
+ v(v),
+ cb(cb),
+ enum_cases(enum_cases)
+ {
+ }
+
+ void operator()(int idx)
+ {
+ if (idx < 0 || idx >= (int)enum_cases.size())
+ v = static_cast<t>(-1);
+ else
+ v = static_cast<t>(t(std::intptr_t(enum_cases[idx])));
+ }
+ };
+
+ struct fn2
+ {
+ value<t>& v;
+ QComboBox* cb;
+ std::vector<int> enum_cases;
+
+ fn2(value<t>& v, QComboBox* cb, const std::vector<int>& enum_cases) :
+ v(v),
+ cb(cb),
+ enum_cases(enum_cases)
+ {
+ }
+
+ void operator()(int val)
+ {
+ for (unsigned i = 0; i < enum_cases.size(); i++)
+ {
+ if (val == enum_cases[i])
+ {
+ cb->setCurrentIndex(i);
+ return;
+ }
+ }
+ cb->setCurrentIndex(-1);
+ }
+ };
+
+ base_value::connect(cb,
+ static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
+ &v,
+ fn1(v, cb, enum_cases),
+ v.DIRECT_CONNTYPE);
+ base_value::connect(&v,
+ static_cast<void (base_value::*)(int)>(&base_value::valueChanged),
+ cb,
+ fn2(v, cb, enum_cases),
+ v.DIRECT_CONNTYPE);
+}
+
+template<>
+inline void tie_setting(value<int>& v, QComboBox* cb)
+{
+ cb->setCurrentIndex(v);
+ v = cb->currentIndex();
+ base_value::connect(cb, SIGNAL(currentIndexChanged(int)), &v, SLOT(setValue(int)), v.DIRECT_CONNTYPE);
+ base_value::connect(&v, SIGNAL(valueChanged(int)), cb, SLOT(setCurrentIndex(int)), v.SAFE_CONNTYPE);
+}
+
+template<>
+inline void tie_setting(value<QString>& v, QComboBox* cb)
+{
+ cb->setCurrentText(v);
+ v = cb->currentText();
+ base_value::connect(cb, SIGNAL(currentTextChanged(QString)), &v, SLOT(setValue(QString)), v.DIRECT_CONNTYPE);
+ base_value::connect(&v, SIGNAL(valueChanged(QString)), cb, SLOT(setCurrentText(QString)), v.SAFE_CONNTYPE);
+}
+
+template<>
+inline void tie_setting(value<bool>& v, QCheckBox* cb)
+{
+ cb->setChecked(v);
+ base_value::connect(cb, SIGNAL(toggled(bool)), &v, SLOT(setValue(bool)), v.DIRECT_CONNTYPE);
+ base_value::connect(&v, SIGNAL(valueChanged(bool)), cb, SLOT(setChecked(bool)), v.SAFE_CONNTYPE);
+}
+
+template<>
+inline void tie_setting(value<double>& v, QDoubleSpinBox* dsb)
+{
+ dsb->setValue(v);
+ base_value::connect(dsb, SIGNAL(valueChanged(double)), &v, SLOT(setValue(double)), v.DIRECT_CONNTYPE);
+ base_value::connect(&v, SIGNAL(valueChanged(double)), dsb, SLOT(setValue(double)), v.SAFE_CONNTYPE);
+}
+
+template<>
+inline void tie_setting(value<int>& v, QSpinBox* sb)
+{
+ sb->setValue(v);
+ base_value::connect(sb, SIGNAL(valueChanged(int)), &v, SLOT(setValue(int)), v.DIRECT_CONNTYPE);
+ base_value::connect(&v, SIGNAL(valueChanged(int)), sb, SLOT(setValue(int)), v.SAFE_CONNTYPE);
+}
+
+template<>
+inline void tie_setting(value<int>& v, QSlider* sl)
+{
+ sl->setValue(v);
+ v = sl->value();
+ base_value::connect(sl, SIGNAL(valueChanged(int)), &v, SLOT(setValue(int)), v.DIRECT_CONNTYPE);
+ base_value::connect(&v, SIGNAL(valueChanged(int)), sl, SLOT(setValue(int)), v.SAFE_CONNTYPE);
+}
+
+template<>
+inline void tie_setting(value<QString>& v, QLineEdit* le)
+{
+ le->setText(v);
+ base_value::connect(le, SIGNAL(textChanged(QString)), &v, SLOT(setValue(QString)), v.DIRECT_CONNTYPE);
+ base_value::connect(&v, SIGNAL(valueChanged(QString)),le, SLOT(setText(QString)), v.SAFE_CONNTYPE);
+}
+
+template<>
+inline void tie_setting(value<QString>& v, QLabel* lb)
+{
+ lb->setText(v);
+ base_value::connect(&v, SIGNAL(valueChanged(QString)), lb, SLOT(setText(QString)), v.DIRECT_CONNTYPE);
+}
+
+template<>
+inline void tie_setting(value<int>& v, QTabWidget* t)
+{
+ t->setCurrentIndex(v);
+ base_value::connect(t, SIGNAL(currentChanged(int)), &v, SLOT(setValue(int)), v.DIRECT_CONNTYPE);
+ base_value::connect(&v, SIGNAL(valueChanged(int)), t, SLOT(setCurrentIndex(int)), v.SAFE_CONNTYPE);
+}
+
+template<>
+inline void tie_setting(value<slider_value>& v, QSlider* w)
+{
+ // we can't get these at runtime since signals cross threads
+ const int q_min = w->minimum();
+ const int q_max = w->maximum();
+ const int q_diff = q_max - q_min;
+
+ slider_value sv(v);
+
+ const double sv_c = sv.max() - sv.min();
+
+ using std::round;
+
+ w->setValue(int((sv.cur() - sv.min()) / sv_c * q_diff + q_min));
+ v = slider_value(q_diff <= 0 ? 0 : (w->value() - q_min) * sv_c / (double)q_diff + sv.min(), sv.min(), sv.max());
+
+ base_value::connect(w,
+ &QSlider::valueChanged,
+ &v,
+ [=, &v](int pos) {
+ const int q_diff = q_max - q_min;
+ if (q_diff <= 0 || pos <= 0)
+ v = slider_value(sv.min(), sv.min(), sv.max());
+ else
+ {
+ const float c = float(sv.max()-sv.min());
+ v = slider_value((pos - q_min) * c / (float)q_diff + float(sv.min()), sv.min(), sv.max());
+ }
+ },
+ v.DIRECT_CONNTYPE);
+ base_value::connect(&v,
+ static_cast<void(base_value::*)(double)>(&base_value::valueChanged),
+ w,
+ [=](double value) { w->setValue(int(round(value * q_diff) + q_min)); },
+ v.SAFE_CONNTYPE);
+}
+
+}
diff --git a/options/value.hpp b/options/value.hpp
new file mode 100644
index 00000000..4d8d69f8
--- /dev/null
+++ b/options/value.hpp
@@ -0,0 +1,167 @@
+#pragma once
+
+#include "bundle.hpp"
+#include "slider.hpp"
+#include <type_traits>
+#include <QString>
+#include <QPointF>
+#include <QList>
+
+#define OPENTRACK_DEFINE_SLOT(t) void setValue(t datum) { store(datum); }
+#define OPENTRACK_DEFINE_SIGNAL(t) void valueChanged(t)
+namespace options {
+
+namespace detail {
+template<typename t> struct value_type_traits { using type = t;};
+template<> struct value_type_traits<QString> { using type = const QString&; };
+template<> struct value_type_traits<slider_value> { using type = const slider_value&; };
+template<typename u> struct value_type_traits<QList<u>>
+{
+ using type = const QList<typename std::remove_const<typename std::remove_reference<u>::type>::type>&;
+};
+template<typename t> using value_type_t = typename value_type_traits<t>::type;
+}
+
+class OPENTRACK_OPTIONS_EXPORT base_value : public QObject
+{
+ Q_OBJECT
+public:
+ QString name() const { return self_name; }
+ base_value(bundle b, const QString& name) : b(b), self_name(name) {}
+signals:
+ OPENTRACK_DEFINE_SIGNAL(double);
+ OPENTRACK_DEFINE_SIGNAL(float);
+ OPENTRACK_DEFINE_SIGNAL(int);
+ OPENTRACK_DEFINE_SIGNAL(bool);
+ OPENTRACK_DEFINE_SIGNAL(const QString&);
+ OPENTRACK_DEFINE_SIGNAL(const slider_value&);
+ OPENTRACK_DEFINE_SIGNAL(const QPointF&);
+
+ OPENTRACK_DEFINE_SIGNAL(const QList<double>&);
+ OPENTRACK_DEFINE_SIGNAL(const QList<float>&);
+ OPENTRACK_DEFINE_SIGNAL(const QList<int>&);
+ OPENTRACK_DEFINE_SIGNAL(const QList<bool>&);
+ OPENTRACK_DEFINE_SIGNAL(const QList<QString>&);
+ OPENTRACK_DEFINE_SIGNAL(const QList<slider_value>&);
+ OPENTRACK_DEFINE_SIGNAL(const QList<QPointF>&);
+protected:
+ bundle b;
+ QString self_name;
+
+ template<typename t>
+ void store(const t& datum)
+ {
+ b->store_kv(self_name, QVariant::fromValue(datum));
+ emit valueChanged(static_cast<detail::value_type_t<t>>(datum));
+ }
+
+ void store(float datum)
+ {
+ store(double(datum));
+ }
+
+public slots:
+ OPENTRACK_DEFINE_SLOT(double)
+ OPENTRACK_DEFINE_SLOT(int)
+ OPENTRACK_DEFINE_SLOT(bool)
+ OPENTRACK_DEFINE_SLOT(const QString&)
+ OPENTRACK_DEFINE_SLOT(const slider_value&)
+ OPENTRACK_DEFINE_SLOT(const QPointF&)
+
+ OPENTRACK_DEFINE_SLOT(const QList<double>&)
+ OPENTRACK_DEFINE_SLOT(const QList<float>&)
+ OPENTRACK_DEFINE_SLOT(const QList<int>&)
+ OPENTRACK_DEFINE_SLOT(const QList<bool>&)
+ OPENTRACK_DEFINE_SLOT(const QList<QString>&)
+ OPENTRACK_DEFINE_SLOT(const QList<slider_value>&)
+ OPENTRACK_DEFINE_SLOT(const QList<QPointF>&)
+ public slots:
+ virtual void reload() = 0;
+};
+
+namespace detail {
+template<typename t>
+struct value_get_traits
+{
+ static inline t get(const t& val, const t&)
+ {
+ return val;
+ }
+};
+
+template<>
+struct value_get_traits<slider_value>
+{
+ using t = slider_value;
+ static inline t get(const t& val, const t& def)
+ {
+ return t(val.cur(), def.min(), def.max());
+ }
+};
+
+template<typename t, typename Enable = void>
+struct value_element_type
+{
+ using type = typename std::remove_reference<typename std::remove_cv<t>::type>::type;
+};
+
+// Qt uses int a lot in slots so use it for all enums
+template<typename t>
+struct value_element_type<t, typename std::enable_if<std::is_enum<t>::value>::type>
+{
+ using type = int;
+};
+
+template<typename t> using value_element_type_t = typename value_element_type<t>::type;
+
+}
+
+template<typename t>
+class value final : public base_value
+{
+public:
+ using element_type = detail::value_element_type_t<t>;
+
+ t operator=(const t& datum)
+ {
+ store(static_cast<element_type>(datum));
+ return datum;
+ }
+
+ static constexpr const Qt::ConnectionType DIRECT_CONNTYPE = Qt::AutoConnection;
+ static constexpr const Qt::ConnectionType SAFE_CONNTYPE = Qt::QueuedConnection;
+
+ value(bundle b, const QString& name, t def) : base_value(b, name), def(def)
+ {
+ QObject::connect(b.get(), SIGNAL(reloading()),
+ this, SLOT(reload()),
+ DIRECT_CONNTYPE);
+ if (!b->contains(name) || b->get<QVariant>(name).type() == QVariant::Invalid)
+ *this = def;
+ }
+
+ value(bundle b, const char* name, t def) : value(b, QString(name), def)
+ {
+ }
+
+ t get() const
+ {
+ t val = b->contains(self_name)
+ ? static_cast<t>(b->get<element_type>(self_name))
+ : def;
+ return detail::value_get_traits<t>::get(val, def);
+ }
+
+ operator t() const { return get(); }
+
+ void reload() override
+ {
+ *this = static_cast<t>(*this);
+ }
+
+private:
+ t def;
+};
+
+
+}
diff --git a/proto-fg/ftnoir_protocol_fg.h b/proto-fg/ftnoir_protocol_fg.h
index 77cb73c8..3cacf1a5 100644
--- a/proto-fg/ftnoir_protocol_fg.h
+++ b/proto-fg/ftnoir_protocol_fg.h
@@ -15,7 +15,7 @@
#include <QUdpSocket>
#include <QMessageBox>
#include "api/plugin-api.hpp"
-#include "compat/options.hpp"
+#include "options/options.hpp"
using namespace options;
struct settings : opts {
diff --git a/proto-fsuipc/ftnoir_protocol_fsuipc.h b/proto-fsuipc/ftnoir_protocol_fsuipc.h
index 8207ac9f..d17527ae 100644
--- a/proto-fsuipc/ftnoir_protocol_fsuipc.h
+++ b/proto-fsuipc/ftnoir_protocol_fsuipc.h
@@ -24,7 +24,7 @@
#include <QDebug>
#include <QFile>
#include <QFileDialog>
-#include "compat/options.hpp"
+#include "options/options.hpp"
using namespace options;
#define FSUIPC_FILENAME "C:\\Program Files\\Microsoft Games\\Flight Simulator 9\\Modules\\FSUIPC.dll"
diff --git a/proto-ft/ftnoir_protocol_ft.h b/proto-ft/ftnoir_protocol_ft.h
index 246a73c4..546cb0e5 100644
--- a/proto-ft/ftnoir_protocol_ft.h
+++ b/proto-ft/ftnoir_protocol_ft.h
@@ -19,7 +19,7 @@
#include <QMutex>
#include <QMutexLocker>
#include "compat/shm.h"
-#include "compat/options.hpp"
+#include "options/options.hpp"
#include "freetrackclient/fttypes.h"
#include <memory>
#include "mutex.hpp"
diff --git a/proto-ftn/ftnoir_protocol_ftn.h b/proto-ftn/ftnoir_protocol_ftn.h
index 9e647980..74120b87 100644
--- a/proto-ftn/ftnoir_protocol_ftn.h
+++ b/proto-ftn/ftnoir_protocol_ftn.h
@@ -16,7 +16,7 @@
#include <QMessageBox>
#include <cmath>
#include "api/plugin-api.hpp"
-#include "compat/options.hpp"
+#include "options/options.hpp"
using namespace options;
struct settings : opts {
diff --git a/proto-mouse/ftnoir_protocol_mouse.h b/proto-mouse/ftnoir_protocol_mouse.h
index 0781366c..f2880822 100644
--- a/proto-mouse/ftnoir_protocol_mouse.h
+++ b/proto-mouse/ftnoir_protocol_mouse.h
@@ -10,7 +10,7 @@
#include "ui_ftnoir_mousecontrols.h"
#include <QDebug>
#include "api/plugin-api.hpp"
-#include "compat/options.hpp"
+#include "options/options.hpp"
using namespace options;
struct settings : opts {
diff --git a/proto-sc/ftnoir_protocol_sc.h b/proto-sc/ftnoir_protocol_sc.h
index 0da6c242..1b3f5eb8 100644
--- a/proto-sc/ftnoir_protocol_sc.h
+++ b/proto-sc/ftnoir_protocol_sc.h
@@ -20,7 +20,7 @@
#include <QProcess>
#include <QDebug>
#include <QFile>
-#include "compat/options.hpp"
+#include "options/options.hpp"
using namespace options;
#include <windows.h>
diff --git a/spline-widget/CMakeLists.txt b/spline-widget/CMakeLists.txt
index 9168fc45..8ef96cc6 100644
--- a/spline-widget/CMakeLists.txt
+++ b/spline-widget/CMakeLists.txt
@@ -1,2 +1,2 @@
opentrack_boilerplate(opentrack-spline-widget NO-COMPAT BIN)
-target_link_libraries(opentrack-spline-widget opentrack-compat)
+target_link_libraries(opentrack-spline-widget opentrack-options opentrack-compat)
diff --git a/tracker-aruco/ftnoir_tracker_aruco.h b/tracker-aruco/ftnoir_tracker_aruco.h
index aeabd438..1dd08cb1 100644
--- a/tracker-aruco/ftnoir_tracker_aruco.h
+++ b/tracker-aruco/ftnoir_tracker_aruco.h
@@ -26,7 +26,7 @@
#include <cinttypes>
-#include "compat/options.hpp"
+#include "options/options.hpp"
using namespace options;
struct settings : opts {
diff --git a/tracker-freepie-udp/ftnoir_tracker_freepie-udp.h b/tracker-freepie-udp/ftnoir_tracker_freepie-udp.h
index 05228006..2fd4baa0 100644
--- a/tracker-freepie-udp/ftnoir_tracker_freepie-udp.h
+++ b/tracker-freepie-udp/ftnoir_tracker_freepie-udp.h
@@ -10,7 +10,7 @@
#include <QThread>
#include "ui_freepie-udp-controls.h"
#include "api/plugin-api.hpp"
-#include "compat/options.hpp"
+#include "options/options.hpp"
using namespace options;
struct settings : opts {
diff --git a/tracker-hatire/ftnoir_tracker_hat_settings.h b/tracker-hatire/ftnoir_tracker_hat_settings.h
index fa24996e..7e9f7244 100644
--- a/tracker-hatire/ftnoir_tracker_hat_settings.h
+++ b/tracker-hatire/ftnoir_tracker_hat_settings.h
@@ -8,7 +8,7 @@
#pragma once
#include <QSerialPort>
-#include "compat/options.hpp"
+#include "options/options.hpp"
using namespace options;
diff --git a/tracker-ht/ftnoir_tracker_ht.h b/tracker-ht/ftnoir_tracker_ht.h
index 96fd258d..a29b3f20 100644
--- a/tracker-ht/ftnoir_tracker_ht.h
+++ b/tracker-ht/ftnoir_tracker_ht.h
@@ -12,7 +12,7 @@
#include "ht_video_widget.h"
#include "compat/shm.h"
#include <QObject>
-#include "compat/options.hpp"
+#include "options/options.hpp"
#include "api/plugin-api.hpp"
#include "api/opencv-camera-dialog.hpp"
diff --git a/tracker-hydra/ftnoir_tracker_hydra.h b/tracker-hydra/ftnoir_tracker_hydra.h
index d8fe6afb..efbcddd6 100644
--- a/tracker-hydra/ftnoir_tracker_hydra.h
+++ b/tracker-hydra/ftnoir_tracker_hydra.h
@@ -1,6 +1,6 @@
#include "ui_ftnoir_hydra_clientcontrols.h"
#include "api/plugin-api.hpp"
-#include "compat/options.hpp"
+#include "options/options.hpp"
using namespace options;
struct settings : opts {
diff --git a/tracker-joystick/ftnoir_tracker_joystick.h b/tracker-joystick/ftnoir_tracker_joystick.h
index 0cd26f36..0af1444e 100644
--- a/tracker-joystick/ftnoir_tracker_joystick.h
+++ b/tracker-joystick/ftnoir_tracker_joystick.h
@@ -18,7 +18,7 @@
#include "api/plugin-api.hpp"
#include "dinput/win32-joystick.hpp"
-#include "compat/options.hpp"
+#include "options/options.hpp"
using namespace options;
struct settings : opts {
diff --git a/tracker-pt/ftnoir_tracker_pt_settings.h b/tracker-pt/ftnoir_tracker_pt_settings.h
index c076082a..8c86fc14 100644
--- a/tracker-pt/ftnoir_tracker_pt_settings.h
+++ b/tracker-pt/ftnoir_tracker_pt_settings.h
@@ -28,7 +28,7 @@ struct pt_types
using mat22 = mat<2, 2>;
};
-#include "compat/options.hpp"
+#include "options/options.hpp"
using namespace options;
struct settings_pt : opts
diff --git a/tracker-rift-025/ftnoir_tracker_rift_025.h b/tracker-rift-025/ftnoir_tracker_rift_025.h
index 33f615ea..20b88a28 100644
--- a/tracker-rift-025/ftnoir_tracker_rift_025.h
+++ b/tracker-rift-025/ftnoir_tracker_rift_025.h
@@ -1,7 +1,7 @@
#pragma once
#include "ui_ftnoir_rift_clientcontrols_025.h"
#include "api/plugin-api.hpp"
-#include "compat/options.hpp"
+#include "options/options.hpp"
#include <OVR.h>
#include <cmath>
#include <memory>
diff --git a/tracker-rift-042/ftnoir_tracker_rift_042.h b/tracker-rift-042/ftnoir_tracker_rift_042.h
index 55a59f18..384df4f9 100644
--- a/tracker-rift-042/ftnoir_tracker_rift_042.h
+++ b/tracker-rift-042/ftnoir_tracker_rift_042.h
@@ -1,7 +1,7 @@
#pragma once
#include "ui_ftnoir_rift_clientcontrols_042.h"
#include "api/plugin-api.hpp"
-#include "compat/options.hpp"
+#include "options/options.hpp"
#include <OVR.h>
#include <QMessageBox>
#include <QWaitCondition>
diff --git a/tracker-rift-080/ftnoir_tracker_rift_080.h b/tracker-rift-080/ftnoir_tracker_rift_080.h
index 6adf6bcf..a7fa1c55 100644
--- a/tracker-rift-080/ftnoir_tracker_rift_080.h
+++ b/tracker-rift-080/ftnoir_tracker_rift_080.h
@@ -1,7 +1,7 @@
#pragma once
#include "ui_ftnoir_rift_clientcontrols_080.h"
#include "api/plugin-api.hpp"
-#include "compat/options.hpp"
+#include "options/options.hpp"
#include <OVR.h>
#include <cmath>
#include <QMessageBox>
diff --git a/tracker-udp/ftnoir_tracker_udp.h b/tracker-udp/ftnoir_tracker_udp.h
index 292df1f5..dfebd866 100644
--- a/tracker-udp/ftnoir_tracker_udp.h
+++ b/tracker-udp/ftnoir_tracker_udp.h
@@ -4,7 +4,7 @@
#include <QThread>
#include <cmath>
#include "api/plugin-api.hpp"
-#include "compat/options.hpp"
+#include "options/options.hpp"
using namespace options;
struct settings : opts {