summaryrefslogtreecommitdiffhomepage
path: root/api
diff options
context:
space:
mode:
Diffstat (limited to 'api')
-rw-r--r--api/CMakeLists.txt3
-rw-r--r--api/dtors.cpp11
-rw-r--r--api/export.hpp28
-rw-r--r--api/is-window-visible.cpp33
-rw-r--r--api/is-window-visible.hpp7
-rw-r--r--api/plugin-api.hpp180
-rw-r--r--api/plugin-support.hpp216
-rw-r--r--api/variance.hpp52
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()); }
+};