From 9040b187a1c4fa380f8a12207b9dd6d04b3a10ac Mon Sep 17 00:00:00 2001
From: Stanislaw Halik <sthalik@misaki.pl>
Date: Fri, 12 Aug 2016 18:00:49 +0200
Subject: all: rename modules s#^opentrack-##. and opentrack -> api

Adjust usages.
---
 CMakeLists.txt                                     |   5 +-
 api/CMakeLists.txt                                 |   3 +
 api/dtors.cpp                                      |  11 +
 api/export.hpp                                     |  28 ++
 api/is-window-visible.cpp                          |  33 ++
 api/is-window-visible.hpp                          |   7 +
 api/plugin-api.hpp                                 | 180 ++++++++
 api/plugin-support.hpp                             | 216 +++++++++
 api/variance.hpp                                   |  52 +++
 cmake/mingw-w64.cmake                              |   2 +-
 cmake/opentrack-version.cmake                      |   2 +-
 compat/CMakeLists.txt                              |   8 +
 compat/camera-names.cpp                            | 111 +++++
 compat/camera-names.hpp                            |  18 +
 compat/export.hpp                                  |  27 ++
 compat/make-unique.hpp                             |  43 ++
 compat/nan.cpp                                     |  17 +
 compat/nan.hpp                                     |  11 +
 compat/options.cpp                                 | 239 ++++++++++
 compat/options.hpp                                 | 481 +++++++++++++++++++++
 compat/pi-constant.hpp                             |   3 +
 compat/process-list.hpp                            | 169 ++++++++
 compat/qcopyable-mutex.hpp                         |  37 ++
 compat/shm.cpp                                     | 199 +++++++++
 compat/shm.h                                       |  48 ++
 compat/sleep.hpp                                   |  22 +
 compat/slider.cpp                                  |  73 ++++
 compat/slider.hpp                                  |  62 +++
 compat/timer.hpp                                   | 100 +++++
 compat/util.hpp                                    |  93 ++++
 compat/win32-com.cpp                               |  60 +++
 compat/win32-com.hpp                               |  18 +
 cv/camera-dialog.hpp                               |   6 +-
 cv/camera-dialog.hpp.OO4364                        |   4 +-
 cv/video-widget.cpp                                |   2 +-
 cv/video-widget.hpp                                |   2 +-
 dinput/CMakeLists.txt                              |   4 +
 dinput/dinput.cpp                                  |  98 +++++
 dinput/dinput.hpp                                  |  58 +++
 dinput/export.hpp                                  |  28 ++
 dinput/keybinding-worker.cpp                       | 201 +++++++++
 dinput/keybinding-worker.hpp                       |  80 ++++
 dinput/win32-joystick.cpp                          | 359 +++++++++++++++
 dinput/win32-joystick.hpp                          | 101 +++++
 filter-accela/ftnoir_filter_accela.cpp             |   2 +-
 filter-accela/ftnoir_filter_accela.h               |   6 +-
 filter-accela/ftnoir_filter_accela_dialog.cpp      |   2 +-
 filter-ewma2/ftnoir_filter_ewma2.cpp               |   2 +-
 filter-ewma2/ftnoir_filter_ewma2.h                 |   6 +-
 filter-ewma2/ftnoir_filter_ewma2_dialog.cpp        |   2 +-
 filter-kalman/kalman.h                             |   6 +-
 gui/keyboard.h                                     |   4 +-
 gui/main-window.cpp                                |   4 +-
 gui/main-window.hpp                                |  14 +-
 gui/main.cpp                                       |   4 +-
 gui/mapping-window.cpp                             |   2 +-
 gui/mapping-window.hpp                             |   2 +-
 gui/new_file_dialog.h                              |   2 +-
 gui/options-dialog.hpp                             |   2 +-
 gui/process_detector.cpp                           |   2 +-
 gui/process_detector.h                             |   6 +-
 logic/CMakeLists.txt                               |   7 +
 logic/export.hpp                                   |  28 ++
 logic/main-settings.hpp                            | 109 +++++
 logic/mappings.hpp                                 |  89 ++++
 logic/selected-libraries.cpp                       |  37 ++
 logic/selected-libraries.hpp                       |  25 ++
 logic/shortcuts.cpp                                | 130 ++++++
 logic/shortcuts.h                                  |  68 +++
 logic/simple-mat.cpp                               |  97 +++++
 logic/simple-mat.hpp                               | 279 ++++++++++++
 logic/state.hpp                                    |  32 ++
 logic/tracker.cpp                                  | 385 +++++++++++++++++
 logic/tracker.h                                    |  85 ++++
 logic/tracklogger.cpp                              |  47 ++
 logic/tracklogger.hpp                              |  65 +++
 logic/win32-shortcuts.cpp                          | 196 +++++++++
 logic/win32-shortcuts.h                            |  24 +
 logic/work.cpp                                     |  73 ++++
 logic/work.hpp                                     |  43 ++
 opentrack-compat/CMakeLists.txt                    |   8 -
 opentrack-compat/camera-names.cpp                  | 111 -----
 opentrack-compat/camera-names.hpp                  |  18 -
 opentrack-compat/export.hpp                        |  27 --
 opentrack-compat/make-unique.hpp                   |  43 --
 opentrack-compat/nan.cpp                           |  17 -
 opentrack-compat/nan.hpp                           |  11 -
 opentrack-compat/options.cpp                       | 239 ----------
 opentrack-compat/options.hpp                       | 481 ---------------------
 opentrack-compat/pi-constant.hpp                   |   3 -
 opentrack-compat/process-list.hpp                  | 169 --------
 opentrack-compat/qcopyable-mutex.hpp               |  37 --
 opentrack-compat/shm.cpp                           | 199 ---------
 opentrack-compat/shm.h                             |  48 --
 opentrack-compat/sleep.hpp                         |  22 -
 opentrack-compat/slider.cpp                        |  73 ----
 opentrack-compat/slider.hpp                        |  62 ---
 opentrack-compat/timer.hpp                         | 100 -----
 opentrack-compat/util.hpp                          |  93 ----
 opentrack-compat/win32-com.cpp                     |  60 ---
 opentrack-compat/win32-com.hpp                     |  18 -
 opentrack-dinput/CMakeLists.txt                    |   4 -
 opentrack-dinput/dinput.cpp                        |  98 -----
 opentrack-dinput/dinput.hpp                        |  58 ---
 opentrack-dinput/export.hpp                        |  28 --
 opentrack-dinput/keybinding-worker.cpp             | 201 ---------
 opentrack-dinput/keybinding-worker.hpp             |  80 ----
 opentrack-dinput/win32-joystick.cpp                | 359 ---------------
 opentrack-dinput/win32-joystick.hpp                | 101 -----
 opentrack-logic/CMakeLists.txt                     |   7 -
 opentrack-logic/export.hpp                         |  28 --
 opentrack-logic/main-settings.hpp                  | 109 -----
 opentrack-logic/mappings.hpp                       |  89 ----
 opentrack-logic/selected-libraries.cpp             |  37 --
 opentrack-logic/selected-libraries.hpp             |  25 --
 opentrack-logic/shortcuts.cpp                      | 130 ------
 opentrack-logic/shortcuts.h                        |  68 ---
 opentrack-logic/simple-mat.cpp                     |  97 -----
 opentrack-logic/simple-mat.hpp                     | 279 ------------
 opentrack-logic/state.hpp                          |  32 --
 opentrack-logic/tracker.cpp                        | 385 -----------------
 opentrack-logic/tracker.h                          |  85 ----
 opentrack-logic/tracklogger.cpp                    |  47 --
 opentrack-logic/tracklogger.hpp                    |  65 ---
 opentrack-logic/win32-shortcuts.cpp                | 196 ---------
 opentrack-logic/win32-shortcuts.h                  |  24 -
 opentrack-logic/work.cpp                           |  73 ----
 opentrack-logic/work.hpp                           |  43 --
 opentrack/CMakeLists.txt                           |   3 -
 opentrack/dtors.cpp                                |  11 -
 opentrack/export.hpp                               |  28 --
 opentrack/is-window-visible.cpp                    |  33 --
 opentrack/is-window-visible.hpp                    |   7 -
 opentrack/plugin-api.hpp                           | 180 --------
 opentrack/plugin-support.hpp                       | 216 ---------
 opentrack/variance.hpp                             |  52 ---
 pose-widget/glwidget.cpp                           |   4 +-
 pose-widget/glwidget.h                             |   6 +-
 proto-fg/ftnoir_protocol_fg.cpp                    |   2 +-
 proto-fg/ftnoir_protocol_fg.h                      |   4 +-
 proto-fg/ftnoir_protocol_fg_dialog.cpp             |   2 +-
 proto-fsuipc/ftnoir_protocol_fsuipc.cpp            |   2 +-
 proto-fsuipc/ftnoir_protocol_fsuipc.h              |   4 +-
 proto-fsuipc/ftnoir_protocol_fsuipc_dialog.cpp     |   2 +-
 proto-ft/ftnoir_protocol_ft.cpp                    |   2 +-
 proto-ft/ftnoir_protocol_ft.h                      |   6 +-
 proto-ftn/ftnoir_protocol_ftn.cpp                  |   2 +-
 proto-ftn/ftnoir_protocol_ftn.h                    |   4 +-
 proto-ftn/ftnoir_protocol_ftn_dialog.cpp           |   2 +-
 proto-libevdev/ftnoir_protocol_libevdev.cpp        |   2 +-
 proto-libevdev/ftnoir_protocol_libevdev.h          |   2 +-
 proto-libevdev/ftnoir_protocol_libevdev_dialog.cpp |   2 +-
 proto-mouse/ftnoir_protocol_mouse.cpp              |   2 +-
 proto-mouse/ftnoir_protocol_mouse.h                |   4 +-
 proto-mouse/ftnoir_protocol_mouse_dialog.cpp       |   2 +-
 proto-sc/ftnoir_protocol_sc.cpp                    |   2 +-
 proto-sc/ftnoir_protocol_sc.h                      |   4 +-
 proto-sc/ftnoir_protocol_sc_dialog.cpp             |   2 +-
 proto-vjoystick/vjoystick.cpp                      |   2 +-
 proto-vjoystick/vjoystick.h                        |   2 +-
 proto-vjoystick/vjoystick_dialog.cpp               |   2 +-
 proto-wine/ftnoir_protocol_wine.h                  |   4 +-
 proto-wine/ftnoir_protocol_wine_dialog.cpp         |   2 +-
 proto-wine/opentrack-wrapper-wine-main.cxx         |   4 +-
 proto-wine/opentrack-wrapper-wine-posix.cxx        |   4 +-
 proto-wine/opentrack-wrapper-wine-windows.cxx      |   4 +-
 spline-widget/spline-widget.cpp                    |   2 +-
 spline-widget/spline-widget.hpp                    |   2 +-
 spline-widget/spline.hpp                           |   2 +-
 tracker-aruco/ftnoir_tracker_aruco.cpp             |   8 +-
 tracker-aruco/ftnoir_tracker_aruco.h               |   4 +-
 tracker-freepie-udp/ftnoir_tracker_freepie-udp.cpp |   4 +-
 tracker-freepie-udp/ftnoir_tracker_freepie-udp.h   |   4 +-
 .../ftnoir_tracker_freepie-udp_dialog.cpp          |   2 +-
 tracker-hatire/ftnoir_tracker_hat.h                |   2 +-
 tracker-hatire/ftnoir_tracker_hat_dialog.h         |   2 +-
 tracker-hatire/ftnoir_tracker_hat_settings.h       |   2 +-
 tracker-hatire/thread.cpp                          |   2 +-
 tracker-hatire/thread.hpp                          |   4 +-
 tracker-ht/ftnoir_tracker_ht.cpp                   |  32 +-
 tracker-ht/ftnoir_tracker_ht.h                     |   8 +-
 tracker-hydra/ftnoir_tracker_hydra.cpp             |   4 +-
 tracker-hydra/ftnoir_tracker_hydra.h               |   4 +-
 tracker-hydra/ftnoir_tracker_hydra_dialog.cpp      |   2 +-
 tracker-joystick/ftnoir_tracker_joystick.cpp       |   2 +-
 tracker-joystick/ftnoir_tracker_joystick.h         |   6 +-
 .../ftnoir_tracker_joystick_dialog.cpp             |   2 +-
 tracker-pt/camera.cpp                              |   4 +-
 tracker-pt/ftnoir_tracker_pt.cpp                   |   4 +-
 tracker-pt/ftnoir_tracker_pt.h                     |   6 +-
 tracker-pt/ftnoir_tracker_pt_dialog.cpp            |   2 +-
 tracker-pt/ftnoir_tracker_pt_dialog.h              |   2 +-
 tracker-pt/ftnoir_tracker_pt_settings.h            |   4 +-
 tracker-pt/point_extractor.cpp                     |   2 +-
 tracker-pt/point_extractor.h                       |   2 +-
 tracker-pt/point_tracker.cpp                       |   8 +-
 tracker-pt/point_tracker.h                         |   2 +-
 tracker-rift-025/ftnoir_tracker_rift_025.cpp       |   4 +-
 tracker-rift-025/ftnoir_tracker_rift_025.h         |   4 +-
 .../ftnoir_tracker_rift_dialog_025.cpp             |   2 +-
 tracker-rift-042/ftnoir_tracker_rift_042.cpp       |   4 +-
 tracker-rift-042/ftnoir_tracker_rift_042.h         |   4 +-
 .../ftnoir_tracker_rift_dialog_042.cpp             |   2 +-
 tracker-rift-080/ftnoir_tracker_rift_080.cpp       |   4 +-
 tracker-rift-080/ftnoir_tracker_rift_080.h         |   4 +-
 .../ftnoir_tracker_rift_dialog_080.cpp             |   2 +-
 tracker-rs/ftnoir_tracker_rs.cpp                   |  40 +-
 tracker-rs/ftnoir_tracker_rs.h                     |   2 +-
 tracker-test/test.cpp                              |   2 +-
 tracker-test/test.h                                |   6 +-
 tracker-test/test_dialog.cpp                       |   2 +-
 tracker-udp/ftnoir_tracker_udp.cpp                 |   2 +-
 tracker-udp/ftnoir_tracker_udp.h                   |   4 +-
 tracker-udp/ftnoir_tracker_udp_dialog.cpp          |   2 +-
 214 files changed, 5324 insertions(+), 5315 deletions(-)
 create mode 100644 api/CMakeLists.txt
 create mode 100644 api/dtors.cpp
 create mode 100644 api/export.hpp
 create mode 100644 api/is-window-visible.cpp
 create mode 100644 api/is-window-visible.hpp
 create mode 100644 api/plugin-api.hpp
 create mode 100644 api/plugin-support.hpp
 create mode 100644 api/variance.hpp
 create mode 100644 compat/CMakeLists.txt
 create mode 100644 compat/camera-names.cpp
 create mode 100644 compat/camera-names.hpp
 create mode 100644 compat/export.hpp
 create mode 100644 compat/make-unique.hpp
 create mode 100644 compat/nan.cpp
 create mode 100644 compat/nan.hpp
 create mode 100644 compat/options.cpp
 create mode 100644 compat/options.hpp
 create mode 100644 compat/pi-constant.hpp
 create mode 100644 compat/process-list.hpp
 create mode 100644 compat/qcopyable-mutex.hpp
 create mode 100644 compat/shm.cpp
 create mode 100644 compat/shm.h
 create mode 100644 compat/sleep.hpp
 create mode 100644 compat/slider.cpp
 create mode 100644 compat/slider.hpp
 create mode 100644 compat/timer.hpp
 create mode 100644 compat/util.hpp
 create mode 100644 compat/win32-com.cpp
 create mode 100644 compat/win32-com.hpp
 create mode 100644 dinput/CMakeLists.txt
 create mode 100644 dinput/dinput.cpp
 create mode 100644 dinput/dinput.hpp
 create mode 100644 dinput/export.hpp
 create mode 100644 dinput/keybinding-worker.cpp
 create mode 100644 dinput/keybinding-worker.hpp
 create mode 100644 dinput/win32-joystick.cpp
 create mode 100644 dinput/win32-joystick.hpp
 create mode 100644 logic/CMakeLists.txt
 create mode 100644 logic/export.hpp
 create mode 100644 logic/main-settings.hpp
 create mode 100644 logic/mappings.hpp
 create mode 100644 logic/selected-libraries.cpp
 create mode 100644 logic/selected-libraries.hpp
 create mode 100644 logic/shortcuts.cpp
 create mode 100644 logic/shortcuts.h
 create mode 100644 logic/simple-mat.cpp
 create mode 100644 logic/simple-mat.hpp
 create mode 100644 logic/state.hpp
 create mode 100644 logic/tracker.cpp
 create mode 100644 logic/tracker.h
 create mode 100644 logic/tracklogger.cpp
 create mode 100644 logic/tracklogger.hpp
 create mode 100644 logic/win32-shortcuts.cpp
 create mode 100644 logic/win32-shortcuts.h
 create mode 100644 logic/work.cpp
 create mode 100644 logic/work.hpp
 delete mode 100644 opentrack-compat/CMakeLists.txt
 delete mode 100644 opentrack-compat/camera-names.cpp
 delete mode 100644 opentrack-compat/camera-names.hpp
 delete mode 100644 opentrack-compat/export.hpp
 delete mode 100644 opentrack-compat/make-unique.hpp
 delete mode 100644 opentrack-compat/nan.cpp
 delete mode 100644 opentrack-compat/nan.hpp
 delete mode 100644 opentrack-compat/options.cpp
 delete mode 100644 opentrack-compat/options.hpp
 delete mode 100644 opentrack-compat/pi-constant.hpp
 delete mode 100644 opentrack-compat/process-list.hpp
 delete mode 100644 opentrack-compat/qcopyable-mutex.hpp
 delete mode 100644 opentrack-compat/shm.cpp
 delete mode 100644 opentrack-compat/shm.h
 delete mode 100644 opentrack-compat/sleep.hpp
 delete mode 100644 opentrack-compat/slider.cpp
 delete mode 100644 opentrack-compat/slider.hpp
 delete mode 100644 opentrack-compat/timer.hpp
 delete mode 100644 opentrack-compat/util.hpp
 delete mode 100644 opentrack-compat/win32-com.cpp
 delete mode 100644 opentrack-compat/win32-com.hpp
 delete mode 100644 opentrack-dinput/CMakeLists.txt
 delete mode 100644 opentrack-dinput/dinput.cpp
 delete mode 100644 opentrack-dinput/dinput.hpp
 delete mode 100644 opentrack-dinput/export.hpp
 delete mode 100644 opentrack-dinput/keybinding-worker.cpp
 delete mode 100644 opentrack-dinput/keybinding-worker.hpp
 delete mode 100644 opentrack-dinput/win32-joystick.cpp
 delete mode 100644 opentrack-dinput/win32-joystick.hpp
 delete mode 100644 opentrack-logic/CMakeLists.txt
 delete mode 100644 opentrack-logic/export.hpp
 delete mode 100644 opentrack-logic/main-settings.hpp
 delete mode 100644 opentrack-logic/mappings.hpp
 delete mode 100644 opentrack-logic/selected-libraries.cpp
 delete mode 100644 opentrack-logic/selected-libraries.hpp
 delete mode 100644 opentrack-logic/shortcuts.cpp
 delete mode 100644 opentrack-logic/shortcuts.h
 delete mode 100644 opentrack-logic/simple-mat.cpp
 delete mode 100644 opentrack-logic/simple-mat.hpp
 delete mode 100644 opentrack-logic/state.hpp
 delete mode 100644 opentrack-logic/tracker.cpp
 delete mode 100644 opentrack-logic/tracker.h
 delete mode 100644 opentrack-logic/tracklogger.cpp
 delete mode 100644 opentrack-logic/tracklogger.hpp
 delete mode 100644 opentrack-logic/win32-shortcuts.cpp
 delete mode 100644 opentrack-logic/win32-shortcuts.h
 delete mode 100644 opentrack-logic/work.cpp
 delete mode 100644 opentrack-logic/work.hpp
 delete mode 100644 opentrack/CMakeLists.txt
 delete mode 100644 opentrack/dtors.cpp
 delete mode 100644 opentrack/export.hpp
 delete mode 100644 opentrack/is-window-visible.cpp
 delete mode 100644 opentrack/is-window-visible.hpp
 delete mode 100644 opentrack/plugin-api.hpp
 delete mode 100644 opentrack/plugin-support.hpp
 delete mode 100644 opentrack/variance.hpp

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 22ad863b..948bfe04 100755
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -22,7 +22,10 @@ file(GLOB opentrack-subprojects
     "tracker-*/${C}"
     "proto-*/${C}"
     "filter-*/${C}"
-    "opentrack*/${C}"
+    "api/${C}"
+    "compat/${C}"
+    "logic/${C}"
+    "dinput/${C}"
     "gui/${C}"
     "x-plane-plugin/${C}"
     "csv/${C}"
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()); }
+};
diff --git a/cmake/mingw-w64.cmake b/cmake/mingw-w64.cmake
index ecf4cff3..860259ca 100644
--- a/cmake/mingw-w64.cmake
+++ b/cmake/mingw-w64.cmake
@@ -10,7 +10,7 @@ unset(c)
 unset(e)
 # specify the cross compiler
 if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
-    set(p P:/mingw-w64/i686-5.3.0-posix-dwarf-rt_v4-rev0/mingw32/bin/)
+    set(p P:/mingw-w64/i686-6.1.0-posix-dwarf-rt_v5-rev0/mingw32/bin/)
     set(e .exe)
 endif()
 set(c ${p}i686-w64-mingw32-)
diff --git a/cmake/opentrack-version.cmake b/cmake/opentrack-version.cmake
index 5a4901ef..bb907525 100644
--- a/cmake/opentrack-version.cmake
+++ b/cmake/opentrack-version.cmake
@@ -19,7 +19,7 @@ file(WRITE ${CMAKE_BINARY_DIR}/opentrack-version.h "#define OPENTRACK_VERSION \"
 
 set(version-string "
 #define BUILD_compat
-#include \"opentrack-compat/export.hpp\"
+#include \"compat/export.hpp\"
 
 #ifdef __cplusplus
 extern \"C\"
diff --git a/compat/CMakeLists.txt b/compat/CMakeLists.txt
new file mode 100644
index 00000000..2bbb496c
--- /dev/null
+++ b/compat/CMakeLists.txt
@@ -0,0 +1,8 @@
+opentrack_boilerplate(opentrack-compat NO-COMPAT BIN)
+if(NOT WIN32 AND NOT APPLE)
+    target_link_libraries(opentrack-compat rt)
+endif()
+if(CMAKE_COMPILER_IS_GNUCXX)
+    set_source_files_properties(nan.cpp PROPERTIES
+        COMPILE_FLAGS "-fno-fast-math -fno-finite-math-only -O0")
+endif()
diff --git a/compat/camera-names.cpp b/compat/camera-names.cpp
new file mode 100644
index 00000000..21ff3b52
--- /dev/null
+++ b/compat/camera-names.cpp
@@ -0,0 +1,111 @@
+#include "camera-names.hpp"
+
+#ifdef _WIN32
+#   define NO_DSHOW_STRSAFE
+#   include <dshow.h>
+#   include "win32-com.hpp"
+#   include <cwchar>
+#elif defined(__unix) || defined(__linux) || defined(__APPLE__)
+#   include <unistd.h>
+#endif
+
+#ifdef __linux
+#   include <fcntl.h>
+#   include <sys/ioctl.h>
+#   include <linux/videodev2.h>
+#   include <cerrno>
+#endif
+
+#include <QDebug>
+
+OPENTRACK_COMPAT_EXPORT int camera_name_to_index(const QString &name)
+{
+    auto list = get_camera_names();
+    int ret = list.indexOf(name);
+    if (ret < 0)
+        ret = 0;
+    return ret;
+}
+
+OPENTRACK_COMPAT_EXPORT QList<QString> get_camera_names()
+{
+    QList<QString> ret;
+#if defined(_WIN32)
+    // Create the System Device Enumerator.
+    HRESULT hr;
+    init_com_threading(com_apartment);
+    ICreateDevEnum *pSysDevEnum = NULL;
+    hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, 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;
+    hr = pSysDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnumCat, 0);
+
+    if (hr == S_OK) {
+        // Enumerate the monikers.
+        IMoniker *pMoniker = NULL;
+        ULONG cFetched;
+        while (pEnumCat->Next(1, &pMoniker, &cFetched) == S_OK) {
+            IPropertyBag *pPropBag;
+            hr = pMoniker->BindToStorage(0, 0, 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);
+                }
+                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();
+    }
+    else
+        qDebug() << "failed CLSID_VideoInputDeviceCategory" << hr;
+
+    pSysDevEnum->Release();
+#endif
+#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;
+
+        if (access(buf, R_OK | W_OK) == 0) {
+            int fd = open(buf, O_RDONLY);
+            if (fd == -1)
+                continue;
+            struct v4l2_capability video_cap;
+            if(ioctl(fd, VIDIOC_QUERYCAP, &video_cap) == -1)
+            {
+                qDebug() << "VIDIOC_QUERYCAP" << errno;
+                close(fd);
+                continue;
+            }
+            ret[ret.size()-1] = reinterpret_cast<const char*>(video_cap.card);
+            close(fd);
+        }
+    }
+#endif
+    return ret;
+}
diff --git a/compat/camera-names.hpp b/compat/camera-names.hpp
new file mode 100644
index 00000000..ae0c6b25
--- /dev/null
+++ b/compat/camera-names.hpp
@@ -0,0 +1,18 @@
+/* Copyright (c) 2014-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 <QList>
+#include <QString>
+
+#include "export.hpp"
+
+OPENTRACK_COMPAT_EXPORT QList<QString> get_camera_names();
+OPENTRACK_COMPAT_EXPORT int camera_name_to_index(const QString &name);
+
diff --git a/compat/export.hpp b/compat/export.hpp
new file mode 100644
index 00000000..ba10f131
--- /dev/null
+++ b/compat/export.hpp
@@ -0,0 +1,27 @@
+#pragma once
+
+#ifdef BUILD_compat
+#   ifdef _WIN32
+#       define OPENTRACK_COMPAT_LINKAGE __declspec(dllexport)
+#   else
+#       define OPENTRACK_COMPAT_LINKAGE
+#   endif
+
+#   ifndef _MSC_VER
+#       define OPENTRACK_COMPAT_EXPORT __attribute__ ((visibility ("default"))) OPENTRACK_COMPAT_LINKAGE
+#   else
+#       define OPENTRACK_COMPAT_EXPORT OPENTRACK_COMPAT_LINKAGE
+#   endif
+#else
+    #ifdef _WIN32
+    #    define OPENTRACK_COMPAT_LINKAGE __declspec(dllimport)
+    #else
+    #    define OPENTRACK_COMPAT_LINKAGE
+    #endif
+
+    #ifndef _MSC_VER
+    #    define OPENTRACK_COMPAT_EXPORT __attribute__ ((visibility ("default"))) OPENTRACK_COMPAT_LINKAGE
+    #else
+    #    define OPENTRACK_COMPAT_EXPORT OPENTRACK_COMPAT_LINKAGE
+    #endif
+#endif
diff --git a/compat/make-unique.hpp b/compat/make-unique.hpp
new file mode 100644
index 00000000..bb5315c5
--- /dev/null
+++ b/compat/make-unique.hpp
@@ -0,0 +1,43 @@
+#pragma once
+
+// GNU 5.4.0 doesn't have std::make_unique in -std=c++14 mode
+
+// this implementation was taken from http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3656.htm
+
+#include <memory>
+#include <utility>
+#include <cstddef>
+
+namespace detail {
+template<class T> struct Unique_if
+{
+    typedef std::unique_ptr<T> Single_object;
+};
+
+template<class T> struct Unique_if<T[]>
+{
+    typedef std::unique_ptr<T[]> Unknown_bound;
+};
+
+template<class T, size_t N> struct Unique_if<T[N]>
+{
+    typedef void Known_bound;
+};
+}
+
+template<class T, class... Args>
+    typename detail::Unique_if<T>::Single_object
+    make_unique(Args&&... args) {
+        return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+    }
+
+template<class T>
+    typename detail::Unique_if<T>::Unknown_bound
+    make_unique(std::size_t n) {
+        typedef typename std::remove_extent<T>::type U;
+        return std::unique_ptr<T>(new U[n]());
+    }
+
+template<class T, class... Args>
+    typename detail::Unique_if<T>::Known_bound
+    make_unique(Args&&...) = delete;
diff --git a/compat/nan.cpp b/compat/nan.cpp
new file mode 100644
index 00000000..899b907a
--- /dev/null
+++ b/compat/nan.cpp
@@ -0,0 +1,17 @@
+#include <cmath>
+#include "export.hpp"
+
+#if defined(__GNUC__)
+extern "C" OPENTRACK_COMPAT_EXPORT bool __attribute__ ((noinline)) nanp(double value)
+#elif defined(_WIN32)
+extern "C" OPENTRACK_COMPAT_EXPORT __declspec(noinline) bool nanp(double value)
+#else
+extern "C" OPENTRACK_COMPAT_EXPORT bool nanp(double value)
+#endif
+{
+    using std::isnan;
+    using std::isinf;
+
+    const volatile double x = value;
+    return isnan(x) || isinf(x);
+}
diff --git a/compat/nan.hpp b/compat/nan.hpp
new file mode 100644
index 00000000..9926da13
--- /dev/null
+++ b/compat/nan.hpp
@@ -0,0 +1,11 @@
+#pragma once
+
+#include "export.hpp"
+
+#if defined(__GNUC__)
+extern "C" OPENTRACK_COMPAT_EXPORT bool __attribute__ ((noinline)) nanp(double value);
+#elif defined(_WIN32)
+extern "C" OPENTRACK_COMPAT_EXPORT __declspec(noinline) bool nanp(double value);
+#else
+extern "C" OPENTRACK_COMPAT_EXPORT bool nanp(double value);
+#endif
diff --git a/compat/options.cpp b/compat/options.cpp
new file mode 100644
index 00000000..8c6e6c65
--- /dev/null
+++ b/compat/options.cpp
@@ -0,0 +1,239 @@
+#include "options.hpp"
+
+namespace options
+{
+
+group::group(const QString& name) : name(name)
+{
+    auto conf = ini_file();
+    conf->beginGroup(name);
+    for (auto& k_ : conf->childKeys())
+    {
+        auto tmp = k_.toUtf8();
+        QString k(tmp);
+        kvs[k] = conf->value(k_);
+    }
+    conf->endGroup();
+}
+
+void group::save() const
+{
+    auto s = ini_file();
+    s->beginGroup(name);
+    for (auto& i : kvs)
+        s->setValue(i.first, i.second);
+    s->endGroup();
+}
+
+void group::put(const QString &s, const QVariant &d)
+{
+    kvs[s] = d;
+}
+
+bool group::contains(const QString &s) const
+{
+    return kvs.count(s) != 0;
+}
+
+QString group::ini_directory()
+{
+    const auto dirs = QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation);
+    if (dirs.size() == 0)
+        return "";
+    if (QDir(dirs[0]).mkpath(OPENTRACK_ORG))
+        return dirs[0] + "/" OPENTRACK_ORG;
+    return "";
+}
+
+QString group::ini_filename()
+{
+    QSettings settings(OPENTRACK_ORG);
+    return settings.value(OPENTRACK_CONFIG_FILENAME_KEY, OPENTRACK_DEFAULT_CONFIG).toString();
+}
+
+QString group::ini_pathname()
+{
+    const auto dir = ini_directory();
+    if (dir == "")
+        return "";
+    return dir + "/" + ini_filename();
+}
+
+const QStringList group::ini_list()
+{
+    const auto dirname = ini_directory();
+    if (dirname == "")
+        return QStringList();
+    QDir settings_dir(dirname);
+    return settings_dir.entryList( QStringList { "*.ini" } , QDir::Files, QDir::Name );
+}
+
+const mem<QSettings> group::ini_file()
+{
+    const auto pathname = ini_pathname();
+    if (pathname != "")
+        return std::make_shared<QSettings>(ini_pathname(), QSettings::IniFormat);
+    return std::make_shared<QSettings>();
+}
+
+bool group::operator==(const group& other) const
+{
+    for (const auto& kv : kvs)
+    {
+        const QVariant val = other.get<QVariant>(kv.first);
+        if (!other.contains(kv.first) || kv.second != val)
+        {
+            qDebug() << "bundle" << name << "modified" << "key" << kv.first << "-" << val << "<>" << kv.second;
+            return false;
+        }
+    }
+
+    for (const auto& kv : other.kvs)
+    {
+        const QVariant val = get<QVariant>(kv.first);
+        if (!contains(kv.first) || kv.second != val)
+        {
+            qDebug() << "bundle" << name << "modified" << "key" << kv.first << "-" << kv.second << "<>" << val;
+            return false;
+        }
+    }
+    return true;
+}
+
+impl_bundle::impl_bundle(const QString& group_name)
+    :
+      mtx(QMutex::Recursive),
+      group_name(group_name),
+      saved(group_name),
+      transient(saved)
+{
+}
+
+void impl_bundle::reload()
+{
+    {
+        QMutexLocker l(&mtx);
+        saved = group(group_name);
+        transient = saved;
+    }
+    emit reloading();
+}
+
+void impl_bundle::store_kv(const QString& name, const QVariant& datum)
+{
+    QMutexLocker l(&mtx);
+
+    transient.put(name, datum);
+}
+
+bool impl_bundle::contains(const QString &name) const
+{
+    QMutexLocker l(const_cast<QMutex*>(&mtx));
+    return transient.contains(name);
+}
+
+void impl_bundle::save()
+{
+    bool modified_ = false;
+
+    {
+        QMutexLocker l(&mtx);
+        if (saved != transient)
+        {
+            qDebug() << "bundle" << group_name << "changed, saving";
+            modified_ = true;
+            saved = transient;
+            saved.save();
+        }
+    }
+
+    if (modified_)
+        emit saving();
+}
+
+bool impl_bundle::modifiedp() const // XXX unused
+{
+    QMutexLocker l(const_cast<QMutex*>(&mtx));
+    return transient != saved;
+}
+
+base_value::base_value(pbundle b, const QString &name) :
+    b(b),
+    self_name(name)
+{
+}
+
+opts::~opts()
+{
+    b->reload();
+}
+
+opts::opts(const QString &name) : b(bundle(name))
+{
+}
+
+custom_type_initializer::custom_type_initializer()
+{
+    qRegisterMetaTypeStreamOperators<slider_value>("slider_value");
+    QMetaType::registerDebugStreamOperator<slider_value>();
+}
+
+custom_type_initializer custom_type_initializer::singleton;
+
+namespace detail {
+
+opt_bundle::opt_bundle(const QString& group_name)
+    : impl_bundle(group_name)
+{
+}
+
+opt_bundle::~opt_bundle()
+{
+    detail::singleton().bundle_decf(group_name);
+}
+
+void opt_singleton::bundle_decf(const opt_singleton::k& key)
+{
+    QMutexLocker l(&implsgl_mtx);
+
+    if (--std::get<0>(implsgl_data[key]) == 0)
+    {
+        qDebug() << "bundle -" << key;
+
+        implsgl_data.erase(key);
+    }
+}
+
+opt_singleton::opt_singleton() : implsgl_mtx(QMutex::Recursive)
+{
+}
+
+pbundle opt_singleton::bundle(const opt_singleton::k &key)
+{
+    QMutexLocker l(&implsgl_mtx);
+
+    if (implsgl_data.count(key) != 0)
+    {
+        auto shared = std::get<1>(implsgl_data[key]).lock();
+        if (shared != nullptr)
+            return shared;
+    }
+
+    qDebug() << "bundle +" << key;
+
+    auto shr = std::make_shared<v>(key);
+    implsgl_data[key] = tt(1, shr);
+    return shr;
+}
+
+OPENTRACK_COMPAT_EXPORT opt_singleton& singleton()
+{
+    static opt_singleton ret;
+    return ret;
+}
+
+
+} // end options::detail
+
+} // end options
+
diff --git a/compat/options.hpp b/compat/options.hpp
new file mode 100644
index 00000000..40aca2de
--- /dev/null
+++ b/compat/options.hpp
@@ -0,0 +1,481 @@
+/* Copyright (c) 2013-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.
+ */
+
+#pragma once
+
+#include <memory>
+#include <tuple>
+#include <map>
+#include <cinttypes>
+#include <vector>
+#include <memory>
+
+#include <QObject>
+#include <QSettings>
+#include <QString>
+#include <QVariant>
+#include <QMutex>
+#include <QMutexLocker>
+#include <QWidget>
+#include <QComboBox>
+#include <QCheckBox>
+#include <QDoubleSpinBox>
+#include <QSpinBox>
+#include <QSlider>
+#include <QLineEdit>
+#include <QLabel>
+#include <QTabWidget>
+#include <QCoreApplication>
+#include <QFileInfo>
+#include <QDir>
+#include <QStandardPaths>
+#include <QApplication>
+
+#include <QMetaType>
+#include <QDataStream>
+
+#include <QDebug>
+
+#include "export.hpp"
+#include "slider.hpp"
+
+#include "util.hpp"
+
+#define OPENTRACK_CONFIG_FILENAME_KEY "settings-filename"
+#define OPENTRACK_DEFAULT_CONFIG "default.ini"
+#define OPENTRACK_ORG "opentrack-2.3"
+
+namespace options {
+    namespace {
+        class custom_type_initializer
+        {
+            custom_type_initializer();
+            static custom_type_initializer singleton;
+        };
+    }
+
+    template<typename k, typename v> using map = std::map<k, v>;
+
+    // snapshot of qsettings group at given time
+    class OPENTRACK_COMPAT_EXPORT group
+    {
+    private:
+        map<QString, QVariant> kvs;
+        QString name;
+    public:
+        group(const QString& name);
+        void save() const;
+        void put(const QString& s, const QVariant& d);
+        bool contains(const QString& s) const;
+        static QString ini_directory();
+        static QString ini_filename();
+        static QString ini_pathname();
+        static const QStringList ini_list();
+        static const mem<QSettings> ini_file();
+        bool operator==(const group& other) const;
+        bool operator!=(const group& other) const { return !(*this == other); }
+
+        template<typename t>
+        t get(const QString& k) const
+        {
+            auto value = kvs.find(k);
+            if (value != kvs.cend())
+                return value->second.value<t>();
+            return t();
+        }
+    };
+
+    class OPENTRACK_COMPAT_EXPORT impl_bundle : public QObject
+    {
+        Q_OBJECT
+    protected:
+        QMutex mtx;
+        const QString group_name;
+        group saved;
+        group transient;
+        impl_bundle(const impl_bundle&) = delete;
+        impl_bundle& operator=(const impl_bundle&) = delete;
+    signals:
+        void reloading();
+        void saving() const;
+    public:
+        impl_bundle(const QString& group_name);
+        QString name() { return group_name; }
+        void reload();
+        void store_kv(const QString& name, const QVariant& datum);
+        bool contains(const QString& name) const;
+        void save();
+        bool modifiedp() const;
+
+        template<typename t>
+        t get(const QString& name) const
+        {
+            QMutexLocker l(const_cast<QMutex*>(&mtx));
+            return transient.get<t>(name);
+        }
+    };
+
+    namespace detail
+    {
+        class OPENTRACK_COMPAT_EXPORT opt_bundle final : public impl_bundle
+        {
+        public:
+            opt_bundle(const QString& group_name);
+            ~opt_bundle();
+        };
+
+        struct OPENTRACK_COMPAT_EXPORT opt_singleton
+        {
+        public:
+            using k = QString;
+            using v = opt_bundle;
+            using cnt = int;
+            using pbundle = std::shared_ptr<v>;
+            using tt = std::tuple<cnt, std::weak_ptr<v>>;
+        private:
+            QMutex implsgl_mtx;
+            map<k, tt> implsgl_data;
+        public:
+            opt_singleton();
+            pbundle bundle(const k& key);
+            void bundle_decf(const k& key);
+        };
+
+        OPENTRACK_COMPAT_EXPORT opt_singleton& singleton();
+    }
+
+    using pbundle = std::shared_ptr<detail::opt_bundle>;
+
+    inline pbundle bundle(const QString& name)
+    {
+         return detail::singleton().bundle(name);
+    }
+
+#define OPENTRACK_DEFINE_SLOT(t) void setValue(t datum) { store(datum); }
+#define OPENTRACK_DEFINE_SIGNAL(t) void valueChanged(t)
+
+    class OPENTRACK_COMPAT_EXPORT base_value : public QObject
+    {
+        Q_OBJECT
+    public:
+        QString name() const { return self_name; }
+        base_value(pbundle b, const QString& name);
+    signals:
+        OPENTRACK_DEFINE_SIGNAL(double);
+        OPENTRACK_DEFINE_SIGNAL(float);
+        OPENTRACK_DEFINE_SIGNAL(int);
+        OPENTRACK_DEFINE_SIGNAL(bool);
+        OPENTRACK_DEFINE_SIGNAL(const QString&);
+        OPENTRACK_DEFINE_SIGNAL(const slider_value&);
+    protected:
+        pbundle b;
+        QString self_name;
+
+        template<typename t>
+        void store(const t& datum)
+        {
+            b->store_kv(self_name, QVariant::fromValue(datum));
+            emit valueChanged(static_cast<t>(datum));
+        }
+        void store(float datum)
+        {
+            store(double(datum));
+        }
+
+    public slots:
+        OPENTRACK_DEFINE_SLOT(double)
+        OPENTRACK_DEFINE_SLOT(int)
+        OPENTRACK_DEFINE_SLOT(bool)
+        OPENTRACK_DEFINE_SLOT(const QString&)
+        OPENTRACK_DEFINE_SLOT(const slider_value&)
+    public slots:
+        virtual void reload() = 0;
+    };
+
+    namespace detail {
+        template<typename t>
+        struct value_get_traits
+        {
+            static inline t get(const t& val, const t&)
+            {
+                return val;
+            }
+        };
+
+        template<>
+        struct value_get_traits<slider_value>
+        {
+            using t = slider_value;
+            static inline t get(const t& val, const t& def)
+            {
+                return t(val.cur(), def.min(), def.max());
+            }
+        };
+    }
+
+    template<typename t_>
+    class value : public base_value
+    {
+        template<typename t__, typename Enable = void>
+        struct get_t
+        { using t = t__; };
+
+        // Qt uses int a lot in slots so use it for all enums
+        template<typename t__>
+        struct get_t<t__, typename std::enable_if<std::is_enum<t__>::value>::type>
+        //{ using t = typename std::underlying_type<t__>::type; };
+        { using t = int; };
+
+        using t = t_;
+    public:
+        using underlying_t = typename get_t<t_>::t;
+
+        t operator=(const t& datum)
+        {
+            store(static_cast<underlying_t>(datum));
+            return datum;
+        }
+
+        static constexpr const Qt::ConnectionType DIRECT_CONNTYPE = Qt::AutoConnection;
+        static constexpr const Qt::ConnectionType SAFE_CONNTYPE = Qt::QueuedConnection;
+
+        value(pbundle b, const QString& name, t def) : base_value(b, name), def(def)
+        {
+            QObject::connect(b.get(), SIGNAL(reloading()),
+                             this, SLOT(reload()),
+                             DIRECT_CONNTYPE);
+            if (!b->contains(name) || b->get<QVariant>(name).type() == QVariant::Invalid)
+                *this = def;
+        }
+
+        value(pbundle b, const char* name, t def) : value(b, QString(name), def)
+        {
+        }
+
+        t get() const
+        {
+            t val = b->contains(self_name)
+                    ? static_cast<t>(b->get<underlying_t>(self_name))
+                    : def;
+            return detail::value_get_traits<t>::get(val, def);
+        }
+
+        operator t() const { return get(); }
+
+        void reload() override
+        {
+            *this = static_cast<t>(*this);
+        }
+
+    private:
+        t def;
+    };
+
+    struct OPENTRACK_COMPAT_EXPORT opts
+    {
+        pbundle b;
+        opts(const QString& name);
+        opts& operator=(const opts&) = delete;
+        opts(const opts&) = delete;
+        ~opts();
+    };
+
+    template<typename t, typename q>
+    inline void tie_setting(value<t>&, q*);
+
+    template<typename t>
+    inline
+    typename std::enable_if<std::is_enum<t>::value>::type
+    tie_setting(value<t>& v, QComboBox* cb)
+    {
+        cb->setCurrentIndex(cb->findData((unsigned)static_cast<t>(v)));
+        v = static_cast<t>(cb->currentData().toInt());
+
+        // QObject::connect plays badly with std::bind of std::shared_ptr. Data seems to get freed.
+        // Direct accesses of cb->currentData within arbitrary thread context cause crashes as well.
+        // Hence we go for a verbose implementation.
+
+        std::vector<int> enum_cases;
+        enum_cases.reserve(unsigned(cb->count()));
+
+        for (int i = 0; i < cb->count(); i++)
+            enum_cases.push_back(cb->itemData(i).toInt());
+
+        struct fn1
+        {
+            value<t>& v;
+            QComboBox* cb;
+            std::vector<int> enum_cases;
+
+            fn1(value<t>& v, QComboBox* cb, const std::vector<int>& enum_cases) :
+                v(v),
+                cb(cb),
+                enum_cases(enum_cases)
+            {
+            }
+
+            void operator()(int idx)
+            {
+                if (idx < 0 || idx >= (int)enum_cases.size())
+                    v = static_cast<t>(-1);
+                else
+                    v = static_cast<t>(t(std::intptr_t(enum_cases[idx])));
+            }
+        };
+
+        struct fn2
+        {
+            value<t>& v;
+            QComboBox* cb;
+            std::vector<int> enum_cases;
+
+            fn2(value<t>& v, QComboBox* cb, const std::vector<int>& enum_cases) :
+                v(v),
+                cb(cb),
+                enum_cases(enum_cases)
+            {
+            }
+
+            void operator()(int val)
+            {
+                for (unsigned i = 0; i < enum_cases.size(); i++)
+                {
+                    if (val == enum_cases[i])
+                    {
+                        cb->setCurrentIndex(i);
+                        return;
+                    }
+                }
+                cb->setCurrentIndex(-1);
+            }
+        };
+
+        base_value::connect(cb,
+                            static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
+                            &v,
+                            fn1(v, cb, enum_cases),
+                            v.DIRECT_CONNTYPE);
+        base_value::connect(&v,
+                            static_cast<void (base_value::*)(int)>(&base_value::valueChanged),
+                            cb,
+                            fn2(v, cb, enum_cases),
+                            v.DIRECT_CONNTYPE);
+    }
+
+    template<>
+    inline void tie_setting(value<int>& v, QComboBox* cb)
+    {
+        cb->setCurrentIndex(v);
+        v = cb->currentIndex();
+        base_value::connect(cb, SIGNAL(currentIndexChanged(int)), &v, SLOT(setValue(int)), v.DIRECT_CONNTYPE);
+        base_value::connect(&v, SIGNAL(valueChanged(int)), cb, SLOT(setCurrentIndex(int)), v.SAFE_CONNTYPE);
+    }
+
+    template<>
+    inline void tie_setting(value<QString>& v, QComboBox* cb)
+    {
+        cb->setCurrentText(v);
+        v = cb->currentText();
+        base_value::connect(cb, SIGNAL(currentTextChanged(QString)), &v, SLOT(setValue(QString)), v.DIRECT_CONNTYPE);
+        base_value::connect(&v, SIGNAL(valueChanged(QString)), cb, SLOT(setCurrentText(QString)), v.SAFE_CONNTYPE);
+    }
+
+    template<>
+    inline void tie_setting(value<bool>& v, QCheckBox* cb)
+    {
+        cb->setChecked(v);
+        base_value::connect(cb, SIGNAL(toggled(bool)), &v, SLOT(setValue(bool)), v.DIRECT_CONNTYPE);
+        base_value::connect(&v, SIGNAL(valueChanged(bool)), cb, SLOT(setChecked(bool)), v.SAFE_CONNTYPE);
+    }
+
+    template<>
+    inline void tie_setting(value<double>& v, QDoubleSpinBox* dsb)
+    {
+        dsb->setValue(v);
+        base_value::connect(dsb, SIGNAL(valueChanged(double)), &v, SLOT(setValue(double)), v.DIRECT_CONNTYPE);
+        base_value::connect(&v, SIGNAL(valueChanged(double)), dsb, SLOT(setValue(double)), v.SAFE_CONNTYPE);
+    }
+
+    template<>
+    inline void tie_setting(value<int>& v, QSpinBox* sb)
+    {
+        sb->setValue(v);
+        base_value::connect(sb, SIGNAL(valueChanged(int)), &v, SLOT(setValue(int)), v.DIRECT_CONNTYPE);
+        base_value::connect(&v, SIGNAL(valueChanged(int)), sb, SLOT(setValue(int)), v.SAFE_CONNTYPE);
+    }
+
+    template<>
+    inline void tie_setting(value<int>& v, QSlider* sl)
+    {
+        sl->setValue(v);
+        v = sl->value();
+        base_value::connect(sl, SIGNAL(valueChanged(int)), &v, SLOT(setValue(int)), v.DIRECT_CONNTYPE);
+        base_value::connect(&v, SIGNAL(valueChanged(int)), sl, SLOT(setValue(int)), v.SAFE_CONNTYPE);
+    }
+
+    template<>
+    inline void tie_setting(value<QString>& v, QLineEdit* le)
+    {
+        le->setText(v);
+        base_value::connect(le, SIGNAL(textChanged(QString)), &v, SLOT(setValue(QString)), v.DIRECT_CONNTYPE);
+        base_value::connect(&v, SIGNAL(valueChanged(QString)),le, SLOT(setText(QString)), v.SAFE_CONNTYPE);
+    }
+
+    template<>
+    inline void tie_setting(value<QString>& v, QLabel* lb)
+    {
+        lb->setText(v);
+        base_value::connect(&v, SIGNAL(valueChanged(QString)), lb, SLOT(setText(QString)), v.DIRECT_CONNTYPE);
+    }
+
+    template<>
+    inline void tie_setting(value<int>& v, QTabWidget* t)
+    {
+        t->setCurrentIndex(v);
+        base_value::connect(t, SIGNAL(currentChanged(int)), &v, SLOT(setValue(int)), v.DIRECT_CONNTYPE);
+        base_value::connect(&v, SIGNAL(valueChanged(int)), t, SLOT(setCurrentIndex(int)), v.SAFE_CONNTYPE);
+    }
+
+    template<>
+    inline void tie_setting(value<slider_value>& v, QSlider* w)
+    {
+        // we can't get these at runtime since signals cross threads
+        const int q_min = w->minimum();
+        const int q_max = w->maximum();
+        const int q_diff = q_max - q_min;
+
+        slider_value sv(v);
+
+        const double sv_max = sv.max();
+        const double sv_min = sv.min();
+        const double sv_c = sv_max - sv_min;
+
+        w->setValue(int((sv.cur() - sv_min) / sv_c * q_diff + q_min));
+        v = slider_value(q_diff <= 0 ? 0 : (w->value() - q_min) * sv_c / (double)q_diff + sv_min, sv_min, sv_max);
+
+        base_value::connect(w,
+                            &QSlider::valueChanged,
+                            &v,
+                            [=, &v](int pos) -> void
+        {
+            if (q_diff <= 0 || pos <= 0)
+                v = slider_value(sv_min, sv_min, sv_max);
+            else
+                v = slider_value((pos - q_min) * sv_c / (double)q_diff + sv_min, sv_min, sv_max);
+        },
+        v.DIRECT_CONNTYPE);
+        base_value::connect(&v,
+                            static_cast<void(base_value::*)(double)>(&base_value::valueChanged),
+                            w,
+                            [=](double value) -> void
+        {
+            w->setValue(int(value * q_diff) + q_min);
+        },
+        v.SAFE_CONNTYPE);
+    }
+}
+
diff --git a/compat/pi-constant.hpp b/compat/pi-constant.hpp
new file mode 100644
index 00000000..52b98a7f
--- /dev/null
+++ b/compat/pi-constant.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+#define OPENTRACK_PI 3.14159265358979323846
diff --git a/compat/process-list.hpp b/compat/process-list.hpp
new file mode 100644
index 00000000..10613791
--- /dev/null
+++ b/compat/process-list.hpp
@@ -0,0 +1,169 @@
+/* Copyright (c) 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 <QDebug>
+#include <QStringList>
+
+#if defined _WIN32
+
+#include <windows.h>
+#include <tlhelp32.h>
+
+template<typename = void>
+static QStringList get_all_executable_names()
+{
+    QStringList ret;
+    HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+    if (h == INVALID_HANDLE_VALUE)
+        return ret;
+
+    PROCESSENTRY32 e;
+    e.dwSize = sizeof(e);
+
+    if (Process32First(h, &e) != TRUE)
+    {
+        CloseHandle(h);
+        return ret;
+    }
+
+    do {
+        ret.append(e.szExeFile);
+    } while (Process32Next(h, &e) == TRUE);
+
+    CloseHandle(h);
+
+    return ret;
+}
+#elif defined __APPLE__
+#include <libproc.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <cerrno>
+#include <cstring>
+#include <vector>
+
+template<typename = void>
+static QStringList get_all_executable_names()
+{
+    QStringList ret;
+    std::vector<int> vec;
+
+    while (true)
+    {
+        int numproc = proc_listpids(PROC_ALL_PIDS, 0, nullptr, 0);
+        if (numproc == -1)
+        {
+            qDebug() << "proc_listpids numproc failed" << errno;
+            return ret;
+        }
+        vec.resize(numproc);
+        int cnt = proc_listpids(PROC_ALL_PIDS, 0, &vec[0], sizeof(int) * numproc);
+
+        if (cnt <= numproc)
+        {
+            std::vector<char> arglist;
+            int mib[2] { CTL_KERN, KERN_ARGMAX };
+            size_t sz = sizeof(int);
+            int maxarg = 0;
+            if (sysctl(mib, 2, &maxarg, &sz, NULL, 0) == -1)
+            {
+                qDebug() << "sysctl KERN_ARGMAX" << errno;
+                return ret;
+            }
+            arglist.resize(maxarg);
+            for (int i = 0; i < numproc; i++)
+            {
+                size_t maxarg_ = (size_t)maxarg;
+                int mib[3] { CTL_KERN, KERN_PROCARGS2, vec[i] };
+                if (sysctl(mib, 3, &arglist[0], &maxarg_, NULL, 0) == -1)
+                {
+                    //qDebug() << "sysctl KERN_PROCARGS2" << vec[i] << errno;
+                    continue;
+                }
+                QStringList cmdline;
+                for (unsigned j = sizeof(int) + strlen(&arglist[sizeof(int)]); j < maxarg_; j++)
+                {
+                    QString arg(&arglist[j]);
+                    if (arg.size() != 0)
+                    {
+                        cmdline << arg;
+                        j += arg.size();
+                    }
+                }
+                if (cmdline.size() > 0)
+                {
+                    int idx = cmdline[0].lastIndexOf('/');
+                    if (idx != -1)
+                    {
+                        QString tmp = cmdline[0].mid(idx+1);
+                        if (cmdline.size() > 1 && (tmp == "wine.bin" || tmp == "wine"))
+                        {
+                            idx = cmdline[1].lastIndexOf('/');
+                            if (idx == -1)
+                                idx = cmdline[1].lastIndexOf('\\');
+                            if (idx != -1)
+                            {
+                                ret.append(cmdline[1].mid(idx+1));
+                            }
+                            else
+                                ret.append(cmdline[1]);
+                        }
+                        else
+                        {
+                            ret.append(tmp);
+                        }
+                    }
+                    else
+                        ret.append(cmdline[0]);
+                }
+            }
+            return ret;
+        }
+    }
+}
+
+#elif defined __linux
+
+#include <proc/readproc.h>
+#include <cerrno>
+template<typename = void>
+static QStringList get_all_executable_names()
+{
+    QStringList ret;
+    proc_t** procs = readproctab(PROC_FILLCOM);
+    if (procs == nullptr)
+    {
+        qDebug() << "readproctab" << errno;
+        return ret;
+    }
+    for (int i = 0; procs[i]; i++)
+    {
+        // note, wine sets argv[0] so no parsing like in OSX case
+        auto proc = procs[i];
+        if (proc->cmdline && proc->cmdline[0])
+        {
+            QString tmp(proc->cmdline[0]);
+            const int idx = std::max(tmp.lastIndexOf('\\'), tmp.lastIndexOf('/'));
+            tmp = tmp.mid(idx == -1 ? 0 : idx+1);
+            ret.append(tmp);
+        }
+        freeproc(procs[i]);
+    }
+    free(procs);
+    return ret;
+}
+
+#else
+template<typename = void>
+static QStringList get_all_executable_names()
+{
+    return QStringList();
+}
+#endif
diff --git a/compat/qcopyable-mutex.hpp b/compat/qcopyable-mutex.hpp
new file mode 100644
index 00000000..57b0030d
--- /dev/null
+++ b/compat/qcopyable-mutex.hpp
@@ -0,0 +1,37 @@
+#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/shm.cpp b/compat/shm.cpp
new file mode 100644
index 00000000..83a3260c
--- /dev/null
+++ b/compat/shm.cpp
@@ -0,0 +1,199 @@
+/* Copyright (c) 2013 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.
+ */
+
+#include "shm.h"
+
+#if defined(_WIN32)
+
+#include <cstring>
+#include <stdio.h>
+
+#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;
+    }
+
+    secattr(DWORD perms) : success(true), pSD(nullptr), pEveryoneSID(nullptr), pACL(nullptr)
+    {
+        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));
+
+        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)
+        {
+            fprintf(stderr, "LocalAlloc: %d\n", (int) GetLastError());
+            goto cleanup;
+        }
+
+        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);
+
+        fflush(stderr);
+
+        return;
+cleanup:
+        fflush(stderr);
+        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(
+                 INVALID_HANDLE_VALUE,
+                 sa.success ? &sa.attrs : nullptr,
+                 PAGE_READWRITE,
+                 0,
+                 mapSize,
+                 shmName);
+    if (!hMapFile)
+    {
+        fprintf(stderr, "CreateFileMappingA: %d\n", (int) GetLastError());
+        fflush(stderr);
+    }
+    mem = MapViewOfFile(hMapFile,
+                        FILE_MAP_WRITE,
+                        0,
+                        0,
+                        mapSize);
+    if (!mem)
+    {
+        fprintf(stderr, "MapViewOfFile: %d\n", (int) GetLastError());
+        fflush(stderr);
+    }
+}
+
+PortableLockedShm::~PortableLockedShm()
+{
+    UnmapViewOfFile(mem);
+    CloseHandle(hMapFile);
+    CloseHandle(hMutex);
+}
+
+void PortableLockedShm::lock()
+{
+    (void) WaitForSingleObject(hMutex, INFINITE);
+}
+
+void PortableLockedShm::unlock()
+{
+    (void) ReleaseMutex(hMutex);
+}
+#else
+
+#include <limits.h>
+
+#pragma GCC diagnostic ignored "-Wunused-result"
+PortableLockedShm::PortableLockedShm(const char *shmName, const char* /*mutexName*/, int mapSize) : size(mapSize)
+{
+    char filename[PATH_MAX+2] = {0};
+    strcpy(filename, "/");
+    strcat(filename, shmName);
+    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);
+}
+
+PortableLockedShm::~PortableLockedShm()
+{
+    (void) munmap(mem, size);
+    (void) close(fd);
+}
+
+void PortableLockedShm::lock()
+{
+    flock(fd, LOCK_EX);
+}
+
+void PortableLockedShm::unlock()
+{
+    flock(fd, LOCK_UN);
+}
+#endif
+
+bool PortableLockedShm::success()
+{
+#ifndef _WIN32
+    return mem != (void*) -1;
+#else
+    return mem != NULL;
+#endif
+}
diff --git a/compat/shm.h b/compat/shm.h
new file mode 100644
index 00000000..f212dc17
--- /dev/null
+++ b/compat/shm.h
@@ -0,0 +1,48 @@
+/* Copyright (c) 2013 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
+
+#if defined(_WIN32)
+#include <windows.h>
+#else
+#include <stdio.h>
+#include <string.h>
+#include <sys/file.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <unistd.h>
+#include <sys/types.h>
+#endif
+
+#ifdef __GNUC__
+#   pragma GCC diagnostic push
+#   pragma GCC diagnostic ignored "-Wattributes"
+#endif
+
+#include "export.hpp"
+
+class OPENTRACK_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:
+    void* mem;
+#if defined(_WIN32)
+    HANDLE hMutex, hMapFile;
+#else
+    int fd, size;
+#endif
+};
+
+#ifdef __GNUC__
+#   pragma GCC diagnostic pop
+#endif
diff --git a/compat/sleep.hpp b/compat/sleep.hpp
new file mode 100644
index 00000000..fab27286
--- /dev/null
+++ b/compat/sleep.hpp
@@ -0,0 +1,22 @@
+#pragma once
+
+#ifdef _WIN32
+#   include <windows.h>
+#else
+#   include <unistd.h>
+#endif
+
+namespace portable
+{
+#ifdef _WIN32
+    inline void sleep(unsigned milliseconds)
+    {
+        Sleep(milliseconds);
+    }
+#else
+    inline void sleep(unsigned milliseconds)
+    {
+        usleep(milliseconds * 1000U); // takes microseconds
+    }
+#endif
+}
diff --git a/compat/slider.cpp b/compat/slider.cpp
new file mode 100644
index 00000000..be1aaeb8
--- /dev/null
+++ b/compat/slider.cpp
@@ -0,0 +1,73 @@
+#include "slider.hpp"
+#include <cmath>
+
+namespace options {
+
+slider_value::slider_value(double cur, double min, double max) :
+    cur_(cur),
+    min_(min),
+    max_(max)
+{
+    if (min_ > max_)
+        min_ = max_;
+    if (cur_ > max_)
+        cur_ = max;
+    if (cur_ < min_)
+        cur_ = min_;
+}
+
+slider_value::slider_value(const slider_value& v) : slider_value(v.cur(), v.min(), v.max())
+{
+}
+
+slider_value::slider_value() : slider_value(0, 0, 0)
+{
+}
+
+slider_value& slider_value::operator=(const slider_value& v)
+{
+    cur_ = v.cur_;
+
+    min_ = v.min_;
+    max_ = v.max_;
+
+    return *this;
+}
+
+bool slider_value::operator==(const slider_value& v) const
+{
+    using std::fabs;
+
+    static constexpr double eps = 1e-3;
+
+    return (fabs(v.cur_ - cur_) < eps &&
+            fabs(v.min_ - min_) < eps &&
+            fabs(v.max_ - max_) < eps);
+}
+
+slider_value slider_value::update_from_slider(int pos, int q_min, int q_max) const
+{
+    slider_value v(*this);
+
+    const int q_diff = q_max - q_min;
+    const double sv_pos = q_diff == 0
+                          ? -1e6
+                          : (((pos - q_min) * (v.max() - v.min())) / q_diff + v.min());
+
+    if (sv_pos < v.min())
+        v = slider_value(v.min(), v.min(), v.max());
+    else if (sv_pos > v.max())
+        v = slider_value(v.max(), v.min(), v.max());
+    else
+        v = slider_value(sv_pos, v.min(), v.max());
+    return v;
+}
+
+int slider_value::to_slider_pos(int q_min, int q_max) const
+{
+    const int q_diff = q_max - q_min;
+
+    return int(std::round(((cur() - min() * q_diff) / (max() - min())) + q_min));
+}
+
+} // end ns options
diff --git a/compat/slider.hpp b/compat/slider.hpp
new file mode 100644
index 00000000..7d54c650
--- /dev/null
+++ b/compat/slider.hpp
@@ -0,0 +1,62 @@
+/* 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.
+ */
+
+#pragma once
+
+#include "export.hpp"
+#include <QMetaType>
+#include <QDataStream>
+#include <QDebug>
+
+namespace options
+{
+    class OPENTRACK_COMPAT_EXPORT slider_value final
+    {
+        double cur_, min_, max_;
+    public:
+        slider_value(double cur, double min, double max);
+        slider_value(const slider_value& v);
+        slider_value();
+        slider_value& operator=(const slider_value& v);
+        bool operator==(const slider_value& v) const;
+        operator double() const { return cur_; }
+        double cur() const { return cur_; }
+        double min() const { return min_; }
+        double max() const { return max_; }
+        slider_value update_from_slider(int pos, int q_min, int q_max) const;
+        int to_slider_pos(int q_min, int q_max) const;
+    };
+}
+
+QT_BEGIN_NAMESPACE
+
+inline QDebug operator << (QDebug dbg, const options::slider_value& val)
+{
+    return dbg << val.cur();
+}
+
+inline QDataStream& operator << (QDataStream& out, const options::slider_value& v)
+{
+    out << v.cur()
+        << v.min()
+        << v.max();
+    return out;
+}
+
+inline QDataStream& operator >> (QDataStream& in, options::slider_value& v)
+{
+    double cur, min, max;
+    in >> cur;
+    in >> min;
+    in >> max;
+    v = options::slider_value(cur, min, max);
+    return in;
+}
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(options::slider_value)
diff --git a/compat/timer.hpp b/compat/timer.hpp
new file mode 100644
index 00000000..300a883c
--- /dev/null
+++ b/compat/timer.hpp
@@ -0,0 +1,100 @@
+/* Copyright (c) 2014-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 <ctime>
+
+#include "export.hpp"
+
+#if defined (_WIN32)
+#   include <windows.h>
+#	ifndef CLOCK_MONOTONIC
+#   	define CLOCK_MONOTONIC -1
+#	endif
+static inline void opentrack_clock_gettime(int, struct timespec* ts)
+{
+    static LARGE_INTEGER freq;
+
+    if (!freq.QuadPart)
+        (void) QueryPerformanceFrequency(&freq);
+
+    LARGE_INTEGER d;
+
+    (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);
+}
+#	define clock_gettime opentrack_clock_gettime
+#else
+#   if defined(__MACH__)
+#       define CLOCK_MONOTONIC 0
+#       include <inttypes.h>
+#       include <mach/mach_time.h>
+static inline void clock_gettime(int, struct timespec* ts)
+{
+    static mach_timebase_info_data_t    sTimebaseInfo;
+    uint64_t state, nsec;
+    if ( sTimebaseInfo.denom == 0 ) {
+        (void) mach_timebase_info(&sTimebaseInfo);
+    }
+    state = mach_absolute_time();
+    nsec = state * sTimebaseInfo.numer / sTimebaseInfo.denom;
+    ts->tv_sec = nsec / 1000000000L;
+    ts->tv_nsec = nsec % 1000000000L;
+}
+#   endif
+#endif
+class Timer
+{
+private:
+    struct timespec state;
+    long long conv_nsecs(const struct timespec& cur) const
+    {
+        return (cur.tv_sec - state.tv_sec) * 1000000000LL + (cur.tv_nsec - state.tv_nsec);
+    }
+    long conv_usecs(const struct timespec& cur) const
+    {
+        return long(cur.tv_sec - state.tv_sec) * 1000000L + long(cur.tv_nsec - state.tv_nsec) / 1000l;
+    }
+public:
+    Timer()
+    {
+        start();
+    }
+    void start()
+    {
+        clock_gettime(CLOCK_MONOTONIC, &state);
+    }
+    long long elapsed_nsecs() const
+    {
+        struct timespec cur;
+        clock_gettime(CLOCK_MONOTONIC, &cur);
+        return conv_nsecs(cur);
+    }
+    long elapsed_usecs() const
+    {
+        struct timespec cur;
+        clock_gettime(CLOCK_MONOTONIC, &cur);
+        return long(conv_usecs(cur));
+    }
+    long elapsed_ms() const
+    {
+        return elapsed_usecs() / 1000L;
+    }
+    double elapsed_seconds() const
+    {
+        return double(elapsed_nsecs() * 1e-9L);
+    }
+};
diff --git a/compat/util.hpp b/compat/util.hpp
new file mode 100644
index 00000000..1217e654
--- /dev/null
+++ b/compat/util.hpp
@@ -0,0 +1,93 @@
+#pragma once
+
+#include "make-unique.hpp"
+
+#include <memory>
+#include <utility>
+#include <type_traits>
+#include <thread>
+#include <condition_variable>
+
+#include <QDebug>
+
+#define progn(...) ([&]() { __VA_ARGS__ }())
+template<typename t> using mem = std::shared_ptr<t>;
+template<typename t> using ptr = std::unique_ptr<t>;
+
+template<typename F>
+void run_in_thread_async(QObject* obj, F&& fun)
+{
+    QObject src;
+    src.moveToThread(obj->thread());
+    QObject::connect(&src, &QObject::destroyed, obj, std::move(fun), Qt::AutoConnection);
+}
+
+namespace detail {
+
+template<typename t>
+struct run_in_thread_traits
+{
+    using type = t;
+    using ret_type = t&&;
+    static inline void assign(t& lvalue, t&& rvalue) { lvalue = rvalue; }
+    static inline ret_type&& pass(ret_type&& val) { return std::move(val); }
+    template<typename F> static ret_type call(F& fun) { return std::move(fun()); }
+};
+
+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 std::move(type(0)); }
+};
+
+}
+
+template<typename F>
+auto run_in_thread_sync(QObject* obj, F&& fun)
+    -> typename detail::run_in_thread_traits<decltype(std::forward<F>(fun)())>::ret_type
+{
+    using lock_guard = std::unique_lock<std::mutex>;
+
+    std::mutex mtx;
+    lock_guard guard(mtx);
+    std::condition_variable cvar;
+
+    std::thread::id waiting_thread = std::this_thread::get_id();
+
+    using traits = detail::run_in_thread_traits<decltype(std::forward<F>(fun)())>;
+
+    typename traits::type ret;
+
+    bool skip_wait = false;
+
+    {
+        QObject src;
+        src.moveToThread(obj->thread());
+        QObject::connect(&src,
+                         &QObject::destroyed,
+                         obj,
+                         [&]() {
+            std::thread::id calling_thread = std::this_thread::get_id();
+            if (waiting_thread == calling_thread)
+            {
+                skip_wait = true;
+                traits::assign(ret, traits::call(fun));
+            }
+            else
+            {
+                lock_guard guard(mtx);
+                traits::assign(ret, traits::call(fun));
+                cvar.notify_one();
+            }
+        },
+        Qt::AutoConnection);
+    }
+
+    if (!skip_wait)
+        cvar.wait(guard);
+    return traits::pass(std::move(ret));
+}
diff --git a/compat/win32-com.cpp b/compat/win32-com.cpp
new file mode 100644
index 00000000..dd7c24a8
--- /dev/null
+++ b/compat/win32-com.cpp
@@ -0,0 +1,60 @@
+#ifdef _WIN32
+
+#include "win32-com.hpp"
+
+#include <QString>
+#include <QThread>
+#include <QDebug>
+
+bool OPENTRACK_COMPAT_EXPORT init_com_threading(com_type t)
+{
+    static thread_local com_type initialized = com_type(-1);
+
+    if (initialized != com_type(-1))
+    {
+        if (t != initialized)
+        {
+            QString tp("invalid type");
+            switch (t)
+            {
+            case com_apartment:
+                tp = "apartment threaded";
+                break;
+            case com_multithreaded:
+                tp = "multithreaded";
+                break;
+            }
+
+            qDebug() << "COM for thread"
+                     << QThread::currentThread() << QThread::currentThreadId()
+                     << "already initialized to" << tp;
+
+            return false;
+        }
+
+        return true;
+    }
+
+    HRESULT ret = CoInitializeEx(0, t);
+
+    if (ret != S_OK && ret != S_FALSE)
+    {
+        qDebug() << "CoInitializeEx failed:" << ret << GetLastError();
+        return false;
+    }
+
+    if (t == com_apartment)
+    {
+        ret = OleInitialize(nullptr);
+
+        if (ret != S_OK && ret != S_FALSE)
+            qDebug() << "OleInitialize() failed:" << ret << GetLastError();
+
+        return false;
+    }
+
+    initialized = t;
+
+    return true;
+}
+#endif
diff --git a/compat/win32-com.hpp b/compat/win32-com.hpp
new file mode 100644
index 00000000..dcbea089
--- /dev/null
+++ b/compat/win32-com.hpp
@@ -0,0 +1,18 @@
+#pragma once
+
+#ifdef _WIN32
+
+#include "export.hpp"
+
+#include <objbase.h>
+#include <ole2.h>
+
+enum com_type : int
+{
+    com_multithreaded = COINIT_MULTITHREADED,
+    com_apartment = COINIT_APARTMENTTHREADED,
+};
+
+bool OPENTRACK_COMPAT_EXPORT init_com_threading(com_type t = com_multithreaded);
+
+#endif
diff --git a/cv/camera-dialog.hpp b/cv/camera-dialog.hpp
index 3fd853c3..c5896ca3 100644
--- a/cv/camera-dialog.hpp
+++ b/cv/camera-dialog.hpp
@@ -8,9 +8,9 @@
 
 #pragma once
 
-#include "opentrack-compat/camera-names.hpp"
-#include "opentrack-compat/sleep.hpp"
-#include "opentrack-compat/win32-com.hpp"
+#include "compat/camera-names.hpp"
+#include "compat/sleep.hpp"
+#include "compat/win32-com.hpp"
 
 #ifdef __linux
 #   include <QProcess>
diff --git a/cv/camera-dialog.hpp.OO4364 b/cv/camera-dialog.hpp.OO4364
index e99b5f6e..6f4c7966 100644
--- a/cv/camera-dialog.hpp.OO4364
+++ b/cv/camera-dialog.hpp.OO4364
@@ -9,8 +9,8 @@
 #pragma once
 
 #include "export.hpp"
-#include "opentrack-compat/camera-names.hpp"
-#include "opentrack-compat/sleep.hpp"
+#include "compat/camera-names.hpp"
+#include "compat/sleep.hpp"
 
 #ifdef __linux
 #   include <QProcess>
diff --git a/cv/video-widget.cpp b/cv/video-widget.cpp
index b514dc8f..bc143378 100644
--- a/cv/video-widget.cpp
+++ b/cv/video-widget.cpp
@@ -9,7 +9,7 @@
 #include "video-widget.hpp"
 #include <opencv2/imgproc.hpp>
 
-#include "opentrack/is-window-visible.hpp"
+#include "api/is-window-visible.hpp"
 
 cv_video_widget::cv_video_widget(QWidget* parent) :
     QWidget(parent),
diff --git a/cv/video-widget.hpp b/cv/video-widget.hpp
index 2d5d673f..46332afe 100644
--- a/cv/video-widget.hpp
+++ b/cv/video-widget.hpp
@@ -8,7 +8,7 @@
 
 #pragma once
 
-#include "opentrack-compat/timer.hpp"
+#include "compat/timer.hpp"
 #include <opencv2/core/core.hpp>
 #include <memory>
 #include <QObject>
diff --git a/dinput/CMakeLists.txt b/dinput/CMakeLists.txt
new file mode 100644
index 00000000..ed0b5990
--- /dev/null
+++ b/dinput/CMakeLists.txt
@@ -0,0 +1,4 @@
+if(WIN32)
+    opentrack_boilerplate(opentrack-dinput BIN)
+    target_link_libraries(opentrack-dinput dinput)
+endif()
diff --git a/dinput/dinput.cpp b/dinput/dinput.cpp
new file mode 100644
index 00000000..d408ff2f
--- /dev/null
+++ b/dinput/dinput.cpp
@@ -0,0 +1,98 @@
+#ifdef _WIN32
+
+#include "dinput.hpp"
+#include "compat/win32-com.hpp"
+#include <QDebug>
+
+std::atomic<int> dinput_handle::refcnt;
+std::atomic_flag dinput_handle::init_lock = ATOMIC_FLAG_INIT;
+dinput_handle::di_t dinput_handle::handle(dinput_handle::make_di());
+
+LPDIRECTINPUT8& dinput_handle::init_di()
+{
+    init_com_threading(com_multithreaded);
+
+    static LPDIRECTINPUT8 di_ = nullptr;
+    if (di_ == nullptr)
+    {
+        if (!SUCCEEDED(DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&di_, NULL)))
+        {
+            di_ = nullptr;
+        }
+    }
+    return di_;
+}
+
+dinput_handle::di_t dinput_handle::make_di()
+{
+    while (init_lock.test_and_set()) { /* busy loop */ }
+
+    LPDIRECTINPUT8& ret = init_di();
+
+    init_lock.clear();
+
+    return di_t(ret);
+}
+
+void dinput_handle::di_t::free_di()
+{
+    if (handle && *handle)
+        (*handle)->Release();
+    *handle = nullptr;
+    handle = nullptr;
+}
+
+void dinput_handle::di_t::ref_di()
+{
+    while (init_lock.test_and_set()) { /* busy loop */ }
+
+    const int refcnt_ = refcnt.fetch_add(1) + 1;
+    qDebug() << "start: dinput refcount now" << (refcnt_);
+
+    init_lock.clear();
+}
+
+dinput_handle::di_t& dinput_handle::di_t::operator=(const di_t& new_di)
+{
+    if (handle)
+        unref_di();
+
+    handle = new_di.handle;
+
+    if (handle)
+        ref_di();
+
+    return *this;
+}
+
+void dinput_handle::di_t::unref_di()
+{
+    while (init_lock.test_and_set()) { /* busy loop */ }
+
+    const int refcnt_ = refcnt.fetch_sub(1) - 1;
+
+    qDebug() << "exit: dinput refcount now" << refcnt_;
+
+    if (refcnt_ == 0)
+    {
+        qDebug() << "exit: deleting di handle";
+        free_di();
+    }
+
+    init_lock.clear();
+}
+
+dinput_handle::di_t::di_t(LPDIRECTINPUT8& handle) : handle(&handle)
+{
+    ref_di();
+}
+
+dinput_handle::di_t::di_t() : handle(nullptr) {}
+
+dinput_handle::di_t::~di_t()
+{
+    if (handle)
+        unref_di();
+}
+
+#endif
diff --git a/dinput/dinput.hpp b/dinput/dinput.hpp
new file mode 100644
index 00000000..53f1c4af
--- /dev/null
+++ b/dinput/dinput.hpp
@@ -0,0 +1,58 @@
+/* Copyright (c) 2016, 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
+
+#ifdef _WIN32
+
+#include "export.hpp"
+
+#ifndef DIRECTINPUT_VERSION
+#   define DIRECTINPUT_VERSION 0x800
+#endif
+#include <dinput.h>
+
+#include <atomic>
+
+class OPENTRACK_DINPUT_EXPORT dinput_handle final
+{
+public:
+    class di_t;
+
+private:
+    static std::atomic<int> refcnt;
+    static std::atomic_flag init_lock;
+    static di_t handle;
+
+    static LPDIRECTINPUT8& init_di();
+public:
+    class di_t final
+    {
+        friend class dinput_handle;
+
+        LPDIRECTINPUT8* handle;
+
+        di_t(LPDIRECTINPUT8& handle);
+        void free_di();
+        void unref_di();
+        void ref_di();
+
+    public:
+        LPDIRECTINPUT8 operator->() { return *handle; }
+        operator LPDIRECTINPUT8() { return *handle; }
+        LPDIRECTINPUT8 di() { return *handle; }
+        di_t& operator=(const di_t& new_di);
+        di_t();
+        ~di_t();
+    };
+
+    static di_t make_di();
+    dinput_handle() = delete;
+};
+
+#endif
diff --git a/dinput/export.hpp b/dinput/export.hpp
new file mode 100644
index 00000000..51ee4531
--- /dev/null
+++ b/dinput/export.hpp
@@ -0,0 +1,28 @@
+#pragma once
+
+#ifdef BUILD_dinput
+#   ifdef _WIN32
+#       define OPENTRACK_DINPUT_LINKAGE __declspec(dllexport)
+#   else
+#       define OPENTRACK_DINPUT_LINKAGE
+#   endif
+
+#   ifndef _MSC_VER
+#       define OPENTRACK_DINPUT_EXPORT __attribute__ ((visibility ("default"))) OPENTRACK_DINPUT_LINKAGE
+#   else
+#       define OPENTRACK_DINPUT_EXPORT OPENTRACK_DINPUT_LINKAGE
+#   endif
+
+#else
+#ifdef _WIN32
+#    define OPENTRACK_DINPUT_LINKAGE __declspec(dllimport)
+#else
+#    define OPENTRACK_DINPUT_LINKAGE
+#endif
+
+#ifndef _MSC_VER
+#    define OPENTRACK_DINPUT_EXPORT __attribute__ ((visibility ("default"))) OPENTRACK_DINPUT_LINKAGE
+#else
+#    define OPENTRACK_DINPUT_EXPORT OPENTRACK_DINPUT_LINKAGE
+#endif
+#endif
diff --git a/dinput/keybinding-worker.cpp b/dinput/keybinding-worker.cpp
new file mode 100644
index 00000000..32bfc6f0
--- /dev/null
+++ b/dinput/keybinding-worker.cpp
@@ -0,0 +1,201 @@
+/* Copyright (c) 2014-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.
+ */
+
+#ifdef _WIN32
+
+#include "keybinding-worker.hpp"
+#include <functional>
+#include <windows.h>
+#include <QDebug>
+#include <QMutexLocker>
+
+bool Key::should_process()
+{
+    if (!enabled || (keycode == 0 && guid == ""))
+        return false;
+    bool ret = timer.elapsed_ms() > 100;
+    timer.start();
+    return ret;
+}
+
+KeybindingWorker::~KeybindingWorker()
+{
+    qDebug() << "exit: destroying keybinding worker";
+
+    should_quit = true;
+    wait();
+    if (dinkeyboard) {
+        dinkeyboard->Unacquire();
+        dinkeyboard->Release();
+    }
+}
+
+void KeybindingWorker::init()
+{
+    din = dinput_handle::make_di();
+
+    if (!din)
+    {
+        qDebug() << "can't create dinput handle";
+        return;
+    }
+
+    if (din->CreateDevice(GUID_SysKeyboard, &dinkeyboard, NULL) != DI_OK) {
+        qDebug() << "setup CreateDevice function failed!" << GetLastError();
+        return;
+    }
+
+    if (dinkeyboard->SetDataFormat(&c_dfDIKeyboard) != DI_OK) {
+        qDebug() << "setup SetDataFormat function failed!" << GetLastError();
+        dinkeyboard->Release();
+        dinkeyboard = 0;
+        return;
+    }
+
+    if (dinkeyboard->SetCooperativeLevel((HWND) fake_main_window.winId(), DISCL_NONEXCLUSIVE | DISCL_BACKGROUND) != DI_OK) {
+        dinkeyboard->Release();
+        dinkeyboard = 0;
+        qDebug() << "setup SetCooperativeLevel function failed!" << GetLastError();
+        return;
+    }
+
+    if (dinkeyboard->Acquire() != DI_OK)
+    {
+        dinkeyboard->Release();
+        dinkeyboard = 0;
+        qDebug() << "setup dinkeyboard Acquire failed!" << GetLastError();
+        return;
+    }
+}
+
+KeybindingWorker::KeybindingWorker() : should_quit(false)
+{
+    start();
+}
+
+KeybindingWorker& KeybindingWorker::make()
+{
+    static KeybindingWorker k;
+    return k;
+}
+
+void KeybindingWorker::run()
+{
+    init();
+
+    BYTE keystate[256] = {0};
+    BYTE old_keystate[256] = {0};
+
+    while (!should_quit)
+    {
+        {
+            QMutexLocker l(&mtx);
+
+            if (receivers.size())
+            {
+                {
+                    const HRESULT hr = dinkeyboard->GetDeviceState(256, (LPVOID)keystate);
+
+                    if (hr != DI_OK) {
+                        qDebug() << "Tracker::run GetDeviceState function failed!" << GetLastError();
+                        Sleep(25);
+                        continue;
+                    }
+                }
+
+                {
+                    using joy_fn = std::function<void(const QString& guid, int idx, bool held)>;
+
+                    joy_fn f = [&](const QString& guid, int idx, bool held) -> void {
+                        Key k;
+                        k.keycode = idx;
+                        k.shift = !!(keystate[DIK_LSHIFT] & 0x80 || keystate[DIK_RSHIFT] & 0x80);
+                        k.alt = !!(keystate[DIK_LALT] & 0x80 || keystate[DIK_RALT] & 0x80);
+                        k.ctrl = !!(keystate[DIK_LCONTROL] & 0x80 || keystate[DIK_RCONTROL] & 0x80);
+                        k.guid = guid;
+                        k.held = held;
+
+                        for (auto& r : receivers)
+                            r->operator()(k);
+                    };
+
+                    joy_ctx.poll(f);
+                }
+
+                for (int i = 0; i < 256; i++)
+                {
+                    Key k;
+                    if (old_keystate[i] != keystate[i])
+                    {
+                        const bool held = keystate[i] & 0x80;
+                        switch (i)
+                        {
+                        case DIK_LCONTROL:
+                        case DIK_LSHIFT:
+                        case DIK_LALT:
+                        case DIK_RCONTROL:
+                        case DIK_RSHIFT:
+                        case DIK_RALT:
+                            break;
+                        default:
+                            k.shift = !!(keystate[DIK_LSHIFT] & 0x80) || !!(keystate[DIK_RSHIFT] & 0x80);
+                            k.alt = !!(keystate[DIK_LALT] & 0x80) || !!(keystate[DIK_RALT] & 0x80);
+                            k.ctrl = !!(keystate[DIK_LCONTROL] & 0x80) || !!(keystate[DIK_RCONTROL] & 0x80);
+                            k.keycode = i;
+                            k.held = held;
+
+                            for (auto& r : receivers)
+                                r->operator()(k);
+                            break;
+                        }
+                    }
+                    old_keystate[i] = keystate[i];
+                }
+            }
+        }
+
+        // keypresses get dropped with high values
+        Sleep(4);
+    }
+}
+
+KeybindingWorker::fun* KeybindingWorker::_add_receiver(fun& receiver)
+{
+    QMutexLocker l(&mtx);
+    receivers.push_back(std::unique_ptr<fun>(new fun(receiver)));
+    fun* f = receivers[receivers.size() - 1].get();
+    //qDebug() << "add receiver" << (long) f;
+    joy_ctx.refresh();
+    return f;
+}
+
+void KeybindingWorker::remove_receiver(KeybindingWorker::fun* pos)
+{
+    QMutexLocker l(&mtx);
+    bool ok = false;
+
+    using s = int;
+
+    for (int i = s(receivers.size()) - 1; i >= 0; i--)
+    {
+        using u = unsigned;
+        if (receivers[u(i)].get() == pos)
+        {
+            ok = true;
+            //qDebug() << "remove receiver" << (long) pos;
+            receivers.erase(receivers.begin() + i);
+            break;
+        }
+    }
+    if (!ok)
+    {
+        qDebug() << "bad remove receiver" << (long) pos;
+    }
+}
+
+#endif
diff --git a/dinput/keybinding-worker.hpp b/dinput/keybinding-worker.hpp
new file mode 100644
index 00000000..39b850e0
--- /dev/null
+++ b/dinput/keybinding-worker.hpp
@@ -0,0 +1,80 @@
+/* Copyright (c) 2014-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 "export.hpp"
+
+#include "compat/timer.hpp"
+#include "win32-joystick.hpp"
+#include "dinput.hpp"
+#include <QThread>
+#include <QMutex>
+#include <QWidget>
+#include <QMainWindow>
+#include <QDebug>
+#include <functional>
+#include <vector>
+
+struct OPENTRACK_DINPUT_EXPORT Key
+{
+    BYTE keycode;
+    QString guid;
+    bool shift;
+    bool ctrl;
+    bool alt;
+    bool held;
+    bool enabled;
+    Timer timer;
+public:
+    Key() : keycode(0), shift(false), ctrl(false), alt(false), held(true), enabled(true) {}
+
+    bool should_process();
+};
+
+struct OPENTRACK_DINPUT_EXPORT KeybindingWorker : private QThread
+{
+    using fun = std::function<void(const Key&)>;
+
+private:
+    LPDIRECTINPUTDEVICE8 dinkeyboard;
+    win32_joy_ctx joy_ctx;
+    std::vector<std::unique_ptr<fun>> receivers;
+    QMutex mtx;
+    QMainWindow fake_main_window;
+    dinput_handle::di_t din;
+    volatile bool should_quit;
+
+    void run() override;
+    void init();
+    KeybindingWorker();
+
+    static KeybindingWorker& make();
+    fun* _add_receiver(fun &receiver);
+    void remove_receiver(fun* pos);
+    ~KeybindingWorker();
+
+    KeybindingWorker(const KeybindingWorker&) = delete;
+    KeybindingWorker& operator=(KeybindingWorker&) = delete;
+public:
+    class Token
+    {
+        fun* pos;
+        Token(const Token&) = delete;
+        Token& operator=(Token&) = delete;
+    public:
+        ~Token()
+        {
+            make().remove_receiver(pos);
+        }
+        Token(fun receiver)
+        {
+            pos = make()._add_receiver(receiver);
+        }
+    };
+};
diff --git a/dinput/win32-joystick.cpp b/dinput/win32-joystick.cpp
new file mode 100644
index 00000000..9502bcd2
--- /dev/null
+++ b/dinput/win32-joystick.cpp
@@ -0,0 +1,359 @@
+#ifdef _WIN32
+
+#undef NDEBUG
+#include "win32-joystick.hpp"
+#include "compat/sleep.hpp"
+#include <cassert>
+#include <cstring>
+#include <algorithm>
+#include <cmath>
+#include <objbase.h>
+
+#include <QDebug>
+
+QMutex win32_joy_ctx::enum_state::mtx;
+win32_joy_ctx::enum_state win32_joy_ctx::enumerator;
+
+void win32_joy_ctx::poll(fn f)
+{
+    //refresh(false);
+
+    QMutexLocker l(&enumerator.mtx);
+
+    auto& joys = enumerator.get_joys();
+
+    for (auto& j : joys)
+    {
+        j.second->poll(f);
+    }
+}
+
+bool win32_joy_ctx::poll_axis(const QString &guid, int* axes)
+{
+    QMutexLocker l(&enumerator.mtx);
+
+    for (int k = 0; k < 10; k++)
+    {
+        if (k > 0)
+            enumerator.refresh();
+
+        const joys_t& joys = enumerator.get_joys();
+        auto iter = joys.find(guid);
+
+        if (iter == joys.end())
+            return false;
+
+        auto& j = iter->second;
+
+        auto& joy_handle = j->joy_handle;
+        bool ok = false;
+        HRESULT hr;
+
+        if (!FAILED(hr = joy_handle->Poll()))
+        {
+            ok = true;
+        }
+
+        if (!ok && FAILED(hr = joy_handle->Acquire()))
+        {
+            //qDebug() << "joy acquire failed" << hr;
+        }
+
+        if (!ok)
+        {
+            portable::sleep(25);
+            (void) joy_handle->Unacquire();
+            continue;
+        }
+
+        DIJOYSTATE2 js;
+        std::memset(&js, 0, sizeof(js));
+
+        if (FAILED(hr = joy_handle->GetDeviceState(sizeof(js), &js)))
+        {
+            //qDebug() << "joy get state failed" << guid << hr;
+            portable::sleep(50);
+            continue;
+        }
+
+        const int values[] =
+        {
+            js.lX,
+            js.lY,
+            js.lZ,
+            js.lRx,
+            js.lRy,
+            js.lRz,
+            js.rglSlider[0],
+            js.rglSlider[1]
+        };
+
+        for (int i = 0; i < 8; i++)
+            axes[i] = values[i];
+
+        return true;
+    }
+
+    return false;
+}
+
+std::vector<win32_joy_ctx::joy_info> win32_joy_ctx::get_joy_info()
+{
+    std::vector<joy_info> ret;
+    QMutexLocker l(&enumerator.mtx);
+    auto& joys = enumerator.get_joys();
+    ret.reserve(joys.size());
+
+    for (auto& j : joys)
+        ret.push_back(joy_info { j.second->name, j.first });
+
+    std::sort(ret.begin(), ret.end(), [&](const joy_info& fst, const joy_info& snd) -> bool { return fst.name < snd.name; });
+
+    return ret;
+}
+
+win32_joy_ctx::win32_joy_ctx()
+{
+    refresh();
+}
+
+void win32_joy_ctx::refresh()
+{
+    QMutexLocker l(&enumerator.mtx);
+    enumerator.refresh();
+}
+
+QString win32_joy_ctx::guid_to_string(const GUID& guid)
+{
+    char buf[40] = {0};
+    wchar_t szGuidW[40] = {0};
+
+    StringFromGUID2(guid, szGuidW, 40);
+    WideCharToMultiByte(0, 0, szGuidW, -1, buf, 40, NULL, NULL);
+
+    return QString(buf);
+}
+
+using fn = win32_joy_ctx::fn;
+
+void win32_joy_ctx::joy::release()
+{
+    if (joy_handle)
+    {
+        (void) joy_handle->Unacquire();
+        joy_handle->Release();
+        joy_handle = nullptr;
+    }
+}
+
+bool win32_joy_ctx::joy::poll(fn f)
+{
+    HRESULT hr;
+    bool ok = false;
+
+    (void) joy_handle->Acquire();
+
+    if (!FAILED(hr = joy_handle->Poll()))
+        ok = true;
+
+    if (!ok)
+    {
+        qDebug() << "joy acquire failed" << guid << hr;
+        (void) joy_handle->Unacquire();
+        return false;
+    }
+
+    DIJOYSTATE2 js;
+    std::memset(&js, 0, sizeof(js));
+
+    if (FAILED(hr = joy_handle->GetDeviceState(sizeof(js), &js)))
+    {
+        qDebug() << "joy get state failed" << guid << hr;
+        return false;
+    }
+
+    for (unsigned i = 0; i < 4; i++)
+    {
+        using std::round;
+
+        unsigned char pos;
+        unsigned pos_ = js.rgdwPOV[i];
+        if ((pos_ & 0xffff) == 0xffff)
+            pos = 0;
+        else if (pos_ == ~0u)
+            pos = 0;
+        else
+        {
+            using uc = unsigned char;
+            pos = uc(((pos_ / 9000u) % 4u) + 1u);
+        }
+
+        const bool state[] =
+        {
+            pos == 1,
+            pos == 2,
+            pos == 3,
+            pos == 4
+        };
+
+        unsigned idx = 128u + i * 4u;
+
+        for (unsigned j = 0; j < 4; j++, idx++)
+        {
+            if (state[j] != pressed[idx])
+            {
+                f(guid, int(idx), state[j]);
+                pressed[idx] = state[j];
+            }
+        }
+    }
+
+    for (int i = 0; i < 128; i++)
+    {
+        const bool state = !!(js.rgbButtons[i] & 0x80);
+        if (state != pressed[i])
+        {
+            f(guid, i, state);
+        }
+        pressed[i] = state;
+    }
+
+    return true;
+}
+
+win32_joy_ctx::enum_state::enum_state() : di(dinput_handle::make_di())
+{
+}
+
+win32_joy_ctx::enum_state::~enum_state()
+{
+    QMutexLocker l(&mtx);
+
+    joys = std::unordered_map<QString, std::shared_ptr<joy>>();
+}
+
+void win32_joy_ctx::enum_state::refresh()
+{
+    all.clear();
+
+    if (!di)
+    {
+        qDebug() << "can't create dinput";
+        return;
+    }
+
+    HRESULT hr;
+
+    if(FAILED(hr = di->EnumDevices(DI8DEVCLASS_GAMECTRL,
+                                   EnumJoysticksCallback,
+                                   this,
+                                   DIEDFL_ATTACHEDONLY)))
+    {
+        qDebug() << "failed enum joysticks" << hr;
+        return;
+    }
+
+    for (auto it = joys.begin(); it != joys.end(); )
+    {
+        if (std::find_if(all.cbegin(), all.cend(), [&](const QString& guid2) -> bool { return it->second->guid == guid2; }) == all.end())
+        {
+            it = joys.erase(it);
+        }
+        else
+        {
+            ++it;
+        }
+    }
+}
+
+const win32_joy_ctx::joys_t& win32_joy_ctx::enum_state::get_joys() const { return joys; }
+
+BOOL CALLBACK win32_joy_ctx::enum_state::EnumJoysticksCallback(const DIDEVICEINSTANCE *pdidInstance, void *pContext)
+{
+    enum_state& state = *reinterpret_cast<enum_state*>(pContext);
+    const QString guid = guid_to_string(pdidInstance->guidInstance);
+    const QString name = QString(pdidInstance->tszInstanceName);
+
+    const bool exists = state.joys.find(guid) != state.joys.end();
+
+    state.all.push_back(guid);
+
+    if (exists)
+        goto end;
+
+    {
+        HRESULT hr;
+        LPDIRECTINPUTDEVICE8 h;
+        if (FAILED(hr = state.di->CreateDevice(pdidInstance->guidInstance, &h, nullptr)))
+        {
+            qDebug() << "createdevice" << guid << hr;
+            goto end;
+        }
+        if (FAILED(h->SetDataFormat(&c_dfDIJoystick2)))
+        {
+            qDebug() << "format";
+            h->Release();
+            goto end;
+        }
+
+        // not a static member - need main() to run for some time first
+        static const QWidget fake_window;
+
+        if (FAILED(h->SetCooperativeLevel(reinterpret_cast<HWND>(fake_window.winId()), DISCL_NONEXCLUSIVE | DISCL_BACKGROUND)))
+        {
+            qDebug() << "coop";
+            h->Release();
+            goto end;
+        }
+        if (FAILED(hr = h->EnumObjects(EnumObjectsCallback, h, DIDFT_ALL)))
+        {
+            qDebug() << "enum-objects";
+            h->Release();
+            goto end;
+        }
+
+        state.joys[guid] = std::make_shared<joy>(h, guid, name);
+    }
+end:
+    return DIENUM_CONTINUE;
+}
+
+BOOL CALLBACK win32_joy_ctx::enum_state::EnumObjectsCallback(const DIDEVICEOBJECTINSTANCE *pdidoi, void *ctx)
+{
+    if (pdidoi->dwType & DIDFT_AXIS)
+    {
+        DIPROPRANGE diprg;
+        std::memset(&diprg, 0, sizeof(diprg));
+        diprg.diph.dwSize = sizeof( DIPROPRANGE );
+        diprg.diph.dwHeaderSize = sizeof( DIPROPHEADER );
+        diprg.diph.dwHow = DIPH_BYID;
+        diprg.diph.dwObj = pdidoi->dwType;
+        diprg.lMax = joy_axis_size;
+        diprg.lMin = -joy_axis_size;
+
+        HRESULT hr;
+
+        if (FAILED(hr = reinterpret_cast<LPDIRECTINPUTDEVICE8>(ctx)->SetProperty(DIPROP_RANGE, &diprg.diph)))
+        {
+            qDebug() << "DIPROP_RANGE" << hr;
+            return DIENUM_STOP;
+        }
+    }
+
+    return DIENUM_CONTINUE;
+}
+
+win32_joy_ctx::joy::joy(LPDIRECTINPUTDEVICE8 handle, const QString &guid, const QString &name)
+    : joy_handle(handle), guid(guid), name(name)
+{
+    qDebug() << "make joy" << guid << name << joy_handle;
+    std::memset(pressed, 0, sizeof(pressed));
+}
+
+win32_joy_ctx::joy::~joy()
+{
+    qDebug() << "nix joy" << guid;
+    release();
+}
+
+#endif
diff --git a/dinput/win32-joystick.hpp b/dinput/win32-joystick.hpp
new file mode 100644
index 00000000..4b48d4a1
--- /dev/null
+++ b/dinput/win32-joystick.hpp
@@ -0,0 +1,101 @@
+/* Copyright (c) 2015-2016, 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
+
+#ifdef _WIN32
+
+#include "dinput.hpp"
+#include "compat/timer.hpp"
+#include "export.hpp"
+#include <cstring>
+#include <memory>
+#include <vector>
+#include <functional>
+#include <algorithm>
+#include <unordered_map>
+#include <QString>
+#include <QDebug>
+#include <QMutex>
+#include <QMutexLocker>
+#include <QWidget>
+
+namespace std {
+template<>
+struct hash<QString>
+{
+    inline std::size_t operator()(const QString& value) const
+    {
+        return qHash(value);
+    }
+};
+}
+
+struct OPENTRACK_DINPUT_EXPORT win32_joy_ctx
+{
+    using fn = std::function<void(const QString& guid, int btn, bool held)>;
+
+    struct joy
+    {
+        LPDIRECTINPUTDEVICE8 joy_handle;
+        QString guid, name;
+        bool pressed[128 + 4 * 4];
+        Timer first_timer;
+
+        joy(LPDIRECTINPUTDEVICE8 handle, const QString& guid, const QString& name);
+        ~joy();
+
+        void release();
+        bool poll(fn f);
+    };
+
+    using joys_t = std::unordered_map<QString, std::shared_ptr<joy>>;
+
+    static constexpr int joy_axis_size = 65535;
+
+    struct joy_info
+    {
+        QString name, guid;
+    };
+
+    void poll(fn f);
+    bool poll_axis(const QString& guid, int* axes);
+    std::vector<joy_info> get_joy_info();
+
+    win32_joy_ctx(const win32_joy_ctx&) = delete;
+    win32_joy_ctx& operator=(const win32_joy_ctx&) = delete;
+
+    win32_joy_ctx();
+    void refresh();
+
+    using di_t = dinput_handle::di_t;
+
+private:
+    static QString guid_to_string(const GUID& guid);
+
+    class OPENTRACK_DINPUT_EXPORT enum_state final
+    {
+        std::vector<QString> all;
+        joys_t joys;
+        dinput_handle::di_t di;
+
+        static BOOL CALLBACK EnumJoysticksCallback(const DIDEVICEINSTANCE* pdidInstance, VOID* pContext);
+        static BOOL CALLBACK EnumObjectsCallback(const DIDEVICEOBJECTINSTANCE* pdidoi, VOID* ctx);
+
+    public:
+        static QMutex mtx;
+
+        enum_state();
+        ~enum_state();
+        void refresh();
+        const joys_t& get_joys() const;
+    };
+
+    static enum_state enumerator;
+};
+
+#endif
diff --git a/filter-accela/ftnoir_filter_accela.cpp b/filter-accela/ftnoir_filter_accela.cpp
index f05b8f41..526b2da6 100644
--- a/filter-accela/ftnoir_filter_accela.cpp
+++ b/filter-accela/ftnoir_filter_accela.cpp
@@ -9,7 +9,7 @@
 #include <cmath>
 #include <QDebug>
 #include <QMutexLocker>
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 
 static constexpr double rot_gains[][2] = {
     { 6, 200 },
diff --git a/filter-accela/ftnoir_filter_accela.h b/filter-accela/ftnoir_filter_accela.h
index eb1a30a4..828b78a0 100644
--- a/filter-accela/ftnoir_filter_accela.h
+++ b/filter-accela/ftnoir_filter_accela.h
@@ -6,15 +6,15 @@
  */
 #pragma once
 #include "ui_ftnoir_accela_filtercontrols.h"
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 #include "spline-widget/spline.hpp"
 #include <atomic>
 #include <QMutex>
 #include <QTimer>
 
-#include "opentrack-compat/options.hpp"
+#include "compat/options.hpp"
 using namespace options;
-#include "opentrack-compat/timer.hpp"
+#include "compat/timer.hpp"
 
 struct settings_accela : opts
 {
diff --git a/filter-accela/ftnoir_filter_accela_dialog.cpp b/filter-accela/ftnoir_filter_accela_dialog.cpp
index 7d257711..83114b20 100644
--- a/filter-accela/ftnoir_filter_accela_dialog.cpp
+++ b/filter-accela/ftnoir_filter_accela_dialog.cpp
@@ -9,7 +9,7 @@
 #include <QDebug>
 #include <algorithm>
 #include <QDoubleSpinBox>
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 #include "spline-widget/spline-widget.hpp"
 #include <QDialog>
 
diff --git a/filter-ewma2/ftnoir_filter_ewma2.cpp b/filter-ewma2/ftnoir_filter_ewma2.cpp
index 7229c6d6..beceb4c2 100644
--- a/filter-ewma2/ftnoir_filter_ewma2.cpp
+++ b/filter-ewma2/ftnoir_filter_ewma2.cpp
@@ -8,7 +8,7 @@
 #include <cmath>
 #include <QDebug>
 #include <QWidget>
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 #include <algorithm>
 #include <QMutexLocker>
 
diff --git a/filter-ewma2/ftnoir_filter_ewma2.h b/filter-ewma2/ftnoir_filter_ewma2.h
index 589b7910..e7fdd49e 100644
--- a/filter-ewma2/ftnoir_filter_ewma2.h
+++ b/filter-ewma2/ftnoir_filter_ewma2.h
@@ -1,11 +1,11 @@
 #pragma once
 
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 #include "ui_ftnoir_ewma_filtercontrols.h"
 #include <QWidget>
 #include <QMutex>
-#include "opentrack-compat/options.hpp"
-#include "opentrack-compat/timer.hpp"
+#include "compat/options.hpp"
+#include "compat/timer.hpp"
 using namespace options;
 
 struct settings : opts {
diff --git a/filter-ewma2/ftnoir_filter_ewma2_dialog.cpp b/filter-ewma2/ftnoir_filter_ewma2_dialog.cpp
index 8e69573e..79fcd959 100644
--- a/filter-ewma2/ftnoir_filter_ewma2_dialog.cpp
+++ b/filter-ewma2/ftnoir_filter_ewma2_dialog.cpp
@@ -2,7 +2,7 @@
 #include <cmath>
 #include <QDebug>
 #include <QString>
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 #include "ui_ftnoir_ewma_filtercontrols.h"
 
 FilterControls::FilterControls()
diff --git a/filter-kalman/kalman.h b/filter-kalman/kalman.h
index aaed3bc1..d28557c6 100644
--- a/filter-kalman/kalman.h
+++ b/filter-kalman/kalman.h
@@ -7,10 +7,10 @@
  */
 
 #include "ui_ftnoir_kalman_filtercontrols.h"
-#include "opentrack/plugin-api.hpp"
-#include "opentrack-compat/options.hpp"
+#include "api/plugin-api.hpp"
+#include "compat/options.hpp"
 using namespace options;
-#include "opentrack-compat/timer.hpp"
+#include "compat/timer.hpp"
 
 #include <Eigen/Core>
 #include <Eigen/LU>
diff --git a/gui/keyboard.h b/gui/keyboard.h
index 27300690..d35fe675 100644
--- a/gui/keyboard.h
+++ b/gui/keyboard.h
@@ -1,8 +1,8 @@
 #pragma once
 #include "ui_keyboard_listener.h"
 #ifdef _WIN32
-#include "opentrack-logic/win32-shortcuts.h"
-#include "opentrack-dinput/keybinding-worker.hpp"
+#include "logic/win32-shortcuts.h"
+#include "dinput/keybinding-worker.hpp"
 #endif
 #include <QLabel>
 #include <QKeyEvent>
diff --git a/gui/main-window.cpp b/gui/main-window.cpp
index 667c3032..70652201 100644
--- a/gui/main-window.cpp
+++ b/gui/main-window.cpp
@@ -7,8 +7,8 @@
  */
 
 #include "main-window.hpp"
-#include "opentrack-logic/tracker.h"
-#include "opentrack-compat/options.hpp"
+#include "logic/tracker.h"
+#include "compat/options.hpp"
 #include "opentrack-library-path.h"
 #include "new_file_dialog.h"
 #include <QFile>
diff --git a/gui/main-window.hpp b/gui/main-window.hpp
index b068d158..e05ee9cc 100644
--- a/gui/main-window.hpp
+++ b/gui/main-window.hpp
@@ -8,16 +8,16 @@
 
 #pragma once
 
-#include "opentrack/plugin-support.hpp"
+#include "api/plugin-support.hpp"
 #include "mapping-window.hpp"
 #include "options-dialog.hpp"
 #include "process_detector.h"
-#include "opentrack-logic/main-settings.hpp"
-#include "opentrack-logic/tracker.h"
-#include "opentrack-logic/shortcuts.h"
-#include "opentrack-logic/work.hpp"
-#include "opentrack-logic/state.hpp"
-#include "opentrack-compat/options.hpp"
+#include "logic/main-settings.hpp"
+#include "logic/tracker.h"
+#include "logic/shortcuts.h"
+#include "logic/work.hpp"
+#include "logic/state.hpp"
+#include "compat/options.hpp"
 
 #include <QMainWindow>
 #include <QKeySequence>
diff --git a/gui/main.cpp b/gui/main.cpp
index a9252c4d..43fd4539 100644
--- a/gui/main.cpp
+++ b/gui/main.cpp
@@ -9,8 +9,8 @@
 #endif
 
 #include "main-window.hpp"
-#include "opentrack-compat/options.hpp"
-#include "opentrack-compat/win32-com.hpp"
+#include "compat/options.hpp"
+#include "compat/win32-com.hpp"
 using namespace options;
 #include <QApplication>
 #include <QCommandLineParser>
diff --git a/gui/mapping-window.cpp b/gui/mapping-window.cpp
index 108e1354..12b36e2a 100644
--- a/gui/mapping-window.cpp
+++ b/gui/mapping-window.cpp
@@ -7,7 +7,7 @@
  */
 
 #include "mapping-window.hpp"
-#include "opentrack-logic/main-settings.hpp"
+#include "logic/main-settings.hpp"
 #include "spline-widget/spline-widget.hpp"
 MapWidget::MapWidget(Mappings& m) :
     m(m)
diff --git a/gui/mapping-window.hpp b/gui/mapping-window.hpp
index e70a27c6..aeaed211 100644
--- a/gui/mapping-window.hpp
+++ b/gui/mapping-window.hpp
@@ -1,6 +1,6 @@
 #pragma once
 #include <QWidget>
-#include "opentrack-logic/mappings.hpp"
+#include "logic/mappings.hpp"
 #include "ui_mapping-window.h"
 
 class MapWidget final : public QWidget
diff --git a/gui/new_file_dialog.h b/gui/new_file_dialog.h
index 5fdabb3a..8cb971c3 100644
--- a/gui/new_file_dialog.h
+++ b/gui/new_file_dialog.h
@@ -1,7 +1,7 @@
 #pragma once
 
 #include "ui_new_config.h"
-#include "opentrack-compat/options.hpp"
+#include "compat/options.hpp"
 #include <QFile>
 #include <QRegExp>
 #include <QString>
diff --git a/gui/options-dialog.hpp b/gui/options-dialog.hpp
index 1e8faa1c..6854107e 100644
--- a/gui/options-dialog.hpp
+++ b/gui/options-dialog.hpp
@@ -1,7 +1,7 @@
 #pragma once
 
 #include "ui_options-dialog.h"
-#include "opentrack-logic/shortcuts.h"
+#include "logic/shortcuts.h"
 #include <QObject>
 #include <QWidget>
 #include <functional>
diff --git a/gui/process_detector.cpp b/gui/process_detector.cpp
index 9c12f783..7df8e980 100644
--- a/gui/process_detector.cpp
+++ b/gui/process_detector.cpp
@@ -8,7 +8,7 @@
 
 #include "process_detector.h"
 #include "main-window.hpp"
-#include "opentrack-compat/process-list.hpp"
+#include "compat/process-list.hpp"
 #include <QList>
 #include <QFileDialog>
 #include <QComboBox>
diff --git a/gui/process_detector.h b/gui/process_detector.h
index e395e1a2..e081fa27 100644
--- a/gui/process_detector.h
+++ b/gui/process_detector.h
@@ -13,7 +13,7 @@
 #include <QTableWidget>
 #include <QResizeEvent>
 
-#include "opentrack-compat/options.hpp"
+#include "compat/options.hpp"
 using namespace options;
 
 class FancyTable : public QTableWidget
@@ -47,10 +47,10 @@ struct settings
 class process_detector : public QWidget
 {
     Q_OBJECT
-    
+
     Ui_Dialog ui;
     settings s;
-    
+
     int add_row(QString exe_name = "...", QString profile = "");
     void add_items();
 public:
diff --git a/logic/CMakeLists.txt b/logic/CMakeLists.txt
new file mode 100644
index 00000000..88b0240f
--- /dev/null
+++ b/logic/CMakeLists.txt
@@ -0,0 +1,7 @@
+opentrack_boilerplate(opentrack-logic BIN)
+target_link_libraries(opentrack-logic opentrack-spline-widget)
+if(NOT WIN32)
+    target_link_libraries(opentrack-logic opentrack-qxt-mini)
+else()
+    target_link_libraries(opentrack-logic opentrack-dinput winmm)
+endif()
diff --git a/logic/export.hpp b/logic/export.hpp
new file mode 100644
index 00000000..2503f3a6
--- /dev/null
+++ b/logic/export.hpp
@@ -0,0 +1,28 @@
+#pragma once
+
+#ifdef BUILD_logic
+#   ifdef _WIN32
+#       define OPENTRACK_LOGIC_LINKAGE __declspec(dllexport)
+#   else
+#       define OPENTRACK_LOGIC_LINKAGE
+#   endif
+
+#   ifndef _MSC_VER
+#       define OPENTRACK_LOGIC_EXPORT __attribute__ ((visibility ("default"))) OPENTRACK_LOGIC_LINKAGE
+#   else
+#       define OPENTRACK_LOGIC_EXPORT OPENTRACK_LOGIC_LINKAGE
+#   endif
+
+#else
+#ifdef _WIN32
+#    define OPENTRACK_LOGIC_LINKAGE __declspec(dllimport)
+#else
+#    define OPENTRACK_LOGIC_LINKAGE
+#endif
+
+#ifndef _MSC_VER
+#    define OPENTRACK_LOGIC_EXPORT __attribute__ ((visibility ("default"))) OPENTRACK_LOGIC_LINKAGE
+#else
+#    define OPENTRACK_LOGIC_EXPORT OPENTRACK_LOGIC_LINKAGE
+#endif
+#endif
diff --git a/logic/main-settings.hpp b/logic/main-settings.hpp
new file mode 100644
index 00000000..26313a26
--- /dev/null
+++ b/logic/main-settings.hpp
@@ -0,0 +1,109 @@
+/* Copyright (c) 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 "compat/options.hpp"
+#include "api/plugin-api.hpp"
+
+using namespace options;
+
+#include "export.hpp"
+
+struct axis_opts
+{
+    pbundle b;
+    value<double> zero;
+    value<bool> invert, altp;
+    value<int> src;
+    axis_opts(pbundle b, QString pfx, int idx) :
+        b(b),
+        zero(b, n(pfx, "zero-pos"), 0),
+        invert(b, n(pfx, "invert-sign"), false),
+        altp(b, n(pfx, "alt-axis-sign"), false),
+        src(b, n(pfx, "source-index"), idx)
+    {}
+private:
+    static inline QString n(QString pfx, QString name) {
+        return QString("%1-%2").arg(pfx, name);
+    }
+};
+
+struct key_opts
+{
+    value<QString> keycode, guid;
+    value<int> button;
+
+    key_opts(pbundle b, const QString& name) :
+        keycode(b, QString("keycode-%1").arg(name), ""),
+        guid(b, QString("guid-%1").arg(name), ""),
+        button(b, QString("button-%1").arg(name), -1)
+    {}
+};
+
+struct module_settings
+{
+    pbundle b;
+    value<QString> tracker_dll, filter_dll, protocol_dll;
+    module_settings() :
+        b(bundle("modules")),
+        tracker_dll(b, "tracker-dll", ""),
+        filter_dll(b, "filter-dll", "Accela"),
+        protocol_dll(b, "protocol-dll", "freetrack 2.0 Enhanced")
+    {
+    }
+};
+
+struct main_settings
+{
+    pbundle b, b_map;
+    axis_opts a_x, a_y, a_z, a_yaw, a_pitch, a_roll;
+    value<bool> tcomp_p, tcomp_tz;
+    value<bool> tray_enabled;
+    value<int> camera_yaw, camera_pitch, camera_roll;
+    value<bool> use_camera_offset_from_centering;
+    value<bool> center_at_startup;
+    value<int> center_method;
+    key_opts key_start_tracking, key_stop_tracking, key_toggle_tracking, key_restart_tracking;
+    key_opts key_center, key_toggle, key_zero;
+    key_opts key_toggle_press, key_zero_press;
+    value<bool> tracklogging_enabled;
+    value<QString> tracklogging_filename;
+    main_settings() :
+        b(bundle("opentrack-ui")),
+        b_map(bundle("opentrack-mappings")),
+        a_x(b_map, "x", TX),
+        a_y(b_map, "y", TY),
+        a_z(b_map, "z", TZ),
+        a_yaw(b_map, "yaw", Yaw),
+        a_pitch(b_map, "pitch", Pitch),
+        a_roll(b_map, "roll", Roll),
+        tcomp_p(b, "compensate-translation", true),
+        tcomp_tz(b, "compensate-translation-disable-z-axis", false),
+        tray_enabled(b, "use-system-tray", false),
+        camera_yaw(b, "camera-yaw", 0),
+        camera_pitch(b, "camera-pitch", 0),
+        camera_roll(b, "camera-roll", 0),
+        use_camera_offset_from_centering(b, "use-camera-offset-from-centering", false),
+        center_at_startup(b, "center-at-startup", true),
+        center_method(b, "centering-method", true),
+        key_start_tracking(b, "start-tracking"),
+        key_stop_tracking(b, "stop-tracking"),
+        key_toggle_tracking(b, "toggle-tracking"),
+        key_restart_tracking(b, "restart-tracking"),
+        key_center(b, "center"),
+        key_toggle(b, "toggle"),
+        key_zero(b, "zero"),
+        key_toggle_press(b, "toggle-press"),
+        key_zero_press(b, "zero-press"),
+        tracklogging_enabled(b, "tracklogging-enabled", false),
+        tracklogging_filename(b, "tracklogging-filename", QString())
+    {
+    }
+};
diff --git a/logic/mappings.hpp b/logic/mappings.hpp
new file mode 100644
index 00000000..0b9373c0
--- /dev/null
+++ b/logic/mappings.hpp
@@ -0,0 +1,89 @@
+/* Copyright (c) 2014-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 <QSettings>
+#include "compat/options.hpp"
+using namespace options;
+#include "spline-widget/spline.hpp"
+#include "main-settings.hpp"
+
+class Map {
+public:
+    Map(QString primary,
+            QString secondary,
+            int max_x,
+            int max_y,
+            axis_opts& opts) :
+        spline_main(max_x, max_y),
+        spline_alt(max_x, max_y),
+        opts(opts),
+        name1(primary),
+        name2(secondary)
+    {
+        mem<QSettings> iniFile = group::ini_file();
+        spline_main.loadSettings(*iniFile, primary);
+        spline_alt.loadSettings(*iniFile, secondary);
+    }
+    spline spline_main;
+    spline spline_alt;
+    axis_opts& opts;
+    QString name1, name2;
+};
+
+class Mappings {
+private:
+    Map axes[6];
+public:
+    Mappings(std::vector<axis_opts*> opts) :
+        axes {
+            Map("tx","tx_alt", 30, 75, *opts[TX]),
+            Map("ty","ty_alt", 30, 75, *opts[TY]),
+            Map("tz","tz_alt", 30, 75, *opts[TZ]),
+            Map("rx", "rx_alt", 180, 180, *opts[Yaw]),
+            Map("ry", "ry_alt", 180, 180, *opts[Pitch]),
+            Map("rz", "rz_alt", 180, 180, *opts[Roll])
+        }
+    {}
+
+    inline Map& operator()(int i) { return axes[i]; }
+    inline const Map& operator()(int i) const { return axes[i]; }
+
+    void load_mappings()
+    {
+        mem<QSettings> iniFile = group::ini_file();
+
+        for (int i = 0; i < 6; i++)
+        {
+            axes[i].spline_main.loadSettings(*iniFile, axes[i].name1);
+            axes[i].spline_alt.loadSettings(*iniFile, axes[i].name2);
+            axes[i].opts.b->reload();
+        }
+    }
+    void save_mappings()
+    {
+        mem<QSettings> iniFile = group::ini_file();
+
+        for (int i = 0; i < 6; i++)
+        {
+            axes[i].spline_main.saveSettings(*iniFile, axes[i].name1);
+            axes[i].spline_alt.saveSettings(*iniFile, axes[i].name2);
+            axes[i].opts.b->save();
+        }
+    }
+
+    void invalidate_unsaved()
+    {
+        for (int i = 0; i < 6; i++)
+        {
+            axes[i].spline_main.invalidate_unsaved_settings();
+            axes[i].spline_alt.invalidate_unsaved_settings();
+            axes[i].opts.b->reload();
+        }
+    }
+};
diff --git a/logic/selected-libraries.cpp b/logic/selected-libraries.cpp
new file mode 100644
index 00000000..4a1a9f09
--- /dev/null
+++ b/logic/selected-libraries.cpp
@@ -0,0 +1,37 @@
+#include "selected-libraries.hpp"
+#include <QDebug>
+
+SelectedLibraries::SelectedLibraries(QFrame* frame, dylibptr t, dylibptr p, dylibptr f) :
+    pTracker(nullptr),
+    pFilter(nullptr),
+    pProtocol(nullptr),
+    correct(false)
+{
+    pProtocol = make_dylib_instance<IProtocol>(p);
+
+    if (!pProtocol)
+    {
+        qDebug() << "protocol dylib load failure";
+        return;
+    }
+
+    if(!pProtocol->correct())
+    {
+        qDebug() << "protocol load failure";
+        pProtocol = nullptr;
+        return;
+    }
+
+    pTracker = make_dylib_instance<ITracker>(t);
+    pFilter = make_dylib_instance<IFilter>(f);
+
+    if (!pTracker)
+    {
+        qDebug() << "tracker dylib load failure";
+        return;
+    }
+
+    pTracker->start_tracker(frame);
+
+    correct = true;
+}
diff --git a/logic/selected-libraries.hpp b/logic/selected-libraries.hpp
new file mode 100644
index 00000000..d782374c
--- /dev/null
+++ b/logic/selected-libraries.hpp
@@ -0,0 +1,25 @@
+/* Copyright (c) 2014-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 "api/plugin-support.hpp"
+#include <QFrame>
+
+#include "export.hpp"
+
+struct OPENTRACK_LOGIC_EXPORT SelectedLibraries
+{
+    using dylibptr = mem<dylib>;
+    mem<ITracker> pTracker;
+    mem<IFilter> pFilter;
+    mem<IProtocol> pProtocol;
+    SelectedLibraries(QFrame* frame, dylibptr t, dylibptr p, dylibptr f);
+    SelectedLibraries() : pTracker(nullptr), pFilter(nullptr), pProtocol(nullptr), correct(false) {}
+    bool correct;
+};
diff --git a/logic/shortcuts.cpp b/logic/shortcuts.cpp
new file mode 100644
index 00000000..8d818180
--- /dev/null
+++ b/logic/shortcuts.cpp
@@ -0,0 +1,130 @@
+/* Copyright (c) 2014-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.
+ */
+
+#include "shortcuts.h"
+#include "win32-shortcuts.h"
+
+void Shortcuts::free_binding(K& key)
+{
+#ifndef _WIN32
+    if (key)
+    {
+        key->setEnabled(false);
+        key->setShortcut(QKeySequence::UnknownKey);
+        std::shared_ptr<QxtGlobalShortcut> tmp(nullptr);
+        key.swap(tmp);
+    }
+#else
+    key.keycode = 0;
+    key.guid = "";
+#endif
+}
+
+void Shortcuts::bind_shortcut(K &key, const key_opts& k, unused_on_unix(bool, held))
+{
+#if !defined(_WIN32)
+    using sh = QxtGlobalShortcut;
+    if (key)
+    {
+        free_binding(key);
+    }
+
+    key = std::make_shared<sh>();
+
+    if (k.keycode != "")
+    {
+        key->setShortcut(QKeySequence::fromString(k.keycode, QKeySequence::PortableText));
+        key->setEnabled();
+    }
+}
+#else
+    key = K();
+    int idx = 0;
+    QKeySequence code;
+
+    if (k.guid != "")
+    {
+        key.guid = k.guid;
+        key.keycode = k.button & ~Qt::KeyboardModifierMask;
+        key.ctrl = !!(k.button & Qt::ControlModifier);
+        key.alt = !!(k.button & Qt::AltModifier);
+        key.shift = !!(k.button & Qt::ShiftModifier);
+    }
+    else
+    {
+        if (k.keycode == "")
+            code = QKeySequence(Qt::Key_unknown);
+        else
+            code = QKeySequence::fromString(k.keycode, QKeySequence::PortableText);
+
+        Qt::KeyboardModifiers mods = Qt::NoModifier;
+        if (code != Qt::Key_unknown)
+            win_key::from_qt(code, idx, mods);
+        key.shift = !!(mods & Qt::ShiftModifier);
+        key.alt = !!(mods & Qt::AltModifier);
+        key.ctrl = !!(mods & Qt::ControlModifier);
+        key.keycode = idx;
+        key.held = held;
+    }
+}
+#endif
+
+#ifdef _WIN32
+void Shortcuts::receiver(const Key& k)
+{
+    const unsigned sz = keys.size();
+    for (unsigned i = 0; i < sz; i++)
+    {
+        K& k_ = std::get<0>(keys[i]);
+        auto& fun = std::get<1>(keys[i]);
+        if (k.guid != k_.guid)
+            continue;
+        if (k.keycode != k_.keycode)
+            continue;
+        if (k_.held && !k.held) continue;
+        if (k_.alt != k.alt) continue;
+        if (k_.ctrl != k.ctrl) continue;
+        if (k_.shift != k.shift) continue;
+        if (!k_.should_process())
+            continue;
+
+        fun(k.held);
+    }
+}
+#endif
+
+void Shortcuts::reload(const t_keys& keys_)
+{
+    const unsigned sz = keys_.size();
+    keys = std::vector<tt>();
+
+    for (unsigned i = 0; i < sz; i++)
+    {
+        const auto& kk = keys_[i];
+        const key_opts& opts = std::get<0>(kk);
+        const bool held = std::get<2>(kk);
+        auto fun = std::get<1>(kk);
+        K k;
+        bind_shortcut(k, opts, held);
+        keys.push_back(tt(k, [=](unused_on_unix(bool, flag)) -> void
+        {
+#ifdef _WIN32
+            fun(flag);
+#else
+            fun(true);
+#endif
+        },
+        held));
+#ifndef _WIN32
+        const int idx = keys.size() - 1;
+        tt& kk_ = keys[idx];
+        auto& fn = std::get<1>(kk_);
+        connect(k.get(), &QxtGlobalShortcut::activated, [=]() -> void { fn(true); });
+#endif
+    }
+}
diff --git a/logic/shortcuts.h b/logic/shortcuts.h
new file mode 100644
index 00000000..d5dfd394
--- /dev/null
+++ b/logic/shortcuts.h
@@ -0,0 +1,68 @@
+/* Copyright (c) 2014-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 <QObject>
+#include <tuple>
+#include <vector>
+#include <functional>
+
+#include "export.hpp"
+
+#include "qxt-mini/QxtGlobalShortcut"
+#include "compat/options.hpp"
+#include "main-settings.hpp"
+
+#ifdef _WIN32
+#   include "dinput/keybinding-worker.hpp"
+#endif
+
+#if defined(__GNUC__) && !defined(_WIN32)
+#   define unused_on_unix(t, i) t __attribute__((unused)) i
+#else
+#   define unused_on_unix(t, i) t i
+#endif
+
+using namespace options;
+
+struct OPENTRACK_LOGIC_EXPORT Shortcuts : public QObject
+{
+    Q_OBJECT
+
+public:
+    using K =
+#ifndef _WIN32
+    mem<QxtGlobalShortcut>
+#else
+    Key
+#endif
+    ;
+
+    using fun = std::function<void(bool)>;
+    using tt = std::tuple<K, fun, bool>;
+    using t_key = std::tuple<key_opts&, fun, bool>;
+    using t_keys = std::vector<t_key>;
+    std::vector<tt> keys;
+#ifdef _WIN32
+    KeybindingWorker::Token key_token;
+#endif
+
+    Shortcuts()
+#ifdef _WIN32
+        : key_token([&](const Key& k) { receiver(k); })
+#endif
+    {}
+
+    void reload(const t_keys& keys_);
+private:
+    void free_binding(K& key);
+    void bind_shortcut(K &key, const key_opts& k, bool held);
+#ifdef _WIN32
+    void receiver(const Key& k);
+#endif
+};
diff --git a/logic/simple-mat.cpp b/logic/simple-mat.cpp
new file mode 100644
index 00000000..f9ed8613
--- /dev/null
+++ b/logic/simple-mat.cpp
@@ -0,0 +1,97 @@
+#include "simple-mat.hpp"
+#include "compat/pi-constant.hpp"
+#include <cmath>
+
+namespace euler {
+
+euler_t OPENTRACK_LOGIC_EXPORT 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)));
+    else
+        return euler_t(atan2(R(0, 1), R(1, 1)),
+                       atan2(R(2, 0), cy),
+                       0);
+}
+
+// tait-bryan angles, not euler
+rmat OPENTRACK_LOGIC_EXPORT euler_to_rmat(const euler_t& 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);
+    const auto s2 = sin(P);
+    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 OPENTRACK_LOGIC_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);
+    }
+}
+
+} // end ns euler
diff --git a/logic/simple-mat.hpp b/logic/simple-mat.hpp
new file mode 100644
index 00000000..514e845f
--- /dev/null
+++ b/logic/simple-mat.hpp
@@ -0,0 +1,279 @@
+/* Copyright (c) 2014-2016, 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 "export.hpp"
+
+#include <initializer_list>
+#include <type_traits>
+#include <utility>
+
+namespace {
+    // 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>
+    struct maybe_add_swizzle
+    {
+        enum { value = (i == 1 || j == 1) && (i >= min || j >= min) };
+    };
+    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<typename num, int h, int w, typename...ts>
+    struct is_arglist_correct
+    {
+        enum { value = h * w == sizeof...(ts) };
+    };
+}
+
+template<typename num, int h_, int w_>
+class Mat
+{
+    static_assert(h_ > 0 && w_ > 0, "must have positive mat dimensions");
+    num data[h_][w_];
+
+public:
+    template<int Q = w_> typename std::enable_if<equals<Q, 1, 0>::value, num>::type
+    inline operator()(int i) const { return data[i][0]; }
+
+    template<int P = h_> typename std::enable_if<equals<P, 1, 1>::value, num>::type
+    inline operator()(int i) const { return data[0][i]; }
+
+    template<int Q = w_> typename std::enable_if<equals<Q, 1, 2>::value, num&>::type
+    inline operator()(int i) { return data[i][0]; }
+
+    template<int P = h_> typename std::enable_if<equals<P, 1, 3>::value, num&>::type
+    inline operator()(int i) { return data[0][i]; }
+
+#define OPENTRACK_ASSERT_SWIZZLE static_assert(P == h_ && Q == w_, "")
+
+    template<int P = h_, int Q = w_> typename std::enable_if<maybe_add_swizzle<P, Q, 1>::value, num>::type
+    x() const { OPENTRACK_ASSERT_SWIZZLE; return operator()(0); }
+
+    template<int P = h_, int Q = w_> typename std::enable_if<maybe_add_swizzle<P, Q, 2>::value, num>::type
+    y() const { OPENTRACK_ASSERT_SWIZZLE; return operator()(1); }
+
+    template<int P = h_, int Q = w_> typename std::enable_if<maybe_add_swizzle<P, Q, 3>::value, num>::type
+    z() const { OPENTRACK_ASSERT_SWIZZLE; return operator()(2); }
+
+    template<int P = h_, int Q = w_> typename std::enable_if<maybe_add_swizzle<P, Q, 4>::value, num>::type
+    w() const { OPENTRACK_ASSERT_SWIZZLE; return operator()(3); }
+
+    template<int P = h_, int Q = w_> typename std::enable_if<maybe_add_swizzle<P, Q, 1>::value, num&>::type
+    x() { OPENTRACK_ASSERT_SWIZZLE; return operator()(0); }
+
+    template<int P = h_, int Q = w_> typename std::enable_if<maybe_add_swizzle<P, Q, 2>::value, num&>::type
+    y() { OPENTRACK_ASSERT_SWIZZLE; return operator()(1); }
+
+    template<int P = h_, int Q = w_> typename std::enable_if<maybe_add_swizzle<P, Q, 3>::value, num&>::type
+    z() { OPENTRACK_ASSERT_SWIZZLE; return operator()(2); }
+
+    template<int P = h_, int Q = w_> typename std::enable_if<maybe_add_swizzle<P, Q, 4>::value, num&>::type
+    w() { OPENTRACK_ASSERT_SWIZZLE; return operator()(3); }
+    // parameters w_ and h_ are rebound so that SFINAE occurs
+    // removing them causes a compile-time error -sh 20150811
+
+    template<int R, int S, int P = h_, int Q = w_>
+    typename std::enable_if<is_vector_pair<R, S, P, Q>::value, num>::type
+    dot(const Mat<num, R, S>& p2) const
+    {
+        static_assert(P == h_ && Q == w_, "");
+
+        num ret = 0;
+        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_>
+    typename std::enable_if<is_dim3<P, Q, R, S>::value, Mat<num, is_dim3<P, Q, R, S>::P, is_dim3<P, Q, R, S>::Q>>::type
+    cross(const Mat<num, R, S>& p2) const
+    {
+        static_assert(P == h_ && Q == w_, "");
+        decltype(*this)& p1 = *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());
+    }
+
+    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++)
+                ret(j, i) = data[j][i] + other.data[j][i];
+        return ret;
+    }
+
+    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++)
+                ret(j, i) = data[j][i] - other.data[j][i];
+        return ret;
+    }
+
+    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;
+    }
+
+    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
+    {
+        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++)
+                    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]; }
+
+    template<typename... ts, int h__ = h_, int w__ = w_,
+             typename = typename std::enable_if<is_arglist_correct<num, h__, w__, ts...>::value>::type>
+    Mat(const ts... xs)
+    {
+        static_assert(h__ == h_ && w__ == w_, "");
+
+        std::initializer_list<num> init = { static_cast<num>(xs)... };
+
+        *this = Mat(std::move(init));
+    }
+
+    Mat()
+    {
+        for (int j = 0; j < h_; j++)
+            for (int i = 0; i < w_; i++)
+                data[j][i] = num(0);
+    }
+
+    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];
+    }
+
+    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++;
+    }
+
+    operator num*() { return reinterpret_cast<num*>(data); }
+    operator const num*() const { return reinterpret_cast<const num*>(data); }
+
+    // XXX add more operators as needed, third-party dependencies mostly
+    // not needed merely for matrix algebra -sh 20141030
+
+    template<int h__ = h_>
+    static typename std::enable_if<h_ == w_, Mat<num, h__, h__>>::type eye()
+    {
+        static_assert(h_ == h__, "");
+
+        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 < h_; i++)
+            ret.data[i][i] = 1;
+
+        return ret;
+    }
+
+    Mat<num, w_, h_> t() const
+    {
+        Mat<num, w_, h_> ret;
+
+        for (int j = 0; j < h_; j++)
+            for (int i = 0; i < w_; i++)
+                ret(i, j) = data[j][i];
+
+        return ret;
+    }
+};
+
+template<int h_, int w_> using dmat = Mat<double, h_, w_>;
+
+template<typename num, int h, int w>
+Mat<num, h, w> operator*(num scalar, const Mat<num, h, w>& mat)
+{
+    return mat * scalar;
+}
+
+template<typename num, int h_, int w_>
+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;
+    return ret;
+}
+
+namespace euler {
+
+template<int y, int x> using dmat = Mat<double, y, x>;
+using rmat = dmat<3, 3>;
+using euler_t = dmat<3, 1>;
+
+rmat OPENTRACK_LOGIC_EXPORT euler_to_rmat(const euler_t& input);
+
+euler_t OPENTRACK_LOGIC_EXPORT rmat_to_euler(const rmat& R);
+
+void OPENTRACK_LOGIC_EXPORT tait_bryan_to_matrices(const euler_t& input,
+                                                   rmat& r_roll,
+                                                   rmat& r_pitch,
+                                                   rmat& r_yaw);
+
+} // end ns euler
diff --git a/logic/state.hpp b/logic/state.hpp
new file mode 100644
index 00000000..4c3bb7a0
--- /dev/null
+++ b/logic/state.hpp
@@ -0,0 +1,32 @@
+/* Copyright (c) 2014-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 "compat/options.hpp"
+using namespace options;
+#include "api/plugin-support.hpp"
+#include "main-settings.hpp"
+#include "mappings.hpp"
+#include "selected-libraries.hpp"
+#include "work.hpp"
+#include <vector>
+#include <QString>
+
+struct State
+{
+    State(const QString& library_path) :
+        modules(library_path),
+        pose(std::vector<axis_opts*>{&s.a_x, &s.a_y, &s.a_z, &s.a_yaw, &s.a_pitch, &s.a_roll})
+    {}
+    Modules modules;
+    SelectedLibraries libs;
+    main_settings s;
+    Mappings pose;
+    mem<Work> work;
+};
diff --git a/logic/tracker.cpp b/logic/tracker.cpp
new file mode 100644
index 00000000..c7580500
--- /dev/null
+++ b/logic/tracker.cpp
@@ -0,0 +1,385 @@
+/* Copyright (c) 2012-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.
+ */
+
+/*
+ * this file appeared originally in facetracknoir, was rewritten completely
+ * following opentrack fork.
+ *
+ * originally written by Wim Vriend.
+ */
+
+#include "compat/sleep.hpp"
+
+#include "tracker.h"
+#include <cmath>
+#include <algorithm>
+#include <cstdio>
+
+#if defined(_WIN32)
+#   include <windows.h>
+#endif
+
+Tracker::Tracker(Mappings &m, SelectedLibraries &libs, TrackLogger &logger) :
+    m(m),
+    newpose {0,0,0, 0,0,0},
+    libs(libs),
+    logger(logger),
+    r_b(get_camera_offset_matrix(c_div).t()),
+    r_b_real(get_camera_offset_matrix(1).t()),
+    t_b {0,0,0},
+    centerp(s.center_at_startup),
+    enabledp(true),
+    zero_(false),
+    should_quit(false)
+{
+}
+
+Tracker::~Tracker()
+{
+    should_quit = true;
+    wait();
+}
+
+Tracker::rmat Tracker::get_camera_offset_matrix(double c)
+{
+    const double off[] =
+    {
+        d2r * c * (double)-s.camera_yaw,
+        d2r * c * (double)-s.camera_pitch,
+        d2r * c * (double)-s.camera_roll
+    };
+
+    return euler::euler_to_rmat(off);
+}
+
+double Tracker::map(double pos, Map& axis)
+{
+    bool altp = (pos < 0) && axis.opts.altp;
+    axis.spline_main.setTrackingActive( !altp );
+    axis.spline_alt.setTrackingActive( altp );
+    auto& fc = altp ? axis.spline_alt : axis.spline_main;
+    return fc.getValue(pos);
+}
+
+void Tracker::t_compensate(const rmat& rmat, const euler_t& xyz_, euler_t& output, bool rz)
+{
+    // TY is really yaw axis. need swapping accordingly.
+    const euler_t ret = rmat * euler_t(xyz_(TZ), -xyz_(TX), -xyz_(TY));
+    if (!rz)
+        output(2) = ret(0);
+    else
+        output(2) = xyz_(2);
+    output(1) = -ret(2);
+    output(0) = -ret(1);
+}
+
+#include "compat/nan.hpp"
+
+static inline double elide_nan(double value, double def)
+{
+    if (nanp(value))
+    {
+        if (nanp(def))
+            return 0;
+        return def;
+    }
+    return value;
+}
+
+static bool is_nan(const dmat<3,3>& r, const dmat<3, 1>& t)
+{
+    for (int i = 0; i < 3; i++)
+        for (int j = 0; j < 3; j++)
+            if (nanp(r(i, j)))
+                return true;
+
+    for (int i = 0; i < 3; i++)
+        if (nanp(t(i)))
+            return true;
+
+    return false;
+}
+
+static bool is_nan(const Pose& value)
+{
+    for (int i = 0; i < 6; i++)
+        if (nanp(value(i)))
+            return true;
+    return false;
+}
+
+constexpr double Tracker::c_mult;
+constexpr double Tracker::c_div;
+
+void Tracker::logic()
+{
+    using namespace euler;
+
+    logger.write_dt();
+    logger.reset_dt();
+
+    Pose value, raw;
+
+    for (int i = 0; i < 6; i++)
+    {
+        auto& axis = m(i);
+        int k = axis.opts.src;
+        if (k < 0 || k >= 6)
+            value(i) = 0;
+        else
+            value(i) = newpose[k];
+        raw(i) = newpose[i];
+    }
+
+    logger.write_pose(raw); // raw
+
+    if (is_nan(raw))
+        raw = last_raw;
+
+    // TODO fix gimbal lock by dividing euler angle input by >=3.
+    // maintain the real rmat separately for translation compensation
+    // TODO split this function, it's too big
+    rmat r, r_real;
+
+    {
+        euler_t tmp = d2r * euler_t(&value[Yaw]);
+        r = euler_to_rmat(c_div * tmp);
+        r_real = euler_to_rmat(tmp);
+    }
+
+    euler_t t(value(0), value(1), value(2));
+
+    bool do_center_now = false;
+    bool nan = is_nan(r, t);
+
+    if (centerp && !nan)
+    {
+        for (int i = 0; i < 6; i++)
+            if (fabs(newpose[i]) != 0)
+            {
+                do_center_now = true;
+                break;
+            }
+    }
+
+    const rmat cam = get_camera_offset_matrix(c_div);
+    const rmat cam_real = get_camera_offset_matrix(1);
+
+    r = r * cam;
+    r_real = r_real * cam_real;
+
+    if (do_center_now)
+    {
+        centerp = false;
+
+        if (libs.pFilter)
+            libs.pFilter->center();
+
+        if (libs.pTracker->center())
+        {
+            r_b = cam.t();
+            r_b_real = cam_real.t();
+            r = rmat::eye();
+            r_real = rmat::eye();
+        }
+        else
+        {
+            r_b = r.t();
+            r_b_real = r_real.t();
+        }
+
+        for (int i = 0; i < 3; i++)
+            t_b[i] = t(i);
+    }
+
+    {
+        switch (s.center_method)
+        {
+        // inertial
+        case 0:
+        default:
+            r = r_b * r;
+            break;
+        // camera
+        case 1:
+            r = r * r_b;
+            break;
+        }
+
+        const euler_t rot = r2d * c_mult * rmat_to_euler(r);
+        euler_t pos(t(0) - t_b[0], t(1) - t_b[1], t(2) - t_b[2]);
+
+        if (s.use_camera_offset_from_centering)
+            t_compensate(r_b_real.t() * cam_real.t(), pos, pos, false);
+        else
+            t_compensate(cam_real.t(), pos, pos, false);
+
+        for (int i = 0; i < 3; i++)
+        {
+            value(i) = pos(i);
+            value(i+3) = rot(i);
+        }
+    }
+
+    logger.write_pose(value); // "corrected" - after various transformations to account for camera position
+
+    // whenever something can corrupt its internal state due to nan/inf, elide the call
+    if (is_nan(value))
+    {
+        nan = true;
+        logger.write_pose(value); // "filtered"
+    }
+    else
+    {
+        {
+            Pose tmp = value;
+
+            if (libs.pFilter)
+                libs.pFilter->filter(tmp, value);
+        }
+        logger.write_pose(value); // "filtered"
+
+        // CAVEAT rotation only, due to tcomp
+        for (int i = 3; i < 6; i++)
+            value(i) = map(value(i), m(i));
+
+        for (int i = 0; i < 6; i++)
+            value(i) += m(i).opts.zero;
+
+        for (int i = 0; i < 6; i++)
+            value(i) *= int(m(i).opts.invert) * -2 + 1;
+
+        if (zero_)
+            for (int i = 0; i < 6; i++)
+                value(i) = 0;
+
+        if (is_nan(value))
+            nan = true;
+    }
+
+    if (s.tcomp_p)
+    {
+        euler_t value_(value(TX), value(TY), value(TZ));
+        t_compensate(euler_to_rmat(euler_t(value(Yaw) * d2r, value(Pitch) * d2r, value(Roll) * d2r)),
+                     value_,
+                     value_,
+                     s.tcomp_tz);
+        if (is_nan(r, value_))
+            nan = true;
+        for (int i = 0; i < 3; i++)
+            value(i) = value_(i);
+    }
+
+    // CAVEAT translation only, due to tcomp
+    for (int i = 0; i < 3; i++)
+        value(i) = map(value(i), m(i));
+
+    logger.write_pose(value); // "mapped"
+
+    if (nan)
+    {
+        value = last_mapped;
+
+        // for widget last value display
+        for (int i = 0; i < 6; i++)
+            (void) map(value(i), m(i));
+    }
+
+    libs.pProtocol->pose(value);
+
+    last_mapped = value;
+    last_raw = raw;
+
+    QMutexLocker foo(&mtx);
+    output_pose = value;
+    raw_6dof = raw;
+
+    logger.reset_dt();
+    logger.next_line();
+}
+
+void Tracker::run()
+{
+#if defined(_WIN32)
+    (void) timeBeginPeriod(1);
+#endif
+
+    setPriority(QThread::HighPriority);
+
+    {
+        static constexpr const char* posechannels[6] = { "TX", "TY", "TZ", "Yaw", "Pitch", "Roll" };
+        static constexpr const char* datachannels[5] = { "dt", "raw", "corrected", "filtered", "mapped" };
+        logger.write(datachannels[0]);
+        char buffer[128];
+        for (unsigned j = 1; j < 5; ++j)
+        {
+            for (unsigned i = 0; i < 6; ++i)
+            {
+                std::sprintf(buffer, "%s%s", datachannels[j], posechannels[i]);
+                logger.write(buffer);
+            }
+        }
+        logger.next_line();
+    }
+
+    t.start();
+    logger.reset_dt();
+
+    while (!should_quit)
+    {
+        double tmp[6] {0,0,0, 0,0,0};
+        libs.pTracker->data(tmp);
+
+        if (enabledp)
+            for (int i = 0; i < 6; i++)
+                newpose[i] = elide_nan(tmp[i], newpose[i]);
+
+        logic();
+
+        static constexpr long const_sleep_us = 4000;
+
+        using std::max;
+        using std::min;
+
+        const long elapsed_usecs = t.elapsed_usecs();
+        const long sleep_us = const_sleep_us * 2 - elapsed_usecs;
+
+        const unsigned sleep_time = unsigned(max(1l, min(const_sleep_us * 4, max(1l, (sleep_us + 200l)/1000l))));
+
+        t.start();
+
+        portable::sleep(unsigned(max(1u, sleep_time)));
+    }
+
+    {
+        // filter may inhibit exact origin
+        Pose p;
+        libs.pProtocol->pose(p);
+    }
+
+#if defined(_WIN32)
+    (void) timeEndPeriod(1);
+#endif
+
+    for (int i = 0; i < 6; i++)
+    {
+        m(i).spline_main.setTrackingActive(false);
+        m(i).spline_alt.setTrackingActive(false);
+    }
+}
+
+void Tracker::get_raw_and_mapped_poses(double* mapped, double* raw) const
+{
+    QMutexLocker foo(&const_cast<Tracker&>(*this).mtx);
+
+    for (int i = 0; i < 6; i++)
+    {
+        raw[i] = raw_6dof(i);
+        mapped[i] = output_pose(i);
+    }
+}
+
diff --git a/logic/tracker.h b/logic/tracker.h
new file mode 100644
index 00000000..15fb9701
--- /dev/null
+++ b/logic/tracker.h
@@ -0,0 +1,85 @@
+/* Copyright (c) 2014-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 <vector>
+
+#include "compat/pi-constant.hpp"
+#include "compat/timer.hpp"
+#include "api/plugin-support.hpp"
+#include "mappings.hpp"
+#include "simple-mat.hpp"
+#include "selected-libraries.hpp"
+
+#include "spline-widget/spline.hpp"
+#include "main-settings.hpp"
+#include "compat/options.hpp"
+#include "tracklogger.hpp"
+
+#include <QMutex>
+#include <QThread>
+
+#include "export.hpp"
+
+using Pose = Mat<double, 6, 1>;
+
+class OPENTRACK_LOGIC_EXPORT Tracker : private QThread
+{
+    Q_OBJECT
+private:
+    using rmat = euler::rmat;
+    using euler_t = euler::euler_t;
+
+    QMutex mtx;
+    main_settings s;
+    Mappings& m;
+
+    Timer t;
+    Pose output_pose, raw_6dof, last_mapped, last_raw;
+
+    double newpose[6];
+    SelectedLibraries const& libs;
+    // The owner of the reference is the main window.
+    // This design might be usefull if we decide later on to swap out
+    // the logger while the tracker is running.
+    TrackLogger &logger;
+
+    rmat r_b, r_b_real;
+    double t_b[3];
+
+    volatile bool centerp;
+    volatile bool enabledp;
+    volatile bool zero_;
+    volatile bool should_quit;
+
+    double map(double pos, Map& axis);
+    void logic();
+    void t_compensate(const rmat& rmat, const euler_t& ypr, euler_t& output, bool rz);
+    void run() override;
+
+    static constexpr double pi = OPENTRACK_PI;
+    static constexpr double r2d = 180. / OPENTRACK_PI;
+    static constexpr double d2r = OPENTRACK_PI / 180.;
+
+    // note: float exponent base is 2
+    static constexpr double c_mult = 4;
+    static constexpr double c_div = 1./c_mult;
+public:
+    Tracker(Mappings& m, SelectedLibraries& libs, TrackLogger &logger);
+    ~Tracker();
+
+    rmat get_camera_offset_matrix(double c);
+    void get_raw_and_mapped_poses(double* mapped, double* raw) const;
+    void start() { QThread::start(); }
+    void toggle_enabled() { enabledp = !enabledp; }
+    void set_toggle(bool value) { enabledp = value; }
+    void set_zero(bool value) { zero_ = value; }
+    void center() { centerp = !centerp; }
+    void zero() { zero_ = !zero_; }
+};
diff --git a/logic/tracklogger.cpp b/logic/tracklogger.cpp
new file mode 100644
index 00000000..64dda579
--- /dev/null
+++ b/logic/tracklogger.cpp
@@ -0,0 +1,47 @@
+#include "tracklogger.hpp"
+#include "tracker.h"
+
+TrackLogger::~TrackLogger() {}
+
+void TrackLogger::reset_dt()
+{
+    t.start();
+}
+
+void TrackLogger::write_dt()
+{
+    const double dt = t.elapsed_seconds();
+    write(&dt, 1);
+}
+
+void TrackLoggerCSV::handle_first_col_sep()
+{
+    if (!first_col)
+        out.put(',');
+    first_col = false;
+}
+
+void TrackLoggerCSV::write(const char *s)
+{
+    handle_first_col_sep();
+    out << s;
+}
+
+
+void TrackLoggerCSV::write(const double *p, int n)
+{
+    handle_first_col_sep();
+    for (int i = 0; i < n-1; ++i)
+    {
+        out << p[i];
+        out.put(',');
+    }
+    out << p[n-1];
+}
+
+void TrackLoggerCSV::next_line()
+{
+    out << std::endl;
+    first_col = true;
+}
+
diff --git a/logic/tracklogger.hpp b/logic/tracklogger.hpp
new file mode 100644
index 00000000..52ab35bc
--- /dev/null
+++ b/logic/tracklogger.hpp
@@ -0,0 +1,65 @@
+#pragma once
+#include "main-settings.hpp"
+#include "compat/options.hpp"
+#include "compat/timer.hpp"
+
+#include <fstream>
+#include <QString>
+#include <QMessageBox>
+#include <QWidget>
+
+class OPENTRACK_LOGIC_EXPORT TrackLogger
+{
+    TrackLogger(TrackLogger&&) = delete;
+    TrackLogger(const TrackLogger&) = delete;
+    TrackLogger& operator=(const TrackLogger&) = delete;
+    TrackLogger& operator=(TrackLogger&&) = delete;
+
+    Timer t;
+
+public:
+    TrackLogger()
+    {
+    }
+
+    virtual ~TrackLogger();
+
+    virtual void write(const char *)
+    {
+    }
+
+    virtual void write(const double *, int)
+    {
+    }
+
+    virtual void next_line()
+    {
+    }
+
+    void write_pose(const double *p)
+    {
+        write(p, 6);
+    }
+
+    void reset_dt();
+    void write_dt();
+};
+
+
+class OPENTRACK_LOGIC_EXPORT TrackLoggerCSV : public TrackLogger
+{
+    std::ofstream out;
+    bool first_col;
+    inline void handle_first_col_sep();
+public:
+    TrackLoggerCSV(const QString &filename) : first_col(true)
+    {
+        out.open(filename.toStdString());
+    }
+
+    bool is_open() const { return out.is_open(); }
+    void write(const char *s) override;
+    void write(const double *p, int n) override;
+    void next_line() override;
+};
+
diff --git a/logic/win32-shortcuts.cpp b/logic/win32-shortcuts.cpp
new file mode 100644
index 00000000..df9145a0
--- /dev/null
+++ b/logic/win32-shortcuts.cpp
@@ -0,0 +1,196 @@
+/* Copyright (c) 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.
+ */
+
+#if defined(_WIN32)
+#   ifndef DIRECTINPUT_VERSION
+#       define DIRECTINPUT_VERSION 0x800
+#   endif
+#   include <dinput.h>
+
+#include "win32-shortcuts.h"
+#include <QList>
+#include <QVariant>
+#include <QDebug>
+
+QList<win_key> windows_key_mods =
+    QList<win_key>({
+        win_key(DIK_LCONTROL, Qt::Key::Key_Control),
+        win_key(DIK_RCONTROL, Qt::Key::Key_Control),
+        win_key(DIK_LALT, Qt::Key::Key_Alt),
+        win_key(DIK_RALT, Qt::Key::Key_Alt),
+        win_key(DIK_LSHIFT, Qt::Key::Key_Shift),
+        win_key(DIK_RSHIFT, Qt::Key::Key_Shift),
+        win_key(DIK_LWIN, Qt::Key::Key_unknown),
+        win_key(DIK_RWIN, Qt::Key::Key_unknown)
+    });
+
+QList<win_key> windows_key_sequences =
+    QList<win_key>({
+       win_key(DIK_F1, Qt::Key::Key_F1 ),
+       win_key(DIK_F2, Qt::Key::Key_F2 ),
+       win_key(DIK_F3, Qt::Key::Key_F3 ),
+       win_key(DIK_F4, Qt::Key::Key_F4 ),
+       win_key(DIK_F5, Qt::Key::Key_F5 ),
+       win_key(DIK_F6, Qt::Key::Key_F6 ),
+       win_key(DIK_F7, Qt::Key::Key_F7 ),
+       win_key(DIK_F8, Qt::Key::Key_F8 ),
+       win_key(DIK_F9, Qt::Key::Key_F9 ),
+       win_key(DIK_F10, Qt::Key::Key_F10 ),
+       win_key(DIK_F11, Qt::Key::Key_F11 ),
+       win_key(DIK_F12, Qt::Key::Key_F12 ),
+       win_key(DIK_LEFT, Qt::Key::Key_Left ),
+       win_key(DIK_RIGHT, Qt::Key::Key_Right ),
+       win_key(DIK_UP, Qt::Key::Key_Up ),
+       win_key(DIK_DOWN, Qt::Key::Key_Down ),
+       win_key(DIK_PRIOR, Qt::Key::Key_PageUp ),
+       win_key(DIK_NEXT, Qt::Key::Key_PageDown ),
+       win_key(DIK_HOME, Qt::Key::Key_Home ),
+       win_key(DIK_END, Qt::Key::Key_End ),
+       win_key(DIK_BACK, Qt::Key::Key_Backspace ),
+       win_key(DIK_COMMA, Qt::Key::Key_Comma ),
+       win_key(DIK_PERIOD, Qt::Key::Key_Period ),
+       win_key(DIK_LBRACKET, Qt::Key::Key_BracketLeft ),
+       win_key(DIK_RBRACKET, Qt::Key::Key_BracketRight ),
+       win_key(DIK_SEMICOLON, Qt::Key::Key_Semicolon ),
+       win_key(DIK_SLASH, Qt::Key::Key_Slash ),
+       win_key(DIK_BACKSLASH, Qt::Key::Key_Backslash ),
+       win_key(DIK_BACKSPACE, Qt::Key::Key_Backspace ),
+       win_key(DIK_APOSTROPHE, Qt::Key::Key_Apostrophe ),
+       win_key(DIK_GRAVE, Qt::Key::Key_QuoteLeft ),
+       win_key(DIK_MINUS, Qt::Key::Key_Minus ),
+       win_key(DIK_EQUALS, Qt::Key::Key_Equal ),
+       win_key(DIK_PERIOD, Qt::Key::Key_Period ),
+       win_key(DIK_F1, Qt::Key::Key_F1 ),
+       win_key(DIK_F2, Qt::Key::Key_F2 ),
+       win_key(DIK_F3, Qt::Key::Key_F3 ),
+       win_key(DIK_F4, Qt::Key::Key_F4 ),
+       win_key(DIK_F5, Qt::Key::Key_F5 ),
+       win_key(DIK_F6, Qt::Key::Key_F6 ),
+       win_key(DIK_F7, Qt::Key::Key_F7 ),
+       win_key(DIK_F8, Qt::Key::Key_F8 ),
+       win_key(DIK_F9, Qt::Key::Key_F9 ),
+       win_key(DIK_F10, Qt::Key::Key_F10 ),
+       win_key(DIK_F11, Qt::Key::Key_F11 ),
+       win_key(DIK_F12, Qt::Key::Key_F12 ),
+       win_key(DIK_0, Qt::Key::Key_0 ),
+       win_key(DIK_1, Qt::Key::Key_1 ),
+       win_key(DIK_2, Qt::Key::Key_2 ),
+       win_key(DIK_3, Qt::Key::Key_3 ),
+       win_key(DIK_4, Qt::Key::Key_4 ),
+       win_key(DIK_5, Qt::Key::Key_5 ),
+       win_key(DIK_6, Qt::Key::Key_6 ),
+       win_key(DIK_7, Qt::Key::Key_7 ),
+       win_key(DIK_8, Qt::Key::Key_8 ),
+       win_key(DIK_9, Qt::Key::Key_9 ),
+       win_key(DIK_A, Qt::Key::Key_A ),
+       win_key(DIK_B, Qt::Key::Key_B ),
+       win_key(DIK_C, Qt::Key::Key_C ),
+       win_key(DIK_D, Qt::Key::Key_D ),
+       win_key(DIK_E, Qt::Key::Key_E ),
+       win_key(DIK_F, Qt::Key::Key_F ),
+       win_key(DIK_G, Qt::Key::Key_G ),
+       win_key(DIK_H, Qt::Key::Key_H ),
+       win_key(DIK_I, Qt::Key::Key_I ),
+       win_key(DIK_J, Qt::Key::Key_J ),
+       win_key(DIK_K, Qt::Key::Key_K ),
+       win_key(DIK_L, Qt::Key::Key_L ),
+       win_key(DIK_M, Qt::Key::Key_M ),
+       win_key(DIK_N, Qt::Key::Key_N ),
+       win_key(DIK_O, Qt::Key::Key_O ),
+       win_key(DIK_P, Qt::Key::Key_P ),
+       win_key(DIK_Q, Qt::Key::Key_Q ),
+       win_key(DIK_R, Qt::Key::Key_R ),
+       win_key(DIK_S, Qt::Key::Key_S ),
+       win_key(DIK_T, Qt::Key::Key_T ),
+       win_key(DIK_U, Qt::Key::Key_U ),
+       win_key(DIK_V, Qt::Key::Key_V ),
+       win_key(DIK_W, Qt::Key::Key_W ),
+       win_key(DIK_X, Qt::Key::Key_X ),
+       win_key(DIK_Y, Qt::Key::Key_Y ),
+       win_key(DIK_Z, Qt::Key::Key_Z ),
+       win_key(DIK_RETURN, Qt::Key::Key_Return),
+       win_key(DIK_INSERT, Qt::Key::Key_Insert),
+       win_key(DIK_DELETE, Qt::Key::Key_Delete),
+       win_key(DIK_SPACE, Qt::Key::Key_Space),
+       win_key(DIK_SYSRQ, Qt::Key::Key_Print),
+       win_key(DIK_SCROLL, Qt::Key::Key_ScrollLock),
+       win_key(DIK_PAUSE, Qt::Key::Key_Pause),
+       win_key(DIK_NUMLOCK, Qt::Key::Key_NumLock),
+#define mod(x, y) static_cast<Qt::Key>(x | y)
+       win_key(DIK_NUMPAD0,      mod(Qt::Key::Key_0,        Qt::KeypadModifier)),
+       win_key(DIK_NUMPAD0,      mod(Qt::Key::Key_0,        Qt::KeypadModifier)),
+       win_key(DIK_NUMPAD1,      mod(Qt::Key::Key_1,        Qt::KeypadModifier)),
+       win_key(DIK_NUMPAD2,      mod(Qt::Key::Key_2,        Qt::KeypadModifier)),
+       win_key(DIK_NUMPAD3,      mod(Qt::Key::Key_3,        Qt::KeypadModifier)),
+       win_key(DIK_NUMPAD4,      mod(Qt::Key::Key_4,        Qt::KeypadModifier)),
+       win_key(DIK_NUMPAD5,      mod(Qt::Key::Key_5,        Qt::KeypadModifier)),
+       win_key(DIK_NUMPAD6,      mod(Qt::Key::Key_6,        Qt::KeypadModifier)),
+       win_key(DIK_NUMPAD7,      mod(Qt::Key::Key_7,        Qt::KeypadModifier)),
+       win_key(DIK_NUMPAD8,      mod(Qt::Key::Key_8,        Qt::KeypadModifier)),
+       win_key(DIK_NUMPAD9,      mod(Qt::Key::Key_9,        Qt::KeypadModifier)),
+       win_key(DIK_NUMPADCOMMA,  mod(Qt::Key::Key_Comma,    Qt::KeypadModifier)),
+       win_key(DIK_NUMPADENTER,  mod(Qt::Key::Key_Enter,    Qt::KeypadModifier)),
+       win_key(DIK_NUMPADEQUALS, mod(Qt::Key::Key_Equal,    Qt::KeypadModifier)),
+       win_key(DIK_NUMPADMINUS,  mod(Qt::Key::Key_Minus,    Qt::KeypadModifier)),
+       win_key(DIK_NUMPADPERIOD, mod(Qt::Key::Key_Period,   Qt::KeypadModifier)),
+       win_key(DIK_NUMPADPLUS,   mod(Qt::Key::Key_Plus,     Qt::KeypadModifier)),
+       win_key(DIK_NUMPADSLASH,  mod(Qt::Key::Key_Slash,    Qt::KeypadModifier)),
+       win_key(DIK_NUMPADSTAR,   mod(Qt::Key::Key_multiply, Qt::KeypadModifier)),
+    });
+
+bool win_key::to_qt(const Key& k, QKeySequence& qt_, Qt::KeyboardModifiers &mods)
+{
+    for (auto& wk : windows_key_sequences)
+    {
+        if (wk.win == k.keycode)
+        {
+            qt_ = wk.qt;
+            mods = Qt::NoModifier;
+            if (k.ctrl) mods |= Qt::ControlModifier;
+            if (k.shift) mods |= Qt::ShiftModifier;
+            if (k.alt) mods |= Qt::AltModifier;
+            return true;
+        }
+    }
+    return false;
+}
+
+bool win_key::from_qt(QKeySequence qt_, int& dik, Qt::KeyboardModifiers& mods)
+{
+    // CAVEAT don't use QVariant::toUInt() or conversion fails
+    const unsigned qt = static_cast<unsigned>(QVariant(qt_).toInt());
+    const unsigned our_mods = qt & Qt::KeyboardModifierMask;
+
+    {
+        const auto key_ = qt;
+        for (auto& wk : windows_key_sequences)
+        {
+            if (wk.qt == key_)
+            {
+                dik = wk.win;
+                mods = Qt::NoModifier;
+                return true;
+            }
+        }
+    }
+    {
+        const unsigned key = qt & ~Qt::KeyboardModifierMask;
+        for (auto& wk : windows_key_sequences)
+        {
+            if (wk.qt == key)
+            {
+                dik = wk.win;
+                mods = static_cast<Qt::KeyboardModifiers>(our_mods);
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+#endif
diff --git a/logic/win32-shortcuts.h b/logic/win32-shortcuts.h
new file mode 100644
index 00000000..7626a31f
--- /dev/null
+++ b/logic/win32-shortcuts.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#ifdef _WIN32
+
+#include <QKeySequence>
+#include "shortcuts.h"
+
+struct win_key;
+
+extern QList<win_key> windows_key_mods;
+extern QList<win_key> windows_key_sequences;
+
+#include "export.hpp"
+
+struct OPENTRACK_LOGIC_EXPORT win_key
+{
+    win_key(int win, Qt::Key qt) : win(win), qt(qt) {}
+    int win;
+    Qt::Key qt;
+    static bool from_qt(QKeySequence qt_, int& dik, Qt::KeyboardModifiers &mods);
+    static bool to_qt(const Key& k, QKeySequence& qt_, Qt::KeyboardModifiers &mods);
+};
+
+#endif
diff --git a/logic/work.cpp b/logic/work.cpp
new file mode 100644
index 00000000..8d00270b
--- /dev/null
+++ b/logic/work.cpp
@@ -0,0 +1,73 @@
+#include "work.hpp"
+
+#include <QMessageBox>
+
+
+std::shared_ptr<TrackLogger> Work::make_logger(const main_settings &s)
+{
+    if (s.tracklogging_enabled)
+    {
+        if (static_cast<QString>(s.tracklogging_filename).isEmpty())
+        {
+            QMessageBox::warning(nullptr, "Logging Error",
+                "No filename given for track logging. Proceeding without logging.",
+                QMessageBox::Ok,
+                QMessageBox::NoButton);
+        }
+        else
+        {
+            auto logger = std::make_shared<TrackLoggerCSV>(s.tracklogging_filename);
+            if (!logger->is_open())
+            {
+                logger = nullptr;
+                QMessageBox::warning(nullptr, "Logging Error",
+                    "Unable to open file: " + s.tracklogging_filename + ". Proceeding  without logging.",
+                    QMessageBox::Ok,
+                    QMessageBox::NoButton);
+            }
+            else
+            {
+                /* As this function has the potential to fill up the hard drive
+                   of the unwary with junk data, a warning is in order. */
+                QMessageBox::warning(nullptr, "Logging Active",
+                    "Just a heads up. You are recoding pose data to " + s.tracklogging_filename + "!",
+                    QMessageBox::Ok,
+                    QMessageBox::NoButton);
+                return logger;
+            }
+        }
+    }
+    return std::make_shared<TrackLogger>();
+}
+
+
+Work::Work(Mappings& m, SelectedLibraries& libs, WId handle) :
+    libs(libs),
+    logger(make_logger(s)),
+    tracker(std::make_shared<Tracker>(m, libs, *logger)),
+    sc(std::make_shared<Shortcuts>()),
+    handle(handle),
+    keys {
+        key_tuple(s.key_center, [&](bool) -> void { tracker->center(); }, true),
+        key_tuple(s.key_toggle, [&](bool) -> void { tracker->toggle_enabled(); }, true),
+        key_tuple(s.key_zero, [&](bool) -> void { tracker->zero(); }, true),
+        key_tuple(s.key_toggle_press, [&](bool x) -> void { tracker->set_toggle(!x); }, false),
+        key_tuple(s.key_zero_press, [&](bool x) -> void { tracker->set_zero(x); }, false),
+        }
+{
+    reload_shortcuts();
+    tracker->start();
+}
+
+void Work::reload_shortcuts()
+{
+    sc->reload(keys);
+}
+
+Work::~Work()
+{
+    sc = nullptr;
+    // order matters, otherwise use-after-free -sh
+    tracker = nullptr;
+    libs = SelectedLibraries();
+}
diff --git a/logic/work.hpp b/logic/work.hpp
new file mode 100644
index 00000000..b20b1824
--- /dev/null
+++ b/logic/work.hpp
@@ -0,0 +1,43 @@
+/* Copyright (c) 2014-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 "main-settings.hpp"
+#include "api/plugin-support.hpp"
+#include "tracker.h"
+#include "shortcuts.h"
+#include "export.hpp"
+#include "tracklogger.hpp"
+
+#include <QObject>
+#include <QFrame>
+#include <memory>
+#include <vector>
+#include <tuple>
+#include <functional>
+
+struct OPENTRACK_LOGIC_EXPORT Work
+{
+    using fn_t = std::function<void(bool)>;
+    using key_tuple = std::tuple<key_opts&, fn_t, bool>;
+    main_settings s; // tracker needs settings, so settings must come before it
+    SelectedLibraries& libs;
+    std::shared_ptr<TrackLogger> logger; // must come before tracker, since tracker depends on it
+    std::shared_ptr<Tracker> tracker;
+    std::shared_ptr<Shortcuts> sc;
+    WId handle;
+    std::vector<key_tuple> keys;
+
+    Work(Mappings& m, SelectedLibraries& libs, WId handle);
+    ~Work();
+    void reload_shortcuts();
+
+private:
+    std::shared_ptr<TrackLogger> make_logger(const main_settings &s);
+};
diff --git a/opentrack-compat/CMakeLists.txt b/opentrack-compat/CMakeLists.txt
deleted file mode 100644
index 2bbb496c..00000000
--- a/opentrack-compat/CMakeLists.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-opentrack_boilerplate(opentrack-compat NO-COMPAT BIN)
-if(NOT WIN32 AND NOT APPLE)
-    target_link_libraries(opentrack-compat rt)
-endif()
-if(CMAKE_COMPILER_IS_GNUCXX)
-    set_source_files_properties(nan.cpp PROPERTIES
-        COMPILE_FLAGS "-fno-fast-math -fno-finite-math-only -O0")
-endif()
diff --git a/opentrack-compat/camera-names.cpp b/opentrack-compat/camera-names.cpp
deleted file mode 100644
index 21ff3b52..00000000
--- a/opentrack-compat/camera-names.cpp
+++ /dev/null
@@ -1,111 +0,0 @@
-#include "camera-names.hpp"
-
-#ifdef _WIN32
-#   define NO_DSHOW_STRSAFE
-#   include <dshow.h>
-#   include "win32-com.hpp"
-#   include <cwchar>
-#elif defined(__unix) || defined(__linux) || defined(__APPLE__)
-#   include <unistd.h>
-#endif
-
-#ifdef __linux
-#   include <fcntl.h>
-#   include <sys/ioctl.h>
-#   include <linux/videodev2.h>
-#   include <cerrno>
-#endif
-
-#include <QDebug>
-
-OPENTRACK_COMPAT_EXPORT int camera_name_to_index(const QString &name)
-{
-    auto list = get_camera_names();
-    int ret = list.indexOf(name);
-    if (ret < 0)
-        ret = 0;
-    return ret;
-}
-
-OPENTRACK_COMPAT_EXPORT QList<QString> get_camera_names()
-{
-    QList<QString> ret;
-#if defined(_WIN32)
-    // Create the System Device Enumerator.
-    HRESULT hr;
-    init_com_threading(com_apartment);
-    ICreateDevEnum *pSysDevEnum = NULL;
-    hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, 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;
-    hr = pSysDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnumCat, 0);
-
-    if (hr == S_OK) {
-        // Enumerate the monikers.
-        IMoniker *pMoniker = NULL;
-        ULONG cFetched;
-        while (pEnumCat->Next(1, &pMoniker, &cFetched) == S_OK) {
-            IPropertyBag *pPropBag;
-            hr = pMoniker->BindToStorage(0, 0, 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);
-                }
-                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();
-    }
-    else
-        qDebug() << "failed CLSID_VideoInputDeviceCategory" << hr;
-
-    pSysDevEnum->Release();
-#endif
-#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;
-
-        if (access(buf, R_OK | W_OK) == 0) {
-            int fd = open(buf, O_RDONLY);
-            if (fd == -1)
-                continue;
-            struct v4l2_capability video_cap;
-            if(ioctl(fd, VIDIOC_QUERYCAP, &video_cap) == -1)
-            {
-                qDebug() << "VIDIOC_QUERYCAP" << errno;
-                close(fd);
-                continue;
-            }
-            ret[ret.size()-1] = reinterpret_cast<const char*>(video_cap.card);
-            close(fd);
-        }
-    }
-#endif
-    return ret;
-}
diff --git a/opentrack-compat/camera-names.hpp b/opentrack-compat/camera-names.hpp
deleted file mode 100644
index ae0c6b25..00000000
--- a/opentrack-compat/camera-names.hpp
+++ /dev/null
@@ -1,18 +0,0 @@
-/* Copyright (c) 2014-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 <QList>
-#include <QString>
-
-#include "export.hpp"
-
-OPENTRACK_COMPAT_EXPORT QList<QString> get_camera_names();
-OPENTRACK_COMPAT_EXPORT int camera_name_to_index(const QString &name);
-
diff --git a/opentrack-compat/export.hpp b/opentrack-compat/export.hpp
deleted file mode 100644
index ba10f131..00000000
--- a/opentrack-compat/export.hpp
+++ /dev/null
@@ -1,27 +0,0 @@
-#pragma once
-
-#ifdef BUILD_compat
-#   ifdef _WIN32
-#       define OPENTRACK_COMPAT_LINKAGE __declspec(dllexport)
-#   else
-#       define OPENTRACK_COMPAT_LINKAGE
-#   endif
-
-#   ifndef _MSC_VER
-#       define OPENTRACK_COMPAT_EXPORT __attribute__ ((visibility ("default"))) OPENTRACK_COMPAT_LINKAGE
-#   else
-#       define OPENTRACK_COMPAT_EXPORT OPENTRACK_COMPAT_LINKAGE
-#   endif
-#else
-    #ifdef _WIN32
-    #    define OPENTRACK_COMPAT_LINKAGE __declspec(dllimport)
-    #else
-    #    define OPENTRACK_COMPAT_LINKAGE
-    #endif
-
-    #ifndef _MSC_VER
-    #    define OPENTRACK_COMPAT_EXPORT __attribute__ ((visibility ("default"))) OPENTRACK_COMPAT_LINKAGE
-    #else
-    #    define OPENTRACK_COMPAT_EXPORT OPENTRACK_COMPAT_LINKAGE
-    #endif
-#endif
diff --git a/opentrack-compat/make-unique.hpp b/opentrack-compat/make-unique.hpp
deleted file mode 100644
index bb5315c5..00000000
--- a/opentrack-compat/make-unique.hpp
+++ /dev/null
@@ -1,43 +0,0 @@
-#pragma once
-
-// GNU 5.4.0 doesn't have std::make_unique in -std=c++14 mode
-
-// this implementation was taken from http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3656.htm
-
-#include <memory>
-#include <utility>
-#include <cstddef>
-
-namespace detail {
-template<class T> struct Unique_if
-{
-    typedef std::unique_ptr<T> Single_object;
-};
-
-template<class T> struct Unique_if<T[]>
-{
-    typedef std::unique_ptr<T[]> Unknown_bound;
-};
-
-template<class T, size_t N> struct Unique_if<T[N]>
-{
-    typedef void Known_bound;
-};
-}
-
-template<class T, class... Args>
-    typename detail::Unique_if<T>::Single_object
-    make_unique(Args&&... args) {
-        return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
-    }
-
-template<class T>
-    typename detail::Unique_if<T>::Unknown_bound
-    make_unique(std::size_t n) {
-        typedef typename std::remove_extent<T>::type U;
-        return std::unique_ptr<T>(new U[n]());
-    }
-
-template<class T, class... Args>
-    typename detail::Unique_if<T>::Known_bound
-    make_unique(Args&&...) = delete;
diff --git a/opentrack-compat/nan.cpp b/opentrack-compat/nan.cpp
deleted file mode 100644
index 899b907a..00000000
--- a/opentrack-compat/nan.cpp
+++ /dev/null
@@ -1,17 +0,0 @@
-#include <cmath>
-#include "export.hpp"
-
-#if defined(__GNUC__)
-extern "C" OPENTRACK_COMPAT_EXPORT bool __attribute__ ((noinline)) nanp(double value)
-#elif defined(_WIN32)
-extern "C" OPENTRACK_COMPAT_EXPORT __declspec(noinline) bool nanp(double value)
-#else
-extern "C" OPENTRACK_COMPAT_EXPORT bool nanp(double value)
-#endif
-{
-    using std::isnan;
-    using std::isinf;
-
-    const volatile double x = value;
-    return isnan(x) || isinf(x);
-}
diff --git a/opentrack-compat/nan.hpp b/opentrack-compat/nan.hpp
deleted file mode 100644
index 9926da13..00000000
--- a/opentrack-compat/nan.hpp
+++ /dev/null
@@ -1,11 +0,0 @@
-#pragma once
-
-#include "export.hpp"
-
-#if defined(__GNUC__)
-extern "C" OPENTRACK_COMPAT_EXPORT bool __attribute__ ((noinline)) nanp(double value);
-#elif defined(_WIN32)
-extern "C" OPENTRACK_COMPAT_EXPORT __declspec(noinline) bool nanp(double value);
-#else
-extern "C" OPENTRACK_COMPAT_EXPORT bool nanp(double value);
-#endif
diff --git a/opentrack-compat/options.cpp b/opentrack-compat/options.cpp
deleted file mode 100644
index 8c6e6c65..00000000
--- a/opentrack-compat/options.cpp
+++ /dev/null
@@ -1,239 +0,0 @@
-#include "options.hpp"
-
-namespace options
-{
-
-group::group(const QString& name) : name(name)
-{
-    auto conf = ini_file();
-    conf->beginGroup(name);
-    for (auto& k_ : conf->childKeys())
-    {
-        auto tmp = k_.toUtf8();
-        QString k(tmp);
-        kvs[k] = conf->value(k_);
-    }
-    conf->endGroup();
-}
-
-void group::save() const
-{
-    auto s = ini_file();
-    s->beginGroup(name);
-    for (auto& i : kvs)
-        s->setValue(i.first, i.second);
-    s->endGroup();
-}
-
-void group::put(const QString &s, const QVariant &d)
-{
-    kvs[s] = d;
-}
-
-bool group::contains(const QString &s) const
-{
-    return kvs.count(s) != 0;
-}
-
-QString group::ini_directory()
-{
-    const auto dirs = QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation);
-    if (dirs.size() == 0)
-        return "";
-    if (QDir(dirs[0]).mkpath(OPENTRACK_ORG))
-        return dirs[0] + "/" OPENTRACK_ORG;
-    return "";
-}
-
-QString group::ini_filename()
-{
-    QSettings settings(OPENTRACK_ORG);
-    return settings.value(OPENTRACK_CONFIG_FILENAME_KEY, OPENTRACK_DEFAULT_CONFIG).toString();
-}
-
-QString group::ini_pathname()
-{
-    const auto dir = ini_directory();
-    if (dir == "")
-        return "";
-    return dir + "/" + ini_filename();
-}
-
-const QStringList group::ini_list()
-{
-    const auto dirname = ini_directory();
-    if (dirname == "")
-        return QStringList();
-    QDir settings_dir(dirname);
-    return settings_dir.entryList( QStringList { "*.ini" } , QDir::Files, QDir::Name );
-}
-
-const mem<QSettings> group::ini_file()
-{
-    const auto pathname = ini_pathname();
-    if (pathname != "")
-        return std::make_shared<QSettings>(ini_pathname(), QSettings::IniFormat);
-    return std::make_shared<QSettings>();
-}
-
-bool group::operator==(const group& other) const
-{
-    for (const auto& kv : kvs)
-    {
-        const QVariant val = other.get<QVariant>(kv.first);
-        if (!other.contains(kv.first) || kv.second != val)
-        {
-            qDebug() << "bundle" << name << "modified" << "key" << kv.first << "-" << val << "<>" << kv.second;
-            return false;
-        }
-    }
-
-    for (const auto& kv : other.kvs)
-    {
-        const QVariant val = get<QVariant>(kv.first);
-        if (!contains(kv.first) || kv.second != val)
-        {
-            qDebug() << "bundle" << name << "modified" << "key" << kv.first << "-" << kv.second << "<>" << val;
-            return false;
-        }
-    }
-    return true;
-}
-
-impl_bundle::impl_bundle(const QString& group_name)
-    :
-      mtx(QMutex::Recursive),
-      group_name(group_name),
-      saved(group_name),
-      transient(saved)
-{
-}
-
-void impl_bundle::reload()
-{
-    {
-        QMutexLocker l(&mtx);
-        saved = group(group_name);
-        transient = saved;
-    }
-    emit reloading();
-}
-
-void impl_bundle::store_kv(const QString& name, const QVariant& datum)
-{
-    QMutexLocker l(&mtx);
-
-    transient.put(name, datum);
-}
-
-bool impl_bundle::contains(const QString &name) const
-{
-    QMutexLocker l(const_cast<QMutex*>(&mtx));
-    return transient.contains(name);
-}
-
-void impl_bundle::save()
-{
-    bool modified_ = false;
-
-    {
-        QMutexLocker l(&mtx);
-        if (saved != transient)
-        {
-            qDebug() << "bundle" << group_name << "changed, saving";
-            modified_ = true;
-            saved = transient;
-            saved.save();
-        }
-    }
-
-    if (modified_)
-        emit saving();
-}
-
-bool impl_bundle::modifiedp() const // XXX unused
-{
-    QMutexLocker l(const_cast<QMutex*>(&mtx));
-    return transient != saved;
-}
-
-base_value::base_value(pbundle b, const QString &name) :
-    b(b),
-    self_name(name)
-{
-}
-
-opts::~opts()
-{
-    b->reload();
-}
-
-opts::opts(const QString &name) : b(bundle(name))
-{
-}
-
-custom_type_initializer::custom_type_initializer()
-{
-    qRegisterMetaTypeStreamOperators<slider_value>("slider_value");
-    QMetaType::registerDebugStreamOperator<slider_value>();
-}
-
-custom_type_initializer custom_type_initializer::singleton;
-
-namespace detail {
-
-opt_bundle::opt_bundle(const QString& group_name)
-    : impl_bundle(group_name)
-{
-}
-
-opt_bundle::~opt_bundle()
-{
-    detail::singleton().bundle_decf(group_name);
-}
-
-void opt_singleton::bundle_decf(const opt_singleton::k& key)
-{
-    QMutexLocker l(&implsgl_mtx);
-
-    if (--std::get<0>(implsgl_data[key]) == 0)
-    {
-        qDebug() << "bundle -" << key;
-
-        implsgl_data.erase(key);
-    }
-}
-
-opt_singleton::opt_singleton() : implsgl_mtx(QMutex::Recursive)
-{
-}
-
-pbundle opt_singleton::bundle(const opt_singleton::k &key)
-{
-    QMutexLocker l(&implsgl_mtx);
-
-    if (implsgl_data.count(key) != 0)
-    {
-        auto shared = std::get<1>(implsgl_data[key]).lock();
-        if (shared != nullptr)
-            return shared;
-    }
-
-    qDebug() << "bundle +" << key;
-
-    auto shr = std::make_shared<v>(key);
-    implsgl_data[key] = tt(1, shr);
-    return shr;
-}
-
-OPENTRACK_COMPAT_EXPORT opt_singleton& singleton()
-{
-    static opt_singleton ret;
-    return ret;
-}
-
-
-} // end options::detail
-
-} // end options
-
diff --git a/opentrack-compat/options.hpp b/opentrack-compat/options.hpp
deleted file mode 100644
index 40aca2de..00000000
--- a/opentrack-compat/options.hpp
+++ /dev/null
@@ -1,481 +0,0 @@
-/* Copyright (c) 2013-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.
- */
-
-#pragma once
-
-#include <memory>
-#include <tuple>
-#include <map>
-#include <cinttypes>
-#include <vector>
-#include <memory>
-
-#include <QObject>
-#include <QSettings>
-#include <QString>
-#include <QVariant>
-#include <QMutex>
-#include <QMutexLocker>
-#include <QWidget>
-#include <QComboBox>
-#include <QCheckBox>
-#include <QDoubleSpinBox>
-#include <QSpinBox>
-#include <QSlider>
-#include <QLineEdit>
-#include <QLabel>
-#include <QTabWidget>
-#include <QCoreApplication>
-#include <QFileInfo>
-#include <QDir>
-#include <QStandardPaths>
-#include <QApplication>
-
-#include <QMetaType>
-#include <QDataStream>
-
-#include <QDebug>
-
-#include "export.hpp"
-#include "slider.hpp"
-
-#include "util.hpp"
-
-#define OPENTRACK_CONFIG_FILENAME_KEY "settings-filename"
-#define OPENTRACK_DEFAULT_CONFIG "default.ini"
-#define OPENTRACK_ORG "opentrack-2.3"
-
-namespace options {
-    namespace {
-        class custom_type_initializer
-        {
-            custom_type_initializer();
-            static custom_type_initializer singleton;
-        };
-    }
-
-    template<typename k, typename v> using map = std::map<k, v>;
-
-    // snapshot of qsettings group at given time
-    class OPENTRACK_COMPAT_EXPORT group
-    {
-    private:
-        map<QString, QVariant> kvs;
-        QString name;
-    public:
-        group(const QString& name);
-        void save() const;
-        void put(const QString& s, const QVariant& d);
-        bool contains(const QString& s) const;
-        static QString ini_directory();
-        static QString ini_filename();
-        static QString ini_pathname();
-        static const QStringList ini_list();
-        static const mem<QSettings> ini_file();
-        bool operator==(const group& other) const;
-        bool operator!=(const group& other) const { return !(*this == other); }
-
-        template<typename t>
-        t get(const QString& k) const
-        {
-            auto value = kvs.find(k);
-            if (value != kvs.cend())
-                return value->second.value<t>();
-            return t();
-        }
-    };
-
-    class OPENTRACK_COMPAT_EXPORT impl_bundle : public QObject
-    {
-        Q_OBJECT
-    protected:
-        QMutex mtx;
-        const QString group_name;
-        group saved;
-        group transient;
-        impl_bundle(const impl_bundle&) = delete;
-        impl_bundle& operator=(const impl_bundle&) = delete;
-    signals:
-        void reloading();
-        void saving() const;
-    public:
-        impl_bundle(const QString& group_name);
-        QString name() { return group_name; }
-        void reload();
-        void store_kv(const QString& name, const QVariant& datum);
-        bool contains(const QString& name) const;
-        void save();
-        bool modifiedp() const;
-
-        template<typename t>
-        t get(const QString& name) const
-        {
-            QMutexLocker l(const_cast<QMutex*>(&mtx));
-            return transient.get<t>(name);
-        }
-    };
-
-    namespace detail
-    {
-        class OPENTRACK_COMPAT_EXPORT opt_bundle final : public impl_bundle
-        {
-        public:
-            opt_bundle(const QString& group_name);
-            ~opt_bundle();
-        };
-
-        struct OPENTRACK_COMPAT_EXPORT opt_singleton
-        {
-        public:
-            using k = QString;
-            using v = opt_bundle;
-            using cnt = int;
-            using pbundle = std::shared_ptr<v>;
-            using tt = std::tuple<cnt, std::weak_ptr<v>>;
-        private:
-            QMutex implsgl_mtx;
-            map<k, tt> implsgl_data;
-        public:
-            opt_singleton();
-            pbundle bundle(const k& key);
-            void bundle_decf(const k& key);
-        };
-
-        OPENTRACK_COMPAT_EXPORT opt_singleton& singleton();
-    }
-
-    using pbundle = std::shared_ptr<detail::opt_bundle>;
-
-    inline pbundle bundle(const QString& name)
-    {
-         return detail::singleton().bundle(name);
-    }
-
-#define OPENTRACK_DEFINE_SLOT(t) void setValue(t datum) { store(datum); }
-#define OPENTRACK_DEFINE_SIGNAL(t) void valueChanged(t)
-
-    class OPENTRACK_COMPAT_EXPORT base_value : public QObject
-    {
-        Q_OBJECT
-    public:
-        QString name() const { return self_name; }
-        base_value(pbundle b, const QString& name);
-    signals:
-        OPENTRACK_DEFINE_SIGNAL(double);
-        OPENTRACK_DEFINE_SIGNAL(float);
-        OPENTRACK_DEFINE_SIGNAL(int);
-        OPENTRACK_DEFINE_SIGNAL(bool);
-        OPENTRACK_DEFINE_SIGNAL(const QString&);
-        OPENTRACK_DEFINE_SIGNAL(const slider_value&);
-    protected:
-        pbundle b;
-        QString self_name;
-
-        template<typename t>
-        void store(const t& datum)
-        {
-            b->store_kv(self_name, QVariant::fromValue(datum));
-            emit valueChanged(static_cast<t>(datum));
-        }
-        void store(float datum)
-        {
-            store(double(datum));
-        }
-
-    public slots:
-        OPENTRACK_DEFINE_SLOT(double)
-        OPENTRACK_DEFINE_SLOT(int)
-        OPENTRACK_DEFINE_SLOT(bool)
-        OPENTRACK_DEFINE_SLOT(const QString&)
-        OPENTRACK_DEFINE_SLOT(const slider_value&)
-    public slots:
-        virtual void reload() = 0;
-    };
-
-    namespace detail {
-        template<typename t>
-        struct value_get_traits
-        {
-            static inline t get(const t& val, const t&)
-            {
-                return val;
-            }
-        };
-
-        template<>
-        struct value_get_traits<slider_value>
-        {
-            using t = slider_value;
-            static inline t get(const t& val, const t& def)
-            {
-                return t(val.cur(), def.min(), def.max());
-            }
-        };
-    }
-
-    template<typename t_>
-    class value : public base_value
-    {
-        template<typename t__, typename Enable = void>
-        struct get_t
-        { using t = t__; };
-
-        // Qt uses int a lot in slots so use it for all enums
-        template<typename t__>
-        struct get_t<t__, typename std::enable_if<std::is_enum<t__>::value>::type>
-        //{ using t = typename std::underlying_type<t__>::type; };
-        { using t = int; };
-
-        using t = t_;
-    public:
-        using underlying_t = typename get_t<t_>::t;
-
-        t operator=(const t& datum)
-        {
-            store(static_cast<underlying_t>(datum));
-            return datum;
-        }
-
-        static constexpr const Qt::ConnectionType DIRECT_CONNTYPE = Qt::AutoConnection;
-        static constexpr const Qt::ConnectionType SAFE_CONNTYPE = Qt::QueuedConnection;
-
-        value(pbundle b, const QString& name, t def) : base_value(b, name), def(def)
-        {
-            QObject::connect(b.get(), SIGNAL(reloading()),
-                             this, SLOT(reload()),
-                             DIRECT_CONNTYPE);
-            if (!b->contains(name) || b->get<QVariant>(name).type() == QVariant::Invalid)
-                *this = def;
-        }
-
-        value(pbundle b, const char* name, t def) : value(b, QString(name), def)
-        {
-        }
-
-        t get() const
-        {
-            t val = b->contains(self_name)
-                    ? static_cast<t>(b->get<underlying_t>(self_name))
-                    : def;
-            return detail::value_get_traits<t>::get(val, def);
-        }
-
-        operator t() const { return get(); }
-
-        void reload() override
-        {
-            *this = static_cast<t>(*this);
-        }
-
-    private:
-        t def;
-    };
-
-    struct OPENTRACK_COMPAT_EXPORT opts
-    {
-        pbundle b;
-        opts(const QString& name);
-        opts& operator=(const opts&) = delete;
-        opts(const opts&) = delete;
-        ~opts();
-    };
-
-    template<typename t, typename q>
-    inline void tie_setting(value<t>&, q*);
-
-    template<typename t>
-    inline
-    typename std::enable_if<std::is_enum<t>::value>::type
-    tie_setting(value<t>& v, QComboBox* cb)
-    {
-        cb->setCurrentIndex(cb->findData((unsigned)static_cast<t>(v)));
-        v = static_cast<t>(cb->currentData().toInt());
-
-        // QObject::connect plays badly with std::bind of std::shared_ptr. Data seems to get freed.
-        // Direct accesses of cb->currentData within arbitrary thread context cause crashes as well.
-        // Hence we go for a verbose implementation.
-
-        std::vector<int> enum_cases;
-        enum_cases.reserve(unsigned(cb->count()));
-
-        for (int i = 0; i < cb->count(); i++)
-            enum_cases.push_back(cb->itemData(i).toInt());
-
-        struct fn1
-        {
-            value<t>& v;
-            QComboBox* cb;
-            std::vector<int> enum_cases;
-
-            fn1(value<t>& v, QComboBox* cb, const std::vector<int>& enum_cases) :
-                v(v),
-                cb(cb),
-                enum_cases(enum_cases)
-            {
-            }
-
-            void operator()(int idx)
-            {
-                if (idx < 0 || idx >= (int)enum_cases.size())
-                    v = static_cast<t>(-1);
-                else
-                    v = static_cast<t>(t(std::intptr_t(enum_cases[idx])));
-            }
-        };
-
-        struct fn2
-        {
-            value<t>& v;
-            QComboBox* cb;
-            std::vector<int> enum_cases;
-
-            fn2(value<t>& v, QComboBox* cb, const std::vector<int>& enum_cases) :
-                v(v),
-                cb(cb),
-                enum_cases(enum_cases)
-            {
-            }
-
-            void operator()(int val)
-            {
-                for (unsigned i = 0; i < enum_cases.size(); i++)
-                {
-                    if (val == enum_cases[i])
-                    {
-                        cb->setCurrentIndex(i);
-                        return;
-                    }
-                }
-                cb->setCurrentIndex(-1);
-            }
-        };
-
-        base_value::connect(cb,
-                            static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
-                            &v,
-                            fn1(v, cb, enum_cases),
-                            v.DIRECT_CONNTYPE);
-        base_value::connect(&v,
-                            static_cast<void (base_value::*)(int)>(&base_value::valueChanged),
-                            cb,
-                            fn2(v, cb, enum_cases),
-                            v.DIRECT_CONNTYPE);
-    }
-
-    template<>
-    inline void tie_setting(value<int>& v, QComboBox* cb)
-    {
-        cb->setCurrentIndex(v);
-        v = cb->currentIndex();
-        base_value::connect(cb, SIGNAL(currentIndexChanged(int)), &v, SLOT(setValue(int)), v.DIRECT_CONNTYPE);
-        base_value::connect(&v, SIGNAL(valueChanged(int)), cb, SLOT(setCurrentIndex(int)), v.SAFE_CONNTYPE);
-    }
-
-    template<>
-    inline void tie_setting(value<QString>& v, QComboBox* cb)
-    {
-        cb->setCurrentText(v);
-        v = cb->currentText();
-        base_value::connect(cb, SIGNAL(currentTextChanged(QString)), &v, SLOT(setValue(QString)), v.DIRECT_CONNTYPE);
-        base_value::connect(&v, SIGNAL(valueChanged(QString)), cb, SLOT(setCurrentText(QString)), v.SAFE_CONNTYPE);
-    }
-
-    template<>
-    inline void tie_setting(value<bool>& v, QCheckBox* cb)
-    {
-        cb->setChecked(v);
-        base_value::connect(cb, SIGNAL(toggled(bool)), &v, SLOT(setValue(bool)), v.DIRECT_CONNTYPE);
-        base_value::connect(&v, SIGNAL(valueChanged(bool)), cb, SLOT(setChecked(bool)), v.SAFE_CONNTYPE);
-    }
-
-    template<>
-    inline void tie_setting(value<double>& v, QDoubleSpinBox* dsb)
-    {
-        dsb->setValue(v);
-        base_value::connect(dsb, SIGNAL(valueChanged(double)), &v, SLOT(setValue(double)), v.DIRECT_CONNTYPE);
-        base_value::connect(&v, SIGNAL(valueChanged(double)), dsb, SLOT(setValue(double)), v.SAFE_CONNTYPE);
-    }
-
-    template<>
-    inline void tie_setting(value<int>& v, QSpinBox* sb)
-    {
-        sb->setValue(v);
-        base_value::connect(sb, SIGNAL(valueChanged(int)), &v, SLOT(setValue(int)), v.DIRECT_CONNTYPE);
-        base_value::connect(&v, SIGNAL(valueChanged(int)), sb, SLOT(setValue(int)), v.SAFE_CONNTYPE);
-    }
-
-    template<>
-    inline void tie_setting(value<int>& v, QSlider* sl)
-    {
-        sl->setValue(v);
-        v = sl->value();
-        base_value::connect(sl, SIGNAL(valueChanged(int)), &v, SLOT(setValue(int)), v.DIRECT_CONNTYPE);
-        base_value::connect(&v, SIGNAL(valueChanged(int)), sl, SLOT(setValue(int)), v.SAFE_CONNTYPE);
-    }
-
-    template<>
-    inline void tie_setting(value<QString>& v, QLineEdit* le)
-    {
-        le->setText(v);
-        base_value::connect(le, SIGNAL(textChanged(QString)), &v, SLOT(setValue(QString)), v.DIRECT_CONNTYPE);
-        base_value::connect(&v, SIGNAL(valueChanged(QString)),le, SLOT(setText(QString)), v.SAFE_CONNTYPE);
-    }
-
-    template<>
-    inline void tie_setting(value<QString>& v, QLabel* lb)
-    {
-        lb->setText(v);
-        base_value::connect(&v, SIGNAL(valueChanged(QString)), lb, SLOT(setText(QString)), v.DIRECT_CONNTYPE);
-    }
-
-    template<>
-    inline void tie_setting(value<int>& v, QTabWidget* t)
-    {
-        t->setCurrentIndex(v);
-        base_value::connect(t, SIGNAL(currentChanged(int)), &v, SLOT(setValue(int)), v.DIRECT_CONNTYPE);
-        base_value::connect(&v, SIGNAL(valueChanged(int)), t, SLOT(setCurrentIndex(int)), v.SAFE_CONNTYPE);
-    }
-
-    template<>
-    inline void tie_setting(value<slider_value>& v, QSlider* w)
-    {
-        // we can't get these at runtime since signals cross threads
-        const int q_min = w->minimum();
-        const int q_max = w->maximum();
-        const int q_diff = q_max - q_min;
-
-        slider_value sv(v);
-
-        const double sv_max = sv.max();
-        const double sv_min = sv.min();
-        const double sv_c = sv_max - sv_min;
-
-        w->setValue(int((sv.cur() - sv_min) / sv_c * q_diff + q_min));
-        v = slider_value(q_diff <= 0 ? 0 : (w->value() - q_min) * sv_c / (double)q_diff + sv_min, sv_min, sv_max);
-
-        base_value::connect(w,
-                            &QSlider::valueChanged,
-                            &v,
-                            [=, &v](int pos) -> void
-        {
-            if (q_diff <= 0 || pos <= 0)
-                v = slider_value(sv_min, sv_min, sv_max);
-            else
-                v = slider_value((pos - q_min) * sv_c / (double)q_diff + sv_min, sv_min, sv_max);
-        },
-        v.DIRECT_CONNTYPE);
-        base_value::connect(&v,
-                            static_cast<void(base_value::*)(double)>(&base_value::valueChanged),
-                            w,
-                            [=](double value) -> void
-        {
-            w->setValue(int(value * q_diff) + q_min);
-        },
-        v.SAFE_CONNTYPE);
-    }
-}
-
diff --git a/opentrack-compat/pi-constant.hpp b/opentrack-compat/pi-constant.hpp
deleted file mode 100644
index 52b98a7f..00000000
--- a/opentrack-compat/pi-constant.hpp
+++ /dev/null
@@ -1,3 +0,0 @@
-#pragma once
-
-#define OPENTRACK_PI 3.14159265358979323846
diff --git a/opentrack-compat/process-list.hpp b/opentrack-compat/process-list.hpp
deleted file mode 100644
index 10613791..00000000
--- a/opentrack-compat/process-list.hpp
+++ /dev/null
@@ -1,169 +0,0 @@
-/* Copyright (c) 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 <QDebug>
-#include <QStringList>
-
-#if defined _WIN32
-
-#include <windows.h>
-#include <tlhelp32.h>
-
-template<typename = void>
-static QStringList get_all_executable_names()
-{
-    QStringList ret;
-    HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
-    if (h == INVALID_HANDLE_VALUE)
-        return ret;
-
-    PROCESSENTRY32 e;
-    e.dwSize = sizeof(e);
-
-    if (Process32First(h, &e) != TRUE)
-    {
-        CloseHandle(h);
-        return ret;
-    }
-
-    do {
-        ret.append(e.szExeFile);
-    } while (Process32Next(h, &e) == TRUE);
-
-    CloseHandle(h);
-
-    return ret;
-}
-#elif defined __APPLE__
-#include <libproc.h>
-#include <sys/param.h>
-#include <sys/types.h>
-#include <sys/sysctl.h>
-#include <cerrno>
-#include <cstring>
-#include <vector>
-
-template<typename = void>
-static QStringList get_all_executable_names()
-{
-    QStringList ret;
-    std::vector<int> vec;
-
-    while (true)
-    {
-        int numproc = proc_listpids(PROC_ALL_PIDS, 0, nullptr, 0);
-        if (numproc == -1)
-        {
-            qDebug() << "proc_listpids numproc failed" << errno;
-            return ret;
-        }
-        vec.resize(numproc);
-        int cnt = proc_listpids(PROC_ALL_PIDS, 0, &vec[0], sizeof(int) * numproc);
-
-        if (cnt <= numproc)
-        {
-            std::vector<char> arglist;
-            int mib[2] { CTL_KERN, KERN_ARGMAX };
-            size_t sz = sizeof(int);
-            int maxarg = 0;
-            if (sysctl(mib, 2, &maxarg, &sz, NULL, 0) == -1)
-            {
-                qDebug() << "sysctl KERN_ARGMAX" << errno;
-                return ret;
-            }
-            arglist.resize(maxarg);
-            for (int i = 0; i < numproc; i++)
-            {
-                size_t maxarg_ = (size_t)maxarg;
-                int mib[3] { CTL_KERN, KERN_PROCARGS2, vec[i] };
-                if (sysctl(mib, 3, &arglist[0], &maxarg_, NULL, 0) == -1)
-                {
-                    //qDebug() << "sysctl KERN_PROCARGS2" << vec[i] << errno;
-                    continue;
-                }
-                QStringList cmdline;
-                for (unsigned j = sizeof(int) + strlen(&arglist[sizeof(int)]); j < maxarg_; j++)
-                {
-                    QString arg(&arglist[j]);
-                    if (arg.size() != 0)
-                    {
-                        cmdline << arg;
-                        j += arg.size();
-                    }
-                }
-                if (cmdline.size() > 0)
-                {
-                    int idx = cmdline[0].lastIndexOf('/');
-                    if (idx != -1)
-                    {
-                        QString tmp = cmdline[0].mid(idx+1);
-                        if (cmdline.size() > 1 && (tmp == "wine.bin" || tmp == "wine"))
-                        {
-                            idx = cmdline[1].lastIndexOf('/');
-                            if (idx == -1)
-                                idx = cmdline[1].lastIndexOf('\\');
-                            if (idx != -1)
-                            {
-                                ret.append(cmdline[1].mid(idx+1));
-                            }
-                            else
-                                ret.append(cmdline[1]);
-                        }
-                        else
-                        {
-                            ret.append(tmp);
-                        }
-                    }
-                    else
-                        ret.append(cmdline[0]);
-                }
-            }
-            return ret;
-        }
-    }
-}
-
-#elif defined __linux
-
-#include <proc/readproc.h>
-#include <cerrno>
-template<typename = void>
-static QStringList get_all_executable_names()
-{
-    QStringList ret;
-    proc_t** procs = readproctab(PROC_FILLCOM);
-    if (procs == nullptr)
-    {
-        qDebug() << "readproctab" << errno;
-        return ret;
-    }
-    for (int i = 0; procs[i]; i++)
-    {
-        // note, wine sets argv[0] so no parsing like in OSX case
-        auto proc = procs[i];
-        if (proc->cmdline && proc->cmdline[0])
-        {
-            QString tmp(proc->cmdline[0]);
-            const int idx = std::max(tmp.lastIndexOf('\\'), tmp.lastIndexOf('/'));
-            tmp = tmp.mid(idx == -1 ? 0 : idx+1);
-            ret.append(tmp);
-        }
-        freeproc(procs[i]);
-    }
-    free(procs);
-    return ret;
-}
-
-#else
-template<typename = void>
-static QStringList get_all_executable_names()
-{
-    return QStringList();
-}
-#endif
diff --git a/opentrack-compat/qcopyable-mutex.hpp b/opentrack-compat/qcopyable-mutex.hpp
deleted file mode 100644
index 57b0030d..00000000
--- a/opentrack-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/opentrack-compat/shm.cpp b/opentrack-compat/shm.cpp
deleted file mode 100644
index 83a3260c..00000000
--- a/opentrack-compat/shm.cpp
+++ /dev/null
@@ -1,199 +0,0 @@
-/* Copyright (c) 2013 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.
- */
-
-#include "shm.h"
-
-#if defined(_WIN32)
-
-#include <cstring>
-#include <stdio.h>
-
-#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;
-    }
-
-    secattr(DWORD perms) : success(true), pSD(nullptr), pEveryoneSID(nullptr), pACL(nullptr)
-    {
-        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));
-
-        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)
-        {
-            fprintf(stderr, "LocalAlloc: %d\n", (int) GetLastError());
-            goto cleanup;
-        }
-
-        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);
-
-        fflush(stderr);
-
-        return;
-cleanup:
-        fflush(stderr);
-        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(
-                 INVALID_HANDLE_VALUE,
-                 sa.success ? &sa.attrs : nullptr,
-                 PAGE_READWRITE,
-                 0,
-                 mapSize,
-                 shmName);
-    if (!hMapFile)
-    {
-        fprintf(stderr, "CreateFileMappingA: %d\n", (int) GetLastError());
-        fflush(stderr);
-    }
-    mem = MapViewOfFile(hMapFile,
-                        FILE_MAP_WRITE,
-                        0,
-                        0,
-                        mapSize);
-    if (!mem)
-    {
-        fprintf(stderr, "MapViewOfFile: %d\n", (int) GetLastError());
-        fflush(stderr);
-    }
-}
-
-PortableLockedShm::~PortableLockedShm()
-{
-    UnmapViewOfFile(mem);
-    CloseHandle(hMapFile);
-    CloseHandle(hMutex);
-}
-
-void PortableLockedShm::lock()
-{
-    (void) WaitForSingleObject(hMutex, INFINITE);
-}
-
-void PortableLockedShm::unlock()
-{
-    (void) ReleaseMutex(hMutex);
-}
-#else
-
-#include <limits.h>
-
-#pragma GCC diagnostic ignored "-Wunused-result"
-PortableLockedShm::PortableLockedShm(const char *shmName, const char* /*mutexName*/, int mapSize) : size(mapSize)
-{
-    char filename[PATH_MAX+2] = {0};
-    strcpy(filename, "/");
-    strcat(filename, shmName);
-    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);
-}
-
-PortableLockedShm::~PortableLockedShm()
-{
-    (void) munmap(mem, size);
-    (void) close(fd);
-}
-
-void PortableLockedShm::lock()
-{
-    flock(fd, LOCK_EX);
-}
-
-void PortableLockedShm::unlock()
-{
-    flock(fd, LOCK_UN);
-}
-#endif
-
-bool PortableLockedShm::success()
-{
-#ifndef _WIN32
-    return mem != (void*) -1;
-#else
-    return mem != NULL;
-#endif
-}
diff --git a/opentrack-compat/shm.h b/opentrack-compat/shm.h
deleted file mode 100644
index f212dc17..00000000
--- a/opentrack-compat/shm.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/* Copyright (c) 2013 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
-
-#if defined(_WIN32)
-#include <windows.h>
-#else
-#include <stdio.h>
-#include <string.h>
-#include <sys/file.h>
-#include <sys/mman.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <unistd.h>
-#include <sys/types.h>
-#endif
-
-#ifdef __GNUC__
-#   pragma GCC diagnostic push
-#   pragma GCC diagnostic ignored "-Wattributes"
-#endif
-
-#include "export.hpp"
-
-class OPENTRACK_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:
-    void* mem;
-#if defined(_WIN32)
-    HANDLE hMutex, hMapFile;
-#else
-    int fd, size;
-#endif
-};
-
-#ifdef __GNUC__
-#   pragma GCC diagnostic pop
-#endif
diff --git a/opentrack-compat/sleep.hpp b/opentrack-compat/sleep.hpp
deleted file mode 100644
index fab27286..00000000
--- a/opentrack-compat/sleep.hpp
+++ /dev/null
@@ -1,22 +0,0 @@
-#pragma once
-
-#ifdef _WIN32
-#   include <windows.h>
-#else
-#   include <unistd.h>
-#endif
-
-namespace portable
-{
-#ifdef _WIN32
-    inline void sleep(unsigned milliseconds)
-    {
-        Sleep(milliseconds);
-    }
-#else
-    inline void sleep(unsigned milliseconds)
-    {
-        usleep(milliseconds * 1000U); // takes microseconds
-    }
-#endif
-}
diff --git a/opentrack-compat/slider.cpp b/opentrack-compat/slider.cpp
deleted file mode 100644
index be1aaeb8..00000000
--- a/opentrack-compat/slider.cpp
+++ /dev/null
@@ -1,73 +0,0 @@
-#include "slider.hpp"
-#include <cmath>
-
-namespace options {
-
-slider_value::slider_value(double cur, double min, double max) :
-    cur_(cur),
-    min_(min),
-    max_(max)
-{
-    if (min_ > max_)
-        min_ = max_;
-    if (cur_ > max_)
-        cur_ = max;
-    if (cur_ < min_)
-        cur_ = min_;
-}
-
-slider_value::slider_value(const slider_value& v) : slider_value(v.cur(), v.min(), v.max())
-{
-}
-
-slider_value::slider_value() : slider_value(0, 0, 0)
-{
-}
-
-slider_value& slider_value::operator=(const slider_value& v)
-{
-    cur_ = v.cur_;
-
-    min_ = v.min_;
-    max_ = v.max_;
-
-    return *this;
-}
-
-bool slider_value::operator==(const slider_value& v) const
-{
-    using std::fabs;
-
-    static constexpr double eps = 1e-3;
-
-    return (fabs(v.cur_ - cur_) < eps &&
-            fabs(v.min_ - min_) < eps &&
-            fabs(v.max_ - max_) < eps);
-}
-
-slider_value slider_value::update_from_slider(int pos, int q_min, int q_max) const
-{
-    slider_value v(*this);
-
-    const int q_diff = q_max - q_min;
-    const double sv_pos = q_diff == 0
-                          ? -1e6
-                          : (((pos - q_min) * (v.max() - v.min())) / q_diff + v.min());
-
-    if (sv_pos < v.min())
-        v = slider_value(v.min(), v.min(), v.max());
-    else if (sv_pos > v.max())
-        v = slider_value(v.max(), v.min(), v.max());
-    else
-        v = slider_value(sv_pos, v.min(), v.max());
-    return v;
-}
-
-int slider_value::to_slider_pos(int q_min, int q_max) const
-{
-    const int q_diff = q_max - q_min;
-
-    return int(std::round(((cur() - min() * q_diff) / (max() - min())) + q_min));
-}
-
-} // end ns options
diff --git a/opentrack-compat/slider.hpp b/opentrack-compat/slider.hpp
deleted file mode 100644
index 7d54c650..00000000
--- a/opentrack-compat/slider.hpp
+++ /dev/null
@@ -1,62 +0,0 @@
-/* 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.
- */
-
-#pragma once
-
-#include "export.hpp"
-#include <QMetaType>
-#include <QDataStream>
-#include <QDebug>
-
-namespace options
-{
-    class OPENTRACK_COMPAT_EXPORT slider_value final
-    {
-        double cur_, min_, max_;
-    public:
-        slider_value(double cur, double min, double max);
-        slider_value(const slider_value& v);
-        slider_value();
-        slider_value& operator=(const slider_value& v);
-        bool operator==(const slider_value& v) const;
-        operator double() const { return cur_; }
-        double cur() const { return cur_; }
-        double min() const { return min_; }
-        double max() const { return max_; }
-        slider_value update_from_slider(int pos, int q_min, int q_max) const;
-        int to_slider_pos(int q_min, int q_max) const;
-    };
-}
-
-QT_BEGIN_NAMESPACE
-
-inline QDebug operator << (QDebug dbg, const options::slider_value& val)
-{
-    return dbg << val.cur();
-}
-
-inline QDataStream& operator << (QDataStream& out, const options::slider_value& v)
-{
-    out << v.cur()
-        << v.min()
-        << v.max();
-    return out;
-}
-
-inline QDataStream& operator >> (QDataStream& in, options::slider_value& v)
-{
-    double cur, min, max;
-    in >> cur;
-    in >> min;
-    in >> max;
-    v = options::slider_value(cur, min, max);
-    return in;
-}
-
-QT_END_NAMESPACE
-
-Q_DECLARE_METATYPE(options::slider_value)
diff --git a/opentrack-compat/timer.hpp b/opentrack-compat/timer.hpp
deleted file mode 100644
index 300a883c..00000000
--- a/opentrack-compat/timer.hpp
+++ /dev/null
@@ -1,100 +0,0 @@
-/* Copyright (c) 2014-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 <ctime>
-
-#include "export.hpp"
-
-#if defined (_WIN32)
-#   include <windows.h>
-#	ifndef CLOCK_MONOTONIC
-#   	define CLOCK_MONOTONIC -1
-#	endif
-static inline void opentrack_clock_gettime(int, struct timespec* ts)
-{
-    static LARGE_INTEGER freq;
-
-    if (!freq.QuadPart)
-        (void) QueryPerformanceFrequency(&freq);
-
-    LARGE_INTEGER d;
-
-    (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);
-}
-#	define clock_gettime opentrack_clock_gettime
-#else
-#   if defined(__MACH__)
-#       define CLOCK_MONOTONIC 0
-#       include <inttypes.h>
-#       include <mach/mach_time.h>
-static inline void clock_gettime(int, struct timespec* ts)
-{
-    static mach_timebase_info_data_t    sTimebaseInfo;
-    uint64_t state, nsec;
-    if ( sTimebaseInfo.denom == 0 ) {
-        (void) mach_timebase_info(&sTimebaseInfo);
-    }
-    state = mach_absolute_time();
-    nsec = state * sTimebaseInfo.numer / sTimebaseInfo.denom;
-    ts->tv_sec = nsec / 1000000000L;
-    ts->tv_nsec = nsec % 1000000000L;
-}
-#   endif
-#endif
-class Timer
-{
-private:
-    struct timespec state;
-    long long conv_nsecs(const struct timespec& cur) const
-    {
-        return (cur.tv_sec - state.tv_sec) * 1000000000LL + (cur.tv_nsec - state.tv_nsec);
-    }
-    long conv_usecs(const struct timespec& cur) const
-    {
-        return long(cur.tv_sec - state.tv_sec) * 1000000L + long(cur.tv_nsec - state.tv_nsec) / 1000l;
-    }
-public:
-    Timer()
-    {
-        start();
-    }
-    void start()
-    {
-        clock_gettime(CLOCK_MONOTONIC, &state);
-    }
-    long long elapsed_nsecs() const
-    {
-        struct timespec cur;
-        clock_gettime(CLOCK_MONOTONIC, &cur);
-        return conv_nsecs(cur);
-    }
-    long elapsed_usecs() const
-    {
-        struct timespec cur;
-        clock_gettime(CLOCK_MONOTONIC, &cur);
-        return long(conv_usecs(cur));
-    }
-    long elapsed_ms() const
-    {
-        return elapsed_usecs() / 1000L;
-    }
-    double elapsed_seconds() const
-    {
-        return double(elapsed_nsecs() * 1e-9L);
-    }
-};
diff --git a/opentrack-compat/util.hpp b/opentrack-compat/util.hpp
deleted file mode 100644
index 1217e654..00000000
--- a/opentrack-compat/util.hpp
+++ /dev/null
@@ -1,93 +0,0 @@
-#pragma once
-
-#include "make-unique.hpp"
-
-#include <memory>
-#include <utility>
-#include <type_traits>
-#include <thread>
-#include <condition_variable>
-
-#include <QDebug>
-
-#define progn(...) ([&]() { __VA_ARGS__ }())
-template<typename t> using mem = std::shared_ptr<t>;
-template<typename t> using ptr = std::unique_ptr<t>;
-
-template<typename F>
-void run_in_thread_async(QObject* obj, F&& fun)
-{
-    QObject src;
-    src.moveToThread(obj->thread());
-    QObject::connect(&src, &QObject::destroyed, obj, std::move(fun), Qt::AutoConnection);
-}
-
-namespace detail {
-
-template<typename t>
-struct run_in_thread_traits
-{
-    using type = t;
-    using ret_type = t&&;
-    static inline void assign(t& lvalue, t&& rvalue) { lvalue = rvalue; }
-    static inline ret_type&& pass(ret_type&& val) { return std::move(val); }
-    template<typename F> static ret_type call(F& fun) { return std::move(fun()); }
-};
-
-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 std::move(type(0)); }
-};
-
-}
-
-template<typename F>
-auto run_in_thread_sync(QObject* obj, F&& fun)
-    -> typename detail::run_in_thread_traits<decltype(std::forward<F>(fun)())>::ret_type
-{
-    using lock_guard = std::unique_lock<std::mutex>;
-
-    std::mutex mtx;
-    lock_guard guard(mtx);
-    std::condition_variable cvar;
-
-    std::thread::id waiting_thread = std::this_thread::get_id();
-
-    using traits = detail::run_in_thread_traits<decltype(std::forward<F>(fun)())>;
-
-    typename traits::type ret;
-
-    bool skip_wait = false;
-
-    {
-        QObject src;
-        src.moveToThread(obj->thread());
-        QObject::connect(&src,
-                         &QObject::destroyed,
-                         obj,
-                         [&]() {
-            std::thread::id calling_thread = std::this_thread::get_id();
-            if (waiting_thread == calling_thread)
-            {
-                skip_wait = true;
-                traits::assign(ret, traits::call(fun));
-            }
-            else
-            {
-                lock_guard guard(mtx);
-                traits::assign(ret, traits::call(fun));
-                cvar.notify_one();
-            }
-        },
-        Qt::AutoConnection);
-    }
-
-    if (!skip_wait)
-        cvar.wait(guard);
-    return traits::pass(std::move(ret));
-}
diff --git a/opentrack-compat/win32-com.cpp b/opentrack-compat/win32-com.cpp
deleted file mode 100644
index dd7c24a8..00000000
--- a/opentrack-compat/win32-com.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-#ifdef _WIN32
-
-#include "win32-com.hpp"
-
-#include <QString>
-#include <QThread>
-#include <QDebug>
-
-bool OPENTRACK_COMPAT_EXPORT init_com_threading(com_type t)
-{
-    static thread_local com_type initialized = com_type(-1);
-
-    if (initialized != com_type(-1))
-    {
-        if (t != initialized)
-        {
-            QString tp("invalid type");
-            switch (t)
-            {
-            case com_apartment:
-                tp = "apartment threaded";
-                break;
-            case com_multithreaded:
-                tp = "multithreaded";
-                break;
-            }
-
-            qDebug() << "COM for thread"
-                     << QThread::currentThread() << QThread::currentThreadId()
-                     << "already initialized to" << tp;
-
-            return false;
-        }
-
-        return true;
-    }
-
-    HRESULT ret = CoInitializeEx(0, t);
-
-    if (ret != S_OK && ret != S_FALSE)
-    {
-        qDebug() << "CoInitializeEx failed:" << ret << GetLastError();
-        return false;
-    }
-
-    if (t == com_apartment)
-    {
-        ret = OleInitialize(nullptr);
-
-        if (ret != S_OK && ret != S_FALSE)
-            qDebug() << "OleInitialize() failed:" << ret << GetLastError();
-
-        return false;
-    }
-
-    initialized = t;
-
-    return true;
-}
-#endif
diff --git a/opentrack-compat/win32-com.hpp b/opentrack-compat/win32-com.hpp
deleted file mode 100644
index dcbea089..00000000
--- a/opentrack-compat/win32-com.hpp
+++ /dev/null
@@ -1,18 +0,0 @@
-#pragma once
-
-#ifdef _WIN32
-
-#include "export.hpp"
-
-#include <objbase.h>
-#include <ole2.h>
-
-enum com_type : int
-{
-    com_multithreaded = COINIT_MULTITHREADED,
-    com_apartment = COINIT_APARTMENTTHREADED,
-};
-
-bool OPENTRACK_COMPAT_EXPORT init_com_threading(com_type t = com_multithreaded);
-
-#endif
diff --git a/opentrack-dinput/CMakeLists.txt b/opentrack-dinput/CMakeLists.txt
deleted file mode 100644
index ed0b5990..00000000
--- a/opentrack-dinput/CMakeLists.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-if(WIN32)
-    opentrack_boilerplate(opentrack-dinput BIN)
-    target_link_libraries(opentrack-dinput dinput)
-endif()
diff --git a/opentrack-dinput/dinput.cpp b/opentrack-dinput/dinput.cpp
deleted file mode 100644
index ae132e70..00000000
--- a/opentrack-dinput/dinput.cpp
+++ /dev/null
@@ -1,98 +0,0 @@
-#ifdef _WIN32
-
-#include "dinput.hpp"
-#include "opentrack-compat/win32-com.hpp"
-#include <QDebug>
-
-std::atomic<int> dinput_handle::refcnt;
-std::atomic_flag dinput_handle::init_lock = ATOMIC_FLAG_INIT;
-dinput_handle::di_t dinput_handle::handle(dinput_handle::make_di());
-
-LPDIRECTINPUT8& dinput_handle::init_di()
-{
-    init_com_threading(com_multithreaded);
-
-    static LPDIRECTINPUT8 di_ = nullptr;
-    if (di_ == nullptr)
-    {
-        if (!SUCCEEDED(DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&di_, NULL)))
-        {
-            di_ = nullptr;
-        }
-    }
-    return di_;
-}
-
-dinput_handle::di_t dinput_handle::make_di()
-{
-    while (init_lock.test_and_set()) { /* busy loop */ }
-
-    LPDIRECTINPUT8& ret = init_di();
-
-    init_lock.clear();
-
-    return di_t(ret);
-}
-
-void dinput_handle::di_t::free_di()
-{
-    if (handle && *handle)
-        (*handle)->Release();
-    *handle = nullptr;
-    handle = nullptr;
-}
-
-void dinput_handle::di_t::ref_di()
-{
-    while (init_lock.test_and_set()) { /* busy loop */ }
-
-    const int refcnt_ = refcnt.fetch_add(1) + 1;
-    qDebug() << "start: dinput refcount now" << (refcnt_);
-
-    init_lock.clear();
-}
-
-dinput_handle::di_t& dinput_handle::di_t::operator=(const di_t& new_di)
-{
-    if (handle)
-        unref_di();
-
-    handle = new_di.handle;
-
-    if (handle)
-        ref_di();
-
-    return *this;
-}
-
-void dinput_handle::di_t::unref_di()
-{
-    while (init_lock.test_and_set()) { /* busy loop */ }
-
-    const int refcnt_ = refcnt.fetch_sub(1) - 1;
-
-    qDebug() << "exit: dinput refcount now" << refcnt_;
-
-    if (refcnt_ == 0)
-    {
-        qDebug() << "exit: deleting di handle";
-        free_di();
-    }
-
-    init_lock.clear();
-}
-
-dinput_handle::di_t::di_t(LPDIRECTINPUT8& handle) : handle(&handle)
-{
-    ref_di();
-}
-
-dinput_handle::di_t::di_t() : handle(nullptr) {}
-
-dinput_handle::di_t::~di_t()
-{
-    if (handle)
-        unref_di();
-}
-
-#endif
diff --git a/opentrack-dinput/dinput.hpp b/opentrack-dinput/dinput.hpp
deleted file mode 100644
index 53f1c4af..00000000
--- a/opentrack-dinput/dinput.hpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/* Copyright (c) 2016, 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
-
-#ifdef _WIN32
-
-#include "export.hpp"
-
-#ifndef DIRECTINPUT_VERSION
-#   define DIRECTINPUT_VERSION 0x800
-#endif
-#include <dinput.h>
-
-#include <atomic>
-
-class OPENTRACK_DINPUT_EXPORT dinput_handle final
-{
-public:
-    class di_t;
-
-private:
-    static std::atomic<int> refcnt;
-    static std::atomic_flag init_lock;
-    static di_t handle;
-
-    static LPDIRECTINPUT8& init_di();
-public:
-    class di_t final
-    {
-        friend class dinput_handle;
-
-        LPDIRECTINPUT8* handle;
-
-        di_t(LPDIRECTINPUT8& handle);
-        void free_di();
-        void unref_di();
-        void ref_di();
-
-    public:
-        LPDIRECTINPUT8 operator->() { return *handle; }
-        operator LPDIRECTINPUT8() { return *handle; }
-        LPDIRECTINPUT8 di() { return *handle; }
-        di_t& operator=(const di_t& new_di);
-        di_t();
-        ~di_t();
-    };
-
-    static di_t make_di();
-    dinput_handle() = delete;
-};
-
-#endif
diff --git a/opentrack-dinput/export.hpp b/opentrack-dinput/export.hpp
deleted file mode 100644
index 51ee4531..00000000
--- a/opentrack-dinput/export.hpp
+++ /dev/null
@@ -1,28 +0,0 @@
-#pragma once
-
-#ifdef BUILD_dinput
-#   ifdef _WIN32
-#       define OPENTRACK_DINPUT_LINKAGE __declspec(dllexport)
-#   else
-#       define OPENTRACK_DINPUT_LINKAGE
-#   endif
-
-#   ifndef _MSC_VER
-#       define OPENTRACK_DINPUT_EXPORT __attribute__ ((visibility ("default"))) OPENTRACK_DINPUT_LINKAGE
-#   else
-#       define OPENTRACK_DINPUT_EXPORT OPENTRACK_DINPUT_LINKAGE
-#   endif
-
-#else
-#ifdef _WIN32
-#    define OPENTRACK_DINPUT_LINKAGE __declspec(dllimport)
-#else
-#    define OPENTRACK_DINPUT_LINKAGE
-#endif
-
-#ifndef _MSC_VER
-#    define OPENTRACK_DINPUT_EXPORT __attribute__ ((visibility ("default"))) OPENTRACK_DINPUT_LINKAGE
-#else
-#    define OPENTRACK_DINPUT_EXPORT OPENTRACK_DINPUT_LINKAGE
-#endif
-#endif
diff --git a/opentrack-dinput/keybinding-worker.cpp b/opentrack-dinput/keybinding-worker.cpp
deleted file mode 100644
index 32bfc6f0..00000000
--- a/opentrack-dinput/keybinding-worker.cpp
+++ /dev/null
@@ -1,201 +0,0 @@
-/* Copyright (c) 2014-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.
- */
-
-#ifdef _WIN32
-
-#include "keybinding-worker.hpp"
-#include <functional>
-#include <windows.h>
-#include <QDebug>
-#include <QMutexLocker>
-
-bool Key::should_process()
-{
-    if (!enabled || (keycode == 0 && guid == ""))
-        return false;
-    bool ret = timer.elapsed_ms() > 100;
-    timer.start();
-    return ret;
-}
-
-KeybindingWorker::~KeybindingWorker()
-{
-    qDebug() << "exit: destroying keybinding worker";
-
-    should_quit = true;
-    wait();
-    if (dinkeyboard) {
-        dinkeyboard->Unacquire();
-        dinkeyboard->Release();
-    }
-}
-
-void KeybindingWorker::init()
-{
-    din = dinput_handle::make_di();
-
-    if (!din)
-    {
-        qDebug() << "can't create dinput handle";
-        return;
-    }
-
-    if (din->CreateDevice(GUID_SysKeyboard, &dinkeyboard, NULL) != DI_OK) {
-        qDebug() << "setup CreateDevice function failed!" << GetLastError();
-        return;
-    }
-
-    if (dinkeyboard->SetDataFormat(&c_dfDIKeyboard) != DI_OK) {
-        qDebug() << "setup SetDataFormat function failed!" << GetLastError();
-        dinkeyboard->Release();
-        dinkeyboard = 0;
-        return;
-    }
-
-    if (dinkeyboard->SetCooperativeLevel((HWND) fake_main_window.winId(), DISCL_NONEXCLUSIVE | DISCL_BACKGROUND) != DI_OK) {
-        dinkeyboard->Release();
-        dinkeyboard = 0;
-        qDebug() << "setup SetCooperativeLevel function failed!" << GetLastError();
-        return;
-    }
-
-    if (dinkeyboard->Acquire() != DI_OK)
-    {
-        dinkeyboard->Release();
-        dinkeyboard = 0;
-        qDebug() << "setup dinkeyboard Acquire failed!" << GetLastError();
-        return;
-    }
-}
-
-KeybindingWorker::KeybindingWorker() : should_quit(false)
-{
-    start();
-}
-
-KeybindingWorker& KeybindingWorker::make()
-{
-    static KeybindingWorker k;
-    return k;
-}
-
-void KeybindingWorker::run()
-{
-    init();
-
-    BYTE keystate[256] = {0};
-    BYTE old_keystate[256] = {0};
-
-    while (!should_quit)
-    {
-        {
-            QMutexLocker l(&mtx);
-
-            if (receivers.size())
-            {
-                {
-                    const HRESULT hr = dinkeyboard->GetDeviceState(256, (LPVOID)keystate);
-
-                    if (hr != DI_OK) {
-                        qDebug() << "Tracker::run GetDeviceState function failed!" << GetLastError();
-                        Sleep(25);
-                        continue;
-                    }
-                }
-
-                {
-                    using joy_fn = std::function<void(const QString& guid, int idx, bool held)>;
-
-                    joy_fn f = [&](const QString& guid, int idx, bool held) -> void {
-                        Key k;
-                        k.keycode = idx;
-                        k.shift = !!(keystate[DIK_LSHIFT] & 0x80 || keystate[DIK_RSHIFT] & 0x80);
-                        k.alt = !!(keystate[DIK_LALT] & 0x80 || keystate[DIK_RALT] & 0x80);
-                        k.ctrl = !!(keystate[DIK_LCONTROL] & 0x80 || keystate[DIK_RCONTROL] & 0x80);
-                        k.guid = guid;
-                        k.held = held;
-
-                        for (auto& r : receivers)
-                            r->operator()(k);
-                    };
-
-                    joy_ctx.poll(f);
-                }
-
-                for (int i = 0; i < 256; i++)
-                {
-                    Key k;
-                    if (old_keystate[i] != keystate[i])
-                    {
-                        const bool held = keystate[i] & 0x80;
-                        switch (i)
-                        {
-                        case DIK_LCONTROL:
-                        case DIK_LSHIFT:
-                        case DIK_LALT:
-                        case DIK_RCONTROL:
-                        case DIK_RSHIFT:
-                        case DIK_RALT:
-                            break;
-                        default:
-                            k.shift = !!(keystate[DIK_LSHIFT] & 0x80) || !!(keystate[DIK_RSHIFT] & 0x80);
-                            k.alt = !!(keystate[DIK_LALT] & 0x80) || !!(keystate[DIK_RALT] & 0x80);
-                            k.ctrl = !!(keystate[DIK_LCONTROL] & 0x80) || !!(keystate[DIK_RCONTROL] & 0x80);
-                            k.keycode = i;
-                            k.held = held;
-
-                            for (auto& r : receivers)
-                                r->operator()(k);
-                            break;
-                        }
-                    }
-                    old_keystate[i] = keystate[i];
-                }
-            }
-        }
-
-        // keypresses get dropped with high values
-        Sleep(4);
-    }
-}
-
-KeybindingWorker::fun* KeybindingWorker::_add_receiver(fun& receiver)
-{
-    QMutexLocker l(&mtx);
-    receivers.push_back(std::unique_ptr<fun>(new fun(receiver)));
-    fun* f = receivers[receivers.size() - 1].get();
-    //qDebug() << "add receiver" << (long) f;
-    joy_ctx.refresh();
-    return f;
-}
-
-void KeybindingWorker::remove_receiver(KeybindingWorker::fun* pos)
-{
-    QMutexLocker l(&mtx);
-    bool ok = false;
-
-    using s = int;
-
-    for (int i = s(receivers.size()) - 1; i >= 0; i--)
-    {
-        using u = unsigned;
-        if (receivers[u(i)].get() == pos)
-        {
-            ok = true;
-            //qDebug() << "remove receiver" << (long) pos;
-            receivers.erase(receivers.begin() + i);
-            break;
-        }
-    }
-    if (!ok)
-    {
-        qDebug() << "bad remove receiver" << (long) pos;
-    }
-}
-
-#endif
diff --git a/opentrack-dinput/keybinding-worker.hpp b/opentrack-dinput/keybinding-worker.hpp
deleted file mode 100644
index 623875ac..00000000
--- a/opentrack-dinput/keybinding-worker.hpp
+++ /dev/null
@@ -1,80 +0,0 @@
-/* Copyright (c) 2014-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 "export.hpp"
-
-#include "opentrack-compat/timer.hpp"
-#include "win32-joystick.hpp"
-#include "dinput.hpp"
-#include <QThread>
-#include <QMutex>
-#include <QWidget>
-#include <QMainWindow>
-#include <QDebug>
-#include <functional>
-#include <vector>
-
-struct OPENTRACK_DINPUT_EXPORT Key
-{
-    BYTE keycode;
-    QString guid;
-    bool shift;
-    bool ctrl;
-    bool alt;
-    bool held;
-    bool enabled;
-    Timer timer;
-public:
-    Key() : keycode(0), shift(false), ctrl(false), alt(false), held(true), enabled(true) {}
-
-    bool should_process();
-};
-
-struct OPENTRACK_DINPUT_EXPORT KeybindingWorker : private QThread
-{
-    using fun = std::function<void(const Key&)>;
-
-private:
-    LPDIRECTINPUTDEVICE8 dinkeyboard;
-    win32_joy_ctx joy_ctx;
-    std::vector<std::unique_ptr<fun>> receivers;
-    QMutex mtx;
-    QMainWindow fake_main_window;
-    dinput_handle::di_t din;
-    volatile bool should_quit;
-
-    void run() override;
-    void init();
-    KeybindingWorker();
-
-    static KeybindingWorker& make();
-    fun* _add_receiver(fun &receiver);
-    void remove_receiver(fun* pos);
-    ~KeybindingWorker();
-
-    KeybindingWorker(const KeybindingWorker&) = delete;
-    KeybindingWorker& operator=(KeybindingWorker&) = delete;
-public:
-    class Token
-    {
-        fun* pos;
-        Token(const Token&) = delete;
-        Token& operator=(Token&) = delete;
-    public:
-        ~Token()
-        {
-            make().remove_receiver(pos);
-        }
-        Token(fun receiver)
-        {
-            pos = make()._add_receiver(receiver);
-        }
-    };
-};
diff --git a/opentrack-dinput/win32-joystick.cpp b/opentrack-dinput/win32-joystick.cpp
deleted file mode 100644
index 18500a0d..00000000
--- a/opentrack-dinput/win32-joystick.cpp
+++ /dev/null
@@ -1,359 +0,0 @@
-#ifdef _WIN32
-
-#undef NDEBUG
-#include "win32-joystick.hpp"
-#include "opentrack-compat/sleep.hpp"
-#include <cassert>
-#include <cstring>
-#include <algorithm>
-#include <cmath>
-#include <objbase.h>
-
-#include <QDebug>
-
-QMutex win32_joy_ctx::enum_state::mtx;
-win32_joy_ctx::enum_state win32_joy_ctx::enumerator;
-
-void win32_joy_ctx::poll(fn f)
-{
-    //refresh(false);
-
-    QMutexLocker l(&enumerator.mtx);
-
-    auto& joys = enumerator.get_joys();
-
-    for (auto& j : joys)
-    {
-        j.second->poll(f);
-    }
-}
-
-bool win32_joy_ctx::poll_axis(const QString &guid, int* axes)
-{
-    QMutexLocker l(&enumerator.mtx);
-
-    for (int k = 0; k < 10; k++)
-    {
-        if (k > 0)
-            enumerator.refresh();
-
-        const joys_t& joys = enumerator.get_joys();
-        auto iter = joys.find(guid);
-
-        if (iter == joys.end())
-            return false;
-
-        auto& j = iter->second;
-
-        auto& joy_handle = j->joy_handle;
-        bool ok = false;
-        HRESULT hr;
-
-        if (!FAILED(hr = joy_handle->Poll()))
-        {
-            ok = true;
-        }
-
-        if (!ok && FAILED(hr = joy_handle->Acquire()))
-        {
-            //qDebug() << "joy acquire failed" << hr;
-        }
-
-        if (!ok)
-        {
-            portable::sleep(25);
-            (void) joy_handle->Unacquire();
-            continue;
-        }
-
-        DIJOYSTATE2 js;
-        std::memset(&js, 0, sizeof(js));
-
-        if (FAILED(hr = joy_handle->GetDeviceState(sizeof(js), &js)))
-        {
-            //qDebug() << "joy get state failed" << guid << hr;
-            portable::sleep(50);
-            continue;
-        }
-
-        const int values[] =
-        {
-            js.lX,
-            js.lY,
-            js.lZ,
-            js.lRx,
-            js.lRy,
-            js.lRz,
-            js.rglSlider[0],
-            js.rglSlider[1]
-        };
-
-        for (int i = 0; i < 8; i++)
-            axes[i] = values[i];
-
-        return true;
-    }
-
-    return false;
-}
-
-std::vector<win32_joy_ctx::joy_info> win32_joy_ctx::get_joy_info()
-{
-    std::vector<joy_info> ret;
-    QMutexLocker l(&enumerator.mtx);
-    auto& joys = enumerator.get_joys();
-    ret.reserve(joys.size());
-
-    for (auto& j : joys)
-        ret.push_back(joy_info { j.second->name, j.first });
-
-    std::sort(ret.begin(), ret.end(), [&](const joy_info& fst, const joy_info& snd) -> bool { return fst.name < snd.name; });
-
-    return ret;
-}
-
-win32_joy_ctx::win32_joy_ctx()
-{
-    refresh();
-}
-
-void win32_joy_ctx::refresh()
-{
-    QMutexLocker l(&enumerator.mtx);
-    enumerator.refresh();
-}
-
-QString win32_joy_ctx::guid_to_string(const GUID& guid)
-{
-    char buf[40] = {0};
-    wchar_t szGuidW[40] = {0};
-
-    StringFromGUID2(guid, szGuidW, 40);
-    WideCharToMultiByte(0, 0, szGuidW, -1, buf, 40, NULL, NULL);
-
-    return QString(buf);
-}
-
-using fn = win32_joy_ctx::fn;
-
-void win32_joy_ctx::joy::release()
-{
-    if (joy_handle)
-    {
-        (void) joy_handle->Unacquire();
-        joy_handle->Release();
-        joy_handle = nullptr;
-    }
-}
-
-bool win32_joy_ctx::joy::poll(fn f)
-{
-    HRESULT hr;
-    bool ok = false;
-
-    (void) joy_handle->Acquire();
-
-    if (!FAILED(hr = joy_handle->Poll()))
-        ok = true;
-
-    if (!ok)
-    {
-        qDebug() << "joy acquire failed" << guid << hr;
-        (void) joy_handle->Unacquire();
-        return false;
-    }
-
-    DIJOYSTATE2 js;
-    std::memset(&js, 0, sizeof(js));
-
-    if (FAILED(hr = joy_handle->GetDeviceState(sizeof(js), &js)))
-    {
-        qDebug() << "joy get state failed" << guid << hr;
-        return false;
-    }
-
-    for (unsigned i = 0; i < 4; i++)
-    {
-        using std::round;
-
-        unsigned char pos;
-        unsigned pos_ = js.rgdwPOV[i];
-        if ((pos_ & 0xffff) == 0xffff)
-            pos = 0;
-        else if (pos_ == ~0u)
-            pos = 0;
-        else
-        {
-            using uc = unsigned char;
-            pos = uc(((pos_ / 9000u) % 4u) + 1u);
-        }
-
-        const bool state[] =
-        {
-            pos == 1,
-            pos == 2,
-            pos == 3,
-            pos == 4
-        };
-
-        unsigned idx = 128u + i * 4u;
-
-        for (unsigned j = 0; j < 4; j++, idx++)
-        {
-            if (state[j] != pressed[idx])
-            {
-                f(guid, int(idx), state[j]);
-                pressed[idx] = state[j];
-            }
-        }
-    }
-
-    for (int i = 0; i < 128; i++)
-    {
-        const bool state = !!(js.rgbButtons[i] & 0x80);
-        if (state != pressed[i])
-        {
-            f(guid, i, state);
-        }
-        pressed[i] = state;
-    }
-
-    return true;
-}
-
-win32_joy_ctx::enum_state::enum_state() : di(dinput_handle::make_di())
-{
-}
-
-win32_joy_ctx::enum_state::~enum_state()
-{
-    QMutexLocker l(&mtx);
-
-    joys = std::unordered_map<QString, std::shared_ptr<joy>>();
-}
-
-void win32_joy_ctx::enum_state::refresh()
-{
-    all.clear();
-
-    if (!di)
-    {
-        qDebug() << "can't create dinput";
-        return;
-    }
-
-    HRESULT hr;
-
-    if(FAILED(hr = di->EnumDevices(DI8DEVCLASS_GAMECTRL,
-                                   EnumJoysticksCallback,
-                                   this,
-                                   DIEDFL_ATTACHEDONLY)))
-    {
-        qDebug() << "failed enum joysticks" << hr;
-        return;
-    }
-
-    for (auto it = joys.begin(); it != joys.end(); )
-    {
-        if (std::find_if(all.cbegin(), all.cend(), [&](const QString& guid2) -> bool { return it->second->guid == guid2; }) == all.end())
-        {
-            it = joys.erase(it);
-        }
-        else
-        {
-            ++it;
-        }
-    }
-}
-
-const win32_joy_ctx::joys_t& win32_joy_ctx::enum_state::get_joys() const { return joys; }
-
-BOOL CALLBACK win32_joy_ctx::enum_state::EnumJoysticksCallback(const DIDEVICEINSTANCE *pdidInstance, void *pContext)
-{
-    enum_state& state = *reinterpret_cast<enum_state*>(pContext);
-    const QString guid = guid_to_string(pdidInstance->guidInstance);
-    const QString name = QString(pdidInstance->tszInstanceName);
-
-    const bool exists = state.joys.find(guid) != state.joys.end();
-
-    state.all.push_back(guid);
-
-    if (exists)
-        goto end;
-
-    {
-        HRESULT hr;
-        LPDIRECTINPUTDEVICE8 h;
-        if (FAILED(hr = state.di->CreateDevice(pdidInstance->guidInstance, &h, nullptr)))
-        {
-            qDebug() << "createdevice" << guid << hr;
-            goto end;
-        }
-        if (FAILED(h->SetDataFormat(&c_dfDIJoystick2)))
-        {
-            qDebug() << "format";
-            h->Release();
-            goto end;
-        }
-
-        // not a static member - need main() to run for some time first
-        static const QWidget fake_window;
-
-        if (FAILED(h->SetCooperativeLevel(reinterpret_cast<HWND>(fake_window.winId()), DISCL_NONEXCLUSIVE | DISCL_BACKGROUND)))
-        {
-            qDebug() << "coop";
-            h->Release();
-            goto end;
-        }
-        if (FAILED(hr = h->EnumObjects(EnumObjectsCallback, h, DIDFT_ALL)))
-        {
-            qDebug() << "enum-objects";
-            h->Release();
-            goto end;
-        }
-
-        state.joys[guid] = std::make_shared<joy>(h, guid, name);
-    }
-end:
-    return DIENUM_CONTINUE;
-}
-
-BOOL CALLBACK win32_joy_ctx::enum_state::EnumObjectsCallback(const DIDEVICEOBJECTINSTANCE *pdidoi, void *ctx)
-{
-    if (pdidoi->dwType & DIDFT_AXIS)
-    {
-        DIPROPRANGE diprg;
-        std::memset(&diprg, 0, sizeof(diprg));
-        diprg.diph.dwSize = sizeof( DIPROPRANGE );
-        diprg.diph.dwHeaderSize = sizeof( DIPROPHEADER );
-        diprg.diph.dwHow = DIPH_BYID;
-        diprg.diph.dwObj = pdidoi->dwType;
-        diprg.lMax = joy_axis_size;
-        diprg.lMin = -joy_axis_size;
-
-        HRESULT hr;
-
-        if (FAILED(hr = reinterpret_cast<LPDIRECTINPUTDEVICE8>(ctx)->SetProperty(DIPROP_RANGE, &diprg.diph)))
-        {
-            qDebug() << "DIPROP_RANGE" << hr;
-            return DIENUM_STOP;
-        }
-    }
-
-    return DIENUM_CONTINUE;
-}
-
-win32_joy_ctx::joy::joy(LPDIRECTINPUTDEVICE8 handle, const QString &guid, const QString &name)
-    : joy_handle(handle), guid(guid), name(name)
-{
-    qDebug() << "make joy" << guid << name << joy_handle;
-    std::memset(pressed, 0, sizeof(pressed));
-}
-
-win32_joy_ctx::joy::~joy()
-{
-    qDebug() << "nix joy" << guid;
-    release();
-}
-
-#endif
diff --git a/opentrack-dinput/win32-joystick.hpp b/opentrack-dinput/win32-joystick.hpp
deleted file mode 100644
index ef8f59b0..00000000
--- a/opentrack-dinput/win32-joystick.hpp
+++ /dev/null
@@ -1,101 +0,0 @@
-/* Copyright (c) 2015-2016, 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
-
-#ifdef _WIN32
-
-#include "dinput.hpp"
-#include "opentrack-compat/timer.hpp"
-#include "export.hpp"
-#include <cstring>
-#include <memory>
-#include <vector>
-#include <functional>
-#include <algorithm>
-#include <unordered_map>
-#include <QString>
-#include <QDebug>
-#include <QMutex>
-#include <QMutexLocker>
-#include <QWidget>
-
-namespace std {
-template<>
-struct hash<QString>
-{
-    inline std::size_t operator()(const QString& value) const
-    {
-        return qHash(value);
-    }
-};
-}
-
-struct OPENTRACK_DINPUT_EXPORT win32_joy_ctx
-{
-    using fn = std::function<void(const QString& guid, int btn, bool held)>;
-
-    struct joy
-    {
-        LPDIRECTINPUTDEVICE8 joy_handle;
-        QString guid, name;
-        bool pressed[128 + 4 * 4];
-        Timer first_timer;
-
-        joy(LPDIRECTINPUTDEVICE8 handle, const QString& guid, const QString& name);
-        ~joy();
-
-        void release();
-        bool poll(fn f);
-    };
-
-    using joys_t = std::unordered_map<QString, std::shared_ptr<joy>>;
-
-    static constexpr int joy_axis_size = 65535;
-
-    struct joy_info
-    {
-        QString name, guid;
-    };
-
-    void poll(fn f);
-    bool poll_axis(const QString& guid, int* axes);
-    std::vector<joy_info> get_joy_info();
-
-    win32_joy_ctx(const win32_joy_ctx&) = delete;
-    win32_joy_ctx& operator=(const win32_joy_ctx&) = delete;
-
-    win32_joy_ctx();
-    void refresh();
-
-    using di_t = dinput_handle::di_t;
-
-private:
-    static QString guid_to_string(const GUID& guid);
-
-    class OPENTRACK_DINPUT_EXPORT enum_state final
-    {
-        std::vector<QString> all;
-        joys_t joys;
-        dinput_handle::di_t di;
-
-        static BOOL CALLBACK EnumJoysticksCallback(const DIDEVICEINSTANCE* pdidInstance, VOID* pContext);
-        static BOOL CALLBACK EnumObjectsCallback(const DIDEVICEOBJECTINSTANCE* pdidoi, VOID* ctx);
-
-    public:
-        static QMutex mtx;
-
-        enum_state();
-        ~enum_state();
-        void refresh();
-        const joys_t& get_joys() const;
-    };
-
-    static enum_state enumerator;
-};
-
-#endif
diff --git a/opentrack-logic/CMakeLists.txt b/opentrack-logic/CMakeLists.txt
deleted file mode 100644
index 88b0240f..00000000
--- a/opentrack-logic/CMakeLists.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-opentrack_boilerplate(opentrack-logic BIN)
-target_link_libraries(opentrack-logic opentrack-spline-widget)
-if(NOT WIN32)
-    target_link_libraries(opentrack-logic opentrack-qxt-mini)
-else()
-    target_link_libraries(opentrack-logic opentrack-dinput winmm)
-endif()
diff --git a/opentrack-logic/export.hpp b/opentrack-logic/export.hpp
deleted file mode 100644
index 2503f3a6..00000000
--- a/opentrack-logic/export.hpp
+++ /dev/null
@@ -1,28 +0,0 @@
-#pragma once
-
-#ifdef BUILD_logic
-#   ifdef _WIN32
-#       define OPENTRACK_LOGIC_LINKAGE __declspec(dllexport)
-#   else
-#       define OPENTRACK_LOGIC_LINKAGE
-#   endif
-
-#   ifndef _MSC_VER
-#       define OPENTRACK_LOGIC_EXPORT __attribute__ ((visibility ("default"))) OPENTRACK_LOGIC_LINKAGE
-#   else
-#       define OPENTRACK_LOGIC_EXPORT OPENTRACK_LOGIC_LINKAGE
-#   endif
-
-#else
-#ifdef _WIN32
-#    define OPENTRACK_LOGIC_LINKAGE __declspec(dllimport)
-#else
-#    define OPENTRACK_LOGIC_LINKAGE
-#endif
-
-#ifndef _MSC_VER
-#    define OPENTRACK_LOGIC_EXPORT __attribute__ ((visibility ("default"))) OPENTRACK_LOGIC_LINKAGE
-#else
-#    define OPENTRACK_LOGIC_EXPORT OPENTRACK_LOGIC_LINKAGE
-#endif
-#endif
diff --git a/opentrack-logic/main-settings.hpp b/opentrack-logic/main-settings.hpp
deleted file mode 100644
index 29f7f62c..00000000
--- a/opentrack-logic/main-settings.hpp
+++ /dev/null
@@ -1,109 +0,0 @@
-/* Copyright (c) 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 "opentrack-compat/options.hpp"
-#include "opentrack/plugin-api.hpp"
-
-using namespace options;
-
-#include "export.hpp"
-
-struct axis_opts
-{
-    pbundle b;
-    value<double> zero;
-    value<bool> invert, altp;
-    value<int> src;
-    axis_opts(pbundle b, QString pfx, int idx) :
-        b(b),
-        zero(b, n(pfx, "zero-pos"), 0),
-        invert(b, n(pfx, "invert-sign"), false),
-        altp(b, n(pfx, "alt-axis-sign"), false),
-        src(b, n(pfx, "source-index"), idx)
-    {}
-private:
-    static inline QString n(QString pfx, QString name) {
-        return QString("%1-%2").arg(pfx, name);
-    }
-};
-
-struct key_opts
-{
-    value<QString> keycode, guid;
-    value<int> button;
-
-    key_opts(pbundle b, const QString& name) :
-        keycode(b, QString("keycode-%1").arg(name), ""),
-        guid(b, QString("guid-%1").arg(name), ""),
-        button(b, QString("button-%1").arg(name), -1)
-    {}
-};
-
-struct module_settings
-{
-    pbundle b;
-    value<QString> tracker_dll, filter_dll, protocol_dll;
-    module_settings() :
-        b(bundle("modules")),
-        tracker_dll(b, "tracker-dll", ""),
-        filter_dll(b, "filter-dll", "Accela"),
-        protocol_dll(b, "protocol-dll", "freetrack 2.0 Enhanced")
-    {
-    }
-};
-
-struct main_settings
-{
-    pbundle b, b_map;
-    axis_opts a_x, a_y, a_z, a_yaw, a_pitch, a_roll;
-    value<bool> tcomp_p, tcomp_tz;
-    value<bool> tray_enabled;
-    value<int> camera_yaw, camera_pitch, camera_roll;
-    value<bool> use_camera_offset_from_centering;
-    value<bool> center_at_startup;
-    value<int> center_method;
-    key_opts key_start_tracking, key_stop_tracking, key_toggle_tracking, key_restart_tracking;
-    key_opts key_center, key_toggle, key_zero;
-    key_opts key_toggle_press, key_zero_press;
-    value<bool> tracklogging_enabled;
-    value<QString> tracklogging_filename;
-    main_settings() :
-        b(bundle("opentrack-ui")),
-        b_map(bundle("opentrack-mappings")),
-        a_x(b_map, "x", TX),
-        a_y(b_map, "y", TY),
-        a_z(b_map, "z", TZ),
-        a_yaw(b_map, "yaw", Yaw),
-        a_pitch(b_map, "pitch", Pitch),
-        a_roll(b_map, "roll", Roll),
-        tcomp_p(b, "compensate-translation", true),
-        tcomp_tz(b, "compensate-translation-disable-z-axis", false),
-        tray_enabled(b, "use-system-tray", false),
-        camera_yaw(b, "camera-yaw", 0),
-        camera_pitch(b, "camera-pitch", 0),
-        camera_roll(b, "camera-roll", 0),
-        use_camera_offset_from_centering(b, "use-camera-offset-from-centering", false),
-        center_at_startup(b, "center-at-startup", true),
-        center_method(b, "centering-method", true),
-        key_start_tracking(b, "start-tracking"),
-        key_stop_tracking(b, "stop-tracking"),
-        key_toggle_tracking(b, "toggle-tracking"),
-        key_restart_tracking(b, "restart-tracking"),
-        key_center(b, "center"),
-        key_toggle(b, "toggle"),
-        key_zero(b, "zero"),
-        key_toggle_press(b, "toggle-press"),
-        key_zero_press(b, "zero-press"),
-        tracklogging_enabled(b, "tracklogging-enabled", false),
-        tracklogging_filename(b, "tracklogging-filename", QString())
-    {
-    }
-};
diff --git a/opentrack-logic/mappings.hpp b/opentrack-logic/mappings.hpp
deleted file mode 100644
index 3e396980..00000000
--- a/opentrack-logic/mappings.hpp
+++ /dev/null
@@ -1,89 +0,0 @@
-/* Copyright (c) 2014-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 <QSettings>
-#include "opentrack-compat/options.hpp"
-using namespace options;
-#include "spline-widget/spline.hpp"
-#include "main-settings.hpp"
-
-class Map {
-public:
-    Map(QString primary,
-            QString secondary,
-            int max_x,
-            int max_y,
-            axis_opts& opts) :
-        spline_main(max_x, max_y),
-        spline_alt(max_x, max_y),
-        opts(opts),
-        name1(primary),
-        name2(secondary)
-    {
-        mem<QSettings> iniFile = group::ini_file();
-        spline_main.loadSettings(*iniFile, primary);
-        spline_alt.loadSettings(*iniFile, secondary);
-    }
-    spline spline_main;
-    spline spline_alt;
-    axis_opts& opts;
-    QString name1, name2;
-};
-
-class Mappings {
-private:
-    Map axes[6];
-public:
-    Mappings(std::vector<axis_opts*> opts) :
-        axes {
-            Map("tx","tx_alt", 30, 75, *opts[TX]),
-            Map("ty","ty_alt", 30, 75, *opts[TY]),
-            Map("tz","tz_alt", 30, 75, *opts[TZ]),
-            Map("rx", "rx_alt", 180, 180, *opts[Yaw]),
-            Map("ry", "ry_alt", 180, 180, *opts[Pitch]),
-            Map("rz", "rz_alt", 180, 180, *opts[Roll])
-        }
-    {}
-
-    inline Map& operator()(int i) { return axes[i]; }
-    inline const Map& operator()(int i) const { return axes[i]; }
-
-    void load_mappings()
-    {
-        mem<QSettings> iniFile = group::ini_file();
-
-        for (int i = 0; i < 6; i++)
-        {
-            axes[i].spline_main.loadSettings(*iniFile, axes[i].name1);
-            axes[i].spline_alt.loadSettings(*iniFile, axes[i].name2);
-            axes[i].opts.b->reload();
-        }
-    }
-    void save_mappings()
-    {
-        mem<QSettings> iniFile = group::ini_file();
-
-        for (int i = 0; i < 6; i++)
-        {
-            axes[i].spline_main.saveSettings(*iniFile, axes[i].name1);
-            axes[i].spline_alt.saveSettings(*iniFile, axes[i].name2);
-            axes[i].opts.b->save();
-        }
-    }
-
-    void invalidate_unsaved()
-    {
-        for (int i = 0; i < 6; i++)
-        {
-            axes[i].spline_main.invalidate_unsaved_settings();
-            axes[i].spline_alt.invalidate_unsaved_settings();
-            axes[i].opts.b->reload();
-        }
-    }
-};
diff --git a/opentrack-logic/selected-libraries.cpp b/opentrack-logic/selected-libraries.cpp
deleted file mode 100644
index 4a1a9f09..00000000
--- a/opentrack-logic/selected-libraries.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-#include "selected-libraries.hpp"
-#include <QDebug>
-
-SelectedLibraries::SelectedLibraries(QFrame* frame, dylibptr t, dylibptr p, dylibptr f) :
-    pTracker(nullptr),
-    pFilter(nullptr),
-    pProtocol(nullptr),
-    correct(false)
-{
-    pProtocol = make_dylib_instance<IProtocol>(p);
-
-    if (!pProtocol)
-    {
-        qDebug() << "protocol dylib load failure";
-        return;
-    }
-
-    if(!pProtocol->correct())
-    {
-        qDebug() << "protocol load failure";
-        pProtocol = nullptr;
-        return;
-    }
-
-    pTracker = make_dylib_instance<ITracker>(t);
-    pFilter = make_dylib_instance<IFilter>(f);
-
-    if (!pTracker)
-    {
-        qDebug() << "tracker dylib load failure";
-        return;
-    }
-
-    pTracker->start_tracker(frame);
-
-    correct = true;
-}
diff --git a/opentrack-logic/selected-libraries.hpp b/opentrack-logic/selected-libraries.hpp
deleted file mode 100644
index 2538adff..00000000
--- a/opentrack-logic/selected-libraries.hpp
+++ /dev/null
@@ -1,25 +0,0 @@
-/* Copyright (c) 2014-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 "opentrack/plugin-support.hpp"
-#include <QFrame>
-
-#include "export.hpp"
-
-struct OPENTRACK_LOGIC_EXPORT SelectedLibraries
-{
-    using dylibptr = mem<dylib>;
-    mem<ITracker> pTracker;
-    mem<IFilter> pFilter;
-    mem<IProtocol> pProtocol;
-    SelectedLibraries(QFrame* frame, dylibptr t, dylibptr p, dylibptr f);
-    SelectedLibraries() : pTracker(nullptr), pFilter(nullptr), pProtocol(nullptr), correct(false) {}
-    bool correct;
-};
diff --git a/opentrack-logic/shortcuts.cpp b/opentrack-logic/shortcuts.cpp
deleted file mode 100644
index 8d818180..00000000
--- a/opentrack-logic/shortcuts.cpp
+++ /dev/null
@@ -1,130 +0,0 @@
-/* Copyright (c) 2014-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.
- */
-
-#include "shortcuts.h"
-#include "win32-shortcuts.h"
-
-void Shortcuts::free_binding(K& key)
-{
-#ifndef _WIN32
-    if (key)
-    {
-        key->setEnabled(false);
-        key->setShortcut(QKeySequence::UnknownKey);
-        std::shared_ptr<QxtGlobalShortcut> tmp(nullptr);
-        key.swap(tmp);
-    }
-#else
-    key.keycode = 0;
-    key.guid = "";
-#endif
-}
-
-void Shortcuts::bind_shortcut(K &key, const key_opts& k, unused_on_unix(bool, held))
-{
-#if !defined(_WIN32)
-    using sh = QxtGlobalShortcut;
-    if (key)
-    {
-        free_binding(key);
-    }
-
-    key = std::make_shared<sh>();
-
-    if (k.keycode != "")
-    {
-        key->setShortcut(QKeySequence::fromString(k.keycode, QKeySequence::PortableText));
-        key->setEnabled();
-    }
-}
-#else
-    key = K();
-    int idx = 0;
-    QKeySequence code;
-
-    if (k.guid != "")
-    {
-        key.guid = k.guid;
-        key.keycode = k.button & ~Qt::KeyboardModifierMask;
-        key.ctrl = !!(k.button & Qt::ControlModifier);
-        key.alt = !!(k.button & Qt::AltModifier);
-        key.shift = !!(k.button & Qt::ShiftModifier);
-    }
-    else
-    {
-        if (k.keycode == "")
-            code = QKeySequence(Qt::Key_unknown);
-        else
-            code = QKeySequence::fromString(k.keycode, QKeySequence::PortableText);
-
-        Qt::KeyboardModifiers mods = Qt::NoModifier;
-        if (code != Qt::Key_unknown)
-            win_key::from_qt(code, idx, mods);
-        key.shift = !!(mods & Qt::ShiftModifier);
-        key.alt = !!(mods & Qt::AltModifier);
-        key.ctrl = !!(mods & Qt::ControlModifier);
-        key.keycode = idx;
-        key.held = held;
-    }
-}
-#endif
-
-#ifdef _WIN32
-void Shortcuts::receiver(const Key& k)
-{
-    const unsigned sz = keys.size();
-    for (unsigned i = 0; i < sz; i++)
-    {
-        K& k_ = std::get<0>(keys[i]);
-        auto& fun = std::get<1>(keys[i]);
-        if (k.guid != k_.guid)
-            continue;
-        if (k.keycode != k_.keycode)
-            continue;
-        if (k_.held && !k.held) continue;
-        if (k_.alt != k.alt) continue;
-        if (k_.ctrl != k.ctrl) continue;
-        if (k_.shift != k.shift) continue;
-        if (!k_.should_process())
-            continue;
-
-        fun(k.held);
-    }
-}
-#endif
-
-void Shortcuts::reload(const t_keys& keys_)
-{
-    const unsigned sz = keys_.size();
-    keys = std::vector<tt>();
-
-    for (unsigned i = 0; i < sz; i++)
-    {
-        const auto& kk = keys_[i];
-        const key_opts& opts = std::get<0>(kk);
-        const bool held = std::get<2>(kk);
-        auto fun = std::get<1>(kk);
-        K k;
-        bind_shortcut(k, opts, held);
-        keys.push_back(tt(k, [=](unused_on_unix(bool, flag)) -> void
-        {
-#ifdef _WIN32
-            fun(flag);
-#else
-            fun(true);
-#endif
-        },
-        held));
-#ifndef _WIN32
-        const int idx = keys.size() - 1;
-        tt& kk_ = keys[idx];
-        auto& fn = std::get<1>(kk_);
-        connect(k.get(), &QxtGlobalShortcut::activated, [=]() -> void { fn(true); });
-#endif
-    }
-}
diff --git a/opentrack-logic/shortcuts.h b/opentrack-logic/shortcuts.h
deleted file mode 100644
index b44864ba..00000000
--- a/opentrack-logic/shortcuts.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/* Copyright (c) 2014-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 <QObject>
-#include <tuple>
-#include <vector>
-#include <functional>
-
-#include "export.hpp"
-
-#include "qxt-mini/QxtGlobalShortcut"
-#include "opentrack-compat/options.hpp"
-#include "main-settings.hpp"
-
-#ifdef _WIN32
-#   include "opentrack-dinput/keybinding-worker.hpp"
-#endif
-
-#if defined(__GNUC__) && !defined(_WIN32)
-#   define unused_on_unix(t, i) t __attribute__((unused)) i
-#else
-#   define unused_on_unix(t, i) t i
-#endif
-
-using namespace options;
-
-struct OPENTRACK_LOGIC_EXPORT Shortcuts : public QObject
-{
-    Q_OBJECT
-
-public:
-    using K =
-#ifndef _WIN32
-    mem<QxtGlobalShortcut>
-#else
-    Key
-#endif
-    ;
-
-    using fun = std::function<void(bool)>;
-    using tt = std::tuple<K, fun, bool>;
-    using t_key = std::tuple<key_opts&, fun, bool>;
-    using t_keys = std::vector<t_key>;
-    std::vector<tt> keys;
-#ifdef _WIN32
-    KeybindingWorker::Token key_token;
-#endif
-
-    Shortcuts()
-#ifdef _WIN32
-        : key_token([&](const Key& k) { receiver(k); })
-#endif
-    {}
-
-    void reload(const t_keys& keys_);
-private:
-    void free_binding(K& key);
-    void bind_shortcut(K &key, const key_opts& k, bool held);
-#ifdef _WIN32
-    void receiver(const Key& k);
-#endif
-};
diff --git a/opentrack-logic/simple-mat.cpp b/opentrack-logic/simple-mat.cpp
deleted file mode 100644
index 8c97f192..00000000
--- a/opentrack-logic/simple-mat.cpp
+++ /dev/null
@@ -1,97 +0,0 @@
-#include "simple-mat.hpp"
-#include "opentrack-compat/pi-constant.hpp"
-#include <cmath>
-
-namespace euler {
-
-euler_t OPENTRACK_LOGIC_EXPORT 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)));
-    else
-        return euler_t(atan2(R(0, 1), R(1, 1)),
-                       atan2(R(2, 0), cy),
-                       0);
-}
-
-// tait-bryan angles, not euler
-rmat OPENTRACK_LOGIC_EXPORT euler_to_rmat(const euler_t& 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);
-    const auto s2 = sin(P);
-    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 OPENTRACK_LOGIC_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);
-    }
-}
-
-} // end ns euler
diff --git a/opentrack-logic/simple-mat.hpp b/opentrack-logic/simple-mat.hpp
deleted file mode 100644
index 514e845f..00000000
--- a/opentrack-logic/simple-mat.hpp
+++ /dev/null
@@ -1,279 +0,0 @@
-/* Copyright (c) 2014-2016, 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 "export.hpp"
-
-#include <initializer_list>
-#include <type_traits>
-#include <utility>
-
-namespace {
-    // 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>
-    struct maybe_add_swizzle
-    {
-        enum { value = (i == 1 || j == 1) && (i >= min || j >= min) };
-    };
-    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<typename num, int h, int w, typename...ts>
-    struct is_arglist_correct
-    {
-        enum { value = h * w == sizeof...(ts) };
-    };
-}
-
-template<typename num, int h_, int w_>
-class Mat
-{
-    static_assert(h_ > 0 && w_ > 0, "must have positive mat dimensions");
-    num data[h_][w_];
-
-public:
-    template<int Q = w_> typename std::enable_if<equals<Q, 1, 0>::value, num>::type
-    inline operator()(int i) const { return data[i][0]; }
-
-    template<int P = h_> typename std::enable_if<equals<P, 1, 1>::value, num>::type
-    inline operator()(int i) const { return data[0][i]; }
-
-    template<int Q = w_> typename std::enable_if<equals<Q, 1, 2>::value, num&>::type
-    inline operator()(int i) { return data[i][0]; }
-
-    template<int P = h_> typename std::enable_if<equals<P, 1, 3>::value, num&>::type
-    inline operator()(int i) { return data[0][i]; }
-
-#define OPENTRACK_ASSERT_SWIZZLE static_assert(P == h_ && Q == w_, "")
-
-    template<int P = h_, int Q = w_> typename std::enable_if<maybe_add_swizzle<P, Q, 1>::value, num>::type
-    x() const { OPENTRACK_ASSERT_SWIZZLE; return operator()(0); }
-
-    template<int P = h_, int Q = w_> typename std::enable_if<maybe_add_swizzle<P, Q, 2>::value, num>::type
-    y() const { OPENTRACK_ASSERT_SWIZZLE; return operator()(1); }
-
-    template<int P = h_, int Q = w_> typename std::enable_if<maybe_add_swizzle<P, Q, 3>::value, num>::type
-    z() const { OPENTRACK_ASSERT_SWIZZLE; return operator()(2); }
-
-    template<int P = h_, int Q = w_> typename std::enable_if<maybe_add_swizzle<P, Q, 4>::value, num>::type
-    w() const { OPENTRACK_ASSERT_SWIZZLE; return operator()(3); }
-
-    template<int P = h_, int Q = w_> typename std::enable_if<maybe_add_swizzle<P, Q, 1>::value, num&>::type
-    x() { OPENTRACK_ASSERT_SWIZZLE; return operator()(0); }
-
-    template<int P = h_, int Q = w_> typename std::enable_if<maybe_add_swizzle<P, Q, 2>::value, num&>::type
-    y() { OPENTRACK_ASSERT_SWIZZLE; return operator()(1); }
-
-    template<int P = h_, int Q = w_> typename std::enable_if<maybe_add_swizzle<P, Q, 3>::value, num&>::type
-    z() { OPENTRACK_ASSERT_SWIZZLE; return operator()(2); }
-
-    template<int P = h_, int Q = w_> typename std::enable_if<maybe_add_swizzle<P, Q, 4>::value, num&>::type
-    w() { OPENTRACK_ASSERT_SWIZZLE; return operator()(3); }
-    // parameters w_ and h_ are rebound so that SFINAE occurs
-    // removing them causes a compile-time error -sh 20150811
-
-    template<int R, int S, int P = h_, int Q = w_>
-    typename std::enable_if<is_vector_pair<R, S, P, Q>::value, num>::type
-    dot(const Mat<num, R, S>& p2) const
-    {
-        static_assert(P == h_ && Q == w_, "");
-
-        num ret = 0;
-        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_>
-    typename std::enable_if<is_dim3<P, Q, R, S>::value, Mat<num, is_dim3<P, Q, R, S>::P, is_dim3<P, Q, R, S>::Q>>::type
-    cross(const Mat<num, R, S>& p2) const
-    {
-        static_assert(P == h_ && Q == w_, "");
-        decltype(*this)& p1 = *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());
-    }
-
-    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++)
-                ret(j, i) = data[j][i] + other.data[j][i];
-        return ret;
-    }
-
-    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++)
-                ret(j, i) = data[j][i] - other.data[j][i];
-        return ret;
-    }
-
-    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;
-    }
-
-    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
-    {
-        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++)
-                    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]; }
-
-    template<typename... ts, int h__ = h_, int w__ = w_,
-             typename = typename std::enable_if<is_arglist_correct<num, h__, w__, ts...>::value>::type>
-    Mat(const ts... xs)
-    {
-        static_assert(h__ == h_ && w__ == w_, "");
-
-        std::initializer_list<num> init = { static_cast<num>(xs)... };
-
-        *this = Mat(std::move(init));
-    }
-
-    Mat()
-    {
-        for (int j = 0; j < h_; j++)
-            for (int i = 0; i < w_; i++)
-                data[j][i] = num(0);
-    }
-
-    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];
-    }
-
-    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++;
-    }
-
-    operator num*() { return reinterpret_cast<num*>(data); }
-    operator const num*() const { return reinterpret_cast<const num*>(data); }
-
-    // XXX add more operators as needed, third-party dependencies mostly
-    // not needed merely for matrix algebra -sh 20141030
-
-    template<int h__ = h_>
-    static typename std::enable_if<h_ == w_, Mat<num, h__, h__>>::type eye()
-    {
-        static_assert(h_ == h__, "");
-
-        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 < h_; i++)
-            ret.data[i][i] = 1;
-
-        return ret;
-    }
-
-    Mat<num, w_, h_> t() const
-    {
-        Mat<num, w_, h_> ret;
-
-        for (int j = 0; j < h_; j++)
-            for (int i = 0; i < w_; i++)
-                ret(i, j) = data[j][i];
-
-        return ret;
-    }
-};
-
-template<int h_, int w_> using dmat = Mat<double, h_, w_>;
-
-template<typename num, int h, int w>
-Mat<num, h, w> operator*(num scalar, const Mat<num, h, w>& mat)
-{
-    return mat * scalar;
-}
-
-template<typename num, int h_, int w_>
-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;
-    return ret;
-}
-
-namespace euler {
-
-template<int y, int x> using dmat = Mat<double, y, x>;
-using rmat = dmat<3, 3>;
-using euler_t = dmat<3, 1>;
-
-rmat OPENTRACK_LOGIC_EXPORT euler_to_rmat(const euler_t& input);
-
-euler_t OPENTRACK_LOGIC_EXPORT rmat_to_euler(const rmat& R);
-
-void OPENTRACK_LOGIC_EXPORT tait_bryan_to_matrices(const euler_t& input,
-                                                   rmat& r_roll,
-                                                   rmat& r_pitch,
-                                                   rmat& r_yaw);
-
-} // end ns euler
diff --git a/opentrack-logic/state.hpp b/opentrack-logic/state.hpp
deleted file mode 100644
index 1c608f7a..00000000
--- a/opentrack-logic/state.hpp
+++ /dev/null
@@ -1,32 +0,0 @@
-/* Copyright (c) 2014-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 "opentrack-compat/options.hpp"
-using namespace options;
-#include "opentrack/plugin-support.hpp"
-#include "main-settings.hpp"
-#include "mappings.hpp"
-#include "selected-libraries.hpp"
-#include "work.hpp"
-#include <vector>
-#include <QString>
-
-struct State
-{
-    State(const QString& library_path) :
-        modules(library_path),
-        pose(std::vector<axis_opts*>{&s.a_x, &s.a_y, &s.a_z, &s.a_yaw, &s.a_pitch, &s.a_roll})
-    {}
-    Modules modules;
-    SelectedLibraries libs;
-    main_settings s;
-    Mappings pose;
-    mem<Work> work;
-};
diff --git a/opentrack-logic/tracker.cpp b/opentrack-logic/tracker.cpp
deleted file mode 100644
index d060fff1..00000000
--- a/opentrack-logic/tracker.cpp
+++ /dev/null
@@ -1,385 +0,0 @@
-/* Copyright (c) 2012-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.
- */
-
-/*
- * this file appeared originally in facetracknoir, was rewritten completely
- * following opentrack fork.
- *
- * originally written by Wim Vriend.
- */
-
-#include "opentrack-compat/sleep.hpp"
-
-#include "tracker.h"
-#include <cmath>
-#include <algorithm>
-#include <cstdio>
-
-#if defined(_WIN32)
-#   include <windows.h>
-#endif
-
-Tracker::Tracker(Mappings &m, SelectedLibraries &libs, TrackLogger &logger) :
-    m(m),
-    newpose {0,0,0, 0,0,0},
-    libs(libs),
-    logger(logger),
-    r_b(get_camera_offset_matrix(c_div).t()),
-    r_b_real(get_camera_offset_matrix(1).t()),
-    t_b {0,0,0},
-    centerp(s.center_at_startup),
-    enabledp(true),
-    zero_(false),
-    should_quit(false)
-{
-}
-
-Tracker::~Tracker()
-{
-    should_quit = true;
-    wait();
-}
-
-Tracker::rmat Tracker::get_camera_offset_matrix(double c)
-{
-    const double off[] =
-    {
-        d2r * c * (double)-s.camera_yaw,
-        d2r * c * (double)-s.camera_pitch,
-        d2r * c * (double)-s.camera_roll
-    };
-
-    return euler::euler_to_rmat(off);
-}
-
-double Tracker::map(double pos, Map& axis)
-{
-    bool altp = (pos < 0) && axis.opts.altp;
-    axis.spline_main.setTrackingActive( !altp );
-    axis.spline_alt.setTrackingActive( altp );
-    auto& fc = altp ? axis.spline_alt : axis.spline_main;
-    return fc.getValue(pos);
-}
-
-void Tracker::t_compensate(const rmat& rmat, const euler_t& xyz_, euler_t& output, bool rz)
-{
-    // TY is really yaw axis. need swapping accordingly.
-    const euler_t ret = rmat * euler_t(xyz_(TZ), -xyz_(TX), -xyz_(TY));
-    if (!rz)
-        output(2) = ret(0);
-    else
-        output(2) = xyz_(2);
-    output(1) = -ret(2);
-    output(0) = -ret(1);
-}
-
-#include "opentrack-compat/nan.hpp"
-
-static inline double elide_nan(double value, double def)
-{
-    if (nanp(value))
-    {
-        if (nanp(def))
-            return 0;
-        return def;
-    }
-    return value;
-}
-
-static bool is_nan(const dmat<3,3>& r, const dmat<3, 1>& t)
-{
-    for (int i = 0; i < 3; i++)
-        for (int j = 0; j < 3; j++)
-            if (nanp(r(i, j)))
-                return true;
-
-    for (int i = 0; i < 3; i++)
-        if (nanp(t(i)))
-            return true;
-
-    return false;
-}
-
-static bool is_nan(const Pose& value)
-{
-    for (int i = 0; i < 6; i++)
-        if (nanp(value(i)))
-            return true;
-    return false;
-}
-
-constexpr double Tracker::c_mult;
-constexpr double Tracker::c_div;
-
-void Tracker::logic()
-{
-    using namespace euler;
-
-    logger.write_dt();
-    logger.reset_dt();
-
-    Pose value, raw;
-
-    for (int i = 0; i < 6; i++)
-    {
-        auto& axis = m(i);
-        int k = axis.opts.src;
-        if (k < 0 || k >= 6)
-            value(i) = 0;
-        else
-            value(i) = newpose[k];
-        raw(i) = newpose[i];
-    }
-
-    logger.write_pose(raw); // raw
-
-    if (is_nan(raw))
-        raw = last_raw;
-
-    // TODO fix gimbal lock by dividing euler angle input by >=3.
-    // maintain the real rmat separately for translation compensation
-    // TODO split this function, it's too big
-    rmat r, r_real;
-
-    {
-        euler_t tmp = d2r * euler_t(&value[Yaw]);
-        r = euler_to_rmat(c_div * tmp);
-        r_real = euler_to_rmat(tmp);
-    }
-
-    euler_t t(value(0), value(1), value(2));
-
-    bool do_center_now = false;
-    bool nan = is_nan(r, t);
-
-    if (centerp && !nan)
-    {
-        for (int i = 0; i < 6; i++)
-            if (fabs(newpose[i]) != 0)
-            {
-                do_center_now = true;
-                break;
-            }
-    }
-
-    const rmat cam = get_camera_offset_matrix(c_div);
-    const rmat cam_real = get_camera_offset_matrix(1);
-
-    r = r * cam;
-    r_real = r_real * cam_real;
-
-    if (do_center_now)
-    {
-        centerp = false;
-
-        if (libs.pFilter)
-            libs.pFilter->center();
-
-        if (libs.pTracker->center())
-        {
-            r_b = cam.t();
-            r_b_real = cam_real.t();
-            r = rmat::eye();
-            r_real = rmat::eye();
-        }
-        else
-        {
-            r_b = r.t();
-            r_b_real = r_real.t();
-        }
-
-        for (int i = 0; i < 3; i++)
-            t_b[i] = t(i);
-    }
-
-    {
-        switch (s.center_method)
-        {
-        // inertial
-        case 0:
-        default:
-            r = r_b * r;
-            break;
-        // camera
-        case 1:
-            r = r * r_b;
-            break;
-        }
-
-        const euler_t rot = r2d * c_mult * rmat_to_euler(r);
-        euler_t pos(t(0) - t_b[0], t(1) - t_b[1], t(2) - t_b[2]);
-
-        if (s.use_camera_offset_from_centering)
-            t_compensate(r_b_real.t() * cam_real.t(), pos, pos, false);
-        else
-            t_compensate(cam_real.t(), pos, pos, false);
-
-        for (int i = 0; i < 3; i++)
-        {
-            value(i) = pos(i);
-            value(i+3) = rot(i);
-        }
-    }
-
-    logger.write_pose(value); // "corrected" - after various transformations to account for camera position
-
-    // whenever something can corrupt its internal state due to nan/inf, elide the call
-    if (is_nan(value))
-    {
-        nan = true;
-        logger.write_pose(value); // "filtered"
-    }
-    else
-    {
-        {
-            Pose tmp = value;
-
-            if (libs.pFilter)
-                libs.pFilter->filter(tmp, value);
-        }
-        logger.write_pose(value); // "filtered"
-
-        // CAVEAT rotation only, due to tcomp
-        for (int i = 3; i < 6; i++)
-            value(i) = map(value(i), m(i));
-
-        for (int i = 0; i < 6; i++)
-            value(i) += m(i).opts.zero;
-
-        for (int i = 0; i < 6; i++)
-            value(i) *= int(m(i).opts.invert) * -2 + 1;
-
-        if (zero_)
-            for (int i = 0; i < 6; i++)
-                value(i) = 0;
-
-        if (is_nan(value))
-            nan = true;
-    }
-
-    if (s.tcomp_p)
-    {
-        euler_t value_(value(TX), value(TY), value(TZ));
-        t_compensate(euler_to_rmat(euler_t(value(Yaw) * d2r, value(Pitch) * d2r, value(Roll) * d2r)),
-                     value_,
-                     value_,
-                     s.tcomp_tz);
-        if (is_nan(r, value_))
-            nan = true;
-        for (int i = 0; i < 3; i++)
-            value(i) = value_(i);
-    }
-
-    // CAVEAT translation only, due to tcomp
-    for (int i = 0; i < 3; i++)
-        value(i) = map(value(i), m(i));
-
-    logger.write_pose(value); // "mapped"
-
-    if (nan)
-    {
-        value = last_mapped;
-
-        // for widget last value display
-        for (int i = 0; i < 6; i++)
-            (void) map(value(i), m(i));
-    }
-
-    libs.pProtocol->pose(value);
-
-    last_mapped = value;
-    last_raw = raw;
-
-    QMutexLocker foo(&mtx);
-    output_pose = value;
-    raw_6dof = raw;
-
-    logger.reset_dt();
-    logger.next_line();
-}
-
-void Tracker::run()
-{
-#if defined(_WIN32)
-    (void) timeBeginPeriod(1);
-#endif
-
-    setPriority(QThread::HighPriority);
-
-    {
-        static constexpr const char* posechannels[6] = { "TX", "TY", "TZ", "Yaw", "Pitch", "Roll" };
-        static constexpr const char* datachannels[5] = { "dt", "raw", "corrected", "filtered", "mapped" };
-        logger.write(datachannels[0]);
-        char buffer[128];
-        for (unsigned j = 1; j < 5; ++j)
-        {
-            for (unsigned i = 0; i < 6; ++i)
-            {
-                std::sprintf(buffer, "%s%s", datachannels[j], posechannels[i]);
-                logger.write(buffer);
-            }
-        }
-        logger.next_line();
-    }
-
-    t.start();
-    logger.reset_dt();
-
-    while (!should_quit)
-    {
-        double tmp[6] {0,0,0, 0,0,0};
-        libs.pTracker->data(tmp);
-
-        if (enabledp)
-            for (int i = 0; i < 6; i++)
-                newpose[i] = elide_nan(tmp[i], newpose[i]);
-
-        logic();
-
-        static constexpr long const_sleep_us = 4000;
-
-        using std::max;
-        using std::min;
-
-        const long elapsed_usecs = t.elapsed_usecs();
-        const long sleep_us = const_sleep_us * 2 - elapsed_usecs;
-
-        const unsigned sleep_time = unsigned(max(1l, min(const_sleep_us * 4, max(1l, (sleep_us + 200l)/1000l))));
-
-        t.start();
-
-        portable::sleep(unsigned(max(1u, sleep_time)));
-    }
-
-    {
-        // filter may inhibit exact origin
-        Pose p;
-        libs.pProtocol->pose(p);
-    }
-
-#if defined(_WIN32)
-    (void) timeEndPeriod(1);
-#endif
-
-    for (int i = 0; i < 6; i++)
-    {
-        m(i).spline_main.setTrackingActive(false);
-        m(i).spline_alt.setTrackingActive(false);
-    }
-}
-
-void Tracker::get_raw_and_mapped_poses(double* mapped, double* raw) const
-{
-    QMutexLocker foo(&const_cast<Tracker&>(*this).mtx);
-
-    for (int i = 0; i < 6; i++)
-    {
-        raw[i] = raw_6dof(i);
-        mapped[i] = output_pose(i);
-    }
-}
-
diff --git a/opentrack-logic/tracker.h b/opentrack-logic/tracker.h
deleted file mode 100644
index 6dc48074..00000000
--- a/opentrack-logic/tracker.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/* Copyright (c) 2014-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 <vector>
-
-#include "opentrack-compat/pi-constant.hpp"
-#include "opentrack-compat/timer.hpp"
-#include "opentrack/plugin-support.hpp"
-#include "mappings.hpp"
-#include "simple-mat.hpp"
-#include "selected-libraries.hpp"
-
-#include "spline-widget/spline.hpp"
-#include "main-settings.hpp"
-#include "opentrack-compat/options.hpp"
-#include "tracklogger.hpp"
-
-#include <QMutex>
-#include <QThread>
-
-#include "export.hpp"
-
-using Pose = Mat<double, 6, 1>;
-
-class OPENTRACK_LOGIC_EXPORT Tracker : private QThread
-{
-    Q_OBJECT
-private:
-    using rmat = euler::rmat;
-    using euler_t = euler::euler_t;
-
-    QMutex mtx;
-    main_settings s;
-    Mappings& m;
-
-    Timer t;
-    Pose output_pose, raw_6dof, last_mapped, last_raw;
-
-    double newpose[6];
-    SelectedLibraries const& libs;
-    // The owner of the reference is the main window.
-    // This design might be usefull if we decide later on to swap out
-    // the logger while the tracker is running.
-    TrackLogger &logger;
-
-    rmat r_b, r_b_real;
-    double t_b[3];
-
-    volatile bool centerp;
-    volatile bool enabledp;
-    volatile bool zero_;
-    volatile bool should_quit;
-
-    double map(double pos, Map& axis);
-    void logic();
-    void t_compensate(const rmat& rmat, const euler_t& ypr, euler_t& output, bool rz);
-    void run() override;
-
-    static constexpr double pi = OPENTRACK_PI;
-    static constexpr double r2d = 180. / OPENTRACK_PI;
-    static constexpr double d2r = OPENTRACK_PI / 180.;
-
-    // note: float exponent base is 2
-    static constexpr double c_mult = 4;
-    static constexpr double c_div = 1./c_mult;
-public:
-    Tracker(Mappings& m, SelectedLibraries& libs, TrackLogger &logger);
-    ~Tracker();
-
-    rmat get_camera_offset_matrix(double c);
-    void get_raw_and_mapped_poses(double* mapped, double* raw) const;
-    void start() { QThread::start(); }
-    void toggle_enabled() { enabledp = !enabledp; }
-    void set_toggle(bool value) { enabledp = value; }
-    void set_zero(bool value) { zero_ = value; }
-    void center() { centerp = !centerp; }
-    void zero() { zero_ = !zero_; }
-};
diff --git a/opentrack-logic/tracklogger.cpp b/opentrack-logic/tracklogger.cpp
deleted file mode 100644
index 64dda579..00000000
--- a/opentrack-logic/tracklogger.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-#include "tracklogger.hpp"
-#include "tracker.h"
-
-TrackLogger::~TrackLogger() {}
-
-void TrackLogger::reset_dt()
-{
-    t.start();
-}
-
-void TrackLogger::write_dt()
-{
-    const double dt = t.elapsed_seconds();
-    write(&dt, 1);
-}
-
-void TrackLoggerCSV::handle_first_col_sep()
-{
-    if (!first_col)
-        out.put(',');
-    first_col = false;
-}
-
-void TrackLoggerCSV::write(const char *s)
-{
-    handle_first_col_sep();
-    out << s;
-}
-
-
-void TrackLoggerCSV::write(const double *p, int n)
-{
-    handle_first_col_sep();
-    for (int i = 0; i < n-1; ++i)
-    {
-        out << p[i];
-        out.put(',');
-    }
-    out << p[n-1];
-}
-
-void TrackLoggerCSV::next_line()
-{
-    out << std::endl;
-    first_col = true;
-}
-
diff --git a/opentrack-logic/tracklogger.hpp b/opentrack-logic/tracklogger.hpp
deleted file mode 100644
index c6c145cf..00000000
--- a/opentrack-logic/tracklogger.hpp
+++ /dev/null
@@ -1,65 +0,0 @@
-#pragma once
-#include "main-settings.hpp"
-#include "opentrack-compat/options.hpp"
-#include "opentrack-compat/timer.hpp"
-
-#include <fstream>
-#include <QString>
-#include <QMessageBox>
-#include <QWidget>
-
-class OPENTRACK_LOGIC_EXPORT TrackLogger
-{
-    TrackLogger(TrackLogger&&) = delete;
-    TrackLogger(const TrackLogger&) = delete;
-    TrackLogger& operator=(const TrackLogger&) = delete;
-    TrackLogger& operator=(TrackLogger&&) = delete;
-
-    Timer t;
-
-public:
-    TrackLogger()
-    {
-    }
-
-    virtual ~TrackLogger();
-
-    virtual void write(const char *)
-    {
-    }
-
-    virtual void write(const double *, int)
-    {
-    }
-
-    virtual void next_line()
-    {
-    }
-
-    void write_pose(const double *p)
-    {
-        write(p, 6);
-    }
-
-    void reset_dt();
-    void write_dt();
-};
-
-
-class OPENTRACK_LOGIC_EXPORT TrackLoggerCSV : public TrackLogger
-{
-    std::ofstream out;
-    bool first_col;
-    inline void handle_first_col_sep();
-public:
-    TrackLoggerCSV(const QString &filename) : first_col(true)
-    {
-        out.open(filename.toStdString());
-    }
-
-    bool is_open() const { return out.is_open(); }
-    void write(const char *s) override;
-    void write(const double *p, int n) override;
-    void next_line() override;
-};
-
diff --git a/opentrack-logic/win32-shortcuts.cpp b/opentrack-logic/win32-shortcuts.cpp
deleted file mode 100644
index df9145a0..00000000
--- a/opentrack-logic/win32-shortcuts.cpp
+++ /dev/null
@@ -1,196 +0,0 @@
-/* Copyright (c) 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.
- */
-
-#if defined(_WIN32)
-#   ifndef DIRECTINPUT_VERSION
-#       define DIRECTINPUT_VERSION 0x800
-#   endif
-#   include <dinput.h>
-
-#include "win32-shortcuts.h"
-#include <QList>
-#include <QVariant>
-#include <QDebug>
-
-QList<win_key> windows_key_mods =
-    QList<win_key>({
-        win_key(DIK_LCONTROL, Qt::Key::Key_Control),
-        win_key(DIK_RCONTROL, Qt::Key::Key_Control),
-        win_key(DIK_LALT, Qt::Key::Key_Alt),
-        win_key(DIK_RALT, Qt::Key::Key_Alt),
-        win_key(DIK_LSHIFT, Qt::Key::Key_Shift),
-        win_key(DIK_RSHIFT, Qt::Key::Key_Shift),
-        win_key(DIK_LWIN, Qt::Key::Key_unknown),
-        win_key(DIK_RWIN, Qt::Key::Key_unknown)
-    });
-
-QList<win_key> windows_key_sequences =
-    QList<win_key>({
-       win_key(DIK_F1, Qt::Key::Key_F1 ),
-       win_key(DIK_F2, Qt::Key::Key_F2 ),
-       win_key(DIK_F3, Qt::Key::Key_F3 ),
-       win_key(DIK_F4, Qt::Key::Key_F4 ),
-       win_key(DIK_F5, Qt::Key::Key_F5 ),
-       win_key(DIK_F6, Qt::Key::Key_F6 ),
-       win_key(DIK_F7, Qt::Key::Key_F7 ),
-       win_key(DIK_F8, Qt::Key::Key_F8 ),
-       win_key(DIK_F9, Qt::Key::Key_F9 ),
-       win_key(DIK_F10, Qt::Key::Key_F10 ),
-       win_key(DIK_F11, Qt::Key::Key_F11 ),
-       win_key(DIK_F12, Qt::Key::Key_F12 ),
-       win_key(DIK_LEFT, Qt::Key::Key_Left ),
-       win_key(DIK_RIGHT, Qt::Key::Key_Right ),
-       win_key(DIK_UP, Qt::Key::Key_Up ),
-       win_key(DIK_DOWN, Qt::Key::Key_Down ),
-       win_key(DIK_PRIOR, Qt::Key::Key_PageUp ),
-       win_key(DIK_NEXT, Qt::Key::Key_PageDown ),
-       win_key(DIK_HOME, Qt::Key::Key_Home ),
-       win_key(DIK_END, Qt::Key::Key_End ),
-       win_key(DIK_BACK, Qt::Key::Key_Backspace ),
-       win_key(DIK_COMMA, Qt::Key::Key_Comma ),
-       win_key(DIK_PERIOD, Qt::Key::Key_Period ),
-       win_key(DIK_LBRACKET, Qt::Key::Key_BracketLeft ),
-       win_key(DIK_RBRACKET, Qt::Key::Key_BracketRight ),
-       win_key(DIK_SEMICOLON, Qt::Key::Key_Semicolon ),
-       win_key(DIK_SLASH, Qt::Key::Key_Slash ),
-       win_key(DIK_BACKSLASH, Qt::Key::Key_Backslash ),
-       win_key(DIK_BACKSPACE, Qt::Key::Key_Backspace ),
-       win_key(DIK_APOSTROPHE, Qt::Key::Key_Apostrophe ),
-       win_key(DIK_GRAVE, Qt::Key::Key_QuoteLeft ),
-       win_key(DIK_MINUS, Qt::Key::Key_Minus ),
-       win_key(DIK_EQUALS, Qt::Key::Key_Equal ),
-       win_key(DIK_PERIOD, Qt::Key::Key_Period ),
-       win_key(DIK_F1, Qt::Key::Key_F1 ),
-       win_key(DIK_F2, Qt::Key::Key_F2 ),
-       win_key(DIK_F3, Qt::Key::Key_F3 ),
-       win_key(DIK_F4, Qt::Key::Key_F4 ),
-       win_key(DIK_F5, Qt::Key::Key_F5 ),
-       win_key(DIK_F6, Qt::Key::Key_F6 ),
-       win_key(DIK_F7, Qt::Key::Key_F7 ),
-       win_key(DIK_F8, Qt::Key::Key_F8 ),
-       win_key(DIK_F9, Qt::Key::Key_F9 ),
-       win_key(DIK_F10, Qt::Key::Key_F10 ),
-       win_key(DIK_F11, Qt::Key::Key_F11 ),
-       win_key(DIK_F12, Qt::Key::Key_F12 ),
-       win_key(DIK_0, Qt::Key::Key_0 ),
-       win_key(DIK_1, Qt::Key::Key_1 ),
-       win_key(DIK_2, Qt::Key::Key_2 ),
-       win_key(DIK_3, Qt::Key::Key_3 ),
-       win_key(DIK_4, Qt::Key::Key_4 ),
-       win_key(DIK_5, Qt::Key::Key_5 ),
-       win_key(DIK_6, Qt::Key::Key_6 ),
-       win_key(DIK_7, Qt::Key::Key_7 ),
-       win_key(DIK_8, Qt::Key::Key_8 ),
-       win_key(DIK_9, Qt::Key::Key_9 ),
-       win_key(DIK_A, Qt::Key::Key_A ),
-       win_key(DIK_B, Qt::Key::Key_B ),
-       win_key(DIK_C, Qt::Key::Key_C ),
-       win_key(DIK_D, Qt::Key::Key_D ),
-       win_key(DIK_E, Qt::Key::Key_E ),
-       win_key(DIK_F, Qt::Key::Key_F ),
-       win_key(DIK_G, Qt::Key::Key_G ),
-       win_key(DIK_H, Qt::Key::Key_H ),
-       win_key(DIK_I, Qt::Key::Key_I ),
-       win_key(DIK_J, Qt::Key::Key_J ),
-       win_key(DIK_K, Qt::Key::Key_K ),
-       win_key(DIK_L, Qt::Key::Key_L ),
-       win_key(DIK_M, Qt::Key::Key_M ),
-       win_key(DIK_N, Qt::Key::Key_N ),
-       win_key(DIK_O, Qt::Key::Key_O ),
-       win_key(DIK_P, Qt::Key::Key_P ),
-       win_key(DIK_Q, Qt::Key::Key_Q ),
-       win_key(DIK_R, Qt::Key::Key_R ),
-       win_key(DIK_S, Qt::Key::Key_S ),
-       win_key(DIK_T, Qt::Key::Key_T ),
-       win_key(DIK_U, Qt::Key::Key_U ),
-       win_key(DIK_V, Qt::Key::Key_V ),
-       win_key(DIK_W, Qt::Key::Key_W ),
-       win_key(DIK_X, Qt::Key::Key_X ),
-       win_key(DIK_Y, Qt::Key::Key_Y ),
-       win_key(DIK_Z, Qt::Key::Key_Z ),
-       win_key(DIK_RETURN, Qt::Key::Key_Return),
-       win_key(DIK_INSERT, Qt::Key::Key_Insert),
-       win_key(DIK_DELETE, Qt::Key::Key_Delete),
-       win_key(DIK_SPACE, Qt::Key::Key_Space),
-       win_key(DIK_SYSRQ, Qt::Key::Key_Print),
-       win_key(DIK_SCROLL, Qt::Key::Key_ScrollLock),
-       win_key(DIK_PAUSE, Qt::Key::Key_Pause),
-       win_key(DIK_NUMLOCK, Qt::Key::Key_NumLock),
-#define mod(x, y) static_cast<Qt::Key>(x | y)
-       win_key(DIK_NUMPAD0,      mod(Qt::Key::Key_0,        Qt::KeypadModifier)),
-       win_key(DIK_NUMPAD0,      mod(Qt::Key::Key_0,        Qt::KeypadModifier)),
-       win_key(DIK_NUMPAD1,      mod(Qt::Key::Key_1,        Qt::KeypadModifier)),
-       win_key(DIK_NUMPAD2,      mod(Qt::Key::Key_2,        Qt::KeypadModifier)),
-       win_key(DIK_NUMPAD3,      mod(Qt::Key::Key_3,        Qt::KeypadModifier)),
-       win_key(DIK_NUMPAD4,      mod(Qt::Key::Key_4,        Qt::KeypadModifier)),
-       win_key(DIK_NUMPAD5,      mod(Qt::Key::Key_5,        Qt::KeypadModifier)),
-       win_key(DIK_NUMPAD6,      mod(Qt::Key::Key_6,        Qt::KeypadModifier)),
-       win_key(DIK_NUMPAD7,      mod(Qt::Key::Key_7,        Qt::KeypadModifier)),
-       win_key(DIK_NUMPAD8,      mod(Qt::Key::Key_8,        Qt::KeypadModifier)),
-       win_key(DIK_NUMPAD9,      mod(Qt::Key::Key_9,        Qt::KeypadModifier)),
-       win_key(DIK_NUMPADCOMMA,  mod(Qt::Key::Key_Comma,    Qt::KeypadModifier)),
-       win_key(DIK_NUMPADENTER,  mod(Qt::Key::Key_Enter,    Qt::KeypadModifier)),
-       win_key(DIK_NUMPADEQUALS, mod(Qt::Key::Key_Equal,    Qt::KeypadModifier)),
-       win_key(DIK_NUMPADMINUS,  mod(Qt::Key::Key_Minus,    Qt::KeypadModifier)),
-       win_key(DIK_NUMPADPERIOD, mod(Qt::Key::Key_Period,   Qt::KeypadModifier)),
-       win_key(DIK_NUMPADPLUS,   mod(Qt::Key::Key_Plus,     Qt::KeypadModifier)),
-       win_key(DIK_NUMPADSLASH,  mod(Qt::Key::Key_Slash,    Qt::KeypadModifier)),
-       win_key(DIK_NUMPADSTAR,   mod(Qt::Key::Key_multiply, Qt::KeypadModifier)),
-    });
-
-bool win_key::to_qt(const Key& k, QKeySequence& qt_, Qt::KeyboardModifiers &mods)
-{
-    for (auto& wk : windows_key_sequences)
-    {
-        if (wk.win == k.keycode)
-        {
-            qt_ = wk.qt;
-            mods = Qt::NoModifier;
-            if (k.ctrl) mods |= Qt::ControlModifier;
-            if (k.shift) mods |= Qt::ShiftModifier;
-            if (k.alt) mods |= Qt::AltModifier;
-            return true;
-        }
-    }
-    return false;
-}
-
-bool win_key::from_qt(QKeySequence qt_, int& dik, Qt::KeyboardModifiers& mods)
-{
-    // CAVEAT don't use QVariant::toUInt() or conversion fails
-    const unsigned qt = static_cast<unsigned>(QVariant(qt_).toInt());
-    const unsigned our_mods = qt & Qt::KeyboardModifierMask;
-
-    {
-        const auto key_ = qt;
-        for (auto& wk : windows_key_sequences)
-        {
-            if (wk.qt == key_)
-            {
-                dik = wk.win;
-                mods = Qt::NoModifier;
-                return true;
-            }
-        }
-    }
-    {
-        const unsigned key = qt & ~Qt::KeyboardModifierMask;
-        for (auto& wk : windows_key_sequences)
-        {
-            if (wk.qt == key)
-            {
-                dik = wk.win;
-                mods = static_cast<Qt::KeyboardModifiers>(our_mods);
-                return true;
-            }
-        }
-    }
-    return false;
-}
-
-#endif
diff --git a/opentrack-logic/win32-shortcuts.h b/opentrack-logic/win32-shortcuts.h
deleted file mode 100644
index 7626a31f..00000000
--- a/opentrack-logic/win32-shortcuts.h
+++ /dev/null
@@ -1,24 +0,0 @@
-#pragma once
-
-#ifdef _WIN32
-
-#include <QKeySequence>
-#include "shortcuts.h"
-
-struct win_key;
-
-extern QList<win_key> windows_key_mods;
-extern QList<win_key> windows_key_sequences;
-
-#include "export.hpp"
-
-struct OPENTRACK_LOGIC_EXPORT win_key
-{
-    win_key(int win, Qt::Key qt) : win(win), qt(qt) {}
-    int win;
-    Qt::Key qt;
-    static bool from_qt(QKeySequence qt_, int& dik, Qt::KeyboardModifiers &mods);
-    static bool to_qt(const Key& k, QKeySequence& qt_, Qt::KeyboardModifiers &mods);
-};
-
-#endif
diff --git a/opentrack-logic/work.cpp b/opentrack-logic/work.cpp
deleted file mode 100644
index 8d00270b..00000000
--- a/opentrack-logic/work.cpp
+++ /dev/null
@@ -1,73 +0,0 @@
-#include "work.hpp"
-
-#include <QMessageBox>
-
-
-std::shared_ptr<TrackLogger> Work::make_logger(const main_settings &s)
-{
-    if (s.tracklogging_enabled)
-    {
-        if (static_cast<QString>(s.tracklogging_filename).isEmpty())
-        {
-            QMessageBox::warning(nullptr, "Logging Error",
-                "No filename given for track logging. Proceeding without logging.",
-                QMessageBox::Ok,
-                QMessageBox::NoButton);
-        }
-        else
-        {
-            auto logger = std::make_shared<TrackLoggerCSV>(s.tracklogging_filename);
-            if (!logger->is_open())
-            {
-                logger = nullptr;
-                QMessageBox::warning(nullptr, "Logging Error",
-                    "Unable to open file: " + s.tracklogging_filename + ". Proceeding  without logging.",
-                    QMessageBox::Ok,
-                    QMessageBox::NoButton);
-            }
-            else
-            {
-                /* As this function has the potential to fill up the hard drive
-                   of the unwary with junk data, a warning is in order. */
-                QMessageBox::warning(nullptr, "Logging Active",
-                    "Just a heads up. You are recoding pose data to " + s.tracklogging_filename + "!",
-                    QMessageBox::Ok,
-                    QMessageBox::NoButton);
-                return logger;
-            }
-        }
-    }
-    return std::make_shared<TrackLogger>();
-}
-
-
-Work::Work(Mappings& m, SelectedLibraries& libs, WId handle) :
-    libs(libs),
-    logger(make_logger(s)),
-    tracker(std::make_shared<Tracker>(m, libs, *logger)),
-    sc(std::make_shared<Shortcuts>()),
-    handle(handle),
-    keys {
-        key_tuple(s.key_center, [&](bool) -> void { tracker->center(); }, true),
-        key_tuple(s.key_toggle, [&](bool) -> void { tracker->toggle_enabled(); }, true),
-        key_tuple(s.key_zero, [&](bool) -> void { tracker->zero(); }, true),
-        key_tuple(s.key_toggle_press, [&](bool x) -> void { tracker->set_toggle(!x); }, false),
-        key_tuple(s.key_zero_press, [&](bool x) -> void { tracker->set_zero(x); }, false),
-        }
-{
-    reload_shortcuts();
-    tracker->start();
-}
-
-void Work::reload_shortcuts()
-{
-    sc->reload(keys);
-}
-
-Work::~Work()
-{
-    sc = nullptr;
-    // order matters, otherwise use-after-free -sh
-    tracker = nullptr;
-    libs = SelectedLibraries();
-}
diff --git a/opentrack-logic/work.hpp b/opentrack-logic/work.hpp
deleted file mode 100644
index 4afb1da4..00000000
--- a/opentrack-logic/work.hpp
+++ /dev/null
@@ -1,43 +0,0 @@
-/* Copyright (c) 2014-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 "main-settings.hpp"
-#include "opentrack/plugin-support.hpp"
-#include "tracker.h"
-#include "shortcuts.h"
-#include "export.hpp"
-#include "tracklogger.hpp"
-
-#include <QObject>
-#include <QFrame>
-#include <memory>
-#include <vector>
-#include <tuple>
-#include <functional>
-
-struct OPENTRACK_LOGIC_EXPORT Work
-{
-    using fn_t = std::function<void(bool)>;
-    using key_tuple = std::tuple<key_opts&, fn_t, bool>;
-    main_settings s; // tracker needs settings, so settings must come before it
-    SelectedLibraries& libs;
-    std::shared_ptr<TrackLogger> logger; // must come before tracker, since tracker depends on it
-    std::shared_ptr<Tracker> tracker;
-    std::shared_ptr<Shortcuts> sc;
-    WId handle;
-    std::vector<key_tuple> keys;
-
-    Work(Mappings& m, SelectedLibraries& libs, WId handle);
-    ~Work();
-    void reload_shortcuts();
-
-private:
-    std::shared_ptr<TrackLogger> make_logger(const main_settings &s);
-};
diff --git a/opentrack/CMakeLists.txt b/opentrack/CMakeLists.txt
deleted file mode 100644
index 5151cbf7..00000000
--- a/opentrack/CMakeLists.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-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/opentrack/dtors.cpp b/opentrack/dtors.cpp
deleted file mode 100644
index 5cc87187..00000000
--- a/opentrack/dtors.cpp
+++ /dev/null
@@ -1,11 +0,0 @@
-#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/opentrack/export.hpp b/opentrack/export.hpp
deleted file mode 100644
index a9f3521e..00000000
--- a/opentrack/export.hpp
+++ /dev/null
@@ -1,28 +0,0 @@
-#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/opentrack/is-window-visible.cpp b/opentrack/is-window-visible.cpp
deleted file mode 100644
index fc25bb7d..00000000
--- a/opentrack/is-window-visible.cpp
+++ /dev/null
@@ -1,33 +0,0 @@
-#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/opentrack/is-window-visible.hpp b/opentrack/is-window-visible.hpp
deleted file mode 100644
index 18c9251a..00000000
--- a/opentrack/is-window-visible.hpp
+++ /dev/null
@@ -1,7 +0,0 @@
-#pragma once
-
-#include <QWidget>
-#include "export.hpp"
-
-OPENTRACK_API_EXPORT bool is_window_visible(const QWidget* widget);
-
diff --git a/opentrack/plugin-api.hpp b/opentrack/plugin-api.hpp
deleted file mode 100644
index 1e5c0fe0..00000000
--- a/opentrack/plugin-api.hpp
+++ /dev/null
@@ -1,180 +0,0 @@
-/* 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/opentrack/plugin-support.hpp b/opentrack/plugin-support.hpp
deleted file mode 100644
index a26d7f08..00000000
--- a/opentrack/plugin-support.hpp
+++ /dev/null
@@ -1,216 +0,0 @@
-/* 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 "opentrack-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/opentrack/variance.hpp b/opentrack/variance.hpp
deleted file mode 100644
index 55060b02..00000000
--- a/opentrack/variance.hpp
+++ /dev/null
@@ -1,52 +0,0 @@
-#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()); }
-};
diff --git a/pose-widget/glwidget.cpp b/pose-widget/glwidget.cpp
index 2e36a095..a2a76726 100644
--- a/pose-widget/glwidget.cpp
+++ b/pose-widget/glwidget.cpp
@@ -6,8 +6,8 @@
  */
 
 #include "glwidget.h"
-#include "opentrack/is-window-visible.hpp"
-#include "opentrack-compat/pi-constant.hpp"
+#include "api/is-window-visible.hpp"
+#include "compat/pi-constant.hpp"
 #include <cmath>
 #include <algorithm>
 #include <QPainter>
diff --git a/pose-widget/glwidget.h b/pose-widget/glwidget.h
index 95be2914..7965130c 100644
--- a/pose-widget/glwidget.h
+++ b/pose-widget/glwidget.h
@@ -10,9 +10,9 @@
 #include <QtGlobal>
 #include <QWidget>
 #include <QPixmap>
-#include "opentrack/plugin-api.hpp"
-#include "opentrack-logic/simple-mat.hpp"
-#include "opentrack-compat/timer.hpp"
+#include "api/plugin-api.hpp"
+#include "logic/simple-mat.hpp"
+#include "compat/timer.hpp"
 
 #ifdef BUILD_pose_widget
 #   define POSE_WIDGET_EXPORT Q_DECL_EXPORT
diff --git a/proto-fg/ftnoir_protocol_fg.cpp b/proto-fg/ftnoir_protocol_fg.cpp
index 660a7352..599a4d2c 100644
--- a/proto-fg/ftnoir_protocol_fg.cpp
+++ b/proto-fg/ftnoir_protocol_fg.cpp
@@ -9,7 +9,7 @@
  * copyright notice and this permission notice appear in all copies.             *
  */
 #include "ftnoir_protocol_fg.h"
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 
 // For Todd and Arda Kutlu
 
diff --git a/proto-fg/ftnoir_protocol_fg.h b/proto-fg/ftnoir_protocol_fg.h
index 5b1cbc81..77cb73c8 100644
--- a/proto-fg/ftnoir_protocol_fg.h
+++ b/proto-fg/ftnoir_protocol_fg.h
@@ -14,8 +14,8 @@
 #include <QThread>
 #include <QUdpSocket>
 #include <QMessageBox>
-#include "opentrack/plugin-api.hpp"
-#include "opentrack-compat/options.hpp"
+#include "api/plugin-api.hpp"
+#include "compat/options.hpp"
 using namespace options;
 
 struct settings : opts {
diff --git a/proto-fg/ftnoir_protocol_fg_dialog.cpp b/proto-fg/ftnoir_protocol_fg_dialog.cpp
index 6a3463b4..50ca2a73 100644
--- a/proto-fg/ftnoir_protocol_fg_dialog.cpp
+++ b/proto-fg/ftnoir_protocol_fg_dialog.cpp
@@ -11,7 +11,7 @@
 #include "ftnoir_protocol_fg.h"
 #include <QObject>
 #include <QFile>
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 
 //*******************************************************************************************************
 // FaceTrackNoIR Client Settings-dialog.
diff --git a/proto-fsuipc/ftnoir_protocol_fsuipc.cpp b/proto-fsuipc/ftnoir_protocol_fsuipc.cpp
index cb289747..92d9bd88 100644
--- a/proto-fsuipc/ftnoir_protocol_fsuipc.cpp
+++ b/proto-fsuipc/ftnoir_protocol_fsuipc.cpp
@@ -9,7 +9,7 @@
  * copyright notice and this permission notice appear in all copies.             *
  */
 #include "ftnoir_protocol_fsuipc.h"
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 
 #include <cmath>
 
diff --git a/proto-fsuipc/ftnoir_protocol_fsuipc.h b/proto-fsuipc/ftnoir_protocol_fsuipc.h
index 42f421ad..8207ac9f 100644
--- a/proto-fsuipc/ftnoir_protocol_fsuipc.h
+++ b/proto-fsuipc/ftnoir_protocol_fsuipc.h
@@ -15,7 +15,7 @@
 #include <windows.h>
 #include <stdlib.h>
 #include "FSUIPC_User.h"
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 #include "ui_ftnoir_fsuipccontrols.h"
 #include <QMessageBox>
 #include <QSettings>
@@ -24,7 +24,7 @@
 #include <QDebug>
 #include <QFile>
 #include <QFileDialog>
-#include "opentrack-compat/options.hpp"
+#include "compat/options.hpp"
 using namespace options;
 
 #define FSUIPC_FILENAME "C:\\Program Files\\Microsoft Games\\Flight Simulator 9\\Modules\\FSUIPC.dll"
diff --git a/proto-fsuipc/ftnoir_protocol_fsuipc_dialog.cpp b/proto-fsuipc/ftnoir_protocol_fsuipc_dialog.cpp
index 504b2dcb..07507cfb 100644
--- a/proto-fsuipc/ftnoir_protocol_fsuipc_dialog.cpp
+++ b/proto-fsuipc/ftnoir_protocol_fsuipc_dialog.cpp
@@ -9,7 +9,7 @@
  * copyright notice and this permission notice appear in all copies.             *
  */
 #include "ftnoir_protocol_fsuipc.h"
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 
 FSUIPCControls::FSUIPCControls()
 {
diff --git a/proto-ft/ftnoir_protocol_ft.cpp b/proto-ft/ftnoir_protocol_ft.cpp
index 851cd939..7cd0b5f7 100644
--- a/proto-ft/ftnoir_protocol_ft.cpp
+++ b/proto-ft/ftnoir_protocol_ft.cpp
@@ -9,7 +9,7 @@
 #include "ftnoir_protocol_ft.h"
 #include "csv/csv.h"
 #include "opentrack-library-path.h"
-#include "opentrack-compat/pi-constant.hpp"
+#include "compat/pi-constant.hpp"
 
 check_for_first_run FTNoIR_Protocol::runonce_check = check_for_first_run();
 
diff --git a/proto-ft/ftnoir_protocol_ft.h b/proto-ft/ftnoir_protocol_ft.h
index d90d916e..246a73c4 100644
--- a/proto-ft/ftnoir_protocol_ft.h
+++ b/proto-ft/ftnoir_protocol_ft.h
@@ -8,7 +8,7 @@
 
 #pragma once
 #include "ui_ftnoir_ftcontrols.h"
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 #include <QMessageBox>
 #include <QSettings>
 #include <QLibrary>
@@ -18,8 +18,8 @@
 #include <QString>
 #include <QMutex>
 #include <QMutexLocker>
-#include "opentrack-compat/shm.h"
-#include "opentrack-compat/options.hpp"
+#include "compat/shm.h"
+#include "compat/options.hpp"
 #include "freetrackclient/fttypes.h"
 #include <memory>
 #include "mutex.hpp"
diff --git a/proto-ftn/ftnoir_protocol_ftn.cpp b/proto-ftn/ftnoir_protocol_ftn.cpp
index db852f55..51db0f9d 100644
--- a/proto-ftn/ftnoir_protocol_ftn.cpp
+++ b/proto-ftn/ftnoir_protocol_ftn.cpp
@@ -10,7 +10,7 @@
  */
 #include "ftnoir_protocol_ftn.h"
 #include <QFile>
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 
 FTNoIR_Protocol::FTNoIR_Protocol()
 {
diff --git a/proto-ftn/ftnoir_protocol_ftn.h b/proto-ftn/ftnoir_protocol_ftn.h
index d8b8aff0..9e647980 100644
--- a/proto-ftn/ftnoir_protocol_ftn.h
+++ b/proto-ftn/ftnoir_protocol_ftn.h
@@ -15,8 +15,8 @@
 #include <QUdpSocket>
 #include <QMessageBox>
 #include <cmath>
-#include "opentrack/plugin-api.hpp"
-#include "opentrack-compat/options.hpp"
+#include "api/plugin-api.hpp"
+#include "compat/options.hpp"
 using namespace options;
 
 struct settings : opts {
diff --git a/proto-ftn/ftnoir_protocol_ftn_dialog.cpp b/proto-ftn/ftnoir_protocol_ftn_dialog.cpp
index 7e191336..70416a69 100644
--- a/proto-ftn/ftnoir_protocol_ftn_dialog.cpp
+++ b/proto-ftn/ftnoir_protocol_ftn_dialog.cpp
@@ -9,7 +9,7 @@
  * copyright notice and this permission notice appear in all copies.             *
  */
 #include "ftnoir_protocol_ftn.h"
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 
 FTNControls::FTNControls()
 {
diff --git a/proto-libevdev/ftnoir_protocol_libevdev.cpp b/proto-libevdev/ftnoir_protocol_libevdev.cpp
index 96805b39..96411ae3 100644
--- a/proto-libevdev/ftnoir_protocol_libevdev.cpp
+++ b/proto-libevdev/ftnoir_protocol_libevdev.cpp
@@ -1,5 +1,5 @@
 #include "ftnoir_protocol_libevdev.h"
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 #include <cstdio>
 #include <algorithm>
 
diff --git a/proto-libevdev/ftnoir_protocol_libevdev.h b/proto-libevdev/ftnoir_protocol_libevdev.h
index ecb3b201..79be398b 100644
--- a/proto-libevdev/ftnoir_protocol_libevdev.h
+++ b/proto-libevdev/ftnoir_protocol_libevdev.h
@@ -8,7 +8,7 @@
 #include "ui_ftnoir_libevdev_controls.h"
 
 #include <QMessageBox>
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 
 extern "C" {
 #   include <libevdev/libevdev.h>
diff --git a/proto-libevdev/ftnoir_protocol_libevdev_dialog.cpp b/proto-libevdev/ftnoir_protocol_libevdev_dialog.cpp
index bb63ba6a..afccf908 100644
--- a/proto-libevdev/ftnoir_protocol_libevdev_dialog.cpp
+++ b/proto-libevdev/ftnoir_protocol_libevdev_dialog.cpp
@@ -1,5 +1,5 @@
 #include "ftnoir_protocol_libevdev.h"
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 
 LibevdevControls::LibevdevControls()
 {
diff --git a/proto-mouse/ftnoir_protocol_mouse.cpp b/proto-mouse/ftnoir_protocol_mouse.cpp
index 73616785..ced53bc6 100644
--- a/proto-mouse/ftnoir_protocol_mouse.cpp
+++ b/proto-mouse/ftnoir_protocol_mouse.cpp
@@ -5,7 +5,7 @@
  * copyright notice and this permission notice appear in all copies.
  */
 #include "ftnoir_protocol_mouse.h"
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 #include <cmath>
 #include <algorithm>
 #include <windows.h>
diff --git a/proto-mouse/ftnoir_protocol_mouse.h b/proto-mouse/ftnoir_protocol_mouse.h
index 2d96915e..0781366c 100644
--- a/proto-mouse/ftnoir_protocol_mouse.h
+++ b/proto-mouse/ftnoir_protocol_mouse.h
@@ -9,8 +9,8 @@
 
 #include "ui_ftnoir_mousecontrols.h"
 #include <QDebug>
-#include "opentrack/plugin-api.hpp"
-#include "opentrack-compat/options.hpp"
+#include "api/plugin-api.hpp"
+#include "compat/options.hpp"
 using namespace options;
 
 struct settings : opts {
diff --git a/proto-mouse/ftnoir_protocol_mouse_dialog.cpp b/proto-mouse/ftnoir_protocol_mouse_dialog.cpp
index d8517487..77b1ff2e 100644
--- a/proto-mouse/ftnoir_protocol_mouse_dialog.cpp
+++ b/proto-mouse/ftnoir_protocol_mouse_dialog.cpp
@@ -1,5 +1,5 @@
 #include "ftnoir_protocol_mouse.h"
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 
 MOUSEControls::MOUSEControls()
 {
diff --git a/proto-sc/ftnoir_protocol_sc.cpp b/proto-sc/ftnoir_protocol_sc.cpp
index fb60cb01..cea064ea 100644
--- a/proto-sc/ftnoir_protocol_sc.cpp
+++ b/proto-sc/ftnoir_protocol_sc.cpp
@@ -10,7 +10,7 @@
  * copyright notice and this permission notice appear in all copies.             *
  */
 #include "ftnoir_protocol_sc.h"
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 #include "opentrack-library-path.h"
 
 FTNoIR_Protocol::FTNoIR_Protocol() : should_stop(false), hSimConnect(nullptr)
diff --git a/proto-sc/ftnoir_protocol_sc.h b/proto-sc/ftnoir_protocol_sc.h
index dcc0ca77..0da6c242 100644
--- a/proto-sc/ftnoir_protocol_sc.h
+++ b/proto-sc/ftnoir_protocol_sc.h
@@ -10,7 +10,7 @@
  * copyright notice and this permission notice appear in all copies.             *
  */
 #pragma once
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 
 #include "ui_ftnoir_sccontrols.h"
 #include <QThread>
@@ -20,7 +20,7 @@
 #include <QProcess>
 #include <QDebug>
 #include <QFile>
-#include "opentrack-compat/options.hpp"
+#include "compat/options.hpp"
 using namespace options;
 #include <windows.h>
 
diff --git a/proto-sc/ftnoir_protocol_sc_dialog.cpp b/proto-sc/ftnoir_protocol_sc_dialog.cpp
index e2ff82fc..a14c6b44 100644
--- a/proto-sc/ftnoir_protocol_sc_dialog.cpp
+++ b/proto-sc/ftnoir_protocol_sc_dialog.cpp
@@ -10,7 +10,7 @@
  */
 #include "ftnoir_protocol_sc.h"
 #include <QDebug>
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 
 SCControls::SCControls()
 {
diff --git a/proto-vjoystick/vjoystick.cpp b/proto-vjoystick/vjoystick.cpp
index d14166be..ad582b6d 100644
--- a/proto-vjoystick/vjoystick.cpp
+++ b/proto-vjoystick/vjoystick.cpp
@@ -1,5 +1,5 @@
 #include "vjoystick.h"
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 
 #include <cstring>
 #include <QDebug>
diff --git a/proto-vjoystick/vjoystick.h b/proto-vjoystick/vjoystick.h
index c4cb7e69..2d3fd378 100644
--- a/proto-vjoystick/vjoystick.h
+++ b/proto-vjoystick/vjoystick.h
@@ -7,7 +7,7 @@
  */
 #pragma once
 #include "ui_vjoystick.h"
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 
 #include <windows.h>
 
diff --git a/proto-vjoystick/vjoystick_dialog.cpp b/proto-vjoystick/vjoystick_dialog.cpp
index 136e898b..461230a6 100644
--- a/proto-vjoystick/vjoystick_dialog.cpp
+++ b/proto-vjoystick/vjoystick_dialog.cpp
@@ -1,5 +1,5 @@
 #include "vjoystick.h"
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 
 vjoystick_dialog::vjoystick_dialog()
 {
diff --git a/proto-wine/ftnoir_protocol_wine.h b/proto-wine/ftnoir_protocol_wine.h
index f78e1364..8b41824c 100644
--- a/proto-wine/ftnoir_protocol_wine.h
+++ b/proto-wine/ftnoir_protocol_wine.h
@@ -8,8 +8,8 @@
 #include <QMutex>
 #include <QMutexLocker>
 #include <QFile>
-#include "opentrack/plugin-api.hpp"
-#include "opentrack-compat/shm.h"
+#include "api/plugin-api.hpp"
+#include "compat/shm.h"
 #include "wine-shm.h"
 
 class FTNoIR_Protocol : public IProtocol
diff --git a/proto-wine/ftnoir_protocol_wine_dialog.cpp b/proto-wine/ftnoir_protocol_wine_dialog.cpp
index d5cb4494..a388df70 100644
--- a/proto-wine/ftnoir_protocol_wine_dialog.cpp
+++ b/proto-wine/ftnoir_protocol_wine_dialog.cpp
@@ -1,6 +1,6 @@
 #include "ftnoir_protocol_wine.h"
 #include <QDebug>
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 
 FTControls::FTControls()
 {
diff --git a/proto-wine/opentrack-wrapper-wine-main.cxx b/proto-wine/opentrack-wrapper-wine-main.cxx
index 082e5dde..1c88755a 100644
--- a/proto-wine/opentrack-wrapper-wine-main.cxx
+++ b/proto-wine/opentrack-wrapper-wine-main.cxx
@@ -6,13 +6,13 @@
 #include <cstdio>
 #include "freetrackclient/fttypes.h"
 #include "wine-shm.h"
-#include "opentrack-compat/export.hpp"
+#include "compat/export.hpp"
 
 enum Axis {
     TX = 0, TY, TZ, Yaw, Pitch, Roll
 };
 
-#include "opentrack-compat/shm.h"
+#include "compat/shm.h"
 
 void create_registry_key(void);
 
diff --git a/proto-wine/opentrack-wrapper-wine-posix.cxx b/proto-wine/opentrack-wrapper-wine-posix.cxx
index 50cce728..21c7622b 100644
--- a/proto-wine/opentrack-wrapper-wine-posix.cxx
+++ b/proto-wine/opentrack-wrapper-wine-posix.cxx
@@ -3,5 +3,5 @@
 #endif
 
 #define PortableLockedShm ShmPosix
-#include "opentrack-compat/shm.h"
-#include "opentrack-compat/shm.cpp"
+#include "compat/shm.h"
+#include "compat/shm.cpp"
diff --git a/proto-wine/opentrack-wrapper-wine-windows.cxx b/proto-wine/opentrack-wrapper-wine-windows.cxx
index cc8ae63a..698e3c3b 100644
--- a/proto-wine/opentrack-wrapper-wine-windows.cxx
+++ b/proto-wine/opentrack-wrapper-wine-windows.cxx
@@ -3,8 +3,8 @@
 #endif
 
 #define PortableLockedShm ShmWine
-#include "opentrack-compat/shm.h"
-#include "opentrack-compat/shm.cpp"
+#include "compat/shm.h"
+#include "compat/shm.cpp"
 #include "wine-shm.h"
 #define OPENTRACK_NO_QT_PATH
 #include "opentrack-library-path.h"
diff --git a/spline-widget/spline-widget.cpp b/spline-widget/spline-widget.cpp
index 17dbe31d..1f049339 100644
--- a/spline-widget/spline-widget.cpp
+++ b/spline-widget/spline-widget.cpp
@@ -5,7 +5,7 @@
  * copyright notice and this permission notice appear in all copies.
  */
 
-#include "opentrack-compat/options.hpp"
+#include "compat/options.hpp"
 using namespace options;
 #include "spline-widget.hpp"
 #include <QPainter>
diff --git a/spline-widget/spline-widget.hpp b/spline-widget/spline-widget.hpp
index 577e6f7f..1dbdb8e1 100644
--- a/spline-widget/spline-widget.hpp
+++ b/spline-widget/spline-widget.hpp
@@ -13,7 +13,7 @@
 #include <QtGui>
 #include <QPointF>
 #include "spline.hpp"
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 
 class SPLINE_WIDGET_EXPORT spline_widget : public QWidget
 {
diff --git a/spline-widget/spline.hpp b/spline-widget/spline.hpp
index 6387c87a..26775402 100644
--- a/spline-widget/spline.hpp
+++ b/spline-widget/spline.hpp
@@ -16,7 +16,7 @@
 #include <QMutex>
 #include <vector>
 #include <limits>
-#include "opentrack-compat/qcopyable-mutex.hpp"
+#include "compat/qcopyable-mutex.hpp"
 
 #ifdef BUILD_spline_widget
 #   define SPLINE_WIDGET_EXPORT Q_DECL_EXPORT
diff --git a/tracker-aruco/ftnoir_tracker_aruco.cpp b/tracker-aruco/ftnoir_tracker_aruco.cpp
index a1ca0ac5..9af182e5 100644
--- a/tracker-aruco/ftnoir_tracker_aruco.cpp
+++ b/tracker-aruco/ftnoir_tracker_aruco.cpp
@@ -6,14 +6,14 @@
  */
 
 #include "ftnoir_tracker_aruco.h"
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 #include <opencv2/core.hpp>
 #include <opencv2/videoio.hpp>
 #include <opencv2/imgproc.hpp>
 #include <opencv2/calib3d.hpp>
-#include "opentrack-compat/camera-names.hpp"
-#include "opentrack-compat/sleep.hpp"
-#include "opentrack-compat/pi-constant.hpp"
+#include "compat/camera-names.hpp"
+#include "compat/sleep.hpp"
+#include "compat/pi-constant.hpp"
 
 #include <QMutexLocker>
 #include <QDebug>
diff --git a/tracker-aruco/ftnoir_tracker_aruco.h b/tracker-aruco/ftnoir_tracker_aruco.h
index 6c28c7db..aeabd438 100644
--- a/tracker-aruco/ftnoir_tracker_aruco.h
+++ b/tracker-aruco/ftnoir_tracker_aruco.h
@@ -8,7 +8,7 @@
 #pragma once
 
 #include "ui_aruco-trackercontrols.h"
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 #include "include/markerdetector.h"
 
 #include "cv/camera-dialog.hpp"
@@ -26,7 +26,7 @@
 
 #include <cinttypes>
 
-#include "opentrack-compat/options.hpp"
+#include "compat/options.hpp"
 using namespace options;
 
 struct settings : opts {
diff --git a/tracker-freepie-udp/ftnoir_tracker_freepie-udp.cpp b/tracker-freepie-udp/ftnoir_tracker_freepie-udp.cpp
index 9a5697dd..800428aa 100644
--- a/tracker-freepie-udp/ftnoir_tracker_freepie-udp.cpp
+++ b/tracker-freepie-udp/ftnoir_tracker_freepie-udp.cpp
@@ -1,6 +1,6 @@
 #include "ftnoir_tracker_freepie-udp.h"
-#include "opentrack/plugin-api.hpp"
-#include "opentrack-compat/pi-constant.hpp"
+#include "api/plugin-api.hpp"
+#include "compat/pi-constant.hpp"
 
 #include <cinttypes>
 #include <algorithm>
diff --git a/tracker-freepie-udp/ftnoir_tracker_freepie-udp.h b/tracker-freepie-udp/ftnoir_tracker_freepie-udp.h
index 9d18c7d1..05228006 100644
--- a/tracker-freepie-udp/ftnoir_tracker_freepie-udp.h
+++ b/tracker-freepie-udp/ftnoir_tracker_freepie-udp.h
@@ -9,8 +9,8 @@
 #include <QUdpSocket>
 #include <QThread>
 #include "ui_freepie-udp-controls.h"
-#include "opentrack/plugin-api.hpp"
-#include "opentrack-compat/options.hpp"
+#include "api/plugin-api.hpp"
+#include "compat/options.hpp"
 using namespace options;
 
 struct settings : opts {
diff --git a/tracker-freepie-udp/ftnoir_tracker_freepie-udp_dialog.cpp b/tracker-freepie-udp/ftnoir_tracker_freepie-udp_dialog.cpp
index 3ebe359f..42a8308b 100644
--- a/tracker-freepie-udp/ftnoir_tracker_freepie-udp_dialog.cpp
+++ b/tracker-freepie-udp/ftnoir_tracker_freepie-udp_dialog.cpp
@@ -1,5 +1,5 @@
 #include "ftnoir_tracker_freepie-udp.h"
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 
 TrackerDialog::TrackerDialog()
 {
diff --git a/tracker-hatire/ftnoir_tracker_hat.h b/tracker-hatire/ftnoir_tracker_hat.h
index 03d00205..0969fd9c 100644
--- a/tracker-hatire/ftnoir_tracker_hat.h
+++ b/tracker-hatire/ftnoir_tracker_hat.h
@@ -1,7 +1,7 @@
 #pragma once
 
 #include "thread.hpp"
-#include "opentrack/plugin-support.hpp"
+#include "api/plugin-support.hpp"
 #include "ftnoir_tracker_hat_settings.h"
 #include "ftnoir_arduino_type.h"
 
diff --git a/tracker-hatire/ftnoir_tracker_hat_dialog.h b/tracker-hatire/ftnoir_tracker_hat_dialog.h
index b93c4301..82615d53 100644
--- a/tracker-hatire/ftnoir_tracker_hat_dialog.h
+++ b/tracker-hatire/ftnoir_tracker_hat_dialog.h
@@ -1,4 +1,4 @@
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 #include "ftnoir_tracker_hat_settings.h"
 #include "ftnoir_tracker_hat.h"
 #include "ui_ftnoir_hatcontrols.h"
diff --git a/tracker-hatire/ftnoir_tracker_hat_settings.h b/tracker-hatire/ftnoir_tracker_hat_settings.h
index 0450856b..fa24996e 100644
--- a/tracker-hatire/ftnoir_tracker_hat_settings.h
+++ b/tracker-hatire/ftnoir_tracker_hat_settings.h
@@ -8,7 +8,7 @@
 #pragma once
 
 #include <QSerialPort>
-#include "opentrack-compat/options.hpp"
+#include "compat/options.hpp"
 
 using namespace options;
 
diff --git a/tracker-hatire/thread.cpp b/tracker-hatire/thread.cpp
index 764bd875..4f5feae5 100644
--- a/tracker-hatire/thread.cpp
+++ b/tracker-hatire/thread.cpp
@@ -1,5 +1,5 @@
 #include "thread.hpp"
-#include "opentrack-compat/sleep.hpp"
+#include "compat/sleep.hpp"
 #include <utility>
 
 #include <QTextStream>
diff --git a/tracker-hatire/thread.hpp b/tracker-hatire/thread.hpp
index 26491cfe..dcdc13ee 100644
--- a/tracker-hatire/thread.hpp
+++ b/tracker-hatire/thread.hpp
@@ -10,8 +10,8 @@
 #include <QFile>
 #include <QCoreApplication>
 
-#include "opentrack/variance.hpp"
-#include "opentrack-compat/timer.hpp"
+#include "api/variance.hpp"
+#include "compat/timer.hpp"
 
 enum results
 {
diff --git a/tracker-ht/ftnoir_tracker_ht.cpp b/tracker-ht/ftnoir_tracker_ht.cpp
index 1ffc387e..045d05f8 100644
--- a/tracker-ht/ftnoir_tracker_ht.cpp
+++ b/tracker-ht/ftnoir_tracker_ht.cpp
@@ -1,21 +1,21 @@
 #include "headtracker-ftnoir.h"
 #include "ftnoir_tracker_ht.h"
 #include "ui_ht-trackercontrols.h"
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 #include <cmath>
-#include "opentrack/camera-names.hpp"
-#include "opentrack-compat/sleep.hpp"
+#include "api/camera-names.hpp"
+#include "compat/sleep.hpp"
 
 typedef struct {
-	int width;
-	int height;
+        int width;
+        int height;
 } resolution_tuple;
 
 static resolution_tuple resolution_choices[] = {
-	{ 640, 480 },
-	{ 320, 240 },
-	{ 320, 200 },
-	{ 0, 0 }
+        { 640, 480 },
+        { 320, 240 },
+        { 320, 200 },
+        { 0, 0 }
 };
 
 void Tracker::load_settings(ht_config_t* config)
@@ -63,9 +63,9 @@ void Tracker::load_settings(ht_config_t* config)
 
     int res = s.resolution;
     if (res < 0 || res >= (int)(sizeof(resolution_choices) / sizeof(resolution_tuple)))
-		res = 0;
-	resolution_tuple r = resolution_choices[res];
-	config->force_width = r.width;
+                res = 0;
+        resolution_tuple r = resolution_choices[res];
+        config->force_width = r.width;
     config->force_height = r.height;
     config->flandmark_delay = 50;
     for (int i = 0; i < 5; i++)
@@ -86,10 +86,10 @@ Tracker::~Tracker()
     should_stop = true;
     wait();
     ht_free_context(ht);
-	if (layout)
-		delete layout;
-	if (videoWidget)
-		delete videoWidget;
+        if (layout)
+                delete layout;
+        if (videoWidget)
+                delete videoWidget;
 }
 
 void Tracker::start_tracker(QFrame* videoframe)
diff --git a/tracker-ht/ftnoir_tracker_ht.h b/tracker-ht/ftnoir_tracker_ht.h
index 1ec3467a..96fd258d 100644
--- a/tracker-ht/ftnoir_tracker_ht.h
+++ b/tracker-ht/ftnoir_tracker_ht.h
@@ -10,11 +10,11 @@
 #include "headtracker-ftnoir.h"
 #include "ui_ht-trackercontrols.h"
 #include "ht_video_widget.h"
-#include "opentrack-compat/shm.h"
+#include "compat/shm.h"
 #include <QObject>
-#include "opentrack-compat/options.hpp"
-#include "opentrack/plugin-api.hpp"
-#include "opentrack/opencv-camera-dialog.hpp"
+#include "compat/options.hpp"
+#include "api/plugin-api.hpp"
+#include "api/opencv-camera-dialog.hpp"
 
 #include <QThread>
 #include <QMutex>
diff --git a/tracker-hydra/ftnoir_tracker_hydra.cpp b/tracker-hydra/ftnoir_tracker_hydra.cpp
index 33156f85..6b52153f 100644
--- a/tracker-hydra/ftnoir_tracker_hydra.cpp
+++ b/tracker-hydra/ftnoir_tracker_hydra.cpp
@@ -1,7 +1,7 @@
 /* Copyright: "i couldn't care less what anyone does with the 5 lines of code i wrote" - mm0zct */
 #include "ftnoir_tracker_hydra.h"
-#include "opentrack/plugin-api.hpp"
-#include "opentrack-compat/pi-constant.hpp"
+#include "api/plugin-api.hpp"
+#include "compat/pi-constant.hpp"
 #include <cstdio>
 #include <cmath>
 #ifdef _WIN32
diff --git a/tracker-hydra/ftnoir_tracker_hydra.h b/tracker-hydra/ftnoir_tracker_hydra.h
index 94396b7c..d8fe6afb 100644
--- a/tracker-hydra/ftnoir_tracker_hydra.h
+++ b/tracker-hydra/ftnoir_tracker_hydra.h
@@ -1,6 +1,6 @@
 #include "ui_ftnoir_hydra_clientcontrols.h"
-#include "opentrack/plugin-api.hpp"
-#include "opentrack-compat/options.hpp"
+#include "api/plugin-api.hpp"
+#include "compat/options.hpp"
 using namespace options;
 
 struct settings : opts {
diff --git a/tracker-hydra/ftnoir_tracker_hydra_dialog.cpp b/tracker-hydra/ftnoir_tracker_hydra_dialog.cpp
index 773f8b50..42019dfa 100644
--- a/tracker-hydra/ftnoir_tracker_hydra_dialog.cpp
+++ b/tracker-hydra/ftnoir_tracker_hydra_dialog.cpp
@@ -1,5 +1,5 @@
 #include "ftnoir_tracker_hydra.h"
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 
 TrackerControls::TrackerControls()
 {
diff --git a/tracker-joystick/ftnoir_tracker_joystick.cpp b/tracker-joystick/ftnoir_tracker_joystick.cpp
index d10b15ca..263a5cb0 100644
--- a/tracker-joystick/ftnoir_tracker_joystick.cpp
+++ b/tracker-joystick/ftnoir_tracker_joystick.cpp
@@ -5,7 +5,7 @@
  * copyright notice and this permission notice appear in all copies.
  */
 #include "ftnoir_tracker_joystick.h"
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 #include <QMutexLocker>
 
 FTNoIR_Tracker::FTNoIR_Tracker()
diff --git a/tracker-joystick/ftnoir_tracker_joystick.h b/tracker-joystick/ftnoir_tracker_joystick.h
index 2d776ebf..0cd26f36 100644
--- a/tracker-joystick/ftnoir_tracker_joystick.h
+++ b/tracker-joystick/ftnoir_tracker_joystick.h
@@ -15,10 +15,10 @@
 #include <QFrame>
 #include <QStringList>
 #include <cmath>
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 
-#include "opentrack-dinput/win32-joystick.hpp"
-#include "opentrack-compat/options.hpp"
+#include "dinput/win32-joystick.hpp"
+#include "compat/options.hpp"
 using namespace options;
 
 struct settings : opts {
diff --git a/tracker-joystick/ftnoir_tracker_joystick_dialog.cpp b/tracker-joystick/ftnoir_tracker_joystick_dialog.cpp
index 035beef6..1d67cdbb 100644
--- a/tracker-joystick/ftnoir_tracker_joystick_dialog.cpp
+++ b/tracker-joystick/ftnoir_tracker_joystick_dialog.cpp
@@ -1,5 +1,5 @@
 #include "ftnoir_tracker_joystick.h"
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 
 TrackerControls::TrackerControls() : tracker(nullptr)
 {
diff --git a/tracker-pt/camera.cpp b/tracker-pt/camera.cpp
index e5901da1..c15bb2be 100644
--- a/tracker-pt/camera.cpp
+++ b/tracker-pt/camera.cpp
@@ -6,8 +6,8 @@
  */
 
 #include "camera.h"
-#include "opentrack-compat/sleep.hpp"
-#include "opentrack-compat/camera-names.hpp"
+#include "compat/sleep.hpp"
+#include "compat/camera-names.hpp"
 #include <string>
 #include <QDebug>
 
diff --git a/tracker-pt/ftnoir_tracker_pt.cpp b/tracker-pt/ftnoir_tracker_pt.cpp
index 429a0305..eaf1de0d 100644
--- a/tracker-pt/ftnoir_tracker_pt.cpp
+++ b/tracker-pt/ftnoir_tracker_pt.cpp
@@ -12,8 +12,8 @@
 #include <QDebug>
 #include <QFile>
 #include <QCoreApplication>
-#include "opentrack-compat/camera-names.hpp"
-#include "opentrack-compat/sleep.hpp"
+#include "compat/camera-names.hpp"
+#include "compat/sleep.hpp"
 #include <functional>
 
 //#define PT_PERF_LOG	//log performance
diff --git a/tracker-pt/ftnoir_tracker_pt.h b/tracker-pt/ftnoir_tracker_pt.h
index f515715e..04a6646f 100644
--- a/tracker-pt/ftnoir_tracker_pt.h
+++ b/tracker-pt/ftnoir_tracker_pt.h
@@ -9,15 +9,15 @@
 #ifndef FTNOIR_TRACKER_PT_H
 #define FTNOIR_TRACKER_PT_H
 
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 #include "ftnoir_tracker_pt_settings.h"
 #include "camera.h"
 #include "point_extractor.h"
 #include "point_tracker.h"
-#include "opentrack-compat/timer.hpp"
+#include "compat/timer.hpp"
 #include "cv/camera-dialog.hpp"
 #include "cv/video-widget.hpp"
-#include "opentrack-compat/pi-constant.hpp"
+#include "compat/pi-constant.hpp"
 
 #include <QThread>
 #include <QMutex>
diff --git a/tracker-pt/ftnoir_tracker_pt_dialog.cpp b/tracker-pt/ftnoir_tracker_pt_dialog.cpp
index 9b09722f..307be1f2 100644
--- a/tracker-pt/ftnoir_tracker_pt_dialog.cpp
+++ b/tracker-pt/ftnoir_tracker_pt_dialog.cpp
@@ -12,7 +12,7 @@
 #include <QDebug>
 #include <opencv2/core/core.hpp>
 #include <memory>
-#include "opentrack-compat/camera-names.hpp"
+#include "compat/camera-names.hpp"
 #include <vector>
 
 //-----------------------------------------------------------------------------
diff --git a/tracker-pt/ftnoir_tracker_pt_dialog.h b/tracker-pt/ftnoir_tracker_pt_dialog.h
index d2647a94..49e8f1b0 100644
--- a/tracker-pt/ftnoir_tracker_pt_dialog.h
+++ b/tracker-pt/ftnoir_tracker_pt_dialog.h
@@ -8,7 +8,7 @@
 #ifndef FTNOIR_TRACKER_PT_DIALOG_H
 #define FTNOIR_TRACKER_PT_DIALOG_H
 
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 #include "ftnoir_tracker_pt_settings.h"
 #include "ftnoir_tracker_pt.h"
 #include "ui_FTNoIR_PT_Controls.h"
diff --git a/tracker-pt/ftnoir_tracker_pt_settings.h b/tracker-pt/ftnoir_tracker_pt_settings.h
index f5256f18..c076082a 100644
--- a/tracker-pt/ftnoir_tracker_pt_settings.h
+++ b/tracker-pt/ftnoir_tracker_pt_settings.h
@@ -8,7 +8,7 @@
 
 #pragma once
 
-#include "opentrack-compat/pi-constant.hpp"
+#include "compat/pi-constant.hpp"
 #include <limits>
 #include <opencv2/core.hpp>
 
@@ -28,7 +28,7 @@ struct pt_types
     using mat22 = mat<2, 2>;
 };
 
-#include "opentrack-compat/options.hpp"
+#include "compat/options.hpp"
 using namespace options;
 
 struct settings_pt : opts
diff --git a/tracker-pt/point_extractor.cpp b/tracker-pt/point_extractor.cpp
index e749934e..f4034b28 100644
--- a/tracker-pt/point_extractor.cpp
+++ b/tracker-pt/point_extractor.cpp
@@ -10,7 +10,7 @@
 #include <QDebug>
 
 #ifdef DEBUG_EXTRACTION
-#   include "opentrack-compat/timer.hpp"
+#   include "compat/timer.hpp"
 #endif
 
 #include <opencv2/videoio.hpp>
diff --git a/tracker-pt/point_extractor.h b/tracker-pt/point_extractor.h
index 9ef2adb4..0a019ec0 100644
--- a/tracker-pt/point_extractor.h
+++ b/tracker-pt/point_extractor.h
@@ -13,7 +13,7 @@
 #include <opencv2/imgproc/imgproc.hpp>
 
 #include "ftnoir_tracker_pt_settings.h"
-#include "opentrack-compat/pi-constant.hpp"
+#include "compat/pi-constant.hpp"
 
 #include <vector>
 
diff --git a/tracker-pt/point_tracker.cpp b/tracker-pt/point_tracker.cpp
index a2511118..fe8ff0f2 100644
--- a/tracker-pt/point_tracker.cpp
+++ b/tracker-pt/point_tracker.cpp
@@ -6,7 +6,7 @@
  */
 
 #include "point_tracker.h"
-#include "opentrack-compat/nan.hpp"
+#include "compat/nan.hpp"
 
 #include <vector>
 #include <algorithm>
@@ -325,11 +325,17 @@ int PointTracker::POSIT(const PointModel& model, const PointOrder& order_, f foc
     for (int i = 0; i < 3; i++)
         for (int j = 0; j < 3; j++)
             if (nanp(r(i, j)))
+            {
+                qDebug() << "posit nan";
                 return -1;
+            }
 
     for (unsigned i = 0; i < 3; i++)
         if (nanp(t[i]))
+        {
+            qDebug() << "posit nan";
             return -1;
+        }
 
     // apply results
     X_CM.R = r;
diff --git a/tracker-pt/point_tracker.h b/tracker-pt/point_tracker.h
index 00e371f5..6ed91069 100644
--- a/tracker-pt/point_tracker.h
+++ b/tracker-pt/point_tracker.h
@@ -8,7 +8,7 @@
 #ifndef POINTTRACKER_H
 #define POINTTRACKER_H
 
-#include "opentrack-compat/timer.hpp"
+#include "compat/timer.hpp"
 #include "ftnoir_tracker_pt_settings.h"
 #include <opencv2/core/core.hpp>
 #include <memory>
diff --git a/tracker-rift-025/ftnoir_tracker_rift_025.cpp b/tracker-rift-025/ftnoir_tracker_rift_025.cpp
index e6cbe3de..feacf5b1 100644
--- a/tracker-rift-025/ftnoir_tracker_rift_025.cpp
+++ b/tracker-rift-025/ftnoir_tracker_rift_025.cpp
@@ -1,7 +1,7 @@
 /* Copyright: "i couldn't care less what anyone does with the 5 lines of code i wrote" - mm0zct */
 #include "ftnoir_tracker_rift_025.h"
-#include "opentrack/plugin-api.hpp"
-#include "opentrack-compat/pi-constant.hpp"
+#include "api/plugin-api.hpp"
+#include "compat/pi-constant.hpp"
 #include <OVR.h>
 #include <cstdio>
 #include <cmath>
diff --git a/tracker-rift-025/ftnoir_tracker_rift_025.h b/tracker-rift-025/ftnoir_tracker_rift_025.h
index ea4d224f..33f615ea 100644
--- a/tracker-rift-025/ftnoir_tracker_rift_025.h
+++ b/tracker-rift-025/ftnoir_tracker_rift_025.h
@@ -1,7 +1,7 @@
 #pragma once
 #include "ui_ftnoir_rift_clientcontrols_025.h"
-#include "opentrack/plugin-api.hpp"
-#include "opentrack-compat/options.hpp"
+#include "api/plugin-api.hpp"
+#include "compat/options.hpp"
 #include <OVR.h>
 #include <cmath>
 #include <memory>
diff --git a/tracker-rift-025/ftnoir_tracker_rift_dialog_025.cpp b/tracker-rift-025/ftnoir_tracker_rift_dialog_025.cpp
index f13883e0..c3a6a198 100644
--- a/tracker-rift-025/ftnoir_tracker_rift_dialog_025.cpp
+++ b/tracker-rift-025/ftnoir_tracker_rift_dialog_025.cpp
@@ -1,5 +1,5 @@
 #include "ftnoir_tracker_rift_025.h"
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 
 TrackerControls::TrackerControls()
 {
diff --git a/tracker-rift-042/ftnoir_tracker_rift_042.cpp b/tracker-rift-042/ftnoir_tracker_rift_042.cpp
index a49f7d23..901e4d1f 100644
--- a/tracker-rift-042/ftnoir_tracker_rift_042.cpp
+++ b/tracker-rift-042/ftnoir_tracker_rift_042.cpp
@@ -1,7 +1,7 @@
 /* Copyright: "i couldn't care less what anyone does with the 5 lines of code i wrote" - mm0zct */
 #include "ftnoir_tracker_rift_042.h"
-#include "opentrack/plugin-api.hpp"
-#include "opentrack-compat/pi-constant.hpp"
+#include "api/plugin-api.hpp"
+#include "compat/pi-constant.hpp"
 #include <OVR_CAPI.h>
 #include <Kernel/OVR_Math.h>
 #include <cstdio>
diff --git a/tracker-rift-042/ftnoir_tracker_rift_042.h b/tracker-rift-042/ftnoir_tracker_rift_042.h
index e39ac654..55a59f18 100644
--- a/tracker-rift-042/ftnoir_tracker_rift_042.h
+++ b/tracker-rift-042/ftnoir_tracker_rift_042.h
@@ -1,7 +1,7 @@
 #pragma once
 #include "ui_ftnoir_rift_clientcontrols_042.h"
-#include "opentrack/plugin-api.hpp"
-#include "opentrack-compat/options.hpp"
+#include "api/plugin-api.hpp"
+#include "compat/options.hpp"
 #include <OVR.h>
 #include <QMessageBox>
 #include <QWaitCondition>
diff --git a/tracker-rift-042/ftnoir_tracker_rift_dialog_042.cpp b/tracker-rift-042/ftnoir_tracker_rift_dialog_042.cpp
index 3fcf632c..f520b4b5 100644
--- a/tracker-rift-042/ftnoir_tracker_rift_dialog_042.cpp
+++ b/tracker-rift-042/ftnoir_tracker_rift_dialog_042.cpp
@@ -1,5 +1,5 @@
 #include "ftnoir_tracker_rift_042.h"
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 
 TrackerControls::TrackerControls()
 {
diff --git a/tracker-rift-080/ftnoir_tracker_rift_080.cpp b/tracker-rift-080/ftnoir_tracker_rift_080.cpp
index d3019ac5..ce1f741a 100644
--- a/tracker-rift-080/ftnoir_tracker_rift_080.cpp
+++ b/tracker-rift-080/ftnoir_tracker_rift_080.cpp
@@ -1,7 +1,7 @@
 /* Copyright: "i couldn't care less what anyone does with the 5 lines of code i wrote" - mm0zct */
 #include "ftnoir_tracker_rift_080.h"
-#include "opentrack/plugin-api.hpp"
-#include "opentrack-compat/pi-constant.hpp"
+#include "api/plugin-api.hpp"
+#include "compat/pi-constant.hpp"
 #include <OVR_CAPI.h>
 #include <Extras/OVR_Math.h>
 #include <OVR_CAPI_0_8_0.h>
diff --git a/tracker-rift-080/ftnoir_tracker_rift_080.h b/tracker-rift-080/ftnoir_tracker_rift_080.h
index 280a729c..6adf6bcf 100644
--- a/tracker-rift-080/ftnoir_tracker_rift_080.h
+++ b/tracker-rift-080/ftnoir_tracker_rift_080.h
@@ -1,7 +1,7 @@
 #pragma once
 #include "ui_ftnoir_rift_clientcontrols_080.h"
-#include "opentrack/plugin-api.hpp"
-#include "opentrack-compat/options.hpp"
+#include "api/plugin-api.hpp"
+#include "compat/options.hpp"
 #include <OVR.h>
 #include <cmath>
 #include <QMessageBox>
diff --git a/tracker-rift-080/ftnoir_tracker_rift_dialog_080.cpp b/tracker-rift-080/ftnoir_tracker_rift_dialog_080.cpp
index fd28475d..aba4f6bf 100644
--- a/tracker-rift-080/ftnoir_tracker_rift_dialog_080.cpp
+++ b/tracker-rift-080/ftnoir_tracker_rift_dialog_080.cpp
@@ -1,5 +1,5 @@
 #include "ftnoir_tracker_rift_080.h"
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 
 TrackerControls::TrackerControls()
 {
diff --git a/tracker-rs/ftnoir_tracker_rs.cpp b/tracker-rs/ftnoir_tracker_rs.cpp
index 4a59d74a..1ab32992 100644
--- a/tracker-rs/ftnoir_tracker_rs.cpp
+++ b/tracker-rs/ftnoir_tracker_rs.cpp
@@ -8,7 +8,7 @@
 #include "ftnoir_tracker_rs.h"
 #include "ftnoir_tracker_rs_controls.h"
 #include "imagewidget.h"
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 #include "opentrack-library-path.h"
 #include <QMessageBox>
 #include <QProcess>
@@ -46,7 +46,7 @@ void RSTracker::configurePreviewFrame()
 
 void RSTracker::start_tracker(QFrame* previewFrame)
 {
-	qDebug() << "tracker_rs: starting tracker";
+        qDebug() << "tracker_rs: starting tracker";
 
     mPreviewFrame = previewFrame;
 
@@ -58,20 +58,20 @@ void RSTracker::start_tracker(QFrame* previewFrame)
 }
 
 void RSTracker::startPreview(){
-	qDebug() << "tracker_rs: starting preview";
+        qDebug() << "tracker_rs: starting preview";
     mPreviewUpdateTimer.start(kPreviewUpdateInterval);
 }
 
 void RSTracker::updatePreview(){
-	if (mImageWidget != nullptr && mImageWidget->isEnabled() && mTrackerWorkerThread.isRunning())
-		mImageWidget->setImage(mTrackerWorkerThread.getPreview());
-	else
-		qDebug() << "tracker_rs: not updating preview. worker thread running: " << mTrackerWorkerThread.isRunning();
+        if (mImageWidget != nullptr && mImageWidget->isEnabled() && mTrackerWorkerThread.isRunning())
+                mImageWidget->setImage(mTrackerWorkerThread.getPreview());
+        else
+                qDebug() << "tracker_rs: not updating preview. worker thread running: " << mTrackerWorkerThread.isRunning();
 }
 
 void RSTracker::stopPreview(){
-	mPreviewUpdateTimer.stop();
-	qDebug() << "tracker_rs: stopped preview";
+        mPreviewUpdateTimer.stop();
+        qDebug() << "tracker_rs: stopped preview";
 }
 
 void RSTracker::handleTrackingEnded(int exitCode){
@@ -98,17 +98,17 @@ void RSTracker::showRealSenseErrorMessageBox(int exitCode)
     msgBox.setIcon(QMessageBox::Critical);
     msgBox.setText("RealSense Tracking Error");
 
-	switch(exitCode){
-	case -101: //The implementation got an invalid handle from the RealSense SDK session/modules
+        switch(exitCode){
+        case -101: //The implementation got an invalid handle from the RealSense SDK session/modules
         msgBox.setInformativeText("Couldn't initialize RealSense tracking. Please make sure SDK Runtime 2016 R2 is installed.");
-		break;
-	case -301: //RealSense SDK runtime execution aborted.
-		msgBox.setInformativeText("Tracking stopped after the RealSense SDK Runtime execution has aborted.");
-		break;
-	case -601: //RealSense Camera stream configuration has changed.
-		msgBox.setInformativeText("Tracking stopped after another program changed camera streams configuration.");
-		break;
-	default:
+                break;
+        case -301: //RealSense SDK runtime execution aborted.
+                msgBox.setInformativeText("Tracking stopped after the RealSense SDK Runtime execution has aborted.");
+                break;
+        case -601: //RealSense Camera stream configuration has changed.
+                msgBox.setInformativeText("Tracking stopped after another program changed camera streams configuration.");
+                break;
+        default:
         msgBox.setInformativeText("Status code: " + QString::number(exitCode) + ".\n\nNote that you need the latest camera drivers and the SDK runtime 2016 R2 to be installed.");
     }
 
@@ -126,7 +126,7 @@ void RSTracker::data(double *data)
 }
 
 RSTracker::~RSTracker() {
-	qDebug() << "tracker is being destroyed.";
+        qDebug() << "tracker is being destroyed.";
 
     stopPreview();
 
diff --git a/tracker-rs/ftnoir_tracker_rs.h b/tracker-rs/ftnoir_tracker_rs.h
index 845e0beb..083794ed 100644
--- a/tracker-rs/ftnoir_tracker_rs.h
+++ b/tracker-rs/ftnoir_tracker_rs.h
@@ -7,7 +7,7 @@
  
 #pragma once
 
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 #include "ui_ftnoir_tracker_rs_controls.h"
 #include  "ftnoir_tracker_rs_worker.h"
 #include <QTimer>
diff --git a/tracker-test/test.cpp b/tracker-test/test.cpp
index b6cc742e..3e0db547 100644
--- a/tracker-test/test.cpp
+++ b/tracker-test/test.cpp
@@ -7,7 +7,7 @@
  */
 
 #include "test.h"
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 #include <cmath>
 
 #include <QDebug>
diff --git a/tracker-test/test.h b/tracker-test/test.h
index afb11f6f..786d7705 100644
--- a/tracker-test/test.h
+++ b/tracker-test/test.h
@@ -1,8 +1,8 @@
 #pragma once
 #include "ui_test.h"
-#include "opentrack/plugin-api.hpp"
-#include "opentrack-compat/timer.hpp"
-#include "opentrack-compat/pi-constant.hpp"
+#include "api/plugin-api.hpp"
+#include "compat/timer.hpp"
+#include "compat/pi-constant.hpp"
 
 class FTNoIR_Tracker : public ITracker
 {
diff --git a/tracker-test/test_dialog.cpp b/tracker-test/test_dialog.cpp
index 38578ffc..5d33555b 100644
--- a/tracker-test/test_dialog.cpp
+++ b/tracker-test/test_dialog.cpp
@@ -1,3 +1,3 @@
 #include "test.h"
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 
diff --git a/tracker-udp/ftnoir_tracker_udp.cpp b/tracker-udp/ftnoir_tracker_udp.cpp
index bc3bebc6..8438d48f 100644
--- a/tracker-udp/ftnoir_tracker_udp.cpp
+++ b/tracker-udp/ftnoir_tracker_udp.cpp
@@ -7,7 +7,7 @@
  */
 
 #include "ftnoir_tracker_udp.h"
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 
 FTNoIR_Tracker::FTNoIR_Tracker() : last_recv_pose { 0,0,0, 0,0,0 }, should_quit(false) {}
 
diff --git a/tracker-udp/ftnoir_tracker_udp.h b/tracker-udp/ftnoir_tracker_udp.h
index 3f0e6d8c..292df1f5 100644
--- a/tracker-udp/ftnoir_tracker_udp.h
+++ b/tracker-udp/ftnoir_tracker_udp.h
@@ -3,8 +3,8 @@
 #include <QUdpSocket>
 #include <QThread>
 #include <cmath>
-#include "opentrack/plugin-api.hpp"
-#include "opentrack-compat/options.hpp"
+#include "api/plugin-api.hpp"
+#include "compat/options.hpp"
 using namespace options;
 
 struct settings : opts {
diff --git a/tracker-udp/ftnoir_tracker_udp_dialog.cpp b/tracker-udp/ftnoir_tracker_udp_dialog.cpp
index df274541..af94183c 100644
--- a/tracker-udp/ftnoir_tracker_udp_dialog.cpp
+++ b/tracker-udp/ftnoir_tracker_udp_dialog.cpp
@@ -1,5 +1,5 @@
 #include "ftnoir_tracker_udp.h"
-#include "opentrack/plugin-api.hpp"
+#include "api/plugin-api.hpp"
 
 TrackerControls::TrackerControls()
 {
-- 
cgit v1.2.3