summaryrefslogtreecommitdiffhomepage
path: root/compat
diff options
context:
space:
mode:
Diffstat (limited to 'compat')
-rw-r--r--compat/CMakeLists.txt6
-rw-r--r--compat/activation-context.cpp51
-rw-r--r--compat/activation-context.hpp26
-rw-r--r--compat/arch.hpp50
-rw-r--r--compat/base-path.cpp12
-rw-r--r--compat/base-path.hpp11
-rw-r--r--compat/camera-names.cpp117
-rw-r--r--compat/camera-names.hpp5
-rw-r--r--compat/check-visible.cpp98
-rw-r--r--compat/check-visible.hpp15
-rw-r--r--compat/correlation-calibrator.cpp160
-rw-r--r--compat/correlation-calibrator.hpp76
-rw-r--r--compat/enum-operators.hpp45
-rw-r--r--compat/euler.cpp134
-rw-r--r--compat/euler.hpp23
-rw-r--r--compat/hamilton-tools.cpp109
-rw-r--r--compat/hamilton-tools.h76
-rw-r--r--compat/lang/de_DE.ts4
-rw-r--r--compat/lang/zh_CN.ts4
-rw-r--r--compat/library-path.hpp4
-rw-r--r--compat/linkage-macros.hpp29
-rw-r--r--compat/macros.h57
-rw-r--r--compat/math-imports.hpp49
-rw-r--r--compat/math.hpp16
-rw-r--r--compat/meta.hpp123
-rw-r--r--compat/mutex.cpp33
-rw-r--r--compat/mutex.hpp24
-rw-r--r--compat/nan.cpp30
-rw-r--r--compat/nan.hpp11
-rw-r--r--compat/ndebug-guard.hpp5
-rw-r--r--compat/process-list.hpp65
-rw-r--r--compat/qcopyable-mutex.hpp37
-rw-r--r--compat/qhash.hpp26
-rw-r--r--compat/qt-dpi.hpp24
-rw-r--r--compat/qt-signal.cpp7
-rw-r--r--compat/qt-signal.hpp72
-rw-r--r--compat/run-in-thread.hpp112
-rw-r--r--compat/shm.cpp195
-rw-r--r--compat/shm.h36
-rw-r--r--compat/simple-mat.hpp441
-rw-r--r--compat/sleep.cpp23
-rw-r--r--compat/sleep.hpp23
-rw-r--r--compat/sysexits.hpp28
-rw-r--r--compat/thread-name.cpp87
-rw-r--r--compat/thread-name.hpp6
-rw-r--r--compat/time.hpp15
-rw-r--r--compat/timer.cpp110
-rw-r--r--compat/timer.hpp36
-rw-r--r--compat/tr.hpp14
-rw-r--r--compat/util.hpp100
-rw-r--r--compat/variance.hpp55
51 files changed, 2002 insertions, 913 deletions
diff --git a/compat/CMakeLists.txt b/compat/CMakeLists.txt
index 40850862..dc81436d 100644
--- a/compat/CMakeLists.txt
+++ b/compat/CMakeLists.txt
@@ -1,9 +1,9 @@
otr_module(compat NO-COMPAT BIN)
if(NOT WIN32 AND NOT APPLE)
- target_link_libraries(opentrack-compat rt)
+ target_link_libraries(${self} rt)
endif()
-if(CMAKE_COMPILER_IS_GNUCXX)
- set_property(SOURCE nan.cpp APPEND_STRING PROPERTY COMPILE_FLAGS "-fno-lto -fno-fast-math -fno-finite-math-only -O0")
+if(WIN32)
+ target_link_libraries(${self} strmiids)
endif()
diff --git a/compat/activation-context.cpp b/compat/activation-context.cpp
new file mode 100644
index 00000000..8d34243d
--- /dev/null
+++ b/compat/activation-context.cpp
@@ -0,0 +1,51 @@
+#ifdef _WIN32
+
+#include "activation-context.hpp"
+#include "compat/library-path.hpp"
+
+#include <QString>
+#include <QFile>
+#include <QDebug>
+
+#include <windows.h>
+
+static_assert(sizeof(std::uintptr_t) == sizeof(ULONG_PTR));
+
+activation_context::activation_context(const QString& module_name, int resid)
+{
+ static const QString prefix = OPENTRACK_BASE_PATH + OPENTRACK_LIBRARY_PATH + OPENTRACK_LIBRARY_PREFIX;
+ QString path = prefix + module_name;
+ QByteArray name = QFile::encodeName(path);
+
+ ACTCTXA actx = {};
+ actx.cbSize = sizeof(actx);
+ actx.lpResourceName = MAKEINTRESOURCEA(resid);
+ actx.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID;
+ actx.lpSource = name.constData();
+
+ handle = CreateActCtxA(&actx);
+
+ if (handle != INVALID_HANDLE_VALUE)
+ {
+ if (!ActivateActCtx(handle, (ULONG_PTR*)&cookie))
+ {
+ qDebug() << "win32: can't set activation context" << GetLastError();
+ ReleaseActCtx(handle);
+ handle = INVALID_HANDLE_VALUE;
+ }
+ else
+ ok = true;
+ } else {
+ qDebug() << "win32: can't create activation context" << GetLastError();
+ }
+}
+
+activation_context::~activation_context()
+{
+ if (handle != INVALID_HANDLE_VALUE)
+ {
+ DeactivateActCtx(0, cookie);
+ ReleaseActCtx(handle);
+ }
+}
+#endif
diff --git a/compat/activation-context.hpp b/compat/activation-context.hpp
new file mode 100644
index 00000000..a3b0429e
--- /dev/null
+++ b/compat/activation-context.hpp
@@ -0,0 +1,26 @@
+#pragma once
+
+#ifdef _WIN32
+
+#include "export.hpp"
+
+#include <cstdint>
+#include <QString>
+
+class OTR_COMPAT_EXPORT activation_context
+{
+public:
+ explicit activation_context(const QString& module_name, int resid);
+ ~activation_context();
+
+ explicit operator bool() const { return ok; }
+
+private:
+ std::uintptr_t cookie = 0;
+ void* handle = (void*)-1;
+ bool ok = false;
+};
+
+#else
+# error "tried to use win32-only activation context"
+#endif
diff --git a/compat/arch.hpp b/compat/arch.hpp
new file mode 100644
index 00000000..3120116a
--- /dev/null
+++ b/compat/arch.hpp
@@ -0,0 +1,50 @@
+#pragma once
+
+// fix MSVC arch check macros
+
+// this file is too simple to fall under copyright, and
+// can be copied, modified, and redistributed freely with
+// no conditions. there's no warranty. -sh 20181226
+
+#if defined _MSC_VER
+# ifdef __clang__
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wreserved-id-macro"
+# pragma clang diagnostic ignored "-Wunused-macros"
+# endif
+
+# if defined _M_AMD64
+# undef __x86_64__
+# define __x86_64__ 1
+# elif defined _M_IX86
+# undef __i386__
+# define __i386__ 1
+# endif
+
+# if defined __AVX__ || defined __x86_64__ || \
+ defined _M_IX86 && _M_IX86_FP >= 2
+# undef __SSE__
+# undef __SSE2__
+# undef __SSE3__
+# define __SSE__ 1
+# define __SSE2__ 1
+# define __SSE3__ 1 // assume SSE3 in the _M_IX86_FP >= 2 case
+# endif
+
+# ifdef __clang__
+# pragma clang diagnostic pop
+# endif
+#endif
+
+#if defined __SSE3__
+# define OTR_ARCH_DENORM_DAZ
+# include <pmmintrin.h>
+#elif defined __SSE2__
+# define OTR_ARCH_DENORM_FTZ
+# include <emmintrin.h>
+#endif
+
+#if defined __SSE2__
+# define OTR_ARCH_FPU_MASK
+# include <xmmintrin.h>
+#endif
diff --git a/compat/base-path.cpp b/compat/base-path.cpp
new file mode 100644
index 00000000..2a26dcff
--- /dev/null
+++ b/compat/base-path.cpp
@@ -0,0 +1,12 @@
+#undef NDEBUG
+#include <cassert>
+
+#include "base-path.hpp"
+#include <QCoreApplication>
+
+const QString& application_base_path()
+{
+ assert(qApp && "logic error");
+ static QString path = QCoreApplication::applicationDirPath();
+ return path;
+}
diff --git a/compat/base-path.hpp b/compat/base-path.hpp
new file mode 100644
index 00000000..7a9d4115
--- /dev/null
+++ b/compat/base-path.hpp
@@ -0,0 +1,11 @@
+#pragma once
+
+#include "export.hpp"
+
+#include <QString>
+
+OTR_COMPAT_EXPORT
+const QString& application_base_path();
+
+#define OPENTRACK_BASE_PATH (application_base_path())
+
diff --git a/compat/camera-names.cpp b/compat/camera-names.cpp
index 22dcdd8f..b9511037 100644
--- a/compat/camera-names.cpp
+++ b/compat/camera-names.cpp
@@ -1,95 +1,130 @@
#include "camera-names.hpp"
+#include <algorithm>
+#include <iterator>
+
#ifdef _WIN32
+# include <cwchar>
# define NO_DSHOW_STRSAFE
# include <dshow.h>
-# include <cwchar>
-#elif defined(__unix) || defined(__linux) || defined(__APPLE__)
+#elif defined(__unix) || defined(__linux__) || defined(__APPLE__)
# include <unistd.h>
#endif
-#ifdef __linux
+#ifdef __APPLE__
+# include <QCameraInfo>
+#endif
+
+#ifdef __linux__
# include <fcntl.h>
# include <sys/ioctl.h>
# include <linux/videodev2.h>
# include <cerrno>
+# include <cstring>
#endif
#include <QDebug>
-OTR_COMPAT_EXPORT int camera_name_to_index(const QString &name)
+int camera_name_to_index(const QString &name)
{
auto list = get_camera_names();
- int ret = list.indexOf(name);
- if (ret < 0)
- ret = 0;
+ auto it = std::find_if(list.cbegin(), list.cend(), [&name](const auto& tuple) {
+ const auto& [str, idx] = tuple;
+ return str == name;
+ });
+ if (it != list.cend())
+ {
+ const auto& [ str, idx ] = *it;
+ return idx;
+ }
+
+ return -1;
+}
+
+#ifdef _WIN32
+# include <QRegularExpression>
+
+static QString prop_to_qstring(IPropertyBag* pPropBag, const wchar_t* name)
+{
+ QString ret{};
+ VARIANT var;
+ VariantInit(&var);
+ HRESULT hr = pPropBag->Read(name, &var, nullptr);
+ if (SUCCEEDED(hr))
+ ret = QString{(const QChar*)var.bstrVal, int(std::wcslen(var.bstrVal))};
+ VariantClear(&var);
return ret;
}
-OTR_COMPAT_EXPORT QList<QString> get_camera_names()
+static QString device_path_from_qstring(const QString& str)
+{
+ // language=RegExp prefix=R"/( suffix=)/"
+ static const QRegularExpression regexp{R"/(#vid_([0-9a-f]{4})&pid_([0-9a-f]{4})&mi_([0-9a-f]{2})#([^#]+))/",
+ QRegularExpression::CaseInsensitiveOption};
+ auto match = regexp.match(str);
+ if (!match.hasMatch())
+ return {};
+ QString id = match.captured(4);
+ id.replace('&', '_');
+ return id;
+}
+
+#endif
+
+std::vector<std::tuple<QString, int>> get_camera_names()
{
- QList<QString> ret;
-#if defined(_WIN32)
+ std::vector<std::tuple<QString, int>> ret;
+#ifdef _WIN32
// Create the System Device Enumerator.
HRESULT hr;
CoInitialize(nullptr);
- ICreateDevEnum *pSysDevEnum = NULL;
- hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void **)&pSysDevEnum);
+ ICreateDevEnum *pSysDevEnum = nullptr;
+ hr = CoCreateInstance(CLSID_SystemDeviceEnum, nullptr, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void **)&pSysDevEnum);
if (FAILED(hr))
{
qDebug() << "failed CLSID_SystemDeviceEnum" << hr;
return ret;
}
// Obtain a class enumerator for the video compressor category.
- IEnumMoniker *pEnumCat = NULL;
+ IEnumMoniker *pEnumCat = nullptr;
hr = pSysDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnumCat, 0);
if (hr == S_OK) {
// Enumerate the monikers.
- IMoniker *pMoniker = NULL;
+ IMoniker *pMoniker = nullptr;
ULONG cFetched;
while (pEnumCat->Next(1, &pMoniker, &cFetched) == S_OK)
{
IPropertyBag *pPropBag;
- hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag);
- if (SUCCEEDED(hr)) {
+ hr = pMoniker->BindToStorage(nullptr, nullptr, IID_IPropertyBag, (void **)&pPropBag);
+ if (SUCCEEDED(hr)) {
// To retrieve the filter's friendly name, do the following:
- VARIANT varName;
- VariantInit(&varName);
- hr = pPropBag->Read(L"FriendlyName", &varName, 0);
if (SUCCEEDED(hr))
{
- // Display the name in your UI somehow.
- QString str((QChar*)varName.bstrVal, int(std::wcslen(varName.bstrVal)));
- ret.append(str);
+ QString str = prop_to_qstring(pPropBag, L"FriendlyName");
+ QString path = device_path_from_qstring(prop_to_qstring(pPropBag, L"DevicePath"));
+ if (!path.isNull())
+ str += QStringLiteral(" [%1]").arg(path);
+ ret.push_back({ str, (int)ret.size() });
}
- VariantClear(&varName);
-
- ////// To create an instance of the filter, do the following:
- ////IBaseFilter *pFilter;
- ////hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter,
- //// (void**)&pFilter);
- // Now add the filter to the graph.
- //Remember to release pFilter later.
pPropBag->Release();
}
pMoniker->Release();
}
pEnumCat->Release();
}
+#if 0
else
qDebug() << "failed CLSID_VideoInputDeviceCategory" << hr;
+#endif
pSysDevEnum->Release();
#endif
-#ifdef __linux
+
+#ifdef __linux__
for (int i = 0; i < 16; i++) {
- char buf[128];
- sprintf(buf, "/dev/video%d", i);
- if (access(buf, F_OK) == 0)
- ret.append(buf);
- else
- continue;
+ char buf[32];
+ snprintf(buf, sizeof(buf), "/dev/video%d", i);
if (access(buf, R_OK | W_OK) == 0) {
int fd = open(buf, O_RDONLY);
@@ -102,10 +137,16 @@ OTR_COMPAT_EXPORT QList<QString> get_camera_names()
close(fd);
continue;
}
- ret[ret.size()-1] = reinterpret_cast<const char*>(video_cap.card);
+ ret.push_back({ QString((const char*)video_cap.card), i});
close(fd);
}
}
#endif
+#ifdef __APPLE__
+ QList<QCameraInfo> cameras = QCameraInfo::availableCameras();
+ for (const QCameraInfo &cameraInfo : cameras)
+ ret.push_back({ cameraInfo.description(), ret.size() });
+#endif
+
return ret;
}
diff --git a/compat/camera-names.hpp b/compat/camera-names.hpp
index 97184c8c..3585edfe 100644
--- a/compat/camera-names.hpp
+++ b/compat/camera-names.hpp
@@ -8,11 +8,12 @@
#pragma once
-#include <QList>
+#include <vector>
#include <QString>
+# include <tuple>
#include "export.hpp"
-OTR_COMPAT_EXPORT QList<QString> get_camera_names();
+OTR_COMPAT_EXPORT std::vector<std::tuple<QString, int>> get_camera_names();
OTR_COMPAT_EXPORT int camera_name_to_index(const QString &name);
diff --git a/compat/check-visible.cpp b/compat/check-visible.cpp
new file mode 100644
index 00000000..621d941a
--- /dev/null
+++ b/compat/check-visible.cpp
@@ -0,0 +1,98 @@
+#include "check-visible.hpp"
+
+#include <QMutex>
+#include <QWidget>
+#include <QDebug>
+
+static QMutex lock;
+static bool visible = true;
+
+#if defined _WIN32
+
+#include "timer.hpp"
+#include "macros.h"
+
+static Timer timer;
+
+constexpr int visible_timeout = 1000;
+constexpr int invisible_timeout = 250;
+
+#include <windows.h>
+
+void set_is_visible(const QWidget& w, bool force)
+{
+ QMutexLocker l(&lock);
+
+ if (w.isHidden() || w.windowState() & Qt::WindowMinimized)
+ {
+ visible = false;
+ return;
+ }
+
+ {
+ int ndisplays = GetSystemMetrics(SM_CMONITORS);
+ if (ndisplays > 1)
+ {
+ visible = true;
+ return;
+ }
+ }
+
+ HWND hwnd = (HWND)w.winId();
+
+ if (!force && timer.elapsed_ms() < (visible ? visible_timeout : invisible_timeout))
+ return;
+
+ timer.start();
+
+ if (RECT r; GetWindowRect(hwnd, &r))
+ {
+ const int x = r.left, y = r.top;
+ const int w = r.right - x, h = r.bottom - y;
+
+ const POINT xs[] {
+ { x + w - 1, y + 1 },
+ { x + 1, y + h - 1 },
+ { x + w - 1, y + h - 1 },
+ { x + 1, y + 1 },
+ { x + w/2, y + h/2 },
+ };
+
+ visible = false;
+
+ for (const POINT& pt : xs)
+ if (WindowFromPoint(pt) == hwnd)
+ {
+
+ visible = true;
+ break;
+ }
+ }
+ else
+ {
+ eval_once(qDebug() << "check-visible: GetWindowRect failed");
+ visible = true;
+ }
+}
+
+#else
+
+void set_is_visible(const QWidget& w, bool)
+{
+ QMutexLocker l(&lock);
+ visible = !(w.isHidden() || w.windowState() & Qt::WindowMinimized);
+}
+
+#endif
+
+bool check_is_visible()
+{
+ QMutexLocker l(&lock);
+ return visible;
+}
+
+void force_is_visible(bool value)
+{
+ QMutexLocker l(&lock);
+ visible = value;
+}
diff --git a/compat/check-visible.hpp b/compat/check-visible.hpp
new file mode 100644
index 00000000..6669b84d
--- /dev/null
+++ b/compat/check-visible.hpp
@@ -0,0 +1,15 @@
+#pragma once
+
+#include "export.hpp"
+#include "macros.h"
+
+class QWidget;
+
+never_inline OTR_COMPAT_EXPORT
+void set_is_visible(QWidget const& w, bool force = false);
+
+OTR_COMPAT_EXPORT
+bool check_is_visible();
+
+OTR_COMPAT_EXPORT
+void force_is_visible(bool value);
diff --git a/compat/correlation-calibrator.cpp b/compat/correlation-calibrator.cpp
new file mode 100644
index 00000000..07ef7d3d
--- /dev/null
+++ b/compat/correlation-calibrator.cpp
@@ -0,0 +1,160 @@
+#include "correlation-calibrator.hpp"
+#include "variance.hpp"
+#include "compat/math.hpp"
+#include "compat/meta.hpp"
+#include "compat/macros.h"
+
+#include <cmath>
+#include <iterator>
+
+#include <QDebug>
+
+#define DEBUG_PRINT
+#ifdef DEBUG_PRINT
+# include <cstdio>
+#endif
+
+namespace correlation_calibrator_impl {
+
+static constexpr unsigned nbuckets[6] =
+{
+ x_nbuckets,
+ y_nbuckets,
+ z_nbuckets,
+
+ yaw_nbuckets,
+ pitch_nbuckets,
+ roll_nbuckets,
+};
+
+static constexpr double spacing[6] =
+{
+ translation_spacing,
+ translation_spacing,
+ translation_spacing,
+
+ yaw_spacing_in_degrees,
+ pitch_spacing_in_degrees,
+ roll_spacing_in_degrees,
+};
+
+static constexpr char const* const names[6] {
+ "x", "y", "z",
+ "yaw", "pitch", "roll",
+};
+
+bool correlation_calibrator::check_buckets(const vec6& data)
+{
+ bool ret = false;
+ unsigned pos[6];
+
+ for (unsigned k = 0; k < 6; k++)
+ {
+ const double val = std::clamp(data[k], min[k], max[k]);
+ pos[k] = unsigned((val-min[k])/spacing[k]);
+
+ if (pos[k] >= nbuckets[k])
+ {
+ eval_once(qDebug() << "idx" << k
+ << "bucket" << (int)pos[k]
+ << "outside bounds" << nbuckets[k]);
+
+ return false;
+ }
+
+ if (!buckets[k][pos[k]])
+ {
+ ret = true;
+ buckets[k][pos[k]] = true;
+ }
+ }
+
+ return ret;
+}
+
+void correlation_calibrator::input(const vec6& data_)
+{
+ if (!check_buckets(data_))
+ return;
+
+ data.push_back(data_);
+}
+
+mat66 correlation_calibrator::get_coefficients() const
+{
+ if (data.size() < min_samples)
+ {
+ qDebug() << "correlation-calibrator: not enough data";
+
+ mat66 ret;
+ for (unsigned k = 0; k < 6; k++)
+ ret(k, k) = 1;
+ return ret;
+ }
+
+ variance vs[6];
+ vec6 devs, means;
+
+ for (const vec6& x : data)
+ for (unsigned i = 0; i < 6; i++)
+ vs[i].input(x(i));
+
+ for (unsigned i = 0; i < 6; i++)
+ {
+ means(i) = vs[i].avg();
+ devs(i) = vs[i].stddev();
+
+ constexpr double EPS = 1e-4;
+
+ if (devs(i) < EPS)
+ devs(i) = EPS;
+ }
+
+ mat66 cs;
+
+ for (const vec6& x : data)
+ for (unsigned k = 0; k < 6; k++)
+ {
+ for (unsigned idx = 0; idx < 6; idx++)
+ {
+ const double zi = (x(idx) - means(idx)),
+ zk = (x(k) - means(k));
+
+ cs(idx, k) += zi * zk / (devs(k)*devs(k));
+ }
+ }
+
+ cs = cs * (1./(data.size() - 1));
+
+#if defined DEBUG_PRINT
+ fprintf(stderr, "v:change-of h:due-to\n");
+ fprintf(stderr, "%10s ", "");
+ for (char const* k : names)
+ fprintf(stderr, "%10s", k);
+ fprintf(stderr, "\n");
+
+ for (unsigned i = 0; i < 6; i++)
+ {
+ fprintf(stderr, "%10s ", names[i]);
+ for (unsigned k = 0; k < 6; k++)
+ fprintf(stderr, "%10.3f", cs(i, k));
+ fprintf(stderr, "\n");
+ }
+ fflush(stderr);
+#endif
+
+ for (unsigned k = 0; k < 6; k++)
+ cs(k, k) = 1;
+
+ // derivations from
+ // https://www.thoughtco.com/how-to-calculate-the-correlation-coefficient-3126228
+
+ return cs;
+}
+
+unsigned correlation_calibrator::sample_count() const
+{
+ return data.size();
+}
+
+} // ns correlation_calibrator_impl
diff --git a/compat/correlation-calibrator.hpp b/compat/correlation-calibrator.hpp
new file mode 100644
index 00000000..44aee537
--- /dev/null
+++ b/compat/correlation-calibrator.hpp
@@ -0,0 +1,76 @@
+#pragma once
+
+#include "simple-mat.hpp"
+#include <array>
+#include <vector>
+#include <tuple>
+
+#include "export.hpp"
+
+namespace correlation_calibrator_impl {
+
+static constexpr double min[6] = {
+ -50,
+ -50,
+ 250,
+
+ -180,
+ -180,
+ -180,
+};
+
+static constexpr double max[6] = {
+ 50,
+ 50,
+ 250,
+
+ 180,
+ 180,
+ 180,
+};
+
+static constexpr double yaw_spacing_in_degrees = 1.5;
+static constexpr double pitch_spacing_in_degrees = 1;
+static constexpr double roll_spacing_in_degrees = 1;
+
+static constexpr unsigned yaw_nbuckets = unsigned(1+ 360./yaw_spacing_in_degrees);
+static constexpr unsigned pitch_nbuckets = unsigned(1+ 360./pitch_spacing_in_degrees);
+static constexpr unsigned roll_nbuckets = unsigned(1+ 360./roll_spacing_in_degrees);
+
+static constexpr double translation_spacing = .25;
+static constexpr unsigned x_nbuckets = unsigned(1+ (max[0]-min[0])/translation_spacing);
+static constexpr unsigned y_nbuckets = unsigned(1+ (max[1]-min[1])/translation_spacing);
+static constexpr unsigned z_nbuckets = unsigned(1+ (max[2]-min[2])/translation_spacing);
+
+using vec6 = Mat<double, 6, 1>;
+using mat66 = Mat<double, 6, 6>;
+
+class OTR_COMPAT_EXPORT correlation_calibrator final
+{
+ // careful to avoid vector copies
+ std::array<std::vector<bool>, 6> buckets =
+ {
+ std::vector<bool>(x_nbuckets, false),
+ std::vector<bool>(y_nbuckets, false),
+ std::vector<bool>(z_nbuckets, false),
+ std::vector<bool>(yaw_nbuckets, false),
+ std::vector<bool>(pitch_nbuckets, false),
+ std::vector<bool>(roll_nbuckets, false),
+ };
+
+ std::vector<vec6> data;
+
+ bool check_buckets(const vec6& data);
+
+public:
+ correlation_calibrator() = default;
+ void input(const vec6& data);
+ mat66 get_coefficients() const;
+ unsigned sample_count() const;
+
+ static constexpr unsigned min_samples = 25;
+};
+
+} // ns correlation_calibrator_impl
+
+using correlation_calibrator = correlation_calibrator_impl::correlation_calibrator;
diff --git a/compat/enum-operators.hpp b/compat/enum-operators.hpp
new file mode 100644
index 00000000..e4f81a39
--- /dev/null
+++ b/compat/enum-operators.hpp
@@ -0,0 +1,45 @@
+#pragma once
+
+#include <type_traits>
+
+#define OTR_FLAGS_OP2(type, op) \
+ constexpr inline type operator op (type a, type b) \
+ { \
+ using t_ = std::underlying_type_t<type>; \
+ return type(t_((a)) op t_((b))); \
+ } // end
+
+#define OTR_FLAGS_DELETE_SHIFT(type, op) \
+ template<typename u> \
+ type operator op (type, u) = delete // end
+
+#define OTR_FLAGS_OP1(type, op) \
+ constexpr inline type operator op (type x) \
+ { \
+ using t_ = std::underlying_type_t<type>; \
+ return type(op t_((x))); \
+ } // end
+
+#define OTR_FLAGS_ASSIGN_OP(type, op) \
+ constexpr inline type& operator op ## = (type& lhs, type rhs) \
+ { \
+ using t_ = std::underlying_type_t<decltype(rhs)>; \
+ lhs = type(t_((lhs)) op t_((rhs))); \
+ return lhs; \
+ } //end
+
+#define OTR_FLAGS_DELETE_SHIFT_ASSIGN(type, op) \
+ type operator op ## = (type& lhs, type rhs) = delete //end
+
+#define DEFINE_ENUM_OPERATORS(type) \
+ OTR_FLAGS_OP2(type, |) \
+ OTR_FLAGS_OP2(type, &) \
+ OTR_FLAGS_OP2(type, ^) \
+ OTR_FLAGS_OP1(type, ~) \
+ OTR_FLAGS_DELETE_SHIFT(type, <<); \
+ OTR_FLAGS_DELETE_SHIFT(type, >>); \
+ OTR_FLAGS_ASSIGN_OP(type, |) \
+ OTR_FLAGS_ASSIGN_OP(type, &) \
+ OTR_FLAGS_ASSIGN_OP(type, ^) \
+ OTR_FLAGS_DELETE_SHIFT_ASSIGN(type, <<); \
+ OTR_FLAGS_DELETE_SHIFT_ASSIGN(type, >>) // end
diff --git a/compat/euler.cpp b/compat/euler.cpp
index e48d977b..5d6fb25b 100644
--- a/compat/euler.cpp
+++ b/compat/euler.cpp
@@ -1,35 +1,34 @@
#include "euler.hpp"
+#include "math-imports.hpp"
#include <cmath>
namespace euler {
-euler_t OTR_COMPAT_EXPORT rmat_to_euler(const rmat& R)
+Pose_ rmat_to_euler(const rmat& R)
{
- using std::atan2;
- using std::sqrt;
-
const double cy = sqrt(R(2,2)*R(2, 2) + R(2, 1)*R(2, 1));
const bool large_enough = cy > 1e-10;
if (large_enough)
- return euler_t(atan2(-R(1, 0), R(0, 0)),
- atan2(R(2, 0), cy),
- atan2(-R(2, 1), R(2, 2)));
+ return {
+ atan2(-R(1, 0), R(0, 0)),
+ atan2(R(2, 0), cy),
+ atan2(-R(2, 1), R(2, 2))
+ };
else
- return euler_t(atan2(R(0, 1), R(1, 1)),
- atan2(R(2, 0), cy),
- 0);
+ return {
+ atan2(R(0, 1), R(1, 1)),
+ atan2(R(2, 0), cy),
+ 0
+ };
}
// tait-bryan angles, not euler
-rmat OTR_COMPAT_EXPORT euler_to_rmat(const euler_t& input)
+rmat euler_to_rmat(const Pose_& input)
{
const double H = -input(0);
const double P = -input(1);
const double B = -input(2);
- using std::cos;
- using std::sin;
-
const auto c1 = cos(H);
const auto s1 = sin(H);
const auto c2 = cos(P);
@@ -37,99 +36,20 @@ rmat OTR_COMPAT_EXPORT euler_to_rmat(const euler_t& input)
const auto c3 = cos(B);
const auto s3 = sin(B);
- return rmat(
- // z
- c1 * c2,
- c1 * s2 * s3 - c3 * s1,
- s1 * s3 + c1 * c3 * s2,
- // y
- c2 * s1,
- c1 * c3 + s1 * s2 * s3,
- c3 * s1 * s2 - c1 * s3,
- // x
- -s2,
- c2 * s3,
- c2 * c3
- );
-}
-
-// https://en.wikipedia.org/wiki/Davenport_chained_rotations#Tait.E2.80.93Bryan_chained_rotations
-void OTR_COMPAT_EXPORT tait_bryan_to_matrices(const euler_t& input,
- rmat& r_roll,
- rmat& r_pitch,
- rmat& r_yaw)
-{
- using std::cos;
- using std::sin;
-
- {
- const double phi = -input(2);
- const double sin_phi = sin(phi);
- const double cos_phi = cos(phi);
-
- r_roll = rmat(1, 0, 0,
- 0, cos_phi, -sin_phi,
- 0, sin_phi, cos_phi);
- }
-
- {
- const double theta = input(1);
- const double sin_theta = sin(theta);
- const double cos_theta = cos(theta);
-
- r_pitch = rmat(cos_theta, 0, -sin_theta,
- 0, 1, 0,
- sin_theta, 0, cos_theta);
- }
-
- {
- const double psi = -input(0);
- const double sin_psi = sin(psi);
- const double cos_psi = cos(psi);
-
- r_yaw = rmat(cos_psi, -sin_psi, 0,
- sin_psi, cos_psi, 0,
- 0, 0, 1);
- }
-}
-
-// from http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/
-Quat matrix_to_quat(const rmat& M)
-{
- Quat q(1, 0, 0, 0);
-
- using std::sqrt;
-
- double trace = M(0, 0) + M(1, 1) + M(2, 2); // I removed + 1.0; see discussion with Ethan
- if( trace > 0 ) {// I changed M_EPSILON to 0
- double s = .5 / std::sqrt(trace + 1);
- q.w() = .25 / s;
- q.x() = ( M(2, 1) - M(1, 2) ) * s;
- q.y() = ( M(0, 2) - M(2, 0) ) * s;
- q.z() = ( M(1, 0) - M(0, 1) ) * s;
- } else {
- if ( M(0, 0) > M(1, 1) && M(0, 0) > M(2, 2) ) {
- double s = 2.0 * sqrt( 1.0 + M(0, 0) - M(1, 1) - M(2, 2));
- q.w() = (M(2, 1) - M(1, 2) ) / s;
- q.x() = .25 * s;
- q.y() = (M(0, 1) + M(1, 0) ) / s;
- q.z() = (M(0, 2) + M(2, 0) ) / s;
- } else if (M(1, 1) > M(2, 2)) {
- double s = 2.0 * sqrt( 1.0 + M(1, 1) - M(0, 0) - M(2, 2));
- q.w() = (M(0, 2) - M(2, 0) ) / s;
- q.x() = (M(0, 1) + M(1, 0) ) / s;
- q.y() = .25 * s;
- q.z() = (M(1, 2) + M(2, 1) ) / s;
- } else {
- double s = 2.0 * sqrt( 1.0 + M(2, 2) - M(0, 0) - M(1, 1) );
- q.w() = (M(1, 0) - M(0, 1) ) / s;
- q.x() = (M(0, 2) + M(2, 0) ) / s;
- q.y() = (M(1, 2) + M(2, 1) ) / s;
- q.z() = .25 * s;
- }
- }
-
- return q;
+ return {
+ // z
+ c1*c2,
+ c1*s2*s3 - c3*s1,
+ s1*s3 + c1*c3*s2,
+ // y
+ c2*s1,
+ c1*c3 + s1*s2*s3,
+ c3*s1*s2 - c1*s3,
+ // x
+ -s2,
+ c2*s3,
+ c2*c3
+ };
}
} // end ns euler
diff --git a/compat/euler.hpp b/compat/euler.hpp
index 4eb6e00c..b2f10ac0 100644
--- a/compat/euler.hpp
+++ b/compat/euler.hpp
@@ -1,5 +1,12 @@
#pragma once
+/* Copyright (c) 2016-2018 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.
+ */
+
#include "export.hpp"
#include "simple-mat.hpp"
@@ -7,22 +14,12 @@ namespace euler {
template<int h_, int w_> using dmat = Mat<double, h_, w_>;
-using dvec2 = Mat<double, 2, 1>;
using dvec3 = Mat<double, 3, 1>;
using rmat = dmat<3, 3>;
-using euler_t = dmat<3, 1>;
-
-rmat OTR_COMPAT_EXPORT euler_to_rmat(const euler_t& input);
-
-euler_t OTR_COMPAT_EXPORT rmat_to_euler(const rmat& R);
-
-void OTR_COMPAT_EXPORT tait_bryan_to_matrices(const euler_t& input,
- rmat& r_roll,
- rmat& r_pitch,
- rmat& r_yaw);
+using Pose_ = dmat<3, 1>;
-Quat OTR_COMPAT_EXPORT matrix_to_quat(const rmat& M);
-//XXX TODO rmat OTR_COMPAT_EXPORT quat_to_matrix(const Quat<double>& Q);
+rmat OTR_COMPAT_EXPORT euler_to_rmat(const Pose_& input);
+Pose_ OTR_COMPAT_EXPORT rmat_to_euler(const rmat& R);
} // end ns euler
diff --git a/compat/hamilton-tools.cpp b/compat/hamilton-tools.cpp
new file mode 100644
index 00000000..eb0ef984
--- /dev/null
+++ b/compat/hamilton-tools.cpp
@@ -0,0 +1,109 @@
+#include "hamilton-tools.h"
+#include <cmath>
+
+tQuat QuatFromAngleAxe(const double angle, const tVector& axe)
+{
+ double a = TO_RAD * 0.5 * angle;
+ double d = sin(a) / VectorLength(axe);
+ return ( tQuat (
+ axe.v[0] * d,
+ axe.v[1] * d,
+ axe.v[2] * d,
+ cos(a)
+ )
+ );
+}
+
+tQuat QuatMultiply(const tQuat& qL, const tQuat& qR)
+{
+ tQuat Q;
+ Q.x = qL.w*qR.x + qL.x*qR.w + qL.y*qR.z - qL.z*qR.y;
+ Q.y = qL.w*qR.y + qL.y*qR.w + qL.z*qR.x - qL.x*qR.z;
+ Q.z = qL.w*qR.z + qL.z*qR.w + qL.x*qR.y - qL.y*qR.x;
+ Q.w = qL.w*qR.w - qL.x*qR.x - qL.y*qR.y - qL.z*qR.z;
+ return(Q);
+}
+
+tQuat QuatFromYPR(const double YPR[])
+{
+ tQuat Q, Qp, Qy;
+ Q = QuatFromAngleAxe( -YPR[2], {0, 0, 1} ); //Roll, Z axe
+ Qp = QuatFromAngleAxe( -YPR[1], {1, 0, 0} ); //Pitch, X axe
+ Qy = QuatFromAngleAxe( -YPR[0], {0, 1, 0} ); //Yaw, Y axe
+
+ Q = QuatMultiply(Qp, Q);
+ return(QuatMultiply(Qy, Q));
+}
+
+void Normalize(tQuat& Q)
+{
+ double m = sqrt(Q.x*Q.x + Q.y*Q.y + Q.z*Q.z + Q.w*Q.w);
+ if (m > EPSILON)
+ {
+ m = 1 / m;
+ Q.x = Q.x * m;
+ Q.y = Q.y * m;
+ Q.z = Q.z * m;
+ Q.w = Q.w * m;
+ }
+ else Q = tQuat(0, 0, 0, 1);
+}
+
+tQuat Slerp(const tQuat& S, const tQuat& D, const double alpha)
+{
+ // calc cosine of half angle
+ double cosin = S.x*D.x + S.y*D.y + S.z*D.z + S.w*D.w;
+
+ // select nearest rotation direction
+ tQuat Q;
+ if (cosin < 0)
+ {
+ cosin = - cosin;
+ Q.x = - D.x;
+ Q.y = - D.y;
+ Q.z = - D.z;
+ Q.w = - D.w;
+ }
+ else Q = D;
+
+ // calculate coefficients
+ double scale0, scale1;
+ if ((1.0 - cosin) > EPSILON)
+ {
+ double omega = acos(cosin);
+ double sinus = 1 / sin(omega);
+ scale0 = sin((1.0 - alpha) * omega) * sinus;
+ scale1 = sin(alpha * omega)* sinus;
+ }
+ else
+ {
+ scale0 = 1.0 - alpha;
+ scale1 = alpha;
+ }
+
+ Q.x = scale0 * S.x + scale1 * Q.x;
+ Q.y = scale0 * S.y + scale1 * Q.y;
+ Q.z = scale0 * S.z + scale1 * Q.z;
+ Q.w = scale0 * S.w + scale1 * Q.w;
+
+ Normalize(Q);
+
+ return( Q );
+}
+
+void QuatToYPR(const tQuat& Q, double YPR[])
+{
+ const double xx = Q.x * Q.x;
+ const double xy = Q.x * Q.y;
+ const double xz = Q.x * Q.z;
+ const double xw = Q.x * Q.w;
+ const double yy = Q.y * Q.y;
+ const double yz = Q.y * Q.z;
+ const double yw = Q.y * Q.w;
+ const double zz = Q.z * Q.z;
+ const double zw = Q.z * Q.w;
+
+ YPR[0] = TO_DEG * ( -atan2( 2 * ( xz + yw ), 1 - 2 * ( xx + yy ) ));
+ YPR[1] = TO_DEG * ( asin ( 2 * ( yz - xw ) ));
+ YPR[2] = TO_DEG * ( -atan2( 2 * ( xy + zw ), 1 - 2 * ( xx + zz ) ));
+}
diff --git a/compat/hamilton-tools.h b/compat/hamilton-tools.h
new file mode 100644
index 00000000..885e9f79
--- /dev/null
+++ b/compat/hamilton-tools.h
@@ -0,0 +1,76 @@
+#pragma once
+
+#include <algorithm>
+#include "export.hpp"
+#include "compat/math.hpp"
+
+constexpr double TO_RAD = (M_PI / 180);
+constexpr double TO_DEG = (180 / M_PI);
+constexpr double EPSILON = 1e-30;
+
+struct tVector
+{
+ double v[3];
+ tVector(double X = 0, double Y = 0, double Z = 0) {v[0]=X; v[1]=Y; v[2]=Z;}
+ tVector(const double V[]) {v[0]=V[0]; v[1]=V[1]; v[2]=V[2];}
+
+ void operator=(const tVector& other)
+ {
+ std::copy(other.v, other.v + 3, v);
+ }
+ inline const tVector operator+(const tVector& other) const
+ {
+ return tVector(v[0] + other.v[0], v[1] + other.v[1], v[2] + other.v[2]);
+ }
+ void operator+=(const tVector& other)
+ {
+ v[0] += other.v[0];
+ v[1] += other.v[1];
+ v[2] += other.v[2];
+ }
+ const tVector operator-(const tVector& other) const
+ {
+ return tVector(v[0] - other.v[0], v[1] - other.v[1], v[2] - other.v[2]);
+ }
+ void operator-=(const tVector& other)
+ {
+ v[0] -= other.v[0];
+ v[1] -= other.v[1];
+ v[2] -= other.v[2];
+ }
+ const tVector operator*(double scalar) const
+ {
+ return tVector(v[0] * scalar, v[1] * scalar, v[2] * scalar);
+ }
+ void operator*=(double scalar)
+ {
+ v[0] *= scalar;
+ v[1] *= scalar;
+ v[2] *= scalar;
+ }
+ const tVector operator/(double scalar) const
+ {
+ return *this * (1.0 / scalar);
+ }
+ void operator/= (double scalar)
+ {
+ *this *= 1.0 / scalar;
+ }
+};
+
+struct tQuat
+{
+ double x, y, z, w;
+ tQuat(double X = 0, double Y = 0, double Z = 0, double W = 1)
+ {x = X; y = Y; z = Z; w = W;}
+};
+
+inline double VectorLength(const tVector& v) { return sqrt(v.v[0] * v.v[0] + v.v[1] * v.v[1] + v.v[2] * v.v[2]); }
+inline double VectorDistance(const tVector& v1, const tVector& v2) { return VectorLength(v2 - v1); }
+inline tVector Lerp(const tVector& s, const tVector& d, const double alpha) { return s + (d - s) * alpha; }
+tQuat OTR_COMPAT_EXPORT QuatFromYPR(const double YPR[]);
+tQuat OTR_COMPAT_EXPORT QuatMultiply(const tQuat& qL, const tQuat& qR);
+inline tQuat QuatDivide(const tQuat& qL, const tQuat& qR) { return QuatMultiply(qL, tQuat(-qR.x, -qR.y, -qR.z, qR.w)); }
+inline double AngleBetween(const tQuat& S, const tQuat& D) { return TO_DEG * 2 * acos(fabs(S.x * D.x + S.y * D.y + S.z * D.z + S.w * D.w)); }
+tQuat OTR_COMPAT_EXPORT Slerp(const tQuat& S, const tQuat& D, const double alpha);
+void OTR_COMPAT_EXPORT QuatToYPR(const tQuat& Q, double YPR[]);
diff --git a/compat/lang/de_DE.ts b/compat/lang/de_DE.ts
new file mode 100644
index 00000000..1552582e
--- /dev/null
+++ b/compat/lang/de_DE.ts
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+</TS>
diff --git a/compat/lang/zh_CN.ts b/compat/lang/zh_CN.ts
new file mode 100644
index 00000000..e5ca8aa9
--- /dev/null
+++ b/compat/lang/zh_CN.ts
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="zh_CN">
+</TS>
diff --git a/compat/library-path.hpp b/compat/library-path.hpp
new file mode 100644
index 00000000..49e2c414
--- /dev/null
+++ b/compat/library-path.hpp
@@ -0,0 +1,4 @@
+#pragma once
+
+// from build directory
+#include "opentrack-library-path.hxx"
diff --git a/compat/linkage-macros.hpp b/compat/linkage-macros.hpp
index b9bc9801..f4d0104b 100644
--- a/compat/linkage-macros.hpp
+++ b/compat/linkage-macros.hpp
@@ -1,15 +1,26 @@
#pragma once
-#if defined _MSC_VER || defined _WIN32
-# define OTR_GENERIC_EXPORT __declspec(dllexport)
-# define OTR_GENERIC_IMPORT __declspec(dllimport)
-#else
-# define OTR_GENERIC_EXPORT __attribute__ ((visibility ("default")))
-# define OTR_GENERIC_IMPORT
+#ifndef OTR_GENERIC_EXPORT
+# if defined _MSC_VER
+# define OTR_GENERIC_EXPORT __declspec(dllexport)
+# define OTR_GENERIC_IMPORT __declspec(dllimport)
+# elif defined _WIN32 && !defined __WINE__
+# define OTR_GENERIC_EXPORT __attribute__((dllexport, visibility ("default")))
+# define OTR_GENERIC_IMPORT __attribute__((dllimport))
+# else
+# define OTR_GENERIC_EXPORT __attribute__((visibility ("default")))
+# define OTR_GENERIC_IMPORT
+# endif
+#endif
+
+#if defined __APPLE__ || defined __MINGW32__
+# define OTR_NO_TMPL_INST // link failure on both targets
#endif
-#if defined _MSC_VER
-# define OTR_GENERIC_TEMPLATE
+#if defined OTR_NO_TMPL_INST
+# define OTR_TEMPLATE_IMPORT(x)
+# define OTR_TEMPLATE_EXPORT(x)
#else
-# define OTR_GENERIC_TEMPLATE __attribute__ ((visibility ("default")))
+# define OTR_TEMPLATE_IMPORT(x) extern template class OTR_GENERIC_IMPORT x;
+# define OTR_TEMPLATE_EXPORT(x) template class OTR_GENERIC_EXPORT x;
#endif
diff --git a/compat/macros.h b/compat/macros.h
new file mode 100644
index 00000000..7de7fb86
--- /dev/null
+++ b/compat/macros.h
@@ -0,0 +1,57 @@
+#pragma once
+
+#if defined _MSC_VER
+# define never_inline __declspec(noinline)
+#else
+# define never_inline __attribute__((noinline))
+#endif
+
+#if defined _MSC_VER
+# define force_inline __forceinline
+#else
+# define force_inline __attribute__((always_inline)) inline
+#endif
+
+#if !defined likely
+# if defined __GNUC__
+# define likely(x) __builtin_expect(!!(x),1)
+# define unlikely(x) __builtin_expect(!!(x),0)
+# else
+# define likely(x) (x)
+# define unlikely(x) (x)
+# endif
+#endif
+
+#if defined _MSC_VER
+# define function_name __FUNCSIG__
+#else
+# define function_name __PRETTY_FUNCTION__
+#endif
+
+#ifdef _MSC_VER
+# define unreachable() do { __assume(0); *(volatile int*)nullptr = 0; } while (0) /* NOLINT(clang-analyzer-core.NullDereference) */
+#else
+# define unreachable() do { __builtin_unreachable(); *(volatile int*)nullptr = 0; } while (0) /* NOLINT(clang-analyzer-core.NullDereference) */
+#endif
+
+#ifdef __cplusplus
+# define progn(...) ([&]() -> decltype(auto) { __VA_ARGS__ }())
+# define eval_once2(expr, ctr) eval_once3(expr, ctr)
+# define eval_once3(expr, ctr) ([&] { [[maybe_unused]] static const char init_ ## ctr = ((void)(expr), 0); }())
+# ifdef QT_NO_DEBUG_OUTPUT
+# define eval_once(expr) void()
+# else
+# define eval_once(expr) eval_once2(expr, __COUNTER__)
+# endif
+
+#define OTR_DISABLE_MOVE_COPY(type) \
+ type(const type&) = delete; \
+ type(type&&) = delete; \
+ type& operator=(const type&) = delete; \
+ type& operator=(type&&) = delete
+#endif
+
+#ifdef _MSC_VER
+# define strncasecmp _strnicmp
+# define strcasecmp _stricmp
+#endif
diff --git a/compat/math-imports.hpp b/compat/math-imports.hpp
new file mode 100644
index 00000000..67b7e6ed
--- /dev/null
+++ b/compat/math-imports.hpp
@@ -0,0 +1,49 @@
+#pragma once
+
+#include <cmath>
+
+using std::copysign;
+
+using std::sqrt;
+using std::pow;
+
+using std::fabs;
+using std::fmin;
+using std::fmax;
+
+using std::atan;
+using std::atan2;
+using std::asin;
+using std::acos;
+
+using std::sin;
+using std::cos;
+using std::tan;
+
+using std::round;
+using std::fmod;
+
+using std::log;
+using std::log2;
+using std::log10;
+using std::exp;
+
+#include <cinttypes>
+
+using std::uintptr_t;
+using std::intptr_t;
+
+using std::int64_t;
+using std::int32_t;
+using std::int8_t;
+
+using std::uint64_t;
+using std::uint32_t;
+using std::uint8_t;
+
+#include <algorithm>
+
+using std::min;
+using std::max;
+
+#include "math.hpp"
diff --git a/compat/math.hpp b/compat/math.hpp
new file mode 100644
index 00000000..c5981cc2
--- /dev/null
+++ b/compat/math.hpp
@@ -0,0 +1,16 @@
+#pragma once
+
+#include <cmath>
+#include <type_traits>
+
+template<typename t>
+inline auto iround(t val) -> std::enable_if_t<std::is_floating_point_v<std::decay_t<t>>, int>
+{
+ return (int)std::round(val);
+}
+
+template <typename t>
+constexpr int signum(const t& x)
+{
+ return x < t{0} ? -1 : 1;
+}
diff --git a/compat/meta.hpp b/compat/meta.hpp
new file mode 100644
index 00000000..49686996
--- /dev/null
+++ b/compat/meta.hpp
@@ -0,0 +1,123 @@
+#pragma once
+
+/* Copyright (c) 2017 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.
+ */
+
+namespace meta::detail {
+
+ template<typename... xs>
+ struct tuple;
+
+ template<typename... xs>
+ struct reverse_;
+
+ template<typename x0, typename... xs, template<typename...> class x, typename... ys>
+ struct reverse_<x<x0, xs...>, x<ys...>>
+ {
+ using type = typename reverse_<x<xs...>, x<x0, ys...>>::type;
+ };
+
+ template<template<typename...> class x, typename... ys>
+ struct reverse_<x<>, x<ys...>>
+ {
+ using type = x<ys...>;
+ };
+
+ template<template<typename...> class, typename, typename...> struct lift_;
+
+ template<template<typename...> class to, template<typename...> class from, typename... xs>
+ struct lift_<to, from<xs...>>
+ {
+ using type = to<xs...>;
+ };
+
+ template<typename> struct append_helper;
+
+ template<typename, typename> struct cons_;
+
+ template<typename x, template<typename...> class t, typename... xs>
+ struct cons_<x, t<xs...>>
+ {
+ using type = t<x, xs...>;
+ };
+
+ template<typename> struct append2;
+
+ template<template<typename...> class t, typename... xs>
+ struct append2<t<xs...>>
+ {
+ template<typename> struct append1;
+
+ template<template<typename...> class u, typename... ys>
+ struct append1<u<ys...>>
+ {
+ using type = t<xs..., ys...>;
+ };
+ };
+
+ template<typename, typename...> struct list__;
+
+ template<typename rest, typename... first>
+ struct list__
+ {
+ template<typename> struct list1;
+
+ template<template<typename...> class t, typename... xs>
+ struct list1<t<xs...>>
+ {
+ using type = t<first..., xs...>;
+ };
+
+ using type = typename list1<rest>::type;
+ };
+
+ template<typename xs, typename ys>
+ struct append_
+ {
+ using t1 = append2<xs>;
+ using type = typename t1::template append1<ys>::type;
+ };
+
+} // ns meta::detail
+
+namespace meta {
+ template<typename... xs>
+ using tuple_ = detail::tuple<xs...>;
+
+ template<typename... xs>
+ using reverse = typename detail::reverse_<detail::tuple<xs...>, detail::tuple<>>::type;
+
+ // the to/from order is awkward but mimics function composition
+ template<template<typename...> class to, typename from>
+ using lift = typename detail::lift_<to, from>::type;
+
+ template<template<typename...> class to, typename from>
+ constexpr inline auto lift_v = detail::lift_<to, from>::type::value;
+
+ template<typename x, typename... xs>
+ using first = x;
+
+ template<typename x, typename... xs>
+ using rest = detail::tuple<xs...>;
+
+ template<typename... xs>
+ using butlast = reverse<rest<reverse<xs...>>>;
+
+ template<typename... xs>
+ using last = lift<first, reverse<xs...>>;
+
+ template<typename x, typename rest>
+ using cons = typename detail::cons_<x, rest>::type;
+
+ template<typename xs, typename ys>
+ using append = typename detail::append_<xs, ys>;
+
+ template<typename rest, typename... xs>
+ using list_ = typename detail::list__<rest, xs...>;
+
+} // ns meta
+
diff --git a/compat/mutex.cpp b/compat/mutex.cpp
new file mode 100644
index 00000000..664677ea
--- /dev/null
+++ b/compat/mutex.cpp
@@ -0,0 +1,33 @@
+#include "mutex.hpp"
+#include <cstdlib>
+
+mutex& mutex::operator=(const mutex& rhs)
+{
+ if (rhs->isRecursive() != inner.isRecursive())
+ std::abort();
+
+ return *this;
+}
+
+mutex::mutex(const mutex& datum) : mutex{datum.inner.isRecursive() ? Recursive : NonRecursive}
+{
+}
+
+mutex::mutex(RecursionMode m) : inner{m}
+{
+}
+
+QMutex* mutex::operator&() const noexcept
+{
+ return &inner;
+}
+
+mutex::operator QMutex*() const noexcept
+{
+ return &inner;
+}
+
+QMutex* mutex::operator->() const noexcept
+{
+ return &inner;
+}
diff --git a/compat/mutex.hpp b/compat/mutex.hpp
new file mode 100644
index 00000000..54758a08
--- /dev/null
+++ b/compat/mutex.hpp
@@ -0,0 +1,24 @@
+#pragma once
+
+#include <QMutex>
+
+#include "export.hpp"
+
+class OTR_COMPAT_EXPORT mutex
+{
+ mutable QMutex inner;
+
+public:
+ using RecursionMode = QMutex::RecursionMode;
+ static constexpr RecursionMode Recursive = RecursionMode::Recursive;
+ static constexpr RecursionMode NonRecursive = RecursionMode::NonRecursive;
+
+ mutex& operator=(const mutex& datum);
+ mutex(const mutex& datum);
+ explicit mutex(RecursionMode m);
+ mutex() : mutex{NonRecursive} {}
+
+ QMutex* operator&() const noexcept;
+ explicit operator QMutex*() const noexcept;
+ QMutex* operator->() const noexcept;
+};
diff --git a/compat/nan.cpp b/compat/nan.cpp
deleted file mode 100644
index 9c7bf147..00000000
--- a/compat/nan.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-#include "export.hpp"
-
-#if defined(_MSC_VER)
-# include <cmath>
-# define my_isnan std::isnan
-# define my_isinf std::isinf
-extern "C" OTR_COMPAT_EXPORT __declspec(noinline) bool nanp(double x)
-#elif defined __MINGW32__
-
-int __cdecl my_isnan(double)__asm__("__isnan");
-int __cdecl my_fpclassify(double)__asm__("___fpclassify");
-
-#define FP_NAN 0x0100
-#define FP_NORMAL 0x0400
-#define FP_INFINITE (FP_NAN | FP_NORMAL)
-#define FP_ZERO 0x4000
-#define FP_SUBNORMAL (FP_NORMAL | FP_ZERO)
-
-#define my_isinf(x) (my_fpclassify(x) == FP_INFINITE)
-
-extern "C" OTR_COMPAT_EXPORT bool __attribute__ ((noinline)) nanp(double x)
-#else
-int my_isnan(double)__asm__("isnan");
-int my_isinf(double)__asm__("isinf");
-
-extern "C" OTR_COMPAT_EXPORT bool __attribute__ ((noinline)) nanp(double x)
-#endif
-{
- return my_isnan(x) || my_isinf(x);
-}
diff --git a/compat/nan.hpp b/compat/nan.hpp
deleted file mode 100644
index 0a2c900c..00000000
--- a/compat/nan.hpp
+++ /dev/null
@@ -1,11 +0,0 @@
-#pragma once
-
-#include "export.hpp"
-
-#if defined(__GNUC__)
-extern "C" OTR_COMPAT_EXPORT bool __attribute__ ((noinline)) nanp(double value);
-#elif defined(_WIN32)
-extern "C" OTR_COMPAT_EXPORT __declspec(noinline) bool nanp(double value);
-#else
-extern "C" OTR_COMPAT_EXPORT bool nanp(double value);
-#endif
diff --git a/compat/ndebug-guard.hpp b/compat/ndebug-guard.hpp
deleted file mode 100644
index e38fd0fe..00000000
--- a/compat/ndebug-guard.hpp
+++ /dev/null
@@ -1,5 +0,0 @@
-#ifdef NDEBUG
-# error "NDEBUG defined, don't define it"
-#endif
-
-#include <cassert>
diff --git a/compat/process-list.hpp b/compat/process-list.hpp
index 10613791..39e12603 100644
--- a/compat/process-list.hpp
+++ b/compat/process-list.hpp
@@ -52,7 +52,7 @@ static QStringList get_all_executable_names()
template<typename = void>
static QStringList get_all_executable_names()
{
- QStringList ret;
+ QStringList ret; ret.reserve(2048);
std::vector<int> vec;
while (true)
@@ -103,7 +103,7 @@ static QStringList get_all_executable_names()
if (idx != -1)
{
QString tmp = cmdline[0].mid(idx+1);
- if (cmdline.size() > 1 && (tmp == "wine.bin" || tmp == "wine"))
+ if (cmdline.size() > 1 && (tmp == QStringLiteral("wine.bin") || tmp == QStringLiteral("wine")))
{
idx = cmdline[1].lastIndexOf('/');
if (idx == -1)
@@ -129,14 +129,68 @@ static QStringList get_all_executable_names()
}
}
-#elif defined __linux
+#elif defined __linux__
+
+#include <cerrno>
+
+#ifdef OTR_HAS_LIBPROC2
+#include <libproc2/pids.h>
+template<typename = void>
+QStringList get_all_executable_names()
+{
+ QStringList ret; ret.reserve(2048);
+ enum pids_item items[] = { PIDS_ID_PID, PIDS_CMD, PIDS_CMDLINE_V };
+
+ enum rel_items { rel_pid, rel_cmd, rel_cmdline };
+ struct pids_info *info = NULL;
+ struct pids_stack *stack;
+ QString tmp; tmp.reserve(255);
+
+ procps_pids_new(&info, items, 3);
+ // procps-ng version 4.0.5 removed an unused argument in PIDS_VAL() macro.
+ // cf. https://gitlab.com/procps-ng/procps/-/commit/967fdcfb06e20aad0f3
+
+ // Although the emitted machine code is identical, backward API
+ // compatibility was silently broken in the patch with no upgrade path
+ // (e.g. deprecating PIDS_VAL() while introducing PIDS_VAL2()).
+
+ // Unfortunately, procps-ng doesn't include a #define for identifying its
+ // version. For these reasons the code below depends on undocumented ABI
+ // compatibility between procps-ng versions.. -sh 20241226
+
+#define OPENTRACK_PIDS_VAL(i, type, stack) stack->head[i].result.type
+
+ while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY)))
+ {
+ char **p_cmdline = OPENTRACK_PIDS_VAL(rel_cmdline, strv, stack);
+
+ // note, wine sets argv[0] so no parsing like in OSX case
+ if (p_cmdline && p_cmdline[0] && p_cmdline[0][0] &&
+ !(p_cmdline[0][0] == '-' && !p_cmdline[0][1]))
+ {
+ tmp = QString{p_cmdline[0]};
+ const int idx = std::max(tmp.lastIndexOf('\\'), tmp.lastIndexOf('/'));
+ if (idx != -1)
+ tmp = tmp.mid(idx+1);
+ //qDebug() << "procps" << tmp;
+ ret.append(tmp);
+ }
+ }
+ //qDebug() << "-- procps end";
+
+ procps_pids_unref(&info);
+
+ return ret;
+}
+#else
#include <proc/readproc.h>
#include <cerrno>
+
template<typename = void>
-static QStringList get_all_executable_names()
+QStringList get_all_executable_names()
{
- QStringList ret;
+ QStringList ret; ret.reserve(2048);
proc_t** procs = readproctab(PROC_FILLCOM);
if (procs == nullptr)
{
@@ -159,6 +213,7 @@ static QStringList get_all_executable_names()
free(procs);
return ret;
}
+#endif
#else
template<typename = void>
diff --git a/compat/qcopyable-mutex.hpp b/compat/qcopyable-mutex.hpp
deleted file mode 100644
index 57b0030d..00000000
--- a/compat/qcopyable-mutex.hpp
+++ /dev/null
@@ -1,37 +0,0 @@
-#pragma once
-
-#include <QMutex>
-
-class MyMutex {
-private:
- QMutex inner;
-
-public:
- QMutex* operator->() { return &inner; }
- QMutex* operator->() const { return &const_cast<MyMutex*>(this)->inner; }
-
- MyMutex operator=(const MyMutex& datum)
- {
- auto mode =
- datum->isRecursive()
- ? QMutex::Recursive
- : QMutex::NonRecursive;
-
- return MyMutex(mode);
- }
-
- MyMutex(const MyMutex& datum)
- {
- *this = datum;
- }
-
- MyMutex(QMutex::RecursionMode mode = QMutex::NonRecursive) :
- inner(mode)
- {
- }
-
- QMutex* operator&() const
- {
- return const_cast<QMutex*>(&inner);
- }
-};
diff --git a/compat/qhash.hpp b/compat/qhash.hpp
new file mode 100644
index 00000000..5286e97e
--- /dev/null
+++ b/compat/qhash.hpp
@@ -0,0 +1,26 @@
+#pragma once
+
+#include <functional>
+
+#include <QtGlobal>
+#include <QString>
+#include <QHashFunctions>
+
+#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
+
+#include <cstdlib>
+
+namespace std {
+template<> struct hash<QString>
+{
+ using argument_type = QString;
+ using result_type = std::size_t;
+
+ std::size_t operator()(const QString& value) const noexcept
+ {
+ return (std::size_t) qHash(value);
+ }
+};
+}
+
+#endif
diff --git a/compat/qt-dpi.hpp b/compat/qt-dpi.hpp
new file mode 100644
index 00000000..fc17a062
--- /dev/null
+++ b/compat/qt-dpi.hpp
@@ -0,0 +1,24 @@
+#pragma once
+
+#include <algorithm>
+#include <QWidget>
+
+static inline double screen_dpi(const QPaintDevice* widget)
+{
+ const auto& x = *widget;
+#ifdef _WIN32
+ return std::max(x.devicePixelRatioF(), 1.);
+#else
+ return std::max(std::max(x.logicalDpiX()/(double)x.physicalDpiX(), x.devicePixelRatioF()), 1.);
+#endif
+}
+
+template<typename self>
+struct screen_dpi_mixin
+{
+protected:
+ double screen_dpi() const
+ {
+ return ::screen_dpi(static_cast<const self*>(this));
+ }
+};
diff --git a/compat/qt-signal.cpp b/compat/qt-signal.cpp
new file mode 100644
index 00000000..8c23866b
--- /dev/null
+++ b/compat/qt-signal.cpp
@@ -0,0 +1,7 @@
+#define OTR_GENERATE_SIGNAL3(type) void sig_##type::operator()(const type& x) const { notify(x); }
+#include "qt-signal.hpp"
+namespace _qt_sig_impl {
+
+sig_void::sig_void(QObject* parent) : QObject(parent) {}
+
+}
diff --git a/compat/qt-signal.hpp b/compat/qt-signal.hpp
new file mode 100644
index 00000000..f74de642
--- /dev/null
+++ b/compat/qt-signal.hpp
@@ -0,0 +1,72 @@
+#pragma once
+
+// this is to avoid dealing with QMetaObject for the time being -sh 20190203
+
+#include "export.hpp"
+namespace options { class slider_value; }
+#include <QObject>
+#include <QList>
+
+namespace _qt_sig_impl {
+
+template<typename t> struct sig;
+
+class OTR_COMPAT_EXPORT sig_void final : public QObject
+{
+ Q_OBJECT
+
+public:
+ template<typename t, typename F>
+ sig_void(t* datum, F&& f, Qt::ConnectionType conntype = Qt::AutoConnection) : QObject(datum)
+ {
+ connect(this, &sig_void::notify, datum, f, conntype);
+ }
+ explicit sig_void(QObject* parent = nullptr);
+ void operator()() const { notify(); }
+
+signals:
+ void notify() const;
+};
+
+template<> struct sig<void> { using t = sig_void; };
+
+#ifndef OTR_GENERATE_SIGNAL3
+# define OTR_GENERATE_SIGNAL3(x)
+#endif
+#define OTR_GENERATE_SIGNAL2(type) \
+ class OTR_COMPAT_EXPORT sig_##type final : public QObject \
+ { \
+ Q_OBJECT \
+ public: \
+ explicit sig_##type(QObject* parent = nullptr) : QObject(parent) {} \
+ void operator()(const type& x) const; \
+ Q_SIGNALS: \
+ void notify(const type& x) const; \
+ }; \
+ OTR_GENERATE_SIGNAL3(type)
+
+# define OTR_GENERATE_SIGNAL(type) \
+ OTR_GENERATE_SIGNAL2(type); \
+ using qlist##type = QList<type>; \
+ OTR_GENERATE_SIGNAL2(qlist##type); \
+ template<> struct sig<type> { using t = sig_##type; }; \
+ template<> struct sig<qlist##type> { using t = qlist##type; }
+
+using slider_value = options::slider_value;
+
+OTR_GENERATE_SIGNAL(int);
+OTR_GENERATE_SIGNAL(double);
+OTR_GENERATE_SIGNAL(float);
+OTR_GENERATE_SIGNAL(bool);
+OTR_GENERATE_SIGNAL(QString);
+OTR_GENERATE_SIGNAL(slider_value);
+OTR_GENERATE_SIGNAL(QPointF);
+OTR_GENERATE_SIGNAL(QVariant);
+
+} // namespace _qt_sig_impl
+
+#undef OTR_GENERATE_SIGNAL2
+#undef OTR_GENERATE_SIGNAL
+
+template<typename t> using qt_signal = typename _qt_sig_impl::sig<t>::t;
+
diff --git a/compat/run-in-thread.hpp b/compat/run-in-thread.hpp
index ba99d0b9..c552c600 100644
--- a/compat/run-in-thread.hpp
+++ b/compat/run-in-thread.hpp
@@ -1,8 +1,12 @@
#pragma once
-#include "ndebug-guard.hpp"
+/* Copyright (c) 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.
+ */
-#include <cassert>
#include <thread>
#include <condition_variable>
#include <utility>
@@ -10,98 +14,56 @@
#include <QObject>
#include <QThread>
-namespace qt_impl_detail {
-
-template<typename t>
-struct run_in_thread_traits
+namespace impl_run_in_thread {
+struct semaphore final
{
- using type = t;
- using ret_type = t;
- static inline void assign(t& lvalue, const t& rvalue) { lvalue = rvalue; }
- static inline t pass(const t& val) { return val; }
- template<typename F> static inline t call(F&& fun) { return std::move(fun()); }
-};
+ using lock_guard = std::unique_lock<std::mutex>;
+ std::mutex mtx;
+ std::condition_variable cvar;
+ bool flag = false;
-template<typename u>
-struct run_in_thread_traits<u&&>
-{
- using t = typename std::remove_reference<u>::type;
- using type = t;
- using ret_type = u;
- static inline void assign(t& lvalue, t&& rvalue) { lvalue = rvalue; }
- static inline t&& pass(t&& val) { return val; }
- template<typename F> static inline t&& call(F&& fun) { return std::move(fun()); }
-};
+ semaphore() = default;
-template<>
-struct run_in_thread_traits<void>
-{
- using type = unsigned char;
- using ret_type = void;
- static inline void assign(unsigned char&, unsigned char&&) {}
- static inline void pass(type&&) {}
- template<typename F> static type call(F&& fun) { fun(); return type(0); }
-};
+ void wait()
+ {
+ lock_guard guard(mtx);
+ while (!flag)
+ cvar.wait(guard);
+ }
+ void notify()
+ {
+ lock_guard guard(mtx);
+ flag = true;
+ cvar.notify_one();
+ }
+};
}
template<typename F>
-auto run_in_thread_sync(QObject* obj, F&& fun)
- -> typename qt_impl_detail::run_in_thread_traits<decltype(std::forward<F>(fun)())>::ret_type
+void run_in_thread_sync(QObject* obj, F&& fun)
{
- using lock_guard = std::unique_lock<std::mutex>;
-
- using traits = qt_impl_detail::run_in_thread_traits<decltype(std::forward<F>(fun)())>;
-
- typename traits::type ret;
-
- struct semaphore final
- {
- std::mutex mtx;
- std::condition_variable cvar;
- bool flag;
-
- semaphore() : flag(false) {}
-
- void wait()
- {
- lock_guard guard(mtx);
- while (!flag)
- cvar.wait(guard);
- }
+ if (obj->thread() == QThread::currentThread())
+ return (void)fun();
- void notify()
- {
- lock_guard guard(mtx);
- flag = true;
- cvar.notify_one();
- }
- };
-
- semaphore sem;
+ impl_run_in_thread::semaphore sem;
{
QObject src;
- QObject::connect(&src,
- &QObject::destroyed,
- obj,
- [&]() {
- traits::assign(ret, traits::call(fun));
- sem.notify();
- },
- Qt::AutoConnection);
+ QObject::connect(&src, &QObject::destroyed,
+ obj, [&] { fun(); sem.notify(); },
+ Qt::QueuedConnection);
}
sem.wait();
- return traits::pass(std::move(ret));
}
template<typename F>
void run_in_thread_async(QObject* obj, F&& fun)
{
+ if (obj->thread() == QThread::currentThread())
+ return (void)fun();
+
QObject src;
- QThread* t(obj->thread());
- assert(t);
- src.moveToThread(t);
- QObject::connect(&src, &QObject::destroyed, obj, std::move(fun), Qt::AutoConnection);
+ QObject::connect(&src, &QObject::destroyed, obj, std::forward<F>(fun), Qt::QueuedConnection);
}
diff --git a/compat/shm.cpp b/compat/shm.cpp
index 6aeb8ca9..f59469dc 100644
--- a/compat/shm.cpp
+++ b/compat/shm.cpp
@@ -7,190 +7,131 @@
#include "shm.h"
-#if defined(_WIN32)
+#if defined _WIN32
#include <cstring>
-#include <stdio.h>
+#include <cstdio>
#include <accctrl.h>
#include <aclapi.h>
-struct secattr
-{
- bool success;
- SECURITY_DESCRIPTOR* pSD;
- SECURITY_ATTRIBUTES attrs;
- PSID pEveryoneSID;
- PACL pACL;
-
- void cleanup()
- {
- if (pEveryoneSID)
- FreeSid(pEveryoneSID);
- if (pACL)
- LocalFree(pACL);
- if (pSD)
- LocalFree(pSD);
- success = false;
- pSD = nullptr;
- pEveryoneSID = nullptr;
- pACL = nullptr;
- }
+#ifdef QT_CORE_LIB
+# include <QDebug>
+# define warn(str, ...) (qDebug() << "shm:" str ": " << __VA_ARGS__)
+#else
+# define warn(str, ...) (void)0
+#endif
- secattr(DWORD perms) : success(true), pSD(nullptr), pEveryoneSID(nullptr), pACL(nullptr)
+shm_wrapper::shm_wrapper(const char* shm_name, const char* mutex_name, int map_size)
+{
+ if (mutex_name == nullptr)
+ mutex = nullptr;
+ else
{
- SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
- EXPLICIT_ACCESS ea;
-
- if(!AllocateAndInitializeSid(&SIDAuthWorld, 1,
- SECURITY_WORLD_RID,
- 0, 0, 0, 0, 0, 0, 0,
- &pEveryoneSID))
- {
- fprintf(stderr, "AllocateAndInitializeSid: %d\n", (int) GetLastError());
- goto cleanup;
- }
-
- memset(&ea, 0, sizeof(ea));
+ mutex = CreateMutexA(nullptr, false, mutex_name);
- ea.grfAccessPermissions = perms;
- ea.grfAccessMode = SET_ACCESS;
- ea.grfInheritance = NO_INHERITANCE;
- ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
- ea.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
- ea.Trustee.ptstrName = (LPTSTR) pEveryoneSID;
-
- if (SetEntriesInAcl(1, &ea, NULL, &pACL) != ERROR_SUCCESS)
- {
- fprintf(stderr, "SetEntriesInAcl: %d\n", (int) GetLastError());
- goto cleanup;
- }
-
- pSD = (SECURITY_DESCRIPTOR*) LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
- if (pSD == nullptr)
+ if (!mutex)
{
- fprintf(stderr, "LocalAlloc: %d\n", (int) GetLastError());
- goto cleanup;
+ warn("CreateMutexA", (int) GetLastError());
+ return;
}
-
- if (!InitializeSecurityDescriptor(pSD,
- SECURITY_DESCRIPTOR_REVISION))
- {
- fprintf(stderr, "InitializeSecurityDescriptor: %d\n", (int) GetLastError());
- goto cleanup;
- }
-
- if (!SetSecurityDescriptorDacl(pSD,
- TRUE,
- pACL,
- FALSE))
- {
- fprintf(stderr, "SetSecurityDescriptorDacl: %d\n", (int) GetLastError());
- goto cleanup;
- }
-
- attrs.bInheritHandle = false;
- attrs.lpSecurityDescriptor = pSD;
- attrs.nLength = sizeof(SECURITY_ATTRIBUTES);
-
- return;
-cleanup:
- cleanup();
- }
-
- ~secattr()
- {
- cleanup();
}
-};
-PortableLockedShm::PortableLockedShm(const char* shmName, const char* mutexName, int mapSize)
-{
- secattr sa(GENERIC_ALL|SYNCHRONIZE);
-
- hMutex = CreateMutexA(sa.success ? &sa.attrs : nullptr, false, mutexName);
- if (!hMutex)
- {
- fprintf(stderr, "CreateMutexA: %d\n", (int) GetLastError());
- fflush(stderr);
- }
- hMapFile = CreateFileMappingA(
+ mapped_file = CreateFileMappingA(
INVALID_HANDLE_VALUE,
- sa.success ? &sa.attrs : nullptr,
+ nullptr,
PAGE_READWRITE,
0,
- mapSize,
- shmName);
- if (!hMapFile)
+ map_size,
+ shm_name);
+
+ if (!mapped_file)
{
- fprintf(stderr, "CreateFileMappingA: %d\n", (int) GetLastError());
- fflush(stderr);
+ warn("CreateFileMappingA", (int) GetLastError());
+
+ return;
}
- mem = MapViewOfFile(hMapFile,
+
+ mem = MapViewOfFile(mapped_file,
FILE_MAP_WRITE,
0,
0,
- mapSize);
+ map_size);
+
if (!mem)
- {
- fprintf(stderr, "MapViewOfFile: %d\n", (int) GetLastError());
- fflush(stderr);
- }
+ warn("MapViewOfFile:", (int) GetLastError());
}
-PortableLockedShm::~PortableLockedShm()
+shm_wrapper::~shm_wrapper()
{
- UnmapViewOfFile(mem);
- CloseHandle(hMapFile);
- CloseHandle(hMutex);
+ if (mem && !UnmapViewOfFile(mem))
+ goto fail;
+
+ if (mapped_file && !CloseHandle(mapped_file))
+ goto fail;
+
+ if (mutex && !CloseHandle(mutex))
+ goto fail;
+
+ return;
+
+fail:
+ warn("failed to close mapping", (int) GetLastError());
}
-void PortableLockedShm::lock()
+bool shm_wrapper::lock()
{
- (void) WaitForSingleObject(hMutex, INFINITE);
+ if (mutex)
+ return WaitForSingleObject(mutex, INFINITE) == WAIT_OBJECT_0;
+ else
+ return false;
}
-void PortableLockedShm::unlock()
+bool shm_wrapper::unlock()
{
- (void) ReleaseMutex(hMutex);
+ if (mutex)
+ return ReleaseMutex(mutex);
+ else
+ return false;
}
#else
#include <limits.h>
#pragma GCC diagnostic ignored "-Wunused-result"
-PortableLockedShm::PortableLockedShm(const char *shmName, const char* /*mutexName*/, int mapSize) : size(mapSize)
+shm_wrapper::shm_wrapper(const char *shm_name, const char* /*mutex_name*/, int map_size) : size(map_size)
{
- char filename[PATH_MAX+2] = {0};
+ char filename[PATH_MAX+2] {};
strcpy(filename, "/");
- strcat(filename, shmName);
+ strcat(filename, shm_name);
fd = shm_open(filename, O_RDWR | O_CREAT, 0600);
- (void) ftruncate(fd, mapSize);
- mem = mmap(NULL, mapSize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, (off_t)0);
+ (void) ftruncate(fd, map_size);
+ mem = mmap(nullptr, map_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, (off_t)0);
}
-PortableLockedShm::~PortableLockedShm()
+shm_wrapper::~shm_wrapper()
{
(void) munmap(mem, size);
(void) close(fd);
}
-void PortableLockedShm::lock()
+bool shm_wrapper::lock()
{
- flock(fd, LOCK_EX);
+ return flock(fd, LOCK_EX) == 0;
}
-void PortableLockedShm::unlock()
+bool shm_wrapper::unlock()
{
- flock(fd, LOCK_UN);
+ return flock(fd, LOCK_UN) == 0;
}
#endif
-bool PortableLockedShm::success()
+bool shm_wrapper::success()
{
#ifndef _WIN32
return mem != (void*) -1;
#else
- return mem != NULL;
+ return mem != nullptr;
#endif
}
+
diff --git a/compat/shm.h b/compat/shm.h
index ffd1e1dc..5036b335 100644
--- a/compat/shm.h
+++ b/compat/shm.h
@@ -19,30 +19,26 @@
#include <sys/types.h>
#endif
-#ifdef __GNUC__
-# pragma GCC diagnostic push
-# pragma GCC diagnostic ignored "-Wattributes"
-#endif
-
#include "export.hpp"
+#include "macros.h"
-class OTR_COMPAT_EXPORT PortableLockedShm {
-public:
- PortableLockedShm(const char *shmName, const char *mutexName, int mapSize);
- ~PortableLockedShm();
- void lock();
- void unlock();
- bool success();
- inline void* ptr() { return mem; }
-private:
+class OTR_COMPAT_EXPORT shm_wrapper final
+{
void* mem;
#if defined(_WIN32)
- HANDLE hMutex, hMapFile;
+ HANDLE mutex, mapped_file;
#else
- int fd, size;
+ int fd;
+ unsigned size;
#endif
-};
-#ifdef __GNUC__
-# pragma GCC diagnostic pop
-#endif
+public:
+ shm_wrapper(const char *shm_name, const char *mutex_name, int map_size);
+ ~shm_wrapper();
+ bool lock();
+ bool unlock();
+ bool success();
+ inline void* ptr() { return mem; }
+
+ OTR_DISABLE_MOVE_COPY(shm_wrapper);
+};
diff --git a/compat/simple-mat.hpp b/compat/simple-mat.hpp
index 1146984a..e697f76a 100644
--- a/compat/simple-mat.hpp
+++ b/compat/simple-mat.hpp
@@ -8,335 +8,328 @@
#pragma once
-#include <cmath>
-#include "export.hpp"
-#include <compat/util.hpp>
-
-#include <initializer_list>
+#include <cstddef>
#include <type_traits>
#include <utility>
+#include <cmath>
+
+namespace simple_mat {
+ // last param to fool SFINAE into overloading
+ template<int i, int j, int>
+ struct equals
+ {
+ enum { value = i == j };
+ };
+ template<int i, int j, int min, int max>
+ struct maybe_add_swizzle
+ {
+ enum { value = (i == 1 || j == 1) && (i >= min || j >= min) && (i <= max || j <= max) };
+ };
-namespace mat_detail {
+ template<int j, int i>
+ struct is_vector
+ {
+ enum { value = j == 1 || i == 1 };
+ };
-// `zz' param to fool into SFINAE member overload
+ template<int i1, int j1, int i2, int j2>
+ struct is_vector_pair
+ {
+ enum { value = (i1 == i2 && j1 == 1 && j2 == 1) || (j1 == j2 && i1 == 1 && i2 == 1) };
+ };
+ template<int i, int j>
+ struct vector_len
+ {
+ enum { value = i > j ? i : j };
+ };
+ template<int a, int b, int c, int d>
+ struct is_dim3
+ {
+ enum { value = (a == 1 && c == 1 && b == 3 && d == 3) || (a == 3 && c == 3 && b == 1 && d == 1) };
+ enum { P = a == 1 ? 1 : 3 };
+ enum { Q = a == 1 ? 3 : 1 };
+ };
-template<int i, int j, int k, int zz>
-constexpr bool equals = ((void)zz, i == k && j != k);
+ template<typename, int h, int w, typename...ts>
+ struct is_arglist_correct
+ {
+ enum { value = h * w == sizeof...(ts) };
+ };
-template<int i, int j, int min>
-constexpr bool maybe_swizzle =
- (i == 1 || j == 1) && (i >= min || j >= min);
+template<typename num, int H, int W>
+class Mat
+{
+ static_assert(H > 0 && W > 0, "must have positive mat dimensions");
+ num data[H][W];
-template<int i1, int j1, int i2, int j2>
-constexpr bool is_vector_pair =
- (i1 == i2 && j1 == 1 && j2 == 1) || (j1 == j2 && i1 == 1 && i2 == 1);
+public:
+ // parameters W and H are rebound so that SFINAE occurs
+ // removing them causes a compile-time error -sh 20150811
-template<int i, int j>
-constexpr unsigned vector_len = i > j ? i : j;
+ template<typename t, int Q = W> std::enable_if_t<equals<Q, 1, 0>::value, num>
+ constexpr inline operator()(t i) const& { return data[(unsigned)i][0]; }
-template<int a, int b, int c, int d>
-constexpr bool dim3 =
- (a == 3 || b == 3) && (c == 3 || d == 3) &&
- (a == 1 || b == 1) && (c == 1 || d == 1);
+ template<typename t, int P = H> std::enable_if_t<equals<P, 1, 1>::value, num>
+ constexpr inline operator()(t i) const& { return data[0][(unsigned)i]; }
-template<int h, int w, typename... ts>
-constexpr bool arglist_correct = h * w == sizeof...(ts);
+ template<typename t, int Q = W> std::enable_if_t<equals<Q, 1, 2>::value, num&>
+ constexpr inline operator()(t i) & { return data[(unsigned)i][0]; }
-template<bool x, typename t>
-using sfinae = typename std::enable_if<x, t>::type;
+ template<typename t, int P = H> std::enable_if_t<equals<P, 1, 3>::value, num&>
+ constexpr inline operator()(t i) & { return data[0][(unsigned)i]; }
-template<typename num, int h_, int w_>
-class Mat
-{
- static_assert(h_ > 0 && w_ > 0, "must have positive mat dimensions");
- num data[h_][w_];
+#define OTR_MAT_ASSERT_SWIZZLE static_assert(P == H && Q == W)
-#define OTR_ASSERT_SWIZZLE static_assert(P == h_ && Q == w_, "")
+ // const variants
+ template<int P = H, int Q = W> std::enable_if_t<maybe_add_swizzle<P, Q, 1, 4>::value, num>
+ constexpr inline x() const& { OTR_MAT_ASSERT_SWIZZLE; return operator()(0); }
- Mat(std::initializer_list<num>&& init)
- {
- auto iter = init.begin();
- for (int j = 0; j < h_; j++)
- for (int i = 0; i < w_; i++)
- data[j][i] = *iter++;
- }
+ template<int P = H, int Q = W> std::enable_if_t<maybe_add_swizzle<P, Q, 2, 4>::value, num>
+ constexpr inline y() const& { OTR_MAT_ASSERT_SWIZZLE; return operator()(1); }
-public:
- // start sfinae-R-us block
+ template<int P = H, int Q = W> std::enable_if_t<maybe_add_swizzle<P, Q, 3, 4>::value, num>
+ constexpr inline z() const& { OTR_MAT_ASSERT_SWIZZLE; return operator()(2); }
- // rebinding w_ and h_ since SFINAE requires dependent variables
+ template<int P = H, int Q = W> std::enable_if_t<maybe_add_swizzle<P, Q, 4, 4>::value, num>
+ constexpr inline w() const& { OTR_MAT_ASSERT_SWIZZLE; return operator()(3); }
- template<int P = h_, int Q = w_> sfinae<equals<Q, P, 1, 0>, num>
- OTR_FLATTEN operator()(int i) const { OTR_ASSERT_SWIZZLE; return data[i][0]; }
- template<int P = h_, int Q = w_> sfinae<equals<Q, 0, 1, 0>, num&>
- OTR_FLATTEN operator()(int i) { OTR_ASSERT_SWIZZLE; return data[i][0]; }
+ // mutable variants
+ template<int P = H, int Q = W> std::enable_if_t<maybe_add_swizzle<P, Q, 1, 4>::value, num&>
+ constexpr inline x() & { OTR_MAT_ASSERT_SWIZZLE; return operator()(0); }
- template<int P = h_, int Q = w_> sfinae<equals<P, Q, 1, 1>, num>
- OTR_FLATTEN operator()(int i) const { OTR_ASSERT_SWIZZLE; return data[0][i]; }
- template<int P = h_, int Q = w_> sfinae<equals<P, Q, 1, 1>, num&>
- OTR_FLATTEN operator()(int i) { OTR_ASSERT_SWIZZLE; return data[0][i]; }
+ template<int P = H, int Q = W> std::enable_if_t<maybe_add_swizzle<P, Q, 2, 4>::value, num&>
+ constexpr inline y() & { OTR_MAT_ASSERT_SWIZZLE; return operator()(1); }
- template<int P = h_, int Q = w_> sfinae<maybe_swizzle<P, Q, 1>, num>
- OTR_FLATTEN x() const { OTR_ASSERT_SWIZZLE; return operator()(0); }
- template<int P = h_, int Q = w_> sfinae<maybe_swizzle<P, Q, 1>, num&>
- OTR_FLATTEN x() { OTR_ASSERT_SWIZZLE; return operator()(0); }
+ template<int P = H, int Q = W> std::enable_if_t<maybe_add_swizzle<P, Q, 3, 4>::value, num&>
+ constexpr inline z() & { OTR_MAT_ASSERT_SWIZZLE; return operator()(2); }
- template<int P = h_, int Q = w_> sfinae<maybe_swizzle<P, Q, 2>, num>
- OTR_FLATTEN y() const { OTR_ASSERT_SWIZZLE; return operator()(1); }
- template<int P = h_, int Q = w_> sfinae<maybe_swizzle<P, Q, 2>, num&>
- OTR_FLATTEN y() { OTR_ASSERT_SWIZZLE; return operator()(1); }
+ template<int P = H, int Q = W> std::enable_if_t<maybe_add_swizzle<P, Q, 4, 4>::value, num&>
+ constexpr inline w() & { OTR_MAT_ASSERT_SWIZZLE; return operator()(3); }
- template<int P = h_, int Q = w_> sfinae<maybe_swizzle<P, Q, 3>, num>
- OTR_FLATTEN z() const { OTR_ASSERT_SWIZZLE; return operator()(2); }
- template<int P = h_, int Q = w_> sfinae<maybe_swizzle<P, Q, 3>, num&>
- OTR_FLATTEN z() { OTR_ASSERT_SWIZZLE; return operator()(2); }
+ template<int P = H, int Q = W>
+ constexpr auto norm_squared() const -> std::enable_if_t<is_vector<P, Q>::value, num>
+ {
+ static_assert(P == H && Q == W);
- template<int P = h_, int Q = w_> sfinae<maybe_swizzle<P, Q, 4>, num>
- OTR_FLATTEN w() const { OTR_ASSERT_SWIZZLE; return operator()(3); }
- template<int P = h_, int Q = w_> sfinae<maybe_swizzle<P, Q, 4>, num&>
- OTR_FLATTEN w() { OTR_ASSERT_SWIZZLE; return operator()(3); }
+ const num val = dot(*this);
+ constexpr num eps = num(1e-4);
- // end sfinae-R-us block
+ if (val < eps)
+ return num(0);
+ else
+ return val;
+ }
- // parameters w_ and h_ are rebound so that SFINAE occurs
- // removing them causes a compile-time error -sh 20150811
+ inline auto norm() const { return num(std::sqrt(norm_squared())); }
- template<int R, int S, int P = h_, int Q = w_>
- sfinae<is_vector_pair<R, S, P, Q>, num>
- dot(const Mat<num, R, S>& p2) const
+ template<int R, int S, int P = H, int Q = W>
+ std::enable_if_t<is_vector_pair<R, S, P, Q>::value, num>
+ constexpr dot(const Mat<num, R, S>& p2) const
{
- OTR_ASSERT_SWIZZLE;
+ static_assert(P == H && Q == W);
num ret = 0;
- static constexpr unsigned len = vector_len<R, S>;
- for (unsigned i = 0; i < len; i++)
+ constexpr int len = vector_len<R, S>::value;
+ for (int i = 0; i < len; i++)
ret += operator()(i) * p2(i);
return ret;
}
- template<int R, int S, int P = h_, int Q = w_> sfinae<dim3<P, Q, R, S>, Mat<num, 3, 1>>
- cross(const Mat<num, R, S>& p2) const
+ template<int R, int S, int P = H, int Q = W>
+ std::enable_if_t<is_dim3<P, Q, R, S>::value, Mat<num, is_dim3<P, Q, R, S>::P, is_dim3<P, Q, R, S>::Q>>
+ constexpr cross(const Mat<num, R, S>& b) const
{
- OTR_ASSERT_SWIZZLE;
- decltype(*this)& OTR_RESTRICT p1 = *this;
+ static_assert(P == H && Q == W);
+ const auto& a = *this;
- return Mat<num, R, S>(p1.y() * p2.z() - p2.y() * p1.z(),
- p2.x() * p1.z() - p1.x() * p2.z(),
- p1.x() * p2.y() - p1.y() * p2.x());
+ return Mat<num, R, S>(a.y()*b.z() - a.z()*b.y(),
+ a.z()*b.x() - a.x()*b.z(),
+ a.x()*b.y() - a.y()*b.x());
}
- Mat<num, h_, w_> operator+(const Mat<num, h_, w_>& other) const
+ constexpr Mat<num, H, W> operator+(const Mat<num, H, W>& other) const
{
- Mat<num, h_, w_> ret;
- for (int j = 0; j < h_; j++)
- for (int i = 0; i < w_; i++)
+ Mat<num, H, W> ret;
+ for (int j = 0; j < H; j++)
+ for (int i = 0; i < W; i++)
ret(j, i) = data[j][i] + other.data[j][i];
return ret;
}
- Mat<num, h_, w_> operator-(const Mat<num, h_, w_>& other) const
+ constexpr Mat<num, H, W> operator-(const Mat<num, H, W>& other) const
{
- Mat<num, h_, w_> ret;
- for (int j = 0; j < h_; j++)
- for (int i = 0; i < w_; i++)
+ Mat<num, H, W> ret;
+ for (int j = 0; j < H; j++)
+ for (int i = 0; i < W; i++)
ret(j, i) = data[j][i] - other.data[j][i];
return ret;
}
+ constexpr Mat<num, H, W> operator+(const num other) const
+ {
+ Mat<num, H, W> ret;
+ for (int j = 0; j < H; j++)
+ for (int i = 0; i < W; i++)
+ ret(j, i) = data[j][i] + other;
+ return ret;
+ }
+
+ constexpr Mat<num, H, W> operator-(const num other) const
+ {
+ Mat<num, H, W> ret;
+ for (int j = 0; j < H; j++)
+ for (int i = 0; i < W; i++)
+ ret(j, i) = data[j][i] - other;
+ return ret;
+ }
+
template<int p>
- Mat<num, h_, p> operator*(const Mat<num, w_, p>& other) const
+ constexpr Mat<num, H, p> operator*(const Mat<num, W, p>& other) const
{
- Mat<num, h_, p> ret;
- for (int k = 0; k < h_; k++)
+ Mat<num, H, p> ret;
+ for (int k = 0; k < H; k++)
for (int i = 0; i < p; i++)
{
ret(k, i) = 0;
- for (int j = 0; j < w_; j++)
+ for (int j = 0; j < W; j++)
ret(k, i) += data[k][j] * other(j, i);
}
return ret;
}
- inline num operator()(int j, int i) const { return data[j][i]; }
- inline num& operator()(int j, int i) { return data[j][i]; }
+ constexpr Mat<num, H, W> mult_elementwise(const Mat<num, H, W>& other) const&
+ {
+ Mat<num, H, W> ret;
- inline num operator()(unsigned j, unsigned i) const { return data[j][i]; }
- inline num& operator()(unsigned j, unsigned i) { return data[j][i]; }
+ for (unsigned j = 0; j < H; j++)
+ for (unsigned i = 0; i < W; i++)
+ ret(j, i) = data[j][i] * other.data[j][i];
- template<typename... ts, int P = h_, int Q = w_,
- typename = sfinae<arglist_correct<P, Q, ts...>, void>>
- Mat(const ts... xs)
- {
- OTR_ASSERT_SWIZZLE;
- static_assert(arglist_correct<P, Q, ts...>, "");
+ return ret;
+ }
- std::initializer_list<num> init = { static_cast<num>(xs)... };
+ template<typename t, typename u>
+ constexpr inline num operator()(t j, u i) const& { return data[(unsigned)j][(unsigned)i]; }
- *this = Mat(std::move(init));
+ template<typename t, typename u>
+ constexpr inline num& operator()(t j, u i) & { return data[(unsigned)j][(unsigned)i]; }
+
+#ifdef __clang__
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wmissing-braces"
+#endif
+
+ template<typename... ts, int h2 = H, int w2 = W,
+ typename = std::enable_if_t<is_arglist_correct<num, h2, w2, ts...>::value>>
+ constexpr Mat(const ts... xs) : data{static_cast<num>(xs)...}
+ {
+ static_assert(h2 == H && w2 == W);
}
- Mat()
+#ifdef __clang__
+# pragma clang diagnostic pop
+#endif
+
+ constexpr Mat()
{
- for (int j = 0; j < h_; j++)
- for (int i = 0; i < w_; i++)
+ for (int j = 0; j < H; j++)
+ for (int i = 0; i < W; i++)
data[j][i] = num(0);
}
- Mat(const num* OTR_RESTRICT mem)
+ constexpr Mat(const num* mem)
{
- for (int j = 0; j < h_; j++)
- for (int i = 0; i < w_; i++)
- data[j][i] = mem[i*h_+j];
+ for (int j = 0; j < H; j++)
+ for (int i = 0; i < W; i++)
+ data[j][i] = mem[i*H+j];
}
- OTR_ALWAYS_INLINE operator num*() { return reinterpret_cast<num*>(data); }
- OTR_ALWAYS_INLINE operator const num*() const { return reinterpret_cast<const num*>(data); }
+ constexpr Mat(const Mat<num, H, W>& x)
+ {
+ for (int j = 0; j < H; j++)
+ for (int i = 0; i < W; i++)
+ data[j][i] = x(j, i);
+ }
+
+ constexpr operator num*() & { return (num*)data; }
+ constexpr operator const num*() const& { return (const num*)data; }
// XXX add more operators as needed, third-party dependencies mostly
// not needed merely for matrix algebra -sh 20141030
- template<int P = h_>
- static typename std::enable_if<P == w_, Mat<num, P, P>>::type eye()
+ template<int H_ = H>
+ static constexpr std::enable_if_t<H == W, Mat<num, H_, H_>> eye()
{
- static_assert(P == h_, "");
-
- Mat<num, P, P> ret;
+ static_assert(H == H_);
- for (int j = 0; j < P; j++)
- for (int i = 0; i < w_; i++)
+ Mat<num, H, H> ret;
+ for (int j = 0; j < H; j++)
+ for (int i = 0; i < W; i++)
ret.data[j][i] = 0;
- for (int i = 0; i < P; i++)
+ for (int i = 0; i < H; i++)
ret.data[i][i] = 1;
return ret;
}
- Mat<num, w_, h_> t() const
+ constexpr Mat<num, W, H> t() const
{
- Mat<num, w_, h_> ret;
+ Mat<num, W, H> ret;
- for (int j = 0; j < h_; j++)
- for (int i = 0; i < w_; i++)
+ for (int j = 0; j < H; j++)
+ for (int i = 0; i < W; i++)
ret(i, j) = data[j][i];
return ret;
}
-};
-
-template<typename num>
-class Quat : Mat<num, 4, 1>
-{
- using quat = Quat<num>;
-
- enum idx { qw, qx, qy, qz };
-
- static quat _from_array(const num* data)
- {
- return quat(data[qw], data[qx], data[qy], data[qz]);
- }
- inline num elt(idx k) const { return operator()(k); }
- inline num& elt(idx k) { return Mat<num, 4, 1>::operator()(int(k)); }
-public:
- Quat(num w, num x, num y, num z) : Mat<num, 4, 1>(w, x, y, z)
- {
- }
-
- Quat() : quat(1, 0, 0, 0) {}
-
- static quat from_vector(const Mat<num, 4, 1>& data)
+ constexpr Mat<num, H, W>& operator=(const Mat<num, H, W>& rhs)
{
- return _from_array(data);
- }
+ for (unsigned j = 0; j < H; j++)
+ for (unsigned i = 0; i < W; i++)
+ data[j][i] = rhs(j, i);
- static quat from_vector(const Mat<num, 1, 4>& data)
- {
- return _from_array(data);
+ return *this;
}
-
- quat normalized() const
- {
- const num x = elt(qx), y = elt(qy), z = elt(qz), w = elt(qw);
- const num inv_n = 1./std::sqrt(x*x + y*y + z*z + w*w);
-
- return Quat<num>(elt(qw) * inv_n,
- elt(qx) * inv_n,
- elt(qy) * inv_n,
- elt(qz) * inv_n);
- }
-
- quat operator*(const quat& q2)
- {
- const quat& OTR_RESTRICT q1 = *this;
- return quat(-q1.x() * q2.x() - q1.y() * q2.y() - q1.z() * q2.z() + q1.w() * q2.w(),
- q1.x() * q2.w() + q1.y() * q2.z() - q1.z() * q2.y() + q1.w() * q2.x(),
- -q1.x() * q2.z() + q1.y() * q2.w() + q1.z() * q2.x() + q1.w() * q2.y(),
- q1.x() * q2.y() - q1.y() * q2.x() + q1.z() * q2.w() + q1.w() * q2.z());
- }
-
- inline num w() const { return elt(qw); }
- inline num x() const { return elt(qx); }
- inline num y() const { return elt(qy); }
- inline num z() const { return elt(qz); }
-
- inline num& w() { return elt(qw); }
- inline num& x() { return elt(qx); }
- inline num& y() { return elt(qy); }
- inline num& z() { return elt(qz); }
};
-} // ns detail
+template<unsigned k, typename num, int h, int w>
+constexpr num get(const Mat<num, h, w>& m) { return m(k); }
-template<typename num, int h, int w>
-using Mat = mat_detail::Mat<num, h, w>;
+template<unsigned k, typename num, int h, int w>
+constexpr num& get(Mat<num, h, w>& m) { return m(k); }
-template<typename num, int h, int w>
-inline Mat<num, h, w> operator*(num scalar, const Mat<num, h, w>& mat) { return mat * scalar; }
+} // ns simple_mat
template<typename num, int h, int w>
-inline Mat<num, h, w> operator-(num scalar, const Mat<num, h, w>& mat) { return mat - scalar; }
+using Mat = simple_mat::Mat<num, h, w>;
template<typename num, int h, int w>
-inline Mat<num, h, w> operator+(num scalar, const Mat<num, h, w>& mat) { return mat + scalar; }
-
-template<typename num, int h_, int w_>
-inline Mat<num, h_, w_> operator*(const Mat<num, h_, w_>& OTR_RESTRICT self, num other)
+constexpr Mat<num, h, w> operator*(num scalar, const Mat<num, h, w>& mat)
{
- Mat<num, h_, w_> ret;
- for (int j = 0; j < h_; j++)
- for (int i = 0; i < w_; i++)
- ret(j, i) = self(j, i) * other;
- return ret;
-}
-
-template<typename num, int h_, int w_>
-inline Mat<num, h_, w_> operator-(const Mat<num, h_, w_>& OTR_RESTRICT self, num other)
-{
- Mat<num, h_, w_> ret;
- for (int j = 0; j < h_; j++)
- for (int i = 0; i < w_; i++)
- ret(j, i) = self(j, i) - other;
- return ret;
+ return mat * scalar;
}
-template<typename num, int h_, int w_>
-inline Mat<num, h_, w_> operator+(const Mat<num, h_, w_>& OTR_RESTRICT self, num other)
+template<typename num, int H, int W>
+constexpr Mat<num, H, W> operator*(const Mat<num, H, W>& self, num other)
{
- Mat<num, h_, w_> ret;
- for (int j = 0; j < h_; j++)
- for (int i = 0; i < w_; i++)
- ret(j, i) = self(j, i) + other;
+ Mat<num, H, W> ret;
+ for (int j = 0; j < H; j++)
+ for (int i = 0; i < W; i++)
+ ret(j, i) = self(j, i) * other;
return ret;
}
-template<typename num>
-using Quat_ = mat_detail::Quat<num>;
-
-using Quat = Quat_<double>;
+namespace std {
+ template<typename num, int H, int W>
+ struct tuple_size<Mat<num, H, W>> :
+ std::integral_constant<std::size_t, H == 1 || W == 1 ? W * H : 0>
+ {};
-template class mat_detail::Mat<float, 3, 3>;
-template class mat_detail::Mat<float, 6, 1>;
-template class mat_detail::Mat<float, 3, 1>;
-
-// eof
+ template<std::size_t k, typename num, int h, int w>
+ struct tuple_element<k, Mat<num, h, w>>
+ {
+ using type = std::remove_const_t<std::remove_reference_t<num>>;
+ };
+} // ns std
diff --git a/compat/sleep.cpp b/compat/sleep.cpp
new file mode 100644
index 00000000..e64e6254
--- /dev/null
+++ b/compat/sleep.cpp
@@ -0,0 +1,23 @@
+#include "sleep.hpp"
+
+#ifdef _WIN32
+# include <windows.h>
+#else
+# include <unistd.h>
+#endif
+
+namespace portable
+{
+ void sleep(int milliseconds)
+ {
+ if (milliseconds > 0)
+ {
+#ifdef _WIN32
+
+ Sleep((unsigned)milliseconds);
+#else
+ usleep(unsigned(milliseconds) * 1000U); // takes microseconds
+#endif
+ }
+ }
+}
diff --git a/compat/sleep.hpp b/compat/sleep.hpp
index 4cd7a397..9419b36b 100644
--- a/compat/sleep.hpp
+++ b/compat/sleep.hpp
@@ -1,24 +1,7 @@
#pragma once
-#ifdef _WIN32
-# include <windows.h>
-#else
-# include <unistd.h>
-#endif
+#include "export.hpp"
-namespace portable
-{
-#ifdef _WIN32
- inline void sleep(int milliseconds)
- {
- if (milliseconds > 0)
- Sleep(milliseconds);
- }
-#else
- inline void sleep(int milliseconds)
- {
- if (milliseconds > 0)
- usleep(unsigned(milliseconds) * 1000U); // takes microseconds
- }
-#endif
+namespace portable {
+ OTR_COMPAT_EXPORT void sleep(int milliseconds);
}
diff --git a/compat/sysexits.hpp b/compat/sysexits.hpp
new file mode 100644
index 00000000..6747fc88
--- /dev/null
+++ b/compat/sysexits.hpp
@@ -0,0 +1,28 @@
+#pragma once
+
+#include <cstdlib> // for EXIT_SUCCESS, EXIT_FAILRUE
+
+#ifndef _WIN32
+# include <sysexits.h>
+#else
+// this conforms to BSD sysexits(3)
+// reference the manual page on FreeBSD or Linux for semantics
+# define EX_OK 0
+# define EX_USAGE 64
+# define EX_DATAERR 65
+# define EX_NOINPUT 66
+# define EX_NOUSER 67
+# define EX_NOHOST 68
+# define EX_UNAVAILABLE 69
+# define EX_SOFTWARE 70
+# define EX_OSERR 71
+# define EX_OSFILE 72
+# define EX_CANTCREAT 73
+# define EX_IOERR 74
+# define EX_TEMPFAIL 75
+# define EX_PROTOCOL 76
+# define EX_NOPERM 77
+# define EX_CONFIG 78
+#endif
+
+
diff --git a/compat/thread-name.cpp b/compat/thread-name.cpp
new file mode 100644
index 00000000..08a7d628
--- /dev/null
+++ b/compat/thread-name.cpp
@@ -0,0 +1,87 @@
+#include "thread-name.hpp"
+#ifdef _WIN32
+# include <QDebug>
+# include <windows.h>
+#else
+# include <QThread>
+#endif
+
+namespace portable {
+
+#ifdef _WIN32
+
+#ifdef _MSC_VER
+struct THREADNAME_INFO
+{
+ DWORD dwType; // must be 0x1000
+ LPCSTR szName; // pointer to name (in user addr space)
+ HANDLE dwThreadID; // thread ID (-1=caller thread)
+ DWORD dwFlags; // reserved for future use, must be zero
+};
+
+static inline
+void set_curthread_name_old_(const char* name)
+{
+ HANDLE curthread = GetCurrentThread();
+ THREADNAME_INFO info; // NOLINT(cppcoreguidelines-pro-type-member-init)
+ info.dwType = 0x1000;
+ info.szName = name;
+ info.dwThreadID = curthread;
+ info.dwFlags = 0;
+ __try
+ {
+ static_assert(sizeof(info) % sizeof(unsigned) == 0);
+ unsigned sz = sizeof(info)/sizeof(unsigned);
+ RaiseException(0x406D1388, 0, sz, (const ULONG_PTR*)&info);
+ }
+ __except (EXCEPTION_CONTINUE_EXECUTION)
+ {
+ }
+}
+
+static inline
+void set_curthread_name_old(const QString& name_)
+{
+ QByteArray str = name_.toLocal8Bit();
+ const char* name = str.constData();
+
+ set_curthread_name_old_(name);
+}
+#else
+
+static inline void set_curthread_name_old(const QString&) {}
+
+#endif
+
+using SetThreadDescr_type = HRESULT (__stdcall *)(HANDLE, const wchar_t*);
+
+static SetThreadDescr_type get_funptr()
+{
+ HMODULE module;
+ if (GetModuleHandleExA(0, "kernel32.dll", &module))
+ return (SetThreadDescr_type)GetProcAddress(module, "SetThreadDescription");
+ else
+ return nullptr;
+}
+
+void set_curthread_name(const QString& name)
+{
+ static_assert(sizeof(wchar_t) == sizeof(decltype(*QString().utf16())));
+ static const SetThreadDescr_type fn = get_funptr();
+
+ if (fn)
+ fn(GetCurrentThread(), (const wchar_t*)name.utf16());
+ else
+ set_curthread_name_old(name);
+}
+
+#else
+
+void set_curthread_name(const QString& name)
+{
+ QThread::currentThread()->setObjectName(name);
+}
+
+#endif
+
+} // ns portable
diff --git a/compat/thread-name.hpp b/compat/thread-name.hpp
new file mode 100644
index 00000000..21256003
--- /dev/null
+++ b/compat/thread-name.hpp
@@ -0,0 +1,6 @@
+#pragma once
+#include "export.hpp"
+#include <QString>
+namespace portable {
+ OTR_COMPAT_EXPORT void set_curthread_name(const QString& name);
+}
diff --git a/compat/time.hpp b/compat/time.hpp
new file mode 100644
index 00000000..0d171038
--- /dev/null
+++ b/compat/time.hpp
@@ -0,0 +1,15 @@
+#pragma once
+
+#include <chrono>
+
+namespace time_units {
+
+template<typename repr, typename ratio = std::ratio<1>>
+using duration = std::chrono::duration<repr, ratio>;
+
+using secs = duration<float>;
+using ms = duration<float, std::milli>;
+using us = duration<float, std::micro>;
+using ns = duration<float, std::nano>;
+
+} // ns time_units
diff --git a/compat/timer.cpp b/compat/timer.cpp
index d5052ccb..fdea185a 100644
--- a/compat/timer.cpp
+++ b/compat/timer.cpp
@@ -6,7 +6,12 @@
* notice appear in all copies.
*/
+#undef NDEBUG
+
#include "timer.hpp"
+#include <cassert>
+#include <cmath>
+#include <QDebug>
Timer::Timer()
{
@@ -15,103 +20,88 @@ Timer::Timer()
void Timer::start()
{
- wrap_gettime(&state);
+ gettime(&state);
}
-// common
-
-void Timer::wrap_gettime(timespec* state)
+struct timespec Timer::get_delta() const
{
-#if defined(_WIN32) || defined(__MACH__)
- otr_clock_gettime(state);
-#else
- (void) clock_gettime(CLOCK_MONOTONIC, state);
-#endif
-}
-
-// nanoseconds
-
-long long Timer::elapsed_nsecs() const
-{
- struct timespec cur = {};
- wrap_gettime(&cur);
- return conv_nsecs(cur);
-}
-
-long long Timer::conv_nsecs(const timespec& cur) const
-{
- // this can and will overflow
- return (cur.tv_sec - state.tv_sec) * 1000000000LL + (cur.tv_nsec - state.tv_nsec);
-}
-
-// microseconds
-
-double Timer::elapsed_usecs() const
-{
- struct timespec cur = {};
- wrap_gettime(&cur);
- const long long nsecs = conv_nsecs(cur);
- return nsecs * 1e-3;
+ struct timespec ts; // NOLINT
+ gettime(&ts);
+ return { ts.tv_sec - state.tv_sec, ts.tv_nsec - state.tv_nsec };
}
// milliseconds
double Timer::elapsed_ms() const
{
- return elapsed_usecs() / 1000.;
+ struct timespec delta = get_delta();
+ return delta.tv_sec * 1000 + delta.tv_nsec * 1e-6;
}
double Timer::elapsed_seconds() const
{
- return double(elapsed_nsecs() * 1e-9L);
+ struct timespec delta = get_delta();
+ return delta.tv_sec + delta.tv_nsec * 1e-9;
}
// --
// platform-specific code starts here
// --
-#if defined _WIN32
-LARGE_INTEGER Timer::otr_get_clock_frequency()
+#if defined (_WIN32)
+# include <windows.h>
+
+static auto otr_get_clock_frequency()
{
- LARGE_INTEGER freq = {};
- (void) QueryPerformanceFrequency(&freq);
- return freq;
+ LARGE_INTEGER freq{};
+ BOOL ret = QueryPerformanceFrequency(&freq);
+ assert(ret && "QueryPerformanceFrequency failed");
+ return freq.QuadPart;
}
-void Timer::otr_clock_gettime(timespec* ts)
+void Timer::gettime(timespec* state)
{
- static LARGE_INTEGER freq = otr_get_clock_frequency();
-
+ static const auto freq = otr_get_clock_frequency();
LARGE_INTEGER d;
+ BOOL ret = QueryPerformanceCounter(&d);
+ assert(ret && "QueryPerformanceCounter failed");
- (void) QueryPerformanceCounter(&d);
-
- using ll = long long;
- using ld = long double;
- const long long part = ll(d.QuadPart / ld(freq.QuadPart) * 1000000000.L);
- using t_s = decltype(ts->tv_sec);
- using t_ns = decltype(ts->tv_nsec);
-
- ts->tv_sec = t_s(part / 1000000000LL);
- ts->tv_nsec = t_ns(part % 1000000000LL);
+ constexpr int nsec = 1'000'000 * 1000;
+ state->tv_sec = (time_t)(d.QuadPart/freq);
+ state->tv_nsec = (decltype(state->tv_nsec))(d.QuadPart % freq * nsec / freq);
}
#elif defined __MACH__
-mach_timebase_info_data_t Timer::otr_get_mach_frequency()
+# include <inttypes.h>
+# include <mach/mach_time.h>
+
+static mach_timebase_info_data_t otr_get_mach_frequency()
{
mach_timebase_info_data_t timebase_info;
- (void) mach_timebase_info(&timebase_info);
+ kern_return_t status = mach_timebase_info(&timebase_info);
+ assert(status == KERN_SUCCESS && "mach_timebase_info failed");
return timebase_info;
}
-double Timer::otr_clock_gettime(timespec* ts)
+void Timer::gettime(timespec* ts)
{
- static mach_timebase_info_data_t timebase_info = otr_get_mach_frequency();
+ static const mach_timebase_info_data_t timebase_info = otr_get_mach_frequency();
uint64_t state, nsec;
state = mach_absolute_time();
nsec = state * timebase_info.numer / timebase_info.denom;
- ts->tv_sec = nsec / 1000000000L;
- ts->tv_nsec = nsec % 1000000000L;
+ ts->tv_sec = nsec / 1000000000UL;
+ ts->tv_nsec = nsec % 1000000000UL;
+}
+
+#else
+
+void Timer::gettime(timespec* ts)
+{
+ int error = clock_gettime(CLOCK_MONOTONIC, ts);
+ assert(error == 0 && "clock_gettime failed");
}
#endif
+
+// common
+
diff --git a/compat/timer.hpp b/compat/timer.hpp
index e9efba79..47072226 100644
--- a/compat/timer.hpp
+++ b/compat/timer.hpp
@@ -9,38 +9,20 @@
#pragma once
#include "export.hpp"
-
-#if defined (_WIN32)
-# include <windows.h>
-#elif defined(__MACH__)
-# include <inttypes.h>
-# include <mach/mach_time.h>
-#endif
-
+#include "time.hpp"
#include <ctime>
-#include <tuple>
-class OTR_COMPAT_EXPORT Timer
+struct OTR_COMPAT_EXPORT Timer final
{
- struct timespec state;
- long long conv_nsecs(const struct timespec& cur) const;
- static void otr_clock_gettime(struct timespec* ts);
-#ifdef _WIN32
- static LARGE_INTEGER otr_get_clock_frequency();
-#elif defined(__MACH__)
- static mach_timebase_info_data_t otr_get_mach_frequency();
-#endif
-
- static void wrap_gettime(struct timespec* state);
-
-public:
Timer();
-
void start();
- long long elapsed_nsecs() const;
- double elapsed_usecs() const;
+
double elapsed_ms() const;
double elapsed_seconds() const;
-};
-
+private:
+ struct timespec state {};
+ static void gettime(struct timespec* state);
+ struct timespec get_delta() const;
+ using ns = time_units::ns;
+};
diff --git a/compat/tr.hpp b/compat/tr.hpp
new file mode 100644
index 00000000..c5a561e7
--- /dev/null
+++ b/compat/tr.hpp
@@ -0,0 +1,14 @@
+#pragma once
+
+#include <QObject>
+
+// The class does nothing except provide a fake assignment operator for QObject
+// It's meant to be used inside classes that need i18n support but are returned by value.
+
+struct TR : QObject
+{
+ TR() = default;
+ TR(const TR&) : QObject(nullptr) {}
+
+ TR& operator=(const TR&) { return *this; }
+};
diff --git a/compat/util.hpp b/compat/util.hpp
deleted file mode 100644
index 60427b53..00000000
--- a/compat/util.hpp
+++ /dev/null
@@ -1,100 +0,0 @@
-#pragma once
-
-#include "ndebug-guard.hpp"
-#include "run-in-thread.hpp"
-
-#include <memory>
-#include <cmath>
-#include <utility>
-
-#include <QSharedPointer>
-#include <QDebug>
-
-#define progn(...) ([&]() { __VA_ARGS__ }())
-
-#define once_only(...) progn(static bool once = false; if (!once) { once = true; __VA_ARGS__; })
-
-template<typename t> using mem = std::shared_ptr<t>;
-template<typename t> using ptr = std::unique_ptr<t>;
-
-#ifdef Q_CREATOR_RUN
-# define DEFUN_WARN_UNUSED
-#elif defined(_MSC_VER)
-# define DEFUN_WARN_UNUSED _Check_return_
-#else
-# define DEFUN_WARN_UNUSED __attribute__((warn_unused_result))
-#endif
-
-#if defined(__GNUG__)
-# define unused(t, i) t __attribute__((unused)) i
-#else
-# define unused(t, i) t
-#endif
-
-#if !defined(_WIN32)
-# define unused_on_unix(t, i) unused(t, i)
-#else
-# define unused_on_unix(t, i) t i
-#endif
-
-template<typename t>
-int iround(const t& val)
-{
- return int(std::round(val));
-}
-
-namespace util_detail {
-
-template<typename n>
-inline auto clamp_(n val, n min, n max) -> n
-{
- if (val > max)
- return max;
- if (val < min)
- return min;
- return val;
-}
-
-}
-
-template<typename t, typename u, typename w>
-inline auto clamp(const t& val, const u& min, const w& max) -> decltype(val * min * max)
-{
- return ::util_detail::clamp_<decltype(val * min * max)>(val, min, max);
-}
-
-template<typename t, typename... xs>
-auto qptr(xs... args)
-{
- return QSharedPointer<t>(new t(std::forward<xs>(args)...));
-}
-
-template<typename t> using qshared = QSharedPointer<t>;
-
-#if defined _MSC_VER
-# define OTR_NEVER_INLINE __declspec(noinline)
-#elif defined __GNUG__
-# define OTR_NEVER_INLINE __attribute__((noinline))
-#else
-# define OTR_NEVER_INLINE
-#endif
-
-#if defined _MSC_VER || defined __GNUG__
-# define OTR_RESTRICT __restrict
-#else
-# define OTR_RESTRICT
-#endif
-
-#if defined _MSC_VER
-# define OTR_ALWAYS_INLINE __forceinline
-#elif defined __GNUG__
-# define OTR_ALWAYS_INLINE __attribute__((always_inline))
-#else
-# define OTR_ALWAYS_INLINE inline
-#endif
-
-#if defined __GNUG__
-# define OTR_FLATTEN __attribute__((flatten))
-#else
-# define OTR_FLATTEN OTR_ALWAYS_INLINE
-#endif
diff --git a/compat/variance.hpp b/compat/variance.hpp
new file mode 100644
index 00000000..8abefd33
--- /dev/null
+++ b/compat/variance.hpp
@@ -0,0 +1,55 @@
+#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.
+
+struct variance final
+{
+ using size_type = unsigned;
+
+ variance& operator=(variance const&) = default;
+ void clear() { *this = {}; }
+
+ constexpr size_type count() const { return cnt; }
+
+ constexpr double avg() const { return cnt > 0 ? m_new : 0; }
+ constexpr double Var() const { return cnt > 1 ? s_new/(cnt - 1) : 0; }
+ double stddev() const { return std::sqrt(Var()); }
+
+ 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;
+ }
+ }
+
+private:
+ double m_old, m_new, s_old, s_new;
+ size_type cnt = 0;
+};