diff options
Diffstat (limited to 'api')
| -rw-r--r-- | api/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | api/lang/de_DE.ts | 11 | ||||
| -rw-r--r-- | api/lang/nl_NL.ts | 7 | ||||
| -rw-r--r-- | api/lang/ru_RU.ts | 7 | ||||
| -rw-r--r-- | api/lang/stub.ts | 7 | ||||
| -rw-r--r-- | api/lang/zh_CN.ts | 11 | ||||
| -rw-r--r-- | api/plugin-api.cpp | 89 | ||||
| -rw-r--r-- | api/plugin-api.hpp | 142 | ||||
| -rw-r--r-- | api/plugin-support.hpp | 332 | ||||
| -rw-r--r-- | api/variance.hpp | 57 |
10 files changed, 388 insertions, 278 deletions
diff --git a/api/CMakeLists.txt b/api/CMakeLists.txt index a181abb9..1454b89e 100644 --- a/api/CMakeLists.txt +++ b/api/CMakeLists.txt @@ -1,3 +1,2 @@ otr_module(api NO-COMPAT BIN) -target_link_libraries(opentrack-api opentrack-options opentrack-compat) -target_include_directories(opentrack-api PUBLIC ${CMAKE_BINARY_DIR}) + diff --git a/api/lang/de_DE.ts b/api/lang/de_DE.ts new file mode 100644 index 00000000..688fe72b --- /dev/null +++ b/api/lang/de_DE.ts @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="de_DE"> +<context> + <name>module_status_mixin</name> + <message> + <source>Unknown error</source> + <translation>Unbekannter Fehler</translation> + </message> +</context> +</TS> diff --git a/api/lang/nl_NL.ts b/api/lang/nl_NL.ts index 9e739505..91ba449c 100644 --- a/api/lang/nl_NL.ts +++ b/api/lang/nl_NL.ts @@ -1,4 +1,11 @@ <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> <TS version="2.1" language="nl_NL"> +<context> + <name>module_status_mixin</name> + <message> + <source>Unknown error</source> + <translation type="unfinished"></translation> + </message> +</context> </TS> diff --git a/api/lang/ru_RU.ts b/api/lang/ru_RU.ts index f62cf2e1..efaaaa47 100644 --- a/api/lang/ru_RU.ts +++ b/api/lang/ru_RU.ts @@ -1,4 +1,11 @@ <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> <TS version="2.1" language="ru_RU"> +<context> + <name>module_status_mixin</name> + <message> + <source>Unknown error</source> + <translation type="unfinished"></translation> + </message> +</context> </TS> diff --git a/api/lang/stub.ts b/api/lang/stub.ts index 6401616d..d29cce1b 100644 --- a/api/lang/stub.ts +++ b/api/lang/stub.ts @@ -1,4 +1,11 @@ <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> <TS version="2.1"> +<context> + <name>module_status_mixin</name> + <message> + <source>Unknown error</source> + <translation type="unfinished"></translation> + </message> +</context> </TS> diff --git a/api/lang/zh_CN.ts b/api/lang/zh_CN.ts new file mode 100644 index 00000000..26bf67a5 --- /dev/null +++ b/api/lang/zh_CN.ts @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="zh_CN"> +<context> + <name>module_status_mixin</name> + <message> + <source>Unknown error</source> + <translation type="unfinished"></translation> + </message> +</context> +</TS> diff --git a/api/plugin-api.cpp b/api/plugin-api.cpp index 729b011f..4d8d90e9 100644 --- a/api/plugin-api.cpp +++ b/api/plugin-api.cpp @@ -1,44 +1,77 @@ #include "plugin-api.hpp" +#include <QCloseEvent> +#include <QDebug> -using namespace plugin_api::detail; +namespace plugin_api::detail { + +BaseDialog::BaseDialog() = default; +void BaseDialog::closeEvent(QCloseEvent* e) +{ + if (isVisible()) + emit closing(); + e->accept(); +} + +void BaseDialog::done(int) +{ + if (isVisible()) + close(); +} + +bool BaseDialog::embeddable() noexcept { return false; } +void BaseDialog::save() {} +void BaseDialog::reload() {} +void BaseDialog::set_buttons_visible(bool) {} + +} // ns plugin_api::detail // these exist so that vtable is emitted in a single compilation unit, not all of them. -Metadata::~Metadata() {} -IFilter::~IFilter() {} -IFilterDialog::~IFilterDialog() {} -IProtocol::~IProtocol() {} -IProtocolDialog::~IProtocolDialog() {} -ITracker::~ITracker() {} -ITrackerDialog::~ITrackerDialog() {} +Metadata_::Metadata_() = default; +Metadata_::~Metadata_() = default; +Metadata::Metadata() = default; +Metadata::~Metadata() = default; +IFilter::IFilter() = default; +IFilter::~IFilter() = default; +IFilterDialog::IFilterDialog() = default; +IFilterDialog::~IFilterDialog() = default; +void IFilterDialog::register_filter(IFilter*) {} +void IFilterDialog::unregister_filter() {} +IProtocol::IProtocol() = default; +IProtocol::~IProtocol() = default; +IProtocolDialog::IProtocolDialog() = default; +IProtocolDialog::~IProtocolDialog() = default; +void IProtocolDialog::register_protocol(IProtocol*){} +void IProtocolDialog::unregister_protocol() {} +ITracker::ITracker() = default; +ITracker::~ITracker() = default; +bool ITracker::center() { return false; } +ITrackerDialog::ITrackerDialog() = default; +ITrackerDialog::~ITrackerDialog() = default; void ITrackerDialog::register_tracker(ITracker*) {} void ITrackerDialog::unregister_tracker() {} -BaseDialog::BaseDialog() {} +bool module_status::is_ok() const +{ + return error.isNull(); +} +module_status_mixin::~module_status_mixin() = default; +module_status::module_status(const QString& error) : error(error) {} +module_status::module_status() = default; +module_status module_status_mixin::status_ok() { return {}; } -void BaseDialog::closeEvent(QCloseEvent*) +module_status module_status_mixin::error(const QString& error) { - if (isVisible()) - { - hide(); - emit closing(); - } + return module_status(error.isEmpty() ? tr("Unknown error") : error); } -Metadata::Metadata() {} -IFilter::IFilter() {} -IFilterDialog::IFilterDialog() {} -IProtocol::IProtocol() {} -IProtocolDialog::IProtocolDialog() {} -ITracker::ITracker() {} -ITrackerDialog::ITrackerDialog() {} +module_status ITracker::status_ok() +{ + return module_status(); +} -void BaseDialog::done(int) +module_status ITracker::error(const QString& error) { - if (isVisible()) - { - hide(); - close(); - } + return module_status_mixin::error(error); } diff --git a/api/plugin-api.hpp b/api/plugin-api.hpp index 533facfb..2d77bdf4 100644 --- a/api/plugin-api.hpp +++ b/api/plugin-api.hpp @@ -14,17 +14,26 @@ #include <QIcon> #include <QWidget> #include <QDialog> +#include <QCoreApplication> +#include "../compat/simple-mat.hpp" +#include "../compat/tr.hpp" #include "export.hpp" -enum Axis { - TX = 0, TY = 1, TZ = 2, Yaw = 3, Pitch = 4, Roll = 5, - // for indexing in general - rYaw = 0, rPitch = 1, rRoll = 2, +using Pose = Mat<double, 6, 1>; + +enum Axis : int +{ + NonAxis = -1, + TX = 0, TY = 1, TZ = 2, + + Yaw = 3, Pitch = 4, Roll = 5, + Axis_MIN = TX, Axis_MAX = 5, + + Axis_COUNT = 6, }; -namespace plugin_api { -namespace detail { +namespace plugin_api::detail { class OTR_API_EXPORT BaseDialog : public QDialog { @@ -33,62 +42,96 @@ protected: BaseDialog(); public: void closeEvent(QCloseEvent *) override; + virtual bool embeddable() noexcept; + virtual void set_buttons_visible(bool x); // XXX TODO remove it once all modules are converted + virtual void save(); // XXX HACK should be pure virtual + virtual void reload(); // XXX HACK should be pure virtual -sh 20211214 signals: void closing(); private slots: void done(int) override; }; -} // ns -} // ns +} // ns plugin_api::detail #define OTR_PLUGIN_EXPORT OTR_GENERIC_EXPORT #define OPENTRACK_DECLARE_PLUGIN_INTERNAL(ctor_class, ctor_ret_class, metadata_class, dialog_class, dialog_ret_class) \ - extern "C" OTR_PLUGIN_EXPORT ctor_ret_class* GetConstructor(); \ - extern "C" OTR_PLUGIN_EXPORT Metadata* GetMetadata(); \ - extern "C" OTR_PLUGIN_EXPORT dialog_ret_class* GetDialog(); \ - \ - extern "C" OTR_PLUGIN_EXPORT ctor_ret_class* GetConstructor() \ - { \ - return new ctor_class; \ - } \ - extern "C" OTR_PLUGIN_EXPORT Metadata* GetMetadata() \ - { \ - return new metadata_class; \ - } \ - extern "C" OTR_PLUGIN_EXPORT dialog_ret_class* GetDialog() \ - { \ - return new dialog_class; \ + extern "C" \ + { \ + OTR_PLUGIN_EXPORT ctor_ret_class* GetConstructor(void); \ + ctor_ret_class* GetConstructor(void) \ + { \ + return new ctor_class; \ + } \ + OTR_PLUGIN_EXPORT Metadata_* GetMetadata(void); \ + Metadata_* GetMetadata(void) \ + { \ + return new metadata_class; \ + } \ + OTR_PLUGIN_EXPORT dialog_ret_class* GetDialog(void); \ + dialog_ret_class* GetDialog(void) \ + { \ + return new dialog_class; \ + } \ } // implement this in all plugins // also you must link against "opentrack-api" in CMakeLists.txt to avoid vtable link errors -struct OTR_API_EXPORT Metadata +class OTR_API_EXPORT Metadata_ { - Metadata(const Metadata&) = delete; - Metadata(Metadata&&) = delete; - Metadata& operator=(const Metadata&) = delete; - Metadata(); +public: + Metadata_(); // plugin name to be displayed in the interface virtual QString name() = 0; // plugin icon, you can return an empty QIcon() virtual QIcon icon() = 0; // optional destructor - virtual ~Metadata(); + virtual ~Metadata_(); +}; + +class OTR_API_EXPORT Metadata : public TR, public Metadata_ +{ + Q_OBJECT + +public: + Metadata(); + ~Metadata() override; +}; + +struct OTR_API_EXPORT module_status final +{ + QString error; + + bool is_ok() const; + module_status(); + module_status(const QString& error); +}; + +/* + * implement in all module types + */ +struct OTR_API_EXPORT module_status_mixin +{ + static module_status status_ok(); // return from initialize() if ok + static module_status error(const QString& error); // return error message on init failure + + virtual module_status initialize() = 0; // where to return from + virtual ~module_status_mixin(); + + Q_DECLARE_TR_FUNCTIONS(module_status_mixin) }; // implement this in filters -struct OTR_API_EXPORT IFilter +struct OTR_API_EXPORT IFilter : module_status_mixin { IFilter(const IFilter&) = delete; - IFilter(IFilter&&) = delete; IFilter& operator=(const IFilter&) = delete; IFilter(); // optional destructor - virtual ~IFilter(); + ~IFilter() override; // perform filtering step. // you have to take care of dt on your own, try "opentrack-compat/timer.hpp" virtual void filter(const double *input, double *output) = 0; @@ -99,9 +142,10 @@ struct OTR_API_EXPORT IFilter struct OTR_API_EXPORT IFilterDialog : public plugin_api::detail::BaseDialog { IFilterDialog(); + ~IFilterDialog() override; // optional destructor - virtual ~IFilterDialog(); + //~IFilterDialog() override; // receive a pointer to the filter from ui thread virtual void register_filter(IFilter* filter) = 0; // received filter pointer is about to get deleted @@ -113,21 +157,17 @@ struct OTR_API_EXPORT IFilterDialog : public plugin_api::detail::BaseDialog OPENTRACK_DECLARE_PLUGIN_INTERNAL(filter_class, IFilter, metadata_class, dialog_class, IFilterDialog) // implement this in protocols -struct OTR_API_EXPORT IProtocol +struct OTR_API_EXPORT IProtocol : module_status_mixin { IProtocol(); + ~IProtocol() override; IProtocol(const IProtocol&) = delete; - IProtocol(IProtocol&&) = delete; IProtocol& operator=(const IProtocol&) = delete; - // optional destructor - virtual ~IProtocol(); - // return true if protocol was properly initialized - virtual bool correct() = 0; // called 250 times a second with XYZ yaw pitch roll pose - // try not to perform intense computation here. if you must, use a thread. - virtual void pose(const double* headpose) = 0; + // try not to perform intense computation here. use a thread. + virtual void pose(const double* pose, const double* raw) = 0; // return game name or placeholder text virtual QString game_name() = 0; }; @@ -135,13 +175,14 @@ struct OTR_API_EXPORT IProtocol struct OTR_API_EXPORT IProtocolDialog : public plugin_api::detail::BaseDialog { // optional destructor - virtual ~IProtocolDialog(); + // ~IProtocolDialog() override; // receive a pointer to the protocol from ui thread virtual void register_protocol(IProtocol *protocol) = 0; // received protocol pointer is about to get deleted virtual void unregister_protocol() = 0; IProtocolDialog(); + ~IProtocolDialog() override; }; // call once with your chosen class names in the plugin @@ -151,34 +192,39 @@ struct OTR_API_EXPORT IProtocolDialog : public plugin_api::detail::BaseDialog // implement this in trackers struct OTR_API_EXPORT ITracker { - ITracker(const ITracker&) = delete; - ITracker(ITracker&&) = delete; - ITracker& operator=(const ITracker&) = delete; ITracker(); // optional destructor virtual ~ITracker(); // start tracking, and grab a frame to display webcam video in, optionally - virtual void start_tracker(QFrame* frame) = 0; + virtual module_status start_tracker(QFrame* frame) = 0; // return XYZ yaw pitch roll data. don't block here, use a separate thread for computation. virtual void data(double *data) = 0; // tracker notified of centering // returning true makes identity the center pose - virtual bool center() { return false; } + virtual bool center(); + + static module_status status_ok(); + static module_status error(const QString& error); + + ITracker(const ITracker&) = delete; + ITracker& operator=(const ITracker&) = delete; }; struct OTR_API_EXPORT ITrackerDialog : public plugin_api::detail::BaseDialog { // optional destructor - virtual ~ITrackerDialog(); + //~ITrackerDialog() override; // receive a pointer to the tracker from ui thread virtual void register_tracker(ITracker *tracker); // received tracker pointer is about to get deleted virtual void unregister_tracker(); ITrackerDialog(); + ~ITrackerDialog() override; }; // call once with your chosen class names in the plugin #define OPENTRACK_DECLARE_TRACKER(tracker_class, dialog_class, metadata_class) \ OPENTRACK_DECLARE_PLUGIN_INTERNAL(tracker_class, ITracker, metadata_class, dialog_class, ITrackerDialog) + diff --git a/api/plugin-support.hpp b/api/plugin-support.hpp index 86994d18..4300da18 100644 --- a/api/plugin-support.hpp +++ b/api/plugin-support.hpp @@ -8,213 +8,259 @@ #pragma once #include "plugin-api.hpp" -#include "options/options.hpp" +#include "compat/library-path.hpp" + +#include <memory> +#include <algorithm> +#include <cstring> +#include <vector> -#include <QWidget> #include <QDebug> #include <QString> #include <QLibrary> -#include <QFrame> -#include <QList> - -#include <cstdio> -#include <cinttypes> -#include <iostream> -#include <algorithm> - -#include <QCoreApplication> -#include <QFile> #include <QDir> -#include <QList> -#include <QStringList> - -#if defined(__APPLE__) -# define OPENTRACK_SOLIB_EXT "dylib" -#elif defined(_WIN32) -# define OPENTRACK_SOLIB_EXT "dll" -#else -# define OPENTRACK_SOLIB_EXT "so" -#endif - -#include <iostream> +#include <QIcon> -#ifdef _MSC_VER -# define OPENTRACK_SOLIB_PREFIX "" -#else -# define OPENTRACK_SOLIB_PREFIX "" -#endif +extern "C" { + using module_ctor_t = void* (*)(void); + using module_metadata_t = Metadata_* (*)(void); +} -extern "C" typedef void* (*OPENTRACK_CTOR_FUNPTR)(void); -extern "C" typedef Metadata* (*OPENTRACK_METADATA_FUNPTR)(void); +enum dylib_load_mode : unsigned +{ + dylib_load_norm = 0, + dylib_load_quiet = 1 << 0, + dylib_load_none = 1 << 1, +}; -struct dylib final { - enum Type : unsigned { Filter, Tracker, Protocol, Invalid = 0xcafebabeU }; +enum class dylib_type : unsigned +{ + Filter = 0xdeadbabe, + Tracker = 0xcafebeef, + Protocol = 0xdeadf00d, + Video = 0xbadf00d, + Invalid = (unsigned)-1, +}; - dylib(const QString& filename, Type t) : - type(Invalid), - filename(filename), - Dialog(nullptr), - Constructor(nullptr), - Meta(nullptr), - handle(nullptr) +struct dylib final +{ + dylib(const QString& filename_, dylib_type t, dylib_load_mode load_mode = dylib_load_norm) : + full_filename(filename_), + module_name(trim_filename(filename_)) { // otherwise dlopen opens the calling executable - if (filename.size() == 0) - return; - - handle.setFileName(filename); - handle.setLoadHints(QLibrary::DeepBindHint | QLibrary::LoadHint::ResolveAllSymbolsHint); - - if (check(!handle.load())) - return; - - if (check((Dialog = (OPENTRACK_CTOR_FUNPTR) handle.resolve("GetDialog"), !Dialog))) + if (filename_.isEmpty() || module_name.isEmpty()) return; - if (check((Constructor = (OPENTRACK_CTOR_FUNPTR) handle.resolve("GetConstructor"), !Constructor))) - return; + handle.setFileName(filename_); + handle.setLoadHints(QLibrary::DeepBindHint | QLibrary::ResolveAllSymbolsHint); - if (check((Meta = (OPENTRACK_METADATA_FUNPTR) handle.resolve("GetMetadata"), !Meta))) - return; - - auto m = mem<Metadata>(Meta()); +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wcomma" +#endif - icon = m->icon(); - name = m->name(); + if (!handle.load()) + goto fail; - type = t; - } - ~dylib() - { - if (handle.isLoaded()) + if (!(load_mode & dylib_load_none)) { - const bool success = handle.unload(); - if (!success) - qDebug() << "can't unload dylib" << filename << handle.errorString(); - } - } + std::unique_ptr<Metadata_> m; - static QList<mem<dylib>> enum_libraries(const QString& library_path) - { - const char* filters_n[] = { OPENTRACK_SOLIB_PREFIX "opentrack-filter-*." OPENTRACK_SOLIB_EXT, - OPENTRACK_SOLIB_PREFIX "opentrack-tracker-*." OPENTRACK_SOLIB_EXT, - OPENTRACK_SOLIB_PREFIX "opentrack-proto-*." OPENTRACK_SOLIB_EXT, - }; - const Type filters_t[] = { Filter, Tracker, Protocol }; + if (Dialog = (module_ctor_t) handle.resolve("GetDialog"), !Dialog) + goto fail; - QDir settingsDir(library_path); + if (Constructor = (module_ctor_t) handle.resolve("GetConstructor"), !Constructor) + goto fail; - QList<mem<dylib>> ret; + if (Meta = (module_metadata_t) handle.resolve("GetMetadata"), !Meta) + goto fail; - for (int i = 0; i < 3; i++) - { - QString glob = filters_n[i]; - Type t = filters_t[i]; - QStringList filenames = settingsDir.entryList(QStringList { glob }, QDir::Files, QDir::Name); + m = std::unique_ptr<Metadata_>(Meta()); - for (const QString& filename : filenames) + if (!m) { - QIcon icon; - QString longName; - auto lib = std::make_shared<dylib>(library_path + filename, t); - if (!get_metadata(lib, longName, icon)) - continue; - if (std::any_of(ret.cbegin(), - ret.cend(), - [&](mem<dylib> a) {return a->type == lib->type && a->name == lib->name;})) + if (!(load_mode & dylib_load_quiet)) { - qDebug() << "Duplicate lib" << lib->filename; - continue; + qDebug() << "library" << module_name << "failed: no metadata"; + load_mode = dylib_load_quiet; } - ret.push_back(lib); + goto fail; } + + icon = m->icon(); + name = m->name(); } + else + name = module_name; - return ret; + type = t; + + return; +#ifdef __clang__ +# pragma clang diagnostic pop +#endif + +fail: + if (!(load_mode & dylib_load_quiet)) + qDebug() << "library" << module_name << "failed:" << handle.errorString(); + + Constructor = nullptr; + Dialog = nullptr; + Meta = nullptr; + + type = dylib_type::Invalid; } - Type type; - QString filename; + // QLibrary refcounts the .dll's so don't forcefully unload + ~dylib() = default; + + dylib_type type = dylib_type::Invalid; + QString full_filename; + QString module_name; QIcon icon; QString name; - OPENTRACK_CTOR_FUNPTR Dialog; - OPENTRACK_CTOR_FUNPTR Constructor; - OPENTRACK_METADATA_FUNPTR Meta; + module_ctor_t Dialog = nullptr; + module_ctor_t Constructor = nullptr; + module_metadata_t Meta = nullptr; + private: QLibrary handle; - bool check(bool fail) + static QString trim_filename(const QString& in_) { - if (fail) + QStringRef in(&in_); + + const int idx = in.lastIndexOf("/"); + + if (idx != -1) { - if (handle.isLoaded()) - (void) handle.unload(); + in = in.mid(idx + 1); - Constructor = nullptr; - Dialog = nullptr; - Meta = nullptr; + if (in.startsWith(OPENTRACK_LIBRARY_PREFIX) && + in.endsWith("." OPENTRACK_LIBRARY_EXTENSION)) + { + constexpr unsigned pfx_len = sizeof(OPENTRACK_LIBRARY_PREFIX) - 1; + constexpr unsigned rst_len = sizeof("." OPENTRACK_LIBRARY_EXTENSION) - 1; - type = Invalid; - } + in = in.mid(pfx_len); + in = in.left(in.size() - rst_len); - return fail; - } + const char* const names[] = + { + OPENTRACK_LIBRARY_PREFIX "opentrack-tracker-", + OPENTRACK_LIBRARY_PREFIX "opentrack-proto-", + OPENTRACK_LIBRARY_PREFIX "opentrack-filter-", + OPENTRACK_LIBRARY_PREFIX "opentrack-video-", + }; - static bool get_metadata(mem<dylib> lib, QString& name, QIcon& icon) - { - Metadata* meta; - if (!lib->Meta || ((meta = lib->Meta()), !meta)) - return false; - name = meta->name(); - icon = meta->icon(); - delete meta; - return true; + for (auto name : names) + { + if (in.startsWith(name)) + return in.mid(std::strlen(name)).toString(); + } + } + } + return {""}; } }; struct Modules final { - Modules(const QString& library_path) : - module_list(dylib::enum_libraries(library_path)), - filter_modules(filter(dylib::Filter)), - tracker_modules(filter(dylib::Tracker)), - protocol_modules(filter(dylib::Protocol)) + using dylib_ptr = std::shared_ptr<dylib>; + using dylib_list = std::vector<dylib_ptr>; + using type = dylib_type; + + Modules(const QString& library_path, dylib_load_mode load_mode = dylib_load_norm) : + module_list(enum_libraries(library_path, load_mode)), + filter_modules(filter(type::Filter)), + tracker_modules(filter(type::Tracker)), + protocol_modules(filter(type::Protocol)), + video_modules(filter(type::Video)) {} - QList<mem<dylib>>& filters() { return filter_modules; } - QList<mem<dylib>>& trackers() { return tracker_modules; } - QList<mem<dylib>>& protocols() { return protocol_modules; } + dylib_list& filters() { return filter_modules; } + dylib_list& trackers() { return tracker_modules; } + dylib_list& protocols() { return protocol_modules; } + private: - QList<mem<dylib>> module_list; - QList<mem<dylib>> filter_modules; - QList<mem<dylib>> tracker_modules; - QList<mem<dylib>> protocol_modules; + dylib_list module_list; + dylib_list filter_modules; + dylib_list tracker_modules; + dylib_list protocol_modules; + dylib_list video_modules; - template<typename t> - static void sort(QList<t>& xs) + static dylib_list& sorted(dylib_list& xs) { - std::sort(xs.begin(), xs.end(), [&](const t& a, const t& b) { return a->name.toLower() < b->name.toLower(); }); + std::sort(xs.begin(), xs.end(), + [&](const dylib_ptr& a, const dylib_ptr& b) { + return a->name.toLower() < b->name.toLower(); + }); + return xs; } - QList<mem<dylib>> filter(dylib::Type t) + dylib_list filter(dylib_type t) { - QList<mem<dylib>> ret; - for (auto x : module_list) + dylib_list ret; ret.reserve(module_list.size()); + for (const auto& x : module_list) if (x->type == t) ret.push_back(x); - sort(ret); + return sorted(ret); + } + + static dylib_list enum_libraries(const QString& library_path, + dylib_load_mode load_mode = dylib_load_norm) + { + QDir dir(library_path); + dylib_list ret; + + const struct filter_ { + type t = type::Invalid; + QString glob; + dylib_load_mode load_mode = dylib_load_norm; + } filters[] = { + { type::Filter, OPENTRACK_LIBRARY_PREFIX "opentrack-filter-*." OPENTRACK_LIBRARY_EXTENSION, }, + { type::Tracker, OPENTRACK_LIBRARY_PREFIX "opentrack-tracker-*." OPENTRACK_LIBRARY_EXTENSION, }, + { type::Protocol, OPENTRACK_LIBRARY_PREFIX "opentrack-proto-*." OPENTRACK_LIBRARY_EXTENSION, }, + { type::Video, OPENTRACK_LIBRARY_PREFIX "opentrack-video-*." OPENTRACK_LIBRARY_EXTENSION, dylib_load_none, }, + }; + + for (const filter_& filter : filters) + { + for (const QString& filename : dir.entryList({ filter.glob }, QDir::Files, QDir::Name)) + { + dylib_load_mode load_mode_{filter.load_mode | load_mode}; + auto lib = std::make_shared<dylib>(QString("%1/%2").arg(library_path, filename), filter.t, load_mode_); + + if (lib->type == type::Invalid) + continue; + + if (std::any_of(ret.cbegin(), + ret.cend(), + [&lib](const std::shared_ptr<dylib>& a) { + return a->type == lib->type && a->name == lib->name; + })) + { + if (!(load_mode & dylib_load_quiet)) + qDebug() << "duplicate lib" << filename << "ident" << lib->name; + continue; + } + + ret.push_back(std::move(lib)); + } + } return ret; } }; template<typename t> -mem<t> make_dylib_instance(mem<dylib> lib) +std::shared_ptr<t> make_dylib_instance(const std::shared_ptr<dylib>& lib) { - mem<t> ret; if (lib != nullptr && lib->Constructor) - ret = mem<t>(reinterpret_cast<t*>(reinterpret_cast<OPENTRACK_CTOR_FUNPTR>(lib->Constructor)())); - return ret; + return std::shared_ptr<t>{(t*)lib->Constructor()}; + else + return nullptr; } diff --git a/api/variance.hpp b/api/variance.hpp deleted file mode 100644 index 7a83154c..00000000 --- a/api/variance.hpp +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once - -#include <cmath> -#include <cinttypes> - -// no copyright information other than the attribution below. - -// code shared as an example/tutorial of running variance -// written by John D. Cook on the website <http://www.johndcook.com/blog/standard_deviation> - -// following references in the site's article: - -// Chan, Tony F.; Golub, Gene H.; LeVeque, Randall J. (1983). -// Algorithms for Computing the Sample Variance: Analysis and Recommendations. -// The American Statistician 37, 242-247. - -// Ling, Robert F. (1974). -// Comparison of Several Algorithms for Computing Sample Means and Variances. -// Journal of the American Statistical Association, Vol. 69, No. 348, 859-866. - -class variance -{ - double m_old, m_new, s_old, s_new; - std::uintptr_t cnt; - -public: - using size_type = std::uintptr_t; - - variance() : cnt(0) {} - - void clear() { *this = variance(); } - - size_type count() { return cnt; } - - void input(double x) - { - cnt++; - - if (cnt == 1) - { - m_old = m_new = x; - s_old = 0; - } - else - { - m_new = m_old + (x - m_old)/cnt; - s_new = s_old + (x - m_old)*(x - m_new); - - m_old = m_new; - s_old = s_new; - } - } - - double avg() const { return cnt > 0 ? m_new : 0; } - double Var() const { return cnt > 1 ? s_new/(cnt - 1) : 0; } - double stddev() const { return std::sqrt(Var()); } -}; |
