summaryrefslogtreecommitdiffhomepage
path: root/api
diff options
context:
space:
mode:
Diffstat (limited to 'api')
-rw-r--r--api/CMakeLists.txt3
-rw-r--r--api/lang/de_DE.ts11
-rw-r--r--api/lang/nl_NL.ts7
-rw-r--r--api/lang/ru_RU.ts7
-rw-r--r--api/lang/stub.ts7
-rw-r--r--api/lang/zh_CN.ts11
-rw-r--r--api/plugin-api.cpp89
-rw-r--r--api/plugin-api.hpp142
-rw-r--r--api/plugin-support.hpp332
-rw-r--r--api/variance.hpp57
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()); }
-};