diff options
Diffstat (limited to 'api')
-rw-r--r-- | api/CMakeLists.txt | 3 | ||||
-rw-r--r-- | api/dtors.cpp | 11 | ||||
-rw-r--r-- | api/export.hpp | 28 | ||||
-rw-r--r-- | api/is-window-visible.cpp | 33 | ||||
-rw-r--r-- | api/is-window-visible.hpp | 7 | ||||
-rw-r--r-- | api/plugin-api.hpp | 180 | ||||
-rw-r--r-- | api/plugin-support.hpp | 216 | ||||
-rw-r--r-- | api/variance.hpp | 52 |
8 files changed, 530 insertions, 0 deletions
diff --git a/api/CMakeLists.txt b/api/CMakeLists.txt new file mode 100644 index 00000000..5151cbf7 --- /dev/null +++ b/api/CMakeLists.txt @@ -0,0 +1,3 @@ +opentrack_boilerplate(opentrack-api NO-COMPAT BIN) +target_link_libraries(opentrack-api opentrack-compat) +target_include_directories(opentrack-api PUBLIC ${CMAKE_BINARY_DIR}) diff --git a/api/dtors.cpp b/api/dtors.cpp new file mode 100644 index 00000000..5cc87187 --- /dev/null +++ b/api/dtors.cpp @@ -0,0 +1,11 @@ +#include "plugin-api.hpp" + +// these exist only 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() {} diff --git a/api/export.hpp b/api/export.hpp new file mode 100644 index 00000000..a9f3521e --- /dev/null +++ b/api/export.hpp @@ -0,0 +1,28 @@ +#pragma once + +#ifdef BUILD_api +# ifdef _WIN32 +# define OPENTRACK_API_LINKAGE __declspec(dllexport) +# else +# define OPENTRACK_API_LINKAGE +# endif + +# ifndef _MSC_VER +# define OPENTRACK_API_EXPORT __attribute__ ((visibility ("default"))) OPENTRACK_API_LINKAGE +# else +# define OPENTRACK_API_EXPORT OPENTRACK_API_LINKAGE +# endif + +#else + #ifdef _WIN32 + # define OPENTRACK_API_LINKAGE __declspec(dllimport) + #else + # define OPENTRACK_API_LINKAGE + #endif + + #ifndef _MSC_VER + # define OPENTRACK_API_EXPORT __attribute__ ((visibility ("default"))) OPENTRACK_API_LINKAGE + #else + # define OPENTRACK_API_EXPORT OPENTRACK_API_LINKAGE + #endif +#endif diff --git a/api/is-window-visible.cpp b/api/is-window-visible.cpp new file mode 100644 index 00000000..fc25bb7d --- /dev/null +++ b/api/is-window-visible.cpp @@ -0,0 +1,33 @@ +#include "is-window-visible.hpp" +#include <QPoint> + +#ifdef _WIN32 + +#include <windows.h> + +OPENTRACK_API_EXPORT bool is_window_visible(const QWidget* widget) +{ + const QPoint p = widget->mapToGlobal(QPoint(0, 0)); + const QSize s = widget->size(); + + const POINT points[] = + { + { p.x(), p.y() }, + { p.x() + s.width(), p.y() }, + { p.x() + s.width(), p.y() + s.height() }, + { p.x(), p.y() + s.height() }, + { p.x() + s.width()/2, p.y() + s.height()/2 }, + }; + + for (const POINT& pt : points) + if (WindowFromPoint(pt) == (HWND) widget->winId()) + return true; + return false; +} + +#else +OPENTRACK_API_EXPORT bool is_window_visible(const QWidget* widget) +{ + return true; +} +#endif diff --git a/api/is-window-visible.hpp b/api/is-window-visible.hpp new file mode 100644 index 00000000..18c9251a --- /dev/null +++ b/api/is-window-visible.hpp @@ -0,0 +1,7 @@ +#pragma once + +#include <QWidget> +#include "export.hpp" + +OPENTRACK_API_EXPORT bool is_window_visible(const QWidget* widget); + diff --git a/api/plugin-api.hpp b/api/plugin-api.hpp new file mode 100644 index 00000000..1e5c0fe0 --- /dev/null +++ b/api/plugin-api.hpp @@ -0,0 +1,180 @@ +/* Copyright (c) 2013-2015, Stanislaw Halik <sthalik@misaki.pl> + + * 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 <QString> +#include <QWidget> +#include <QFrame> +#include <QIcon> + +#include "export.hpp" + +#ifndef OPENTRACK_PLUGIN_EXPORT +# ifdef _WIN32 +# define OPENTRACK_PLUGIN_LINKAGE __declspec(dllexport) +# else +# define OPENTRACK_PLUGIN_LINKAGE +# endif +# ifndef _MSC_VER +# define OPENTRACK_PLUGIN_EXPORT __attribute__ ((visibility ("default"))) OPENTRACK_PLUGIN_LINKAGE +# else +# define OPENTRACK_PLUGIN_EXPORT OPENTRACK_PLUGIN_LINKAGE +# endif +#endif + +enum Axis { + TX, TY, TZ, Yaw, Pitch, Roll +}; + +namespace plugin_api { +namespace detail { + +class OPENTRACK_API_EXPORT BaseDialog : public QWidget +{ + Q_OBJECT +public: + void closeEvent(QCloseEvent *) override { emit closing(); } +signals: + void closing(); +}; + +} // ns +} // ns + +#define OPENTRACK_DECLARE_PLUGIN_INTERNAL(ctor_class, ctor_ret_class, metadata_class, dialog_class, dialog_ret_class) \ + extern "C" OPENTRACK_PLUGIN_EXPORT ctor_ret_class* GetConstructor(); \ + extern "C" OPENTRACK_PLUGIN_EXPORT Metadata* GetMetadata(); \ + extern "C" OPENTRACK_PLUGIN_EXPORT dialog_ret_class* GetDialog(); \ + \ + extern "C" OPENTRACK_PLUGIN_EXPORT ctor_ret_class* GetConstructor() \ + { \ + return new ctor_class; \ + } \ + extern "C" OPENTRACK_PLUGIN_EXPORT Metadata* GetMetadata() \ + { \ + return new metadata_class; \ + } \ + extern "C" OPENTRACK_PLUGIN_EXPORT dialog_ret_class* GetDialog() \ + { \ + 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 OPENTRACK_API_EXPORT Metadata +{ + Metadata(const Metadata&) = delete; + Metadata(Metadata&&) = delete; + Metadata& operator=(const Metadata&) = delete; + inline 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(); +}; + +// implement this in filters +struct OPENTRACK_API_EXPORT IFilter +{ + IFilter(const IFilter&) = delete; + IFilter(IFilter&&) = delete; + IFilter& operator=(const IFilter&) = delete; + inline IFilter() {} + + // optional destructor + virtual ~IFilter(); + // 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; + // optionally reset the filter when centering + virtual void center() {} +}; + +struct OPENTRACK_API_EXPORT IFilterDialog : public plugin_api::detail::BaseDialog +{ + // optional destructor + virtual ~IFilterDialog(); + // receive a pointer to the filter from ui thread + virtual void register_filter(IFilter* filter) = 0; + // received filter pointer is about to get deleted + virtual void unregister_filter() = 0; +}; + +// call once with your chosen class names in the plugin +#define OPENTRACK_DECLARE_FILTER(filter_class, dialog_class, metadata_class) \ + OPENTRACK_DECLARE_PLUGIN_INTERNAL(filter_class, IFilter, metadata_class, dialog_class, IFilterDialog) + +// implement this in protocols +struct OPENTRACK_API_EXPORT IProtocol +{ + IProtocol(const IProtocol&) = delete; + IProtocol(IProtocol&&) = delete; + IProtocol& operator=(const IProtocol&) = delete; + inline IProtocol() {} + + // 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; + // return game name or placeholder text + virtual QString game_name() = 0; +}; + +struct OPENTRACK_API_EXPORT IProtocolDialog : public plugin_api::detail::BaseDialog +{ + // optional destructor + virtual ~IProtocolDialog(); + // 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; +}; + +// call once with your chosen class names in the plugin +#define OPENTRACK_DECLARE_PROTOCOL(protocol_class, dialog_class, metadata_class) \ + OPENTRACK_DECLARE_PLUGIN_INTERNAL(protocol_class, IProtocol, metadata_class, dialog_class, IProtocolDialog) + +// implement this in trackers +struct OPENTRACK_API_EXPORT ITracker +{ + ITracker(const ITracker&) = delete; + ITracker(ITracker&&) = delete; + ITracker& operator=(const ITracker&) = delete; + inline ITracker() {} + + // optional destructor + virtual ~ITracker(); + // start tracking, and grab a frame to display webcam video in, optionally + virtual void 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; } +}; + +struct OPENTRACK_API_EXPORT ITrackerDialog : public plugin_api::detail::BaseDialog +{ + // optional destructor + virtual ~ITrackerDialog(); + // receive a pointer to the tracker from ui thread + virtual void register_tracker(ITracker *tracker) = 0; + // received tracker pointer is about to get deleted + virtual void unregister_tracker() = 0; +}; + +// 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 new file mode 100644 index 00000000..072c8da7 --- /dev/null +++ b/api/plugin-support.hpp @@ -0,0 +1,216 @@ +/* Copyright (c) 2015 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 "plugin-api.hpp" +#include "compat/options.hpp" + +#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> + +#ifdef _MSC_VER +# define OPENTRACK_SOLIB_PREFIX "" +#else +# define OPENTRACK_SOLIB_PREFIX "lib" +#endif + +extern "C" typedef void* (*OPENTRACK_CTOR_FUNPTR)(void); +extern "C" typedef Metadata* (*OPENTRACK_METADATA_FUNPTR)(void); + +struct dylib final { + enum Type { Filter, Tracker, Protocol }; + + dylib(const QString& filename, Type t) : + type(t), + filename(filename), + Dialog(nullptr), + Constructor(nullptr), + Meta(nullptr), + handle(nullptr) + { + // otherwise dlopen opens the calling executable + if (filename.size() == 0) + return; + + handle = new QLibrary(filename); + handle->setLoadHints(QLibrary::PreventUnloadHint | handle->loadHints()); + + struct _foo { + static bool die(QLibrary*& l, bool failp) + { + if (failp) + { + qDebug() << "failed" << l->errorString(); + delete l; + l = nullptr; + } + return failp; + } + }; + + if (_foo::die(handle, !handle->load())) + return; + + Dialog = (OPENTRACK_CTOR_FUNPTR) handle->resolve("GetDialog"); + if (_foo::die(handle, !Dialog)) + return; + + Constructor = (OPENTRACK_CTOR_FUNPTR) handle->resolve("GetConstructor"); + if (_foo::die(handle, !Constructor)) + return; + + Meta = (OPENTRACK_METADATA_FUNPTR) handle->resolve("GetMetadata"); + if (_foo::die(handle, !Meta)) + return; + + auto m = mem<Metadata>(Meta()); + + icon = m->icon(); + name = m->name(); + } + ~dylib() + { + if (handle) + delete handle; + } + + 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 }; + + QDir settingsDir(library_path); + + QList<mem<dylib>> ret; + + 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); + + for (const QString& filename : filenames) + { + QIcon icon; + QString longName; + auto lib = std::make_shared<dylib>(library_path + filename, t); + qDebug() << "Loading" << filename; + std::cout.flush(); + if (!get_metadata(lib, longName, icon)) + continue; + using d = const mem<dylib>&; + if (std::any_of(ret.cbegin(), + ret.cend(), + [&](d a) {return a->type == lib->type && a->name == lib->name;})) + { + qDebug() << "Duplicate lib" << lib->filename; + continue; + } + ret.push_back(lib); + } + } + + return ret; + } + + Type type; + QString filename; + + QIcon icon; + QString name; + + OPENTRACK_CTOR_FUNPTR Dialog; + OPENTRACK_CTOR_FUNPTR Constructor; + OPENTRACK_METADATA_FUNPTR Meta; +private: + QLibrary* handle; + + 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; + } +}; + +struct Modules +{ + 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)) + {} + QList<mem<dylib>>& filters() { return filter_modules; } + QList<mem<dylib>>& trackers() { return tracker_modules; } + QList<mem<dylib>>& 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; + + template<typename t> + static void sort(QList<t>& xs) + { + std::sort(xs.begin(), xs.end(), [&](const t& a, const t& b) { return a->name.toLower() < b->name.toLower(); }); + } + + QList<mem<dylib>> filter(dylib::Type t) + { + QList<mem<dylib>> ret; + for (auto x : module_list) + if (x->type == t) + ret.push_back(x); + + sort(ret); + + return ret; + } +}; + +template<typename t> +mem<t> make_dylib_instance(mem<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; +} diff --git a/api/variance.hpp b/api/variance.hpp new file mode 100644 index 00000000..55060b02 --- /dev/null +++ b/api/variance.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include <cmath> + +// 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; + unsigned long cnt; + +public: + variance() : cnt(0) {} + + void clear() { *this = variance(); } + + 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()); } +}; |