diff options
64 files changed, 1030 insertions, 593 deletions
diff --git a/api/plugin-support.hpp b/api/plugin-support.hpp index 2ec55f0e..1bafd6ff 100644 --- a/api/plugin-support.hpp +++ b/api/plugin-support.hpp @@ -8,25 +8,17 @@ #pragma once #include "plugin-api.hpp" -#include "options/options.hpp" -#include <QWidget> +#include <memory> +#include <algorithm> + #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> +#include <QIcon> #if defined(__APPLE__) # define OPENTRACK_SOLIB_EXT "dylib" @@ -36,27 +28,25 @@ # define OPENTRACK_SOLIB_EXT "so" #endif -#include <iostream> - #ifdef _MSC_VER # define OPENTRACK_SOLIB_PREFIX "" #else -# define OPENTRACK_SOLIB_PREFIX "" +# 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 : unsigned { Filter, Tracker, Protocol, Invalid = 0xcafebabeU }; +struct dylib final +{ + enum Type { Filter = 0xdeadbabe, Tracker = 0xcafebeef, Protocol = 0xdeadf00d, Invalid = 0xcafebabe }; dylib(const QString& filename, Type t) : type(Invalid), filename(filename), Dialog(nullptr), Constructor(nullptr), - Meta(nullptr), - handle(nullptr) + Meta(nullptr) { // otherwise dlopen opens the calling executable if (filename.size() == 0) @@ -77,7 +67,7 @@ struct dylib final { if (check((Meta = (OPENTRACK_METADATA_FUNPTR) handle.resolve("GetMetadata"), !Meta))) return; - auto m = mem<Metadata>(Meta()); + auto m = std::unique_ptr<Metadata>(Meta()); icon = m->icon(); name = m->name(); @@ -94,38 +84,42 @@ struct dylib final { } } - static QList<mem<dylib>> enum_libraries(const QString& library_path) + static QList<std::shared_ptr<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++) + QDir module_directory(library_path); + QList<std::shared_ptr<dylib>> ret; + + static const struct filter_ { + Type type; + QString glob; + } filters[] = { + { Filter, OPENTRACK_SOLIB_PREFIX "opentrack-filter-*." OPENTRACK_SOLIB_EXT, }, + { Tracker, OPENTRACK_SOLIB_PREFIX "opentrack-tracker-*." OPENTRACK_SOLIB_EXT, }, + { Protocol, OPENTRACK_SOLIB_PREFIX "opentrack-proto-*." OPENTRACK_SOLIB_EXT, }, + }; + + for (const filter_& filter : filters) { - 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) + for (const QString& filename : module_directory.entryList({ filter.glob }, QDir::Files, QDir::Name)) { - QIcon icon; - QString longName; - auto lib = std::make_shared<dylib>(library_path + filename, t); - if (!get_metadata(lib, longName, icon)) + std::shared_ptr<dylib> lib = std::make_shared<dylib>(QStringLiteral("%1/%2").arg(library_path).arg(filename), filter.type); + + if (lib->type == Invalid) + { + qDebug() << "can't load dylib" << filename; continue; + } + if (std::any_of(ret.cbegin(), ret.cend(), - [&](mem<dylib> a) {return a->type == lib->type && a->name == lib->name;})) + [&lib](const std::shared_ptr<dylib>& a) { + return a->type == lib->type && a->name == lib->name; + })) { - qDebug() << "Duplicate lib" << lib->filename; + qDebug() << "duplicate lib" << filename << "ident" << lib->name; continue; } + ret.push_back(lib); } } @@ -161,60 +155,50 @@ private: return fail; } - - 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 final { + using dylib_ptr = std::shared_ptr<dylib>; + using dylib_list = QList<dylib_ptr>; + 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; } + dylib_list& filters() { return filter_modules; } + dylib_list& trackers() { return tracker_modules; } + dylib_list& protocols() { return protocol_modules; } private: - QList<mem<dylib>> module_list; - QList<mem<dylib>> filter_modules; - QList<mem<dylib>> tracker_modules; - QList<mem<dylib>> protocol_modules; + dylib_list module_list; + dylib_list filter_modules; + dylib_list tracker_modules; + dylib_list protocol_modules; - template<typename t> - static void sort(QList<t>& xs) + static dylib_list& sorted(dylib_list& xs) { - std::sort(xs.begin(), xs.end(), [&](const t& a, const t& b) { return a->name.toLower() < b->name.toLower(); }); + std::sort(xs.begin(), xs.end(), [&](const dylib_ptr& a, const dylib_ptr& b) { return a->name.toLower() < b->name.toLower(); }); + return xs; } - QList<mem<dylib>> filter(dylib::Type t) + dylib_list filter(dylib::Type t) { - QList<mem<dylib>> ret; + QList<std::shared_ptr<dylib>> ret; for (auto x : module_list) if (x->type == t) ret.push_back(x); - sort(ret); - - return ret; + return sorted(ret); } }; template<typename t> -mem<t> make_dylib_instance(mem<dylib> lib) +static inline std::shared_ptr<t> make_dylib_instance(const std::shared_ptr<dylib>& lib) { - mem<t> ret; + std::shared_ptr<t> ret; if (lib != nullptr && lib->Constructor) - ret = mem<t>(reinterpret_cast<t*>(reinterpret_cast<OPENTRACK_CTOR_FUNPTR>(lib->Constructor)())); + ret = std::shared_ptr<t>(reinterpret_cast<t*>(reinterpret_cast<OPENTRACK_CTOR_FUNPTR>(lib->Constructor)())); return ret; } diff --git a/bin/drmemory-suppressions.txt b/bin/drmemory-suppressions.txt new file mode 100644 index 00000000..1f94c108 --- /dev/null +++ b/bin/drmemory-suppressions.txt @@ -0,0 +1,43 @@ +UNADDRESSABLE ACCESS +name=false positive 1 +Qt5Gui.dll!qt_blend_argb32_on_argb32_ssse3 + +INVALID HEAP ARGUMENT +name=stfu qtcore +Qt5Core.dll!* + +INVALID HEAP ARGUMENT +name=stfu qwindows 1 +qwindows.dll!* + +LEAK +name=stfu qwindows 2 +qwindows.dll!* + +INVALID HEAP ARGUMENT +name=stfu qtgui +Qt5Gui.dll!* + +INVALID HEAP ARGUMENT +name=stfu sixense 1 +sixense.dll!* + +UNADDRESSABLE ACCESS +name=stfu sixense 2 +sixense.dll!* + +UNINITIALIZED READ +name=dinput +system call NtUserGetRawInputDeviceInfo parameter value #2 + +LEAK +name=stfu leak qtcore 1 +Qt5Core.dll!* + +LEAK +name=cv 1 +opentrack-tracker-pt.dll!cv::`dynamic initializer for '__initialization_mutex_initializer'' + +LEAK +name=cv 2 +opentrack-tracker-aruco.dll!cv::`dynamic initializer for '__initialization_mutex_initializer''
\ No newline at end of file diff --git a/cmake/msvc.cmake b/cmake/msvc.cmake index bab3f3df..c0aee783 100644 --- a/cmake/msvc.cmake +++ b/cmake/msvc.cmake @@ -43,7 +43,7 @@ if(CMAKE_PROJECT_NAME STREQUAL "opentrack") set(cc "${cc} /GR- /arch:SSE2") endif() -set(silly "${warns_} /MT /Gm") +set(silly "${warns_} -MT -Gm -Zi") set(_CFLAGS "${silly}") set(_CXXFLAGS "${silly}") diff --git a/cmake/opentrack-boilerplate.cmake b/cmake/opentrack-boilerplate.cmake index 58f80573..3c5bd90a 100644 --- a/cmake/opentrack-boilerplate.cmake +++ b/cmake/opentrack-boilerplate.cmake @@ -75,7 +75,7 @@ endfunction() function(otr_compat target) if(NOT MSVC) - set_property(SOURCE ${${target}-moc} APPEND_STRING PROPERTY COMPILE_FLAGS "-w -Wno-error") + set_property(SOURCE ${${target}-moc} APPEND_STRING PROPERTY COMPILE_FLAGS " -w -Wno-error") endif() if(WIN32) target_link_libraries(${target} dinput8 dxguid strmiids) @@ -84,6 +84,8 @@ function(otr_compat target) set(c-props) set(l-props) + get_property(linker-lang TARGET ${target} PROPERTY LINKER_LANGUAGE) + if(CMAKE_COMPILER_IS_GNUCXX) set(c-props " -fvisibility=hidden") if(NOT is-c-only) @@ -95,8 +97,8 @@ function(otr_compat target) set(l-props "-Wl,--as-needed") endif() - set_property(TARGET ${target} APPEND_STRING PROPERTY COMPILE_FLAGS "${c-props} ${arg_COMPILE}") - set_property(TARGET ${target} APPEND_STRING PROPERTY LINK_FLAGS "${l-props} ${arg_LINK}") + set_property(TARGET ${target} APPEND_STRING PROPERTY COMPILE_FLAGS " ${c-props} ${arg_COMPILE}") + set_property(TARGET ${target} APPEND_STRING PROPERTY LINK_FLAGS " ${l-props} ${arg_LINK}") endfunction() include(CMakeParseArguments) @@ -126,11 +128,12 @@ endfunction() function(otr_module n_) message(STATUS "module ${n_}") cmake_parse_arguments(arg - "STATIC;NO-COMPAT;BIN;EXECUTABLE;NO-QT;WIN32-CONSOLE;NO-INSTALL" + "STATIC;NO-COMPAT;BIN;EXECUTABLE;NO-QT;WIN32-CONSOLE;NO-INSTALL;RELINK" "LINK;COMPILE" "SOURCES" ${ARGN} ) + if(NOT "${arg_UNPARSED_ARGUMENTS}" STREQUAL "") message(FATAL_ERROR "otr_module bad formals: ${arg_UNPARSED_ARGUMENTS}") endif() @@ -164,6 +167,12 @@ function(otr_module n_) add_library(${n} ${link-mode} "${${n}-all}") endif() + if(NOT arg_RELINK) + set_property(TARGET ${n} PROPERTY LINK_DEPENDS_NO_SHARED TRUE) + else() + set_property(TARGET ${n} PROPERTY LINK_DEPENDS_NO_SHARED FALSE) + endif() + if(NOT arg_NO-QT) target_link_libraries(${n} ${MY_QT_LIBS}) endif() diff --git a/cmake/opentrack-platform.cmake b/cmake/opentrack-platform.cmake index 5bb7581a..3e0319ac 100644 --- a/cmake/opentrack-platform.cmake +++ b/cmake/opentrack-platform.cmake @@ -43,6 +43,7 @@ endif() if(MSVC) add_definitions(-DNOMINMAX -D_CRT_SECURE_NO_WARNINGS) add_definitions(-D_ITERATOR_DEBUG_LEVEL=0 -D_HAS_ITERATOR_DEBUGGING=0 -D_SECURE_SCL=0) + add_definitions(-DHAS_EXCEPTIONS=0) set(CMAKE_CXX_FLAGS "-std:c++14 ${CMAKE_CXX_FLAGS}") if(SDK_INSTALL_DEBUG_INFO) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Zi") diff --git a/cmake/opentrack-policy.cmake b/cmake/opentrack-policy.cmake index 2d88e218..7ded20f7 100644 --- a/cmake/opentrack-policy.cmake +++ b/cmake/opentrack-policy.cmake @@ -1,27 +1,5 @@ -if(POLICY CMP0020) - cmake_policy(SET CMP0020 NEW) -endif() - -if(POLICY CMP0058) - cmake_policy(SET CMP0058 NEW) -endif() - -if(POLICY CMP0028) - cmake_policy(SET CMP0028 NEW) -endif() - -if(POLICY CMP0042) - cmake_policy(SET CMP0042 NEW) -endif() - -if(POLICY CMP0063) - cmake_policy(SET CMP0063 NEW) -endif() - -if(POLICY CMP0053) - cmake_policy(SET CMP0053 OLD) -endif() - -if(POLICY CMP0011) - cmake_policy(SET CMP0011 NEW) -endif() +foreach(k CMP0020 CMP0058 CMP0028 CMP0042 CMP0063 CMP0053 CMP0011 CMP0054) + if(POLICY ${k}) + cmake_policy(SET ${k} NEW) + endif() +endforeach() diff --git a/cmake/opentrack-qt.cmake b/cmake/opentrack-qt.cmake index d6c8315b..21bc42e1 100644 --- a/cmake/opentrack-qt.cmake +++ b/cmake/opentrack-qt.cmake @@ -6,7 +6,11 @@ set(MY_QT_LIBS ${Qt5Core_LIBRARIES} ${Qt5Gui_LIBRARIES} ${Qt5Widgets_LIBRARIES} if(WIN32) foreach(i Qt5Core Qt5Gui Qt5Network Qt5SerialPort Qt5Widgets) - install(FILES "${Qt5_DIR}/../../../bin/${i}.dll" DESTINATION .) + set(path "${Qt5_DIR}/../../../bin/${i}") + install(FILES "${path}.dll" DESTINATION .) + if(EXISTS "${path}.pdb") + install(FILES "${path}.pdb" DESTINATION "${opentrack-hier-debug}") + endif() endforeach() install(FILES "${Qt5_DIR}/../../../plugins/platforms/qwindows.dll" DESTINATION "./platforms") endif() diff --git a/cmake/opentrack-version.cmake b/cmake/opentrack-version.cmake index 9cd597d3..00a5df1d 100644 --- a/cmake/opentrack-version.cmake +++ b/cmake/opentrack-version.cmake @@ -40,6 +40,6 @@ endif() add_library(opentrack-version STATIC ${CMAKE_BINARY_DIR}/version.c) if(NOT MSVC) - set_property(TARGET opentrack-version APPEND_STRING PROPERTY COMPILE_FLAGS "-fno-lto") + set_property(TARGET opentrack-version APPEND_STRING PROPERTY COMPILE_FLAGS " -fno-lto") endif() otr_compat(opentrack-version) diff --git a/compat/CMakeLists.txt b/compat/CMakeLists.txt index 40850862..7655a223 100644 --- a/compat/CMakeLists.txt +++ b/compat/CMakeLists.txt @@ -5,5 +5,5 @@ if(NOT WIN32 AND NOT APPLE) endif() if(CMAKE_COMPILER_IS_GNUCXX) - set_property(SOURCE nan.cpp APPEND_STRING PROPERTY COMPILE_FLAGS "-fno-lto -fno-fast-math -fno-finite-math-only -O0") + set_property(SOURCE nan.cpp APPEND_STRING PROPERTY COMPILE_FLAGS " -fno-lto -fno-fast-math -fno-finite-math-only -O0") endif() diff --git a/compat/functional.hpp b/compat/functional.hpp new file mode 100644 index 00000000..893fe1a0 --- /dev/null +++ b/compat/functional.hpp @@ -0,0 +1,86 @@ +#pragma once + +#include <algorithm> +#include <iterator> +#include <type_traits> + +template<typename t> +using remove_qualifiers = std::remove_reference_t<std::remove_cv_t<t>>; + +namespace functools +{ + +template<typename seq_, typename = void> +struct reserver_ +{ + static inline void maybe_reserve_space(seq_&, unsigned) + { + //qDebug() << "nada"; + } +}; + +template<typename seq_> +struct reserver_<seq_, decltype(std::declval<seq_>().reserve(0u), (void)0)> +{ + static inline void maybe_reserve_space(seq_& seq, unsigned sz) + { + seq.reserve(sz); + } +}; + +template<typename seq_> +inline void maybe_reserve_space(seq_& seq, unsigned sz) +{ + reserver_<seq_, void>::maybe_reserve_space(seq, sz); +} + +} // ns + +template<typename t, t value_> +struct constant final +{ + using type = t; + constexpr type operator()() const noexcept + { + return value_; + } + static constexpr type value = value_; + + constant() = delete; +}; + +template<typename seq_, typename F> +auto map(F&& fun, const seq_& seq) +{ + using seq_type = remove_qualifiers<seq_>; + + seq_type ret; + std::back_insert_iterator<seq_type> it = std::back_inserter(ret); + + for (const auto& elt : seq) + it = fun(elt); + + return ret; +} + +template<typename seq_, typename F> +auto remove_if_not(F&& fun, const seq_& seq) +{ + using namespace functools; + + using seq_type = remove_qualifiers<seq_>; + using value_type = typename std::iterator_traits<decltype(std::begin(std::declval<seq_>()))>::value_type; + using fun_ret_type = decltype(fun(std::declval<const value_type&>())); + static_assert(std::is_convertible<fun_ret_type, bool>::value, "must return bool"); + + seq_type ret; + maybe_reserve_space(ret, seq.size()); + + std::back_insert_iterator<seq_type> it = std::back_inserter(ret); + + for (const value_type& elt : seq) + if (fun(elt)) + it = elt; + + return ret; +} diff --git a/compat/meta.hpp b/compat/meta.hpp index b1c1dd1a..900c515f 100644 --- a/compat/meta.hpp +++ b/compat/meta.hpp @@ -1,6 +1,8 @@ #pragma once +#include <type_traits> #include <tuple> +#include <cstddef> namespace meta { @@ -29,7 +31,20 @@ namespace detail { { using type = inst<xs...>; }; -} + + template<typename N, N max, N pos, typename... xs> + struct index_sequence_ + { + using part = std::integral_constant<N, pos>; + using type = typename index_sequence_<N, max, pos+1, xs..., part>::type; + }; + + template<typename N, N max, typename... xs> + struct index_sequence_<N, max, max, xs...> + { + using type = std::tuple<xs...>; + }; +} // ns detail template<typename... xs> @@ -50,4 +65,7 @@ using butlast = reverse<rest<reverse<xs...>>>; template<typename... xs> using last = lift<first, reverse<xs...>>; -} +template<std::size_t max> +using index_sequence = typename detail::index_sequence_<std::size_t, max, std::size_t(0)>::type; + +} // ns meta diff --git a/compat/time.hpp b/compat/time.hpp new file mode 100644 index 00000000..fbe7469a --- /dev/null +++ b/compat/time.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include <chrono> +#include <type_traits> + +namespace time_units { + +template<typename repr, typename ratio> +using duration = std::chrono::duration<repr, ratio>; + +template<typename t, typename u> +static inline constexpr auto time_cast(const u& in) +{ + return std::chrono::duration_cast<t>(in); +} + +using secs = duration<double, std::ratio<1, 1>>; +using secs_ = duration<long long, std::ratio<1, 1>>; +using ms = duration<double, std::milli>; +using ms_ = duration<long long, std::milli>; +using us = duration<double, std::micro>; +using us_ = duration<long long, std::micro>; +using ns = duration<long long, std::nano>; + +} // ns time_units diff --git a/compat/timer-resolution.cpp b/compat/timer-resolution.cpp index 597ee91e..d59c250d 100644 --- a/compat/timer-resolution.cpp +++ b/compat/timer-resolution.cpp @@ -8,12 +8,11 @@ #include "timer-resolution.hpp" #if defined _WIN32 -# include <windows.h> - # include <QLibrary> # include <QDebug> -typedef LONG (__stdcall *funptr_NtSetTimerResolution) (ULONG, BOOLEAN, PULONG); +using namespace timer_impl; + static funptr_NtSetTimerResolution init_timer_resolution_funptr(); static funptr_NtSetTimerResolution get_funptr(); @@ -41,62 +40,72 @@ static funptr_NtSetTimerResolution init_timer_resolution_funptr() static funptr_NtSetTimerResolution get_funptr() { + // starting with C++11 static initializers are fully + // thread-safe and run the first time function is called + + // cf. http://stackoverflow.com/questions/8102125/is-local-static-variable-initialization-thread-safe-in-c11 + static auto ret = init_timer_resolution_funptr(); return ret; } -timer_resolution::timer_resolution(int msecs) : old_value(-1) +timer_resolution::timer_resolution(int msecs) : old_value(fail_()) { - if (msecs <= 0 || msecs > 30) + if (msecs <= 0 || msecs > 100) { qDebug() << "can't set timer resolution to" << msecs << "ms"; return; } - funptr_NtSetTimerResolution f = get_funptr(); - if (f == nullptr) + funptr_NtSetTimerResolution set_timer_res = get_funptr(); + if (set_timer_res == nullptr) return; // hundredth of a nanosecond - const ULONG value = msecs * ULONG(10000); - NTSTATUS error = f(value, TRUE, &old_value); - ULONG old_value_ = -1; + const ulong_ value = msecs * ulong_(10000); + ntstatus_ res; - if (error != 0) + res = set_timer_res(value, true_(), &old_value); + + if (res < 0) { - old_value = -1; - qDebug() << "NtSetTimerResolution erred with" << error; + old_value = fail_(); + qDebug() << "NtSetTimerResolution erred with" << res; return; } // see if it stuck - error = f(value, TRUE, &old_value_); - if (error != 0) + ulong_ old_value_ = fail_(); + + res = set_timer_res(value, true_(), &old_value_); + if (res < 0) { - old_value = -1; - qDebug() << "NtSetTimerResolution check erred with" << error; + old_value = fail_(); + qDebug() << "NtSetTimerResolution check erred with" << res; return; } if (old_value_ != old_value) { + using t = long long; + qDebug() << "NtSetTimerResolution:" << "old value didn't stick" - << "current resolution" << old_value_ - << "* 100 ns"; - old_value = -1; + << "current resolution" << (t(old_value_) * t(100)) + << "ns"; + old_value = fail_(); return; } } timer_resolution::~timer_resolution() { - if (old_value != ULONG(-1)) + if (old_value != fail_()) { funptr_NtSetTimerResolution f = get_funptr(); - ULONG fuzz = -1; - (void) f(old_value, TRUE, &fuzz); + ulong_ fuzz = fail_(); + (void) f(old_value, true_(), &fuzz); } } diff --git a/compat/timer-resolution.hpp b/compat/timer-resolution.hpp index 5db877c0..8c18a600 100644 --- a/compat/timer-resolution.hpp +++ b/compat/timer-resolution.hpp @@ -3,14 +3,57 @@ #if defined _WIN32 # include "export.hpp" +#include <type_traits> + +namespace timer_impl +{ + +// cf. https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396 +// for NTSTATUS, see http://stackoverflow.com/questions/3378622/how-to-understand-the-ntstatus-nt-success-typedef-in-windows-ddk + +using ulong_ = unsigned long; +using long_ = long; +using byte_ = unsigned char; +using boolean_ = byte_; + +using true_ = std::integral_constant<int, 1>; + +using fail_ = std::integral_constant<byte_, byte_(-1)>; + +// cf. http://stackoverflow.com/questions/3378622/how-to-understand-the-ntstatus-nt-success-typedef-in-windows-ddk + +// typedef __success(return >= 0) LONG NTSTATUS; + +// __success(expr) T f() : indicates whether function f succeeded or +// not. If is true at exit, all the function's guarantees (as given +// by other annotations) must hold. If is false at exit, the caller +// should not expect any of the function's guarantees to hold. If not used, +// the function must always satisfy its guarantees. Added automatically to +// functions that indicate success in standard ways, such as by returning an +// HRESULT. + +using ntstatus_ = long_; + +// finally what we want +// this is equivalent to typedef syntax + +using funptr_NtSetTimerResolution = ntstatus_ (__stdcall *)(ulong_, boolean_, ulong_*); + +// RAII wrapper + class OTR_COMPAT_EXPORT timer_resolution final { - unsigned long old_value; + ulong_ old_value; public: timer_resolution(int msecs); ~timer_resolution(); }; + +} + +using timer_impl::timer_resolution; + #else struct timer_resolution final { diff --git a/compat/timer.cpp b/compat/timer.cpp index d5052ccb..3ecdf528 100644 --- a/compat/timer.cpp +++ b/compat/timer.cpp @@ -7,6 +7,7 @@ */ #include "timer.hpp" +#include <cmath> Timer::Timer() { @@ -15,17 +16,20 @@ Timer::Timer() void Timer::start() { - wrap_gettime(&state); + gettime(&state); } // common -void Timer::wrap_gettime(timespec* state) +void Timer::gettime(timespec* state) { #if defined(_WIN32) || defined(__MACH__) otr_clock_gettime(state); +#elif defined CLOCK_MONOTONIC + const int res = clock_gettime(CLOCK_MONOTONIC, state); + assert(res == 0 && "must support CLOCK_MONOTONIC"); #else - (void) clock_gettime(CLOCK_MONOTONIC, state); +# error "timer query method not known" #endif } @@ -33,14 +37,13 @@ void Timer::wrap_gettime(timespec* state) long long Timer::elapsed_nsecs() const { - struct timespec cur = {}; - wrap_gettime(&cur); + timespec cur{}; + gettime(&cur); return conv_nsecs(cur); } long long Timer::conv_nsecs(const timespec& cur) const { - // this can and will overflow return (cur.tv_sec - state.tv_sec) * 1000000000LL + (cur.tv_nsec - state.tv_nsec); } @@ -48,8 +51,8 @@ long long Timer::conv_nsecs(const timespec& cur) const double Timer::elapsed_usecs() const { - struct timespec cur = {}; - wrap_gettime(&cur); + timespec cur{}; + gettime(&cur); const long long nsecs = conv_nsecs(cur); return nsecs * 1e-3; } @@ -73,27 +76,26 @@ double Timer::elapsed_seconds() const #if defined _WIN32 LARGE_INTEGER Timer::otr_get_clock_frequency() { - LARGE_INTEGER freq = {}; + LARGE_INTEGER freq{}; (void) QueryPerformanceFrequency(&freq); return freq; } void Timer::otr_clock_gettime(timespec* ts) { - static LARGE_INTEGER freq = otr_get_clock_frequency(); + static const LARGE_INTEGER freq = otr_get_clock_frequency(); 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); + const ll part = ll(std::roundl((d.QuadPart * 1000000000.L) / ll(freq.QuadPart))); 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); + ts->tv_sec = t_s(part / 1000000000); + ts->tv_nsec = t_ns(part % 1000000000); } #elif defined __MACH__ @@ -106,7 +108,7 @@ mach_timebase_info_data_t Timer::otr_get_mach_frequency() double Timer::otr_clock_gettime(timespec* ts) { - static mach_timebase_info_data_t timebase_info = otr_get_mach_frequency(); + static const mach_timebase_info_data_t timebase_info = otr_get_mach_frequency(); uint64_t state, nsec; state = mach_absolute_time(); nsec = state * timebase_info.numer / timebase_info.denom; diff --git a/compat/timer.hpp b/compat/timer.hpp index e9efba79..58e1c7d6 100644 --- a/compat/timer.hpp +++ b/compat/timer.hpp @@ -20,7 +20,9 @@ #include <ctime> #include <tuple> -class OTR_COMPAT_EXPORT Timer +#include "time.hpp" + +class OTR_COMPAT_EXPORT Timer final { struct timespec state; long long conv_nsecs(const struct timespec& cur) const; @@ -31,16 +33,22 @@ class OTR_COMPAT_EXPORT Timer static mach_timebase_info_data_t otr_get_mach_frequency(); #endif - static void wrap_gettime(struct timespec* state); + static void gettime(struct timespec* state); + using ns = time_units::ns; public: Timer(); - void start(); + + template<typename t> + t elapsed() const + { + using namespace time_units; + return static_cast<const t&>(ns(elapsed_nsecs())); + } + long long elapsed_nsecs() const; double elapsed_usecs() const; double elapsed_ms() const; double elapsed_seconds() const; }; - - diff --git a/compat/util.hpp b/compat/util.hpp index b904978b..823b83d1 100644 --- a/compat/util.hpp +++ b/compat/util.hpp @@ -3,6 +3,7 @@ #include "ndebug-guard.hpp" #include "run-in-thread.hpp" #include "meta.hpp" +#include "functional.hpp" #include <memory> #include <cmath> @@ -12,10 +13,14 @@ #include <QDebug> #define progn(...) ([&]() { __VA_ARGS__ }()) +#define prog1(x, ...) (([&]() { auto _ret1324 = (x); do { __VA_ARGS__; } while (0); return _ret1324; })()) #define once_only(...) progn(static bool once = false; if (!once) { once = true; __VA_ARGS__; }) - -#define load_time_value(x) progn(static const auto _value132((x)); return _value132;) +#define load_time_value(x) \ + progn( \ + static const auto _value132((x)); \ + return static_cast<decltype(_value132)&>(value132); \ + ) template<typename t> using mem = std::shared_ptr<t>; template<typename t> using ptr = std::unique_ptr<t>; @@ -40,20 +45,34 @@ template<typename t> using ptr = std::unique_ptr<t>; # define unused_on_unix(t, i) t i #endif +#if defined __GNUC__ +# define likely(x) __builtin_expect((x),1) +# define unlikely(x) __builtin_expect((x),0) +#else +# define likely(x) (x) +# define unlikely(x) (x) +#endif + template<typename t> -int iround(const t& val) +inline int iround(const t& val) { return int(std::round(val)); } +template<typename t> +inline unsigned uround(const t& val) +{ + return std::round(std::fmax(t(0), val)); +} + namespace util_detail { template<typename n> inline auto clamp_(n val, n min, n max) -> n { - if (val > max) + if (unlikely(val > max)) return max; - if (val < min) + if (unlikely(val < min)) return min; return val; } @@ -61,9 +80,9 @@ inline auto clamp_(n val, n min, n max) -> n } template<typename t, typename u, typename w> -inline auto clamp(const t& val, const u& min, const w& max) -> decltype(val * min * max) +inline auto clamp(const t& val, const u& min, const w& max) -> decltype(val + min + max) { - return ::util_detail::clamp_<decltype(val * min * max)>(val, min, max); + return ::util_detail::clamp_<decltype(val + min + max)>(val, min, max); } template<typename t, typename... xs> diff --git a/csv/CMakeLists.txt b/csv/CMakeLists.txt index 82595688..177db52e 100644 --- a/csv/CMakeLists.txt +++ b/csv/CMakeLists.txt @@ -1,2 +1 @@ otr_module(csv) -target_link_libraries(opentrack-csv opentrack-api) diff --git a/dinput/keybinding-worker.cpp b/dinput/keybinding-worker.cpp index b94b762e..0e16ac71 100644 --- a/dinput/keybinding-worker.cpp +++ b/dinput/keybinding-worker.cpp @@ -9,6 +9,7 @@ #ifdef _WIN32 #include "keybinding-worker.hpp" +#include "compat/util.hpp" #include <functional> #include <windows.h> #include <QDebug> @@ -18,8 +19,8 @@ bool Key::should_process() { if (!enabled || (keycode == 0 && guid == "")) return false; - bool ret = !held || timer.elapsed_ms() > 100; - timer.start(); + bool ret = prog1(!held || timer.elapsed_ms() > 100, + timer.start()); return ret; } diff --git a/filter-accela/ftnoir_filter_accela.cpp b/filter-accela/ftnoir_filter_accela.cpp index 3f78c7a6..b8a0ac77 100644 --- a/filter-accela/ftnoir_filter_accela.cpp +++ b/filter-accela/ftnoir_filter_accela.cpp @@ -32,6 +32,7 @@ static inline constexpr T signum(T x) } template<int N = 3, typename F> +OTR_NEVER_INLINE static void do_deltas(const double* deltas, double* output, double alpha, double& smoothed, F&& fun) { double norm[N]; @@ -152,6 +153,12 @@ void accela::filter(const double* input, double *output) { output[k] *= dt; output[k] += last_output[k]; + + if (signum(last_output[k] - output[k]) < 0) + output[k] = std::fmax(input[k], output[k]); + else + output[k] = std::fmin(input[k], output[k]); + last_output[k] = output[k]; } } diff --git a/filter-ewma2/ftnoir_ewma_filtercontrols.ui b/filter-ewma2/ftnoir_ewma_filtercontrols.ui index d5cc082e..e0a98174 100644 --- a/filter-ewma2/ftnoir_ewma_filtercontrols.ui +++ b/filter-ewma2/ftnoir_ewma_filtercontrols.ui @@ -23,7 +23,7 @@ <string>EWMA filter settings</string> </property> <property name="windowIcon"> - <iconset resource="../gui/ui-res.qrc"> + <iconset resource="../gui/opentrack-res.qrc"> <normaloff>:/images/filter-16.png</normaloff>:/images/filter-16.png</iconset> </property> <property name="layoutDirection"> @@ -73,7 +73,7 @@ <number>0</number> </property> <property name="maximum"> - <number>1000</number> + <number>990</number> </property> <property name="singleStep"> <number>5</number> @@ -82,7 +82,7 @@ <number>50</number> </property> <property name="value"> - <number>1000</number> + <number>990</number> </property> <property name="orientation"> <enum>Qt::Horizontal</enum> @@ -123,10 +123,10 @@ </sizepolicy> </property> <property name="minimum"> - <number>1</number> + <number>0</number> </property> <property name="maximum"> - <number>1000</number> + <number>990</number> </property> <property name="singleStep"> <number>5</number> @@ -135,7 +135,7 @@ <number>50</number> </property> <property name="value"> - <number>1000</number> + <number>980</number> </property> <property name="orientation"> <enum>Qt::Horizontal</enum> @@ -173,7 +173,7 @@ <number>0</number> </property> <property name="maximum"> - <number>1000</number> + <number>2450</number> </property> <property name="singleStep"> <number>5</number> @@ -182,7 +182,7 @@ <number>50</number> </property> <property name="value"> - <number>1000</number> + <number>490</number> </property> <property name="orientation"> <enum>Qt::Horizontal</enum> @@ -287,7 +287,7 @@ p, li { white-space: pre-wrap; } </layout> </widget> <resources> - <include location="../gui/ui-res.qrc"/> + <include location="../gui/opentrack-res.qrc"/> </resources> <connections/> <slots> diff --git a/filter-ewma2/ftnoir_filter_ewma2.h b/filter-ewma2/ftnoir_filter_ewma2.h index 8babc540..f8c77280 100644 --- a/filter-ewma2/ftnoir_filter_ewma2.h +++ b/filter-ewma2/ftnoir_filter_ewma2.h @@ -19,7 +19,6 @@ struct settings : opts { {} }; - class ewma : public IFilter { public: @@ -54,7 +53,6 @@ private: private slots: void doOK(); void doCancel(); - void update_labels(int); }; class ewmaDll : public Metadata diff --git a/filter-ewma2/ftnoir_filter_ewma2_dialog.cpp b/filter-ewma2/ftnoir_filter_ewma2_dialog.cpp index ad07737b..55e9782e 100644 --- a/filter-ewma2/ftnoir_filter_ewma2_dialog.cpp +++ b/filter-ewma2/ftnoir_filter_ewma2_dialog.cpp @@ -1,7 +1,10 @@ #include "ftnoir_filter_ewma2.h" + #include <cmath> + #include <QDebug> #include <QString> + #include "api/plugin-api.hpp" #include "ui_ftnoir_ewma_filtercontrols.h" @@ -16,17 +19,23 @@ dialog_ewma::dialog_ewma() tie_setting(s.kMinSmoothing, ui.minSmooth); tie_setting(s.kSmoothingScaleCurve, ui.powCurve); - connect(ui.powCurve, &QSlider::valueChanged, this, &dialog_ewma::update_labels); - connect(ui.minSmooth, &QSlider::valueChanged, this, &dialog_ewma::update_labels); - connect(ui.maxSmooth, &QSlider::valueChanged, this, &dialog_ewma::update_labels); + tie_setting(s.kSmoothingScaleCurve, ui.curve_label, + [](auto& x) { return x * 100; }, + "%1%", 0, 'f', 2); + + tie_setting(s.kMinSmoothing, ui.min_label, + [](auto& x) { return x * 100; }, + "%1%", 0, 'f', 2); + + tie_setting(s.kMaxSmoothing, ui.max_label, + [](auto& x) { return x * 100; }, + "%1%", 0, 'f', 2); connect(ui.minSmooth, &QSlider::valueChanged, this, [&](int v) -> void { if (ui.maxSmooth->value() < v) ui.maxSmooth->setValue(v); }); connect(ui.maxSmooth, &QSlider::valueChanged, this, [&](int v) -> void { if (ui.minSmooth->value() > v) ui.minSmooth->setValue(v); }); - - update_labels(0); } void dialog_ewma::doOK() @@ -39,10 +48,3 @@ void dialog_ewma::doCancel() { close(); } - -void dialog_ewma::update_labels(int) -{ - ui.curve_label->setText(QString::number(static_cast<slider_value>(s.kSmoothingScaleCurve).cur() * 100, 'f', 2) + "%"); - ui.min_label->setText(QString::number(static_cast<slider_value>(s.kMinSmoothing).cur() * 100, 'f', 2) + "%"); - ui.max_label->setText(QString::number(static_cast<slider_value>(s.kMaxSmoothing).cur() * 100, 'f', 2) + "%"); -} diff --git a/gui/main-window.cpp b/gui/main-window.cpp index d64858c4..aa8e16f3 100644 --- a/gui/main-window.cpp +++ b/gui/main-window.cpp @@ -74,13 +74,13 @@ MainWindow::MainWindow() : { modules.filters().push_front(std::make_shared<dylib>("", dylib::Filter)); - for (mem<dylib>& x : modules.trackers()) + for (std::shared_ptr<dylib>& x : modules.trackers()) ui.iconcomboTrackerSource->addItem(x->icon, x->name); - for (mem<dylib>& x : modules.protocols()) + for (std::shared_ptr<dylib>& x : modules.protocols()) ui.iconcomboProtocol->addItem(x->icon, x->name); - for (mem<dylib>& x : modules.filters()) + for (std::shared_ptr<dylib>& x : modules.filters()) ui.iconcomboFilter->addItem(x->icon, x->name); } @@ -277,6 +277,14 @@ void MainWindow::die_on_config_not_writable() bool MainWindow::maybe_die_on_config_not_writable(const QString& current, QStringList* ini_list_) { + const bool writable = + group::with_settings_object([&](QSettings& s) { + return s.isWritable(); + }); + + if (writable) + return false; + const bool open = QFile(group::ini_combine(current)).open(QFile::ReadWrite); const QStringList ini_list = group::ini_list(); @@ -593,7 +601,7 @@ inline bool MainWindow::mk_window(ptr<t>& place, Args&&... params) } template<typename t> -bool MainWindow::mk_dialog(mem<dylib> lib, ptr<t>& d) +bool MainWindow::mk_dialog(std::shared_ptr<dylib> lib, ptr<t>& d) { const bool just_created = mk_window_common(d, [&]() -> t* { if (lib && lib->Dialog) diff --git a/gui/main-window.hpp b/gui/main-window.hpp index 71e372f0..ef6143e7 100644 --- a/gui/main-window.hpp +++ b/gui/main-window.hpp @@ -68,15 +68,15 @@ class MainWindow : public QMainWindow, private State menu_action_tracker, menu_action_filter, menu_action_proto, menu_action_options, menu_action_mappings; - mem<dylib> current_tracker() + std::shared_ptr<dylib> current_tracker() { return modules.trackers().value(ui.iconcomboTrackerSource->currentIndex(), nullptr); } - mem<dylib> current_protocol() + std::shared_ptr<dylib> current_protocol() { return modules.protocols().value(ui.iconcomboProtocol->currentIndex(), nullptr); } - mem<dylib> current_filter() + std::shared_ptr<dylib> current_filter() { return modules.filters().value(ui.iconcomboFilter->currentIndex(), nullptr); } @@ -99,7 +99,7 @@ class MainWindow : public QMainWindow, private State // only use in impl file since no definition in header! template<typename t> - bool mk_dialog(mem<dylib> lib, ptr<t>& d); + bool mk_dialog(std::shared_ptr<dylib> lib, ptr<t>& d); // idem template<typename t, typename... Args> diff --git a/gui/main.cpp b/gui/main.cpp index 80040732..16e08b6e 100644 --- a/gui/main.cpp +++ b/gui/main.cpp @@ -129,13 +129,13 @@ main(int argc, char** argv) if (!QSettings(OPENTRACK_ORG).value("disable-translation", false).toBool()) { - (void) t.load(QLocale(), "", "", QCoreApplication::applicationDirPath() + "/" + OPENTRACK_I18N_PATH, ".qm"); + (void) t.load(QLocale(), "", "", QCoreApplication::applicationDirPath() + "/" OPENTRACK_I18N_PATH, ".qm"); (void) QCoreApplication::installTranslator(&t); } do { - mem<MainWindow> w = std::make_shared<MainWindow>(); + std::shared_ptr<MainWindow> w = std::make_shared<MainWindow>(); if (!w->isEnabled()) break; diff --git a/gui/mapping-window.cpp b/gui/mapping-window.cpp index 0ee8c562..0a5d3049 100644 --- a/gui/mapping-window.cpp +++ b/gui/mapping-window.cpp @@ -114,18 +114,15 @@ void MapWidget::closeEvent(QCloseEvent*) void MapWidget::save_dialog() { - mem<QSettings> settings_ = group::ini_file(); - QSettings& settings = *settings_; - - s.b_map->save_deferred(settings); + s.b_map->save(); for (int i = 0; i < 6; i++) { m.forall([&](Map& s) { - s.spline_main.save(settings); - s.spline_alt.save(settings); - s.opts.b_mapping_window->save_deferred(settings); + s.spline_main.save(); + s.spline_alt.save(); + s.opts.b_mapping_window->save(); }); } } diff --git a/logic/mappings.cpp b/logic/mappings.cpp index d7764375..bae8c2c0 100644 --- a/logic/mappings.cpp +++ b/logic/mappings.cpp @@ -11,10 +11,10 @@ Map::Map(QString primary, QString secondary, int max_x, int max_y, axis_opts& op spline_alt.set_max_input(opts.clamp); } -void Map::save(QSettings& s) +void Map::save() { - spline_main.save(s); - spline_alt.save(s); + spline_main.save(); + spline_alt.save(); } void Map::load() diff --git a/logic/mappings.hpp b/logic/mappings.hpp index 4e0f7218..b3587749 100644 --- a/logic/mappings.hpp +++ b/logic/mappings.hpp @@ -17,7 +17,7 @@ struct OTR_LOGIC_EXPORT Map final { Map(QString primary, QString secondary, int max_x, int max_y, axis_opts& opts); - void save(QSettings& s); + void save(); void load(); axis_opts& opts; diff --git a/logic/selected-libraries.hpp b/logic/selected-libraries.hpp index 689cbec3..65e9733e 100644 --- a/logic/selected-libraries.hpp +++ b/logic/selected-libraries.hpp @@ -15,11 +15,14 @@ struct OTR_LOGIC_EXPORT SelectedLibraries { - using dylibptr = mem<dylib>; - mem<ITracker> pTracker; - mem<IFilter> pFilter; - mem<IProtocol> pProtocol; + using dylibptr = std::shared_ptr<dylib>; + + std::shared_ptr<ITracker> pTracker; + std::shared_ptr<IFilter> pFilter; + std::shared_ptr<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/state.hpp b/logic/state.hpp index f5892557..8bef71ad 100644 --- a/logic/state.hpp +++ b/logic/state.hpp @@ -26,5 +26,5 @@ struct State Modules modules; main_settings s; Mappings pose; - mem<Work> work; + std::shared_ptr<Work> work; }; diff --git a/logic/tracker.cpp b/logic/tracker.cpp index 7411d3b4..8367ee2f 100644 --- a/logic/tracker.cpp +++ b/logic/tracker.cpp @@ -24,6 +24,7 @@ using namespace euler; using namespace gui_tracker_impl; +using namespace time_units; constexpr double Tracker::r2d; constexpr double Tracker::d2r; @@ -32,7 +33,7 @@ Tracker::Tracker(Mappings& m, SelectedLibraries& libs, TrackLogger& logger) : m(m), libs(libs), logger(logger), - backlog_time(0), + backlog_time(ns(0)), tracking_started(false) { } @@ -403,21 +404,22 @@ void Tracker::run() { logic(); - static constexpr long const_sleep_us = 4000; - const long elapsed_usecs = t.elapsed_usecs(); - t.start(); + static constexpr ns const_sleep_ms(time_cast<ns>(ms(4))); + const ns elapsed_nsecs = prog1(t.elapsed<ns>(), t.start()); - backlog_time += elapsed_usecs - const_sleep_us; + backlog_time += ns(elapsed_nsecs - const_sleep_ms); - if (std::fabs(backlog_time) > 3000 * 1000) + if (backlog_time > secs_(3) || backlog_time < secs_(-3)) { - qDebug() << "tracker: backlog interval overflow" << backlog_time; - backlog_time = 0; + qDebug() << "tracker: backlog interval overflow" + << time_cast<ms>(backlog_time).count() << "ms"; + backlog_time = backlog_time.zero(); } - const unsigned sleep_time = unsigned(std::round(clamp((const_sleep_us - backlog_time)/1000., 0, const_sleep_us*2.5/1000))); + const int sleep_time_ms = iround(clamp(const_sleep_ms - backlog_time, + ns(0), ms(50)).count()); - portable::sleep(sleep_time); + portable::sleep(sleep_time_ms); } { diff --git a/logic/tracker.h b/logic/tracker.h index 85bd1ed8..2f29aca7 100644 --- a/logic/tracker.h +++ b/logic/tracker.h @@ -85,7 +85,7 @@ private: state real_rotation, scaled_rotation; euler_t t_center; - long backlog_time; + time_units::ns backlog_time; bool tracking_started; diff --git a/logic/work.cpp b/logic/work.cpp index 7b9e550e..6829e62b 100644 --- a/logic/work.cpp +++ b/logic/work.cpp @@ -60,7 +60,7 @@ std::shared_ptr<TrackLogger> Work::make_logger(main_settings &s) } -Work::Work(Mappings& m, QFrame* frame, mem<dylib> tracker_, mem<dylib> filter_, mem<dylib> proto_) : +Work::Work(Mappings& m, QFrame* frame, std::shared_ptr<dylib> tracker_, std::shared_ptr<dylib> filter_, std::shared_ptr<dylib> proto_) : libs(frame, tracker_, filter_, proto_), logger(make_logger(s)), tracker(std::make_shared<Tracker>(m, libs, *logger)), diff --git a/logic/work.hpp b/logic/work.hpp index f1d5e401..dc32536c 100644 --- a/logic/work.hpp +++ b/logic/work.hpp @@ -35,7 +35,7 @@ struct OTR_LOGIC_EXPORT Work std::shared_ptr<Shortcuts> sc; std::vector<key_tuple> keys; - Work(Mappings& m, QFrame* frame, mem<dylib> tracker, mem<dylib> filter, mem<dylib> proto); + Work(Mappings& m, QFrame* frame, std::shared_ptr<dylib> tracker, std::shared_ptr<dylib> filter, std::shared_ptr<dylib> proto); ~Work(); void reload_shortcuts(); bool is_ok() const; diff --git a/migration/20160906_00-mappings.cpp b/migration/20160906_00-mappings.cpp index 58674843..a5184825 100644 --- a/migration/20160906_00-mappings.cpp +++ b/migration/20160906_00-mappings.cpp @@ -36,31 +36,30 @@ struct mappings_from_2_3_0_rc11 : migration "rz", "rz_alt", }; - std::shared_ptr<QSettings> settings_ = options::group::ini_file(); - QSettings& settings(*settings_); - - for (const char* name : names) - { - QList<QPointF> points; + return group::with_settings_object([&](QSettings& settings) { + for (const char* name : names) + { + QList<QPointF> points; - settings.beginGroup(QString("Curves-%1").arg(name)); + settings.beginGroup(QString("Curves-%1").arg(name)); - const int max = settings.value("point-count", 0).toInt(); + const int max = settings.value("point-count", 0).toInt(); - for (int i = 0; i < max; i++) - { - QPointF new_point(settings.value(QString("point-%1-x").arg(i), 0).toDouble(), - settings.value(QString("point-%1-y").arg(i), 0).toDouble()); + for (int i = 0; i < max; i++) + { + QPointF new_point(settings.value(QString("point-%1-x").arg(i), 0).toDouble(), + settings.value(QString("point-%1-y").arg(i), 0).toDouble()); - points.append(new_point); - } + points.append(new_point); + } - settings.endGroup(); + settings.endGroup(); - ret.append(points); - } + ret.append(points); + } - return ret; + return ret; + }); } QString unique_date() const override { return "20160909_00"; } @@ -94,9 +93,6 @@ struct mappings_from_2_3_0_rc11 : migration const QList<QList<QPointF>> old_mappings = get_old_splines(); Mappings m = get_new_mappings(); - std::shared_ptr<QSettings> s_ = options::group::ini_file(); - QSettings& s = *s_; - for (int i = 0; i < 12; i++) { spline& spl = (i % 2) == 0 ? m(i / 2).spline_main : m(i / 2).spline_alt; @@ -104,7 +100,7 @@ struct mappings_from_2_3_0_rc11 : migration const QList<QPointF>& points = old_mappings[i]; for (const QPointF& pt : points) spl.add_point(pt); - spl.save(s); + spl.save(); } } }; diff --git a/migration/migration.cpp b/migration/migration.cpp index bb8386fc..747ea06b 100644 --- a/migration/migration.cpp +++ b/migration/migration.cpp @@ -78,11 +78,11 @@ QString migrator::last_migration_time() { QString ret; - std::shared_ptr<QSettings> s(options::group::ini_file()); - - s->beginGroup("migrations"); - ret = s->value("last-migration-at", "19700101_00").toString(); - s->endGroup(); + options::group::with_settings_object([&](QSettings& s) { + s.beginGroup("migrations"); + ret = s.value("last-migration-at", "19700101_00").toString(); + s.endGroup(); + }); return ret; } @@ -102,11 +102,16 @@ QString migrator::time_after_migrations() void migrator::set_last_migration_time(const QString& val) { - std::shared_ptr<QSettings> s(options::group::ini_file()); - - s->beginGroup("migrations"); - s->setValue("last-migration-at", val); - s->endGroup(); + options::group::with_settings_object([&](QSettings& s) { + s.beginGroup("migrations"); + const QString old_value = s.value("last-migration-at", "").toString(); + if (val != old_value) + { + s.setValue("last-migration-at", val); + options::group::mark_ini_modified(); + } + s.endGroup(); + }); } std::vector<migration*> migrator::sorted_migrations() diff --git a/options/CMakeLists.txt b/options/CMakeLists.txt index cbebeb87..0c4b53a5 100644 --- a/options/CMakeLists.txt +++ b/options/CMakeLists.txt @@ -2,3 +2,4 @@ otr_module(options NO-COMPAT BIN) if(NOT WIN32 AND NOT APPLE) target_link_libraries(opentrack-options rt) endif() +target_link_libraries(opentrack-options opentrack-compat) diff --git a/options/base-value.cpp b/options/base-value.cpp new file mode 100644 index 00000000..29f4b496 --- /dev/null +++ b/options/base-value.cpp @@ -0,0 +1,27 @@ +#include "base-value.hpp" + +using namespace options; + +base_value::base_value(bundle b, const QString& name, base_value::comparator cmp, std::type_index type_idx) : + b(b), + self_name(name), + cmp(cmp), + type_index(type_idx) +{ + b->on_value_created(name, this); +} + +base_value::~base_value() +{ + b->on_value_destructed(self_name, this); +} + +void base_value::store(const QVariant& datum) +{ + b->store_kv(self_name, datum); +} + +void ::options::detail::set_base_value_to_default(base_value* val) +{ + val->set_to_default(); +} diff --git a/options/base-value.hpp b/options/base-value.hpp new file mode 100644 index 00000000..4f90e3fc --- /dev/null +++ b/options/base-value.hpp @@ -0,0 +1,85 @@ +#pragma once + +#include "bundle.hpp" +#include "slider.hpp" +#include "connector.hpp" + +#include "export.hpp" + +#include <QObject> +#include <QString> +#include <QList> +#include <QPointF> +#include <QVariant> + +#include <typeindex> + +#define OPENTRACK_DEFINE_SLOT(t) void setValue(t datum) { store(datum); } +#define OPENTRACK_DEFINE_SIGNAL(t) void valueChanged(t) const + +namespace options { + +class OTR_OPTIONS_EXPORT base_value : public QObject +{ + Q_OBJECT + friend class detail::connector; + + using comparator = bool(*)(const QVariant& val1, const QVariant& val2); +public: + QString name() const { return self_name; } + base_value(bundle b, const QString& name, comparator cmp, std::type_index type_idx); + ~base_value() override; +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&); + OPENTRACK_DEFINE_SIGNAL(const QPointF&); + OPENTRACK_DEFINE_SIGNAL(const QVariant&); + + OPENTRACK_DEFINE_SIGNAL(const QList<double>&); + OPENTRACK_DEFINE_SIGNAL(const QList<float>&); + OPENTRACK_DEFINE_SIGNAL(const QList<int>&); + OPENTRACK_DEFINE_SIGNAL(const QList<bool>&); + OPENTRACK_DEFINE_SIGNAL(const QList<QString>&); + OPENTRACK_DEFINE_SIGNAL(const QList<slider_value>&); + OPENTRACK_DEFINE_SIGNAL(const QList<QPointF>&); +protected: + bundle b; + QString self_name; + comparator cmp; + std::type_index type_index; + + void store(const QVariant& datum); + + template<typename t> + void store(const t& datum) + { + b->store_kv(self_name, QVariant::fromValue(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&) + OPENTRACK_DEFINE_SLOT(const QPointF&) + OPENTRACK_DEFINE_SLOT(const QVariant&) + + OPENTRACK_DEFINE_SLOT(const QList<double>&) + OPENTRACK_DEFINE_SLOT(const QList<float>&) + OPENTRACK_DEFINE_SLOT(const QList<int>&) + OPENTRACK_DEFINE_SLOT(const QList<bool>&) + OPENTRACK_DEFINE_SLOT(const QList<QString>&) + OPENTRACK_DEFINE_SLOT(const QList<slider_value>&) + OPENTRACK_DEFINE_SLOT(const QList<QPointF>&) + + virtual void reload() = 0; + virtual void bundle_value_changed() const = 0; + virtual void set_to_default() = 0; +}; + +} //ns options diff --git a/options/bundle.cpp b/options/bundle.cpp index 43f4a8e3..c78df274 100644 --- a/options/bundle.cpp +++ b/options/bundle.cpp @@ -31,12 +31,12 @@ bundle::~bundle() { } -void bundle::reload(std::shared_ptr<QSettings> settings) +void bundle::reload() { if (group_name.size()) { QMutexLocker l(&mtx); - saved = group(group_name, settings); + saved = group(group_name); const bool has_changes = is_modified(); transient = saved; @@ -51,7 +51,12 @@ void bundle::reload(std::shared_ptr<QSettings> settings) void bundle::set_all_to_default() { + QMutexLocker l(&mtx); + forall([](const QString&, base_value* val) { set_base_value_to_default(val); }); + + if (is_modified()) + group::mark_ini_modified(); } void bundle::store_kv(const QString& name, const QVariant& datum) @@ -72,10 +77,10 @@ bool bundle::contains(const QString &name) const return transient.contains(name); } -void bundle::save_deferred(QSettings& s) +void bundle::save() { if (QThread::currentThread() != qApp->thread()) - qCritical() << "group::save - current thread not ui thread"; + qDebug() << "group::save - current thread not ui thread"; if (group_name.size() == 0) return; @@ -84,12 +89,13 @@ void bundle::save_deferred(QSettings& s) { QMutexLocker l(&mtx); + if (is_modified()) { //qDebug() << "bundle" << group_name << "changed, saving"; modified_ = true; saved = transient; - saved.save_deferred(s); + saved.save(); } } @@ -97,11 +103,6 @@ void bundle::save_deferred(QSettings& s) emit saving(); } -void bundle::save() -{ - save_deferred(*group::ini_file()); -} - bool bundle::is_modified() const { QMutexLocker l(mtx); @@ -119,8 +120,7 @@ bool bundle::is_modified() const for (const auto& kv : saved.kvs) { - const QVariant other = transient.get<QVariant>(kv.first); - if (!transient.contains(kv.first) || !is_equal(kv.first, kv.second, other)) + if (!transient.contains(kv.first)) { //if (logspam) // qDebug() << "bundle" << group_name << "modified" << "key" << kv.first << "-" << other << "<>" << kv.second; @@ -135,8 +135,6 @@ void bundler::after_profile_changed_() { QMutexLocker l(&implsgl_mtx); - std::shared_ptr<QSettings> s = group::ini_file(); - for (auto& kv : implsgl_data) { weak bundle = kv.second; @@ -144,7 +142,7 @@ void bundler::after_profile_changed_() if (bundle_) { //qDebug() << "bundle: reverting" << kv.first << "due to profile change"; - bundle_->reload(s); + bundle_->reload(); } } } diff --git a/options/bundle.hpp b/options/bundle.hpp index f05999a7..63ee82d0 100644 --- a/options/bundle.hpp +++ b/options/bundle.hpp @@ -81,8 +81,7 @@ public: } public slots: void save(); - void reload(std::shared_ptr<QSettings> settings = group::ini_file()); - void save_deferred(QSettings& s); + void reload(); void set_all_to_default(); }; diff --git a/options/connector.cpp b/options/connector.cpp index 63d70ca7..075a57e1 100644 --- a/options/connector.cpp +++ b/options/connector.cpp @@ -83,11 +83,12 @@ void connector::on_value_created(const QString& name, value_type val) QMutexLocker l(get_mtx()); - if (on_value_destructed_impl(name, val)) + int i = 1; + while (on_value_destructed_impl(name, val)) { qWarning() << "options/connector: value created twice;" - << "bundle" - << val->b->name() + << "cnt" << i++ + << "bundle" << val->b->name() << "value-name" << name << "value-ptr" << quintptr(val); } diff --git a/options/group.cpp b/options/group.cpp index 9a4bd912..60e8a7b4 100644 --- a/options/group.cpp +++ b/options/group.cpp @@ -8,46 +8,49 @@ #include "group.hpp" #include "defs.hpp" + +#include "compat/timer.hpp" + +#include <cmath> + #include <QStandardPaths> #include <QDir> - #include <QDebug> namespace options { -group::group(const QString& name, std::shared_ptr<QSettings> conf) : name(name) +group::group(const QString& name) : name(name) { if (name == "") return; - conf->beginGroup(name); - for (auto& k_ : conf->childKeys()) - { - auto tmp = k_.toUtf8(); - QString k(tmp); - kvs[k] = conf->value(k_); - } - conf->endGroup(); -} - -group::group(const QString& name) : group(name, ini_file()) -{ + with_settings_object([&](QSettings& conf) { + conf.beginGroup(name); + for (auto& k_ : conf.childKeys()) + { + auto tmp = k_.toUtf8(); + QString k(tmp); + QVariant val = conf.value(k_); + if (val.type() != QVariant::Invalid) + kvs[k] = std::move(val); + } + conf.endGroup(); + }); } void group::save() const { - save_deferred(*ini_file()); -} - -void group::save_deferred(QSettings& s) const -{ if (name == "") return; - s.beginGroup(name); - for (auto& i : kvs) - s.setValue(i.first, i.second); - s.endGroup(); + with_settings_object([&](QSettings& s) { + s.beginGroup(name); + for (auto& i : kvs) + s.setValue(i.first, i.second); + s.endGroup(); + + mark_ini_modified(); + }); } void group::put(const QString &s, const QVariant &d) @@ -103,12 +106,52 @@ QStringList group::ini_list() return list; } -std::shared_ptr<QSettings> group::ini_file() +void group::mark_ini_modified() +{ + QMutexLocker l(&cur_ini_mtx); + ini_modifiedp = true; +} + +QString group::cur_ini_pathname; +std::shared_ptr<QSettings> group::cur_ini; +QMutex group::cur_ini_mtx(QMutex::Recursive); +int group::ini_refcount = 0; +bool group::ini_modifiedp = false; + +std::shared_ptr<QSettings> group::cur_settings_object() { - const auto pathname = ini_pathname(); - if (pathname != "") - return std::make_shared<QSettings>(ini_pathname(), QSettings::IniFormat); - return std::make_shared<QSettings>(); + const QString pathname = ini_pathname(); + + if (pathname.isEmpty()) + return std::make_shared<QSettings>(); + + QMutexLocker l(&cur_ini_mtx); + + if (pathname != cur_ini_pathname) + { + cur_ini = std::make_shared<QSettings>(pathname, QSettings::IniFormat); + cur_ini_pathname = pathname; + } + + return cur_ini; } +group::saver_::~saver_() +{ + if (--ini_refcount == 0 && ini_modifiedp) + { + ini_modifiedp = false; + static Timer t; + const double tm = t.elapsed_seconds(); + qDebug() << QStringLiteral("%1.%2").arg(int(tm)).arg(int(std::fmod(tm, 1.)*10)).toLatin1().data() + << "saving .ini file" << cur_ini_pathname; + s.sync(); + } } + +group::saver_::saver_(QSettings& s, QMutex& mtx) : s(s), mtx(mtx), lck(&mtx) +{ + ini_refcount++; +} + +} // ns options diff --git a/options/group.hpp b/options/group.hpp index e2a8058c..b0e13a6a 100644 --- a/options/group.hpp +++ b/options/group.hpp @@ -1,13 +1,16 @@ #pragma once #include "export.hpp" + #include "compat/util.hpp" + #include <map> #include <memory> #include <QString> #include <QList> #include <QVariant> #include <QSettings> +#include <QMutex> namespace options { @@ -15,12 +18,27 @@ namespace options { class OTR_OPTIONS_EXPORT group final { QString name; + + static QString cur_ini_pathname; + static std::shared_ptr<QSettings> cur_ini; + static QMutex cur_ini_mtx; + static int ini_refcount; + static bool ini_modifiedp; + struct OTR_OPTIONS_EXPORT saver_ final + { + QSettings& s; + QMutex& mtx; + QMutexLocker lck; + + ~saver_(); + saver_(QSettings& s, QMutex&); + }; + static std::shared_ptr<QSettings> cur_settings_object(); + public: std::map<QString, QVariant> kvs; - group(const QString& name, mem<QSettings> s); group(const QString& name); void save() const; - void save_deferred(QSettings& s) const; void put(const QString& s, const QVariant& d); bool contains(const QString& s) const; static QString ini_directory(); @@ -28,9 +46,11 @@ public: static QString ini_pathname(); static QString ini_combine(const QString& filename); static QStringList ini_list(); - static std::shared_ptr<QSettings> ini_file(); + + static void mark_ini_modified(); template<typename t> + OTR_NEVER_INLINE t get(const QString& k) const { auto value = kvs.find(k); @@ -38,6 +58,15 @@ public: return value->second.value<t>(); return t(); } + + template<typename F> + OTR_NEVER_INLINE + static auto with_settings_object(F&& fun) + { + saver_ saver { *cur_settings_object(), cur_ini_mtx }; + + return fun(static_cast<QSettings&>(saver.s)); + } }; } diff --git a/options/scoped.cpp b/options/scoped.cpp index 96f4a261..58a4ee02 100644 --- a/options/scoped.cpp +++ b/options/scoped.cpp @@ -2,8 +2,8 @@ #include <QApplication> #include <QThread> -// for std::abort() #include <cstdlib> +#include <atomic> #include <QDebug> diff --git a/options/tie.hpp b/options/tie.hpp index a3b5a19f..94fe5d49 100644 --- a/options/tie.hpp +++ b/options/tie.hpp @@ -58,6 +58,23 @@ void tie_setting(value<t>& v, QLabel* lb, const QString& format, const xs&... ar v.SAFE_CONNTYPE); } +// Clang 3.9 has a bug +// error: missing default argument on parameter 'args' + +// cf. http://stackoverflow.com/questions/29098835/can-parameter-pack-function-arguments-be-defaulted + +template<typename t, typename F, typename... xs> +decltype((void)((std::declval<F>())(std::declval<const t&>()))) +tie_setting(value<t>& v, QLabel* lb, F&& fun, const QString& fmt = QStringLiteral("%1"), const xs&... args) +{ + auto closure = [=](const t& x) { lb->setText(fmt.arg(fun(x), args...)); }; + + closure(v()); + base_value::connect(&v, static_cast<void(base_value::*)(const t&) const>(&base_value::valueChanged), + lb, closure, + v.SAFE_CONNTYPE); +} + OTR_OPTIONS_EXPORT void tie_setting(value<int>& v, QComboBox* cb); OTR_OPTIONS_EXPORT void tie_setting(value<QString>& v, QComboBox* cb); OTR_OPTIONS_EXPORT void tie_setting(value<QVariant>& v, QComboBox* cb); diff --git a/options/value-traits.hpp b/options/value-traits.hpp new file mode 100644 index 00000000..cf12649c --- /dev/null +++ b/options/value-traits.hpp @@ -0,0 +1,50 @@ +#include "export.hpp" + +#include "compat/functional.hpp" +#include "slider.hpp" + +#include <QString> + +#include <type_traits> + +namespace options { +namespace detail { + +template<typename t, typename u = t, typename Enable = void> +struct default_value_traits +{ + using element_type = remove_qualifiers<t>; + using value_type = u; + + static inline value_type from_value(const value_type& val, const value_type&) { return val; } + static inline value_type from_storage(const element_type& x) { return static_cast<value_type>(x); } + static inline element_type to_storage(const value_type& val) { return static_cast<element_type>(val); } +}; + +template<typename t, typename u = t, typename Enable = void> +struct value_traits : default_value_traits<t, u, Enable> +{ +}; + +template<> +struct value_traits<slider_value> : default_value_traits<slider_value> +{ + static inline slider_value from_value(const slider_value& val, const slider_value& def) + { + return slider_value(val.cur(), def.min(), def.max()); + } +}; + +// Qt uses int a lot in slots so use it for all enums +template<typename t> +struct value_traits<t, t, std::enable_if_t<std::is_enum<t>::value>> : public default_value_traits<int, t> +{ +}; + +template<> +struct value_traits<float> : public default_value_traits<float, double, void> +{ +}; + +} // ns detail +} // ns options diff --git a/options/value.cpp b/options/value.cpp deleted file mode 100644 index d3d24a58..00000000 --- a/options/value.cpp +++ /dev/null @@ -1,35 +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. - */ - -#include "value.hpp" - -namespace options { - -base_value::base_value(bundle b, const QString& name, base_value::comparator cmp, std::type_index type_idx) : - b(b), - self_name(name), - cmp(cmp), - type_index(type_idx) -{ - b->on_value_created(name, this); -} - -base_value::~base_value() -{ - b->on_value_destructed(self_name, this); -} - -namespace detail -{ - void set_base_value_to_default(base_value* val) - { - val->set_to_default(); - } -} - -} // ns options diff --git a/options/value.hpp b/options/value.hpp index 020cbeea..ebb2096c 100644 --- a/options/value.hpp +++ b/options/value.hpp @@ -12,153 +12,59 @@ #include "compat/util.hpp" -#include "connector.hpp" #include "bundle.hpp" #include "slider.hpp" +#include "base-value.hpp" +#include "value-traits.hpp" + +#include <cstdio> #include <type_traits> #include <typeinfo> #include <typeindex> +#include <utility> + +#include <QVariant> #include <QString> #include <QPointF> #include <QList> +#include <QMutex> -#define OPENTRACK_DEFINE_SLOT(t) void setValue(t datum) { store(datum); } -#define OPENTRACK_DEFINE_SIGNAL(t) void valueChanged(t) const namespace options { namespace detail { -template<typename t> struct value_type_traits { using type = t;}; -template<> struct value_type_traits<QString> { using type = const QString&; }; -template<> struct value_type_traits<slider_value> { using type = const slider_value&; }; -template<typename u> struct value_type_traits<QList<u>> -{ - using type = const QList<u>&; -}; -template<typename t> using value_type_t = typename value_type_traits<t>::type; -} - -class OTR_OPTIONS_EXPORT base_value : public QObject -{ - Q_OBJECT - friend class ::options::detail::connector; - using comparator = bool(*)(const QVariant& val1, const QVariant& val2); -public: - QString name() const { return self_name; } - base_value(bundle b, const QString& name, comparator cmp, std::type_index type_idx); - ~base_value() override; -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&); - OPENTRACK_DEFINE_SIGNAL(const QPointF&); - OPENTRACK_DEFINE_SIGNAL(const QVariant&); - - OPENTRACK_DEFINE_SIGNAL(const QList<double>&); - OPENTRACK_DEFINE_SIGNAL(const QList<float>&); - OPENTRACK_DEFINE_SIGNAL(const QList<int>&); - OPENTRACK_DEFINE_SIGNAL(const QList<bool>&); - OPENTRACK_DEFINE_SIGNAL(const QList<QString>&); - OPENTRACK_DEFINE_SIGNAL(const QList<slider_value>&); - OPENTRACK_DEFINE_SIGNAL(const QList<QPointF>&); -protected: - bundle b; - QString self_name; - comparator cmp; - std::type_index type_index; - - void store(const QVariant& datum) - { - b->store_kv(self_name, datum); - } - - template<typename t> - void store(const t& datum) - { - b->store_kv(self_name, QVariant::fromValue(datum)); - } +OTR_OPTIONS_EXPORT void acct_lookup(bool is_fresh); -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&) - OPENTRACK_DEFINE_SLOT(const QPointF&) - OPENTRACK_DEFINE_SLOT(const QVariant&) - - OPENTRACK_DEFINE_SLOT(const QList<double>&) - OPENTRACK_DEFINE_SLOT(const QList<float>&) - OPENTRACK_DEFINE_SLOT(const QList<int>&) - OPENTRACK_DEFINE_SLOT(const QList<bool>&) - OPENTRACK_DEFINE_SLOT(const QList<QString>&) - OPENTRACK_DEFINE_SLOT(const QList<slider_value>&) - OPENTRACK_DEFINE_SLOT(const QList<QPointF>&) - - virtual void reload() = 0; - virtual void bundle_value_changed() const = 0; - virtual void set_to_default() = 0; -}; +} // ns detail -namespace detail { template<typename t> -struct value_get_traits +class value final : public base_value { - static inline t get(const t& val, const t&) - { - return val; - } -}; + using traits = detail::value_traits<t, t, void>; + using element_type = typename traits::element_type; -template<> -struct value_get_traits<slider_value> -{ - using t = slider_value; - static inline t get(const t& val, const t& def) + static bool is_equal(const QVariant& val1, const QVariant& val2) { - return t(val.cur(), def.min(), def.max()); + return val1.value<element_type>() == val2.value<element_type>(); } -}; -template<typename t, typename Enable = void> -struct value_element_type -{ - using type = typename std::remove_reference<typename std::remove_cv<t>::type>::type; -}; - -// Qt uses int a lot in slots so use it for all enums -template<typename t> -struct value_element_type<t, typename std::enable_if<std::is_enum<t>::value>::type> -{ - using type = int; -}; - -template<> struct value_element_type<float, void> { using type = double; }; - -template<typename t> using value_element_type_t = typename value_element_type<t>::type; + OTR_NEVER_INLINE + t get() const + { + if (!b->contains(self_name) || b->get<QVariant>(self_name).type() == QVariant::Invalid) + return def; -} + const element_type x(b->get<element_type>(self_name)); -template<typename t> -class value final : public base_value -{ - static bool is_equal(const QVariant& val1, const QVariant& val2) - { - return val1.value<element_type>() == val2.value<element_type>(); + return traits::from_value(traits::from_storage(x), def); } public: - using element_type = detail::value_element_type_t<t>; - OTR_NEVER_INLINE t operator=(const t& datum) { - const element_type tmp = static_cast<element_type>(datum); - if (tmp != get()) - store(tmp); + if (datum != get()) + store(traits::to_storage(datum)); return datum; } @@ -167,13 +73,12 @@ public: OTR_NEVER_INLINE value(bundle b, const QString& name, t def) : - base_value(b, name, &is_equal, std::type_index(typeid(element_type))), def(def) + base_value(b, name, &is_equal, std::type_index(typeid(element_type))), + 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; } OTR_NEVER_INLINE @@ -181,6 +86,7 @@ public: { } + OTR_NEVER_INLINE t default_value() const { return def; @@ -193,7 +99,7 @@ public: } OTR_NEVER_INLINE - operator t() const { return get(); } + operator t() const { return std::forward<t>(get()); } OTR_NEVER_INLINE void reload() override @@ -201,34 +107,26 @@ public: *this = static_cast<t>(*this); } + OTR_NEVER_INLINE void bundle_value_changed() const override { - emit valueChanged(static_cast<detail::value_type_t<t>>(get())); + emit valueChanged(traits::to_storage(get())); } OTR_NEVER_INLINE t operator()() const { - return static_cast<t>(get()); + return get(); } template<typename u> OTR_NEVER_INLINE u to() const { - return static_cast<u>(get()); + return static_cast<u>(std::forward<t>(get())); } private: - OTR_NEVER_INLINE - t get() const - { - t val = b->contains(self_name) - ? static_cast<t>(b->get<element_type>(self_name)) - : def; - return detail::value_get_traits<t>::get(val, def); - } - const t def; }; diff --git a/pose-widget/pose-widget.cpp b/pose-widget/pose-widget.cpp index b511f4c5..8646df30 100644 --- a/pose-widget/pose-widget.cpp +++ b/pose-widget/pose-widget.cpp @@ -18,11 +18,12 @@ using namespace pose_widget_impl; +static constexpr int offset = 2; + pose_transform::pose_transform(QWidget* dst) : dst(dst), - image(w, h, QImage::Format_ARGB32), - image2(w, h, QImage::Format_ARGB32), - width(w), height(h), + image(w+offset*2, h+offset*2, QImage::Format_ARGB32), + image2(w+offset*2, h+offset*2, QImage::Format_ARGB32), fresh(false) { front = QImage(QString(":/images/side1.png")); @@ -43,8 +44,10 @@ pose_transform::~pose_transform() void pose_widget::paintEvent(QPaintEvent* event) { QPainter p(this); - xform.with_image_lock([&](const QImage& image) { - p.drawImage(event->rect(), image); + + xform.with_image_lock([&](const QImage& image) + { + p.drawImage(event->rect(), image, QRect(offset, offset, pose_transform::w, pose_transform::h)); }); } @@ -68,7 +71,7 @@ void pose_transform::run() project_quad_texture(); end: - portable::sleep(9); + portable::sleep(23); } } @@ -186,7 +189,7 @@ void pose_transform::project_quad_texture() num dir; vec2 pt[4]; - const int sx = width - 1, sy = height - 1; + const int sx = w - 1, sy = h - 1; vec2 projected[3]; { @@ -252,7 +255,7 @@ void pose_transform::project_quad_texture() const unsigned dest_pitch = image.bytesPerLine(); const unsigned char* orig = tex.bits(); - unsigned char* dest = image.bits(); + unsigned char* dest = image.bits() + offset*dest_pitch; const int orig_depth = tex.depth() / 8; const int dest_depth = image.depth() / 8; @@ -270,8 +273,8 @@ void pose_transform::project_quad_texture() return; } - for (int y = 1; y < sy; y++) - for (int x = 1; x < sx; x++) + for (int y = 0; y < sy; y++) + for (int x = 0; x < sx; x++) { vec2 pos(x, y); vec2 uv; @@ -309,7 +312,7 @@ void pose_transform::project_quad_texture() const float ax = 1 - ax_; const float ay = 1 - ay_; - const unsigned pos = y * dest_pitch + x * dest_depth; + const unsigned pos = y * dest_pitch + (x+offset) * dest_depth; for (int k = 0; k < 4; k++) { @@ -336,13 +339,13 @@ vec2 pose_transform::project(const vec3 &point) vec3 ret = rotation * point; num z = std::fmax(num(.5), 1 + translation.z()/-80); - num w = width, h = height; - num x = w * translation.x() / 2 / -80; - if (fabs(x) > w/2) - x = x > 0 ? w/2 : w/-2; - num y = h * translation.y() / 2 / -80; - if (fabs(y) > h/2) - y = y > 0 ? h/2 : h/-2; + num w_ = w, h_ = h; + num x = w_ * translation.x() / 2 / -80; + if (fabs(x) > w_/2) + x = x > 0 ? w_/2 : w_/-2; + num y = h_ * translation.y() / 2 / -80; + if (fabs(y) > h_/2) + y = y > 0 ? h_/2 : h_/-2; return vec2(z * (ret.x() + x), z * (ret.y() + y)); } diff --git a/pose-widget/pose-widget.hpp b/pose-widget/pose-widget.hpp index b528d394..a27bf4b9 100644 --- a/pose-widget/pose-widget.hpp +++ b/pose-widget/pose-widget.hpp @@ -37,7 +37,7 @@ using lock_guard = std::unique_lock<std::mutex>; class pose_widget; -class pose_transform final : private QThread +struct pose_transform final : private QThread { pose_transform(QWidget* dst); ~pose_transform(); @@ -69,8 +69,6 @@ class pose_transform final : private QThread QImage front, back; QImage image, image2; - int width, height; - std::atomic<bool> fresh; static constexpr int w = 320, h = 240; diff --git a/proto-fsuipc/CMakeLists.txt b/proto-fsuipc/CMakeLists.txt index b3e94c32..24006644 100644 --- a/proto-fsuipc/CMakeLists.txt +++ b/proto-fsuipc/CMakeLists.txt @@ -5,7 +5,7 @@ if(WIN32) target_link_libraries(opentrack-proto-fsuipc ${SDK_FSUIPC}/FSUIPC_User.lib) target_include_directories(opentrack-proto-fsuipc SYSTEM PUBLIC ${SDK_FSUIPC}) if(MSVC) - set_property(TARGET opentrack-proto-fsuipc APPEND_STRING PROPERTY LINK_FLAGS "/NODEFAULTLIB:LIBC.lib") + set_property(TARGET opentrack-proto-fsuipc APPEND_STRING PROPERTY LINK_FLAGS " /NODEFAULTLIB:LIBC.lib") endif() endif() endif() diff --git a/proto-libevdev/CMakeLists.txt b/proto-libevdev/CMakeLists.txt index f2809c93..04f6924e 100644 --- a/proto-libevdev/CMakeLists.txt +++ b/proto-libevdev/CMakeLists.txt @@ -5,6 +5,6 @@ if(LINUX OR APPLE) otr_module(proto-libevdev) pkg_check_modules(libevdev REQUIRED QUIET libevdev) target_link_libraries(opentrack-proto-libevdev ${libevdev_LIBRARIES}) - include_directories(opentrack-proto-libevdev SYSTEM PUBLIC ${libevdev_INCLUDE_DIRS}) + target_include_directories(opentrack-proto-libevdev SYSTEM PUBLIC ${libevdev_INCLUDE_DIRS}) endif() endif() diff --git a/qxt-mini/CMakeLists.txt b/qxt-mini/CMakeLists.txt index d09cbdfa..bf8a30f9 100644 --- a/qxt-mini/CMakeLists.txt +++ b/qxt-mini/CMakeLists.txt @@ -3,6 +3,6 @@ if(UNIX OR APPLE) if(NOT APPLE) target_link_libraries(opentrack-qxt-mini X11) else() - set_property(TARGET opentrack-qxt-mini APPEND_STRING PROPERTY LINK_FLAGS "-framework Carbon -framework CoreFoundation") + set_property(TARGET opentrack-qxt-mini APPEND_STRING PROPERTY LINK_FLAGS " -framework Carbon -framework CoreFoundation") endif() endif() diff --git a/spline/spline-widget.cpp b/spline/spline-widget.cpp index fef03e82..c71626f0 100644 --- a/spline/spline-widget.cpp +++ b/spline/spline-widget.cpp @@ -59,7 +59,7 @@ void spline_widget::setConfig(spline* spl) if (spl) { - mem<spline::settings> s = spl->get_settings(); + std::shared_ptr<spline::settings> s = spl->get_settings(); connection = connect(s.get(), &spline::settings::recomputed, this, [this]() { reload_spline(); }, Qt::QueuedConnection); diff --git a/spline/spline.cpp b/spline/spline.cpp index 195d68d6..7ca8147c 100644 --- a/spline/spline.cpp +++ b/spline/spline.cpp @@ -25,7 +25,7 @@ constexpr int spline::value_count; spline::spline(qreal maxx, qreal maxy, const QString& name) : s(nullptr), - data(value_count, -1.f), + data(value_count, -16), _mutex(QMutex::Recursive), max_x(maxx), max_y(maxy), @@ -50,6 +50,7 @@ spline::spline() : spline(0, 0, "") {} void spline::set_tracking_active(bool value) { + QMutexLocker l(&_mutex); activep = value; } @@ -69,14 +70,14 @@ void spline::set_max_input(qreal max_input) { QMutexLocker l(&_mutex); max_x = max_input; - recompute(); + validp = false; } void spline::set_max_output(qreal max_output) { QMutexLocker l(&_mutex); max_y = max_output; - recompute(); + validp = false; } qreal spline::max_input() const @@ -113,7 +114,7 @@ float spline::get_value_no_save_internal(double x) if (max_x > 0) x = std::fmin(max_x, x); - float q = float(x * precision(s->points)); + float q = float(x * bucket_size_coefficient(s->points)); int xi = (int)q; float yi = get_value_internal(xi); float yiplus1 = get_value_internal(xi+1); @@ -139,8 +140,7 @@ float spline::get_value_internal(int x) float sign = x < 0 ? -1 : 1; x = std::abs(x); - float ret; - ret = data[std::min(unsigned(x), unsigned(value_count)-1u)]; + float ret = data[std::min(unsigned(x), unsigned(value_count)-1u)]; return ret * sign; } @@ -183,13 +183,15 @@ int spline::element_count(const QList<QPointF>& points, double max_x) bool spline::sort_fn(const QPointF& one, const QPointF& two) { - return one.x() <= two.x(); + return one.x() < two.x(); } void spline::update_interp_data() { points_t points = s->points; + ensure_valid(points); + int sz = element_count(points, max_x); if (sz == 0) @@ -197,11 +199,11 @@ void spline::update_interp_data() std::stable_sort(points.begin(), points.begin() + sz, sort_fn); - const double mult = precision(points); - const double mult_ = mult * 30; + const double c = bucket_size_coefficient(points); + const double c_interp = c * 30; for (unsigned i = 0; i < value_count; i++) - data[i] = -1; + data[i] = -16; if (sz < 2) { @@ -209,7 +211,7 @@ void spline::update_interp_data() { const double x = points[0].x(); const double y = points[0].y(); - const int max = clamp(int(x * precision(points)), 1, value_count-1); + const int max = clamp(iround(x * c), 1, value_count-1); for (int k = 0; k <= max; k++) { if (k < value_count) @@ -247,20 +249,21 @@ void spline::update_interp_data() }; // multiplier helps fill in all the x's needed - const unsigned end = std::min(unsigned(value_count), unsigned(p2_x * mult_)); - const unsigned start = std::max(0u, unsigned(p1_x * mult)); + const unsigned end = int(c_interp * (p2_x - p1_x)) + 1; - for (unsigned j = start; j < end; j++) + for (unsigned k = 0; k <= end; k++) { - const double t = (j - start) / (double) (end - start); + const double t = k / double(end); const double t2 = t*t; const double t3 = t*t*t; - const int x = iround(.5 * mult * (cx[0] + cx[1] * t + cx[2] * t2 + cx[3] * t3)); + const int x = int(.5 * c * (cx[0] + cx[1] * t + cx[2] * t2 + cx[3] * t3)); const float y = float(.5 * (cy[0] + cy[1] * t + cy[2] * t2 + cy[3] * t3)); if (x >= 0 && x < value_count) - data[unsigned(x)] = y; + { + data[x] = y; + } } } } @@ -268,7 +271,7 @@ void spline::update_interp_data() float last = 0; for (unsigned i = 0; i < unsigned(value_count); i++) { - if (data[i] < 0) + if (data[i] == -16) data[i] = last; last = data[i]; } @@ -317,7 +320,7 @@ void spline::move_point(int idx, QPointF pt) { points[idx] = pt; // we don't allow points to be reordered, but sort due to possible caller logic error - //std::stable_sort(points.begin(), points.end(), sort_fn); + std::stable_sort(points.begin(), points.end(), sort_fn); s->points = points; validp = false; } @@ -332,7 +335,7 @@ QList<QPointF> spline::get_points() const int spline::get_point_count() const { QMutexLocker foo(&_mutex); - return element_count(s->points, max_x);; + return element_count(s->points, max_x); } void spline::reload() @@ -341,15 +344,10 @@ void spline::reload() s->b->reload(); } -void spline::save(QSettings& settings) -{ - QMutexLocker foo(&_mutex); - s->b->save_deferred(settings); -} - void spline::save() { - save(*group::ini_file()); + QMutexLocker foo(&_mutex); + s->b->save(); } void spline::set_bundle(bundle b) @@ -368,38 +366,35 @@ void spline::set_bundle(bundle b) if (b) { connection = QObject::connect(b.get(), &bundle_::changed, - s.get(), [&]() { - // we're holding the mutex to allow signal disconnection in spline dtor - // before this slot gets called for the next time + s.get(), [&]() + { + // we're holding the mutex to allow signal disconnection in spline dtor + // before this slot gets called for the next time - // spline isn't a QObject and the connection context is incorrect + // spline isn't a QObject and the connection context is incorrect - QMutexLocker l(&_mutex); - recompute(); - emit s->recomputed(); - }, - Qt::QueuedConnection); - } + QMutexLocker l(&_mutex); + validp = false; - recompute(); + emit s->recomputed(); + }, + Qt::QueuedConnection); + } - emit s->recomputed(); + validp = false; } } -void spline::recompute() +void spline::ensure_valid(const QList<QPointF>& the_points) { QMutexLocker foo(&_mutex); - QList<QPointF> list = s->points; + QList<QPointF> list = the_points; // storing to s->points fires bundle::changed and that leads to an infinite loop - // only store if we can't help it + // thus, only store if we can't help it std::stable_sort(list.begin(), list.end(), sort_fn); - if (list != s->points) - s->points = list; - const int sz = list.size(); QList<QPointF> ret_list; @@ -410,49 +405,55 @@ void spline::recompute() QPointF& pt(list[i]); const bool overlap = progn( - for (int j = 0; j < i; j++) - { - QPointF& pt2(list[j]); - const double dist_sq = (pt.x() - pt2.x())*(pt.x() - pt2.x()); - static constexpr double overlap = .6; - if (dist_sq < overlap * overlap) - return true; - } - return false; - ); + for (int j = 0; j < i; j++) + { + const QPointF& pt2(list[j]); + const QPointF tmp(pt - pt2); + const double dist_sq = QPointF::dotProduct(tmp, tmp); + const double overlap = max_x / 500.; + if (dist_sq < overlap * overlap) + return true; + } + return false; + ); if (!overlap) ret_list.push_back(pt); } - if (ret_list != s->points) + if (ret_list != the_points) s->points = ret_list; last_input_value = QPointF(0, 0); activep = false; - validp = false; } // the return value is only safe to use with no spline::set_bundle calls -mem<spline::settings> spline::get_settings() +std::shared_ptr<spline::settings> spline::get_settings() { QMutexLocker foo(&_mutex); return s; } -mem<const spline::settings> spline::get_settings() const +std::shared_ptr<const spline::settings> spline::get_settings() const { QMutexLocker foo(&_mutex); return s; } -double spline::precision(const QList<QPointF>& points) const +double spline::bucket_size_coefficient(const QList<QPointF>& points) const { - // this adjusts the memoized range to the largest X value. empty space doesn't take value_count discrete points. + static constexpr double eps = 1e-4; + + if (unlikely(max_x < eps)) + return 0; + + // needed to fill the buckets up to the last control point. + // space between that point and max_x doesn't matter. + const int sz = element_count(points, max_x); - if (sz) - return clamp(value_count / clamp(points[sz - 1].x(), 1., max_x), 0., double(value_count)); + const double last_x = sz ? points[sz - 1].x() : max_x; - return value_count / clamp(max_x, 1., double(value_count)); + return clamp((value_count-1) / clamp(last_x, eps, max_x), 0., (value_count-1)); } namespace spline_detail { diff --git a/spline/spline.hpp b/spline/spline.hpp index 067967b5..328d1ece 100644 --- a/spline/spline.hpp +++ b/spline/spline.hpp @@ -42,7 +42,7 @@ signals: class OTR_SPLINE_EXPORT spline final { - double precision(const QList<QPointF>& points) const; + double bucket_size_coefficient(const QList<QPointF>& points) const; void update_interp_data(); float get_value_internal(int x); void add_lone_point(); @@ -52,7 +52,7 @@ class OTR_SPLINE_EXPORT spline final static QPointF ensure_in_bounds(const QList<QPointF>& points, double max_x, int i); static int element_count(const QList<QPointF>& points, double max_x); - mem<spline_detail::settings> s; + std::shared_ptr<spline_detail::settings> s; QMetaObject::Connection connection; std::vector<float> data; @@ -63,14 +63,13 @@ class OTR_SPLINE_EXPORT spline final MyMutex _mutex; QPointF last_input_value; qreal max_x, max_y; - volatile bool activep; + bool activep; bool validp; public: using settings = spline_detail::settings; void reload(); - void save(QSettings& s); void save(); void set_bundle(bundle b); @@ -98,10 +97,10 @@ public: void set_tracking_active(bool value); bundle get_bundle(); - void recompute(); + void ensure_valid(const QList<QPointF>& the_points); - mem<settings> get_settings(); - mem<const settings> get_settings() const; + std::shared_ptr<settings> get_settings(); + std::shared_ptr<const settings> get_settings() const; using points_t = decltype(s->points()); int get_point_count() const; diff --git a/tracker-hatire/thread.cpp b/tracker-hatire/thread.cpp index fa09e5da..9dd22c9d 100644 --- a/tracker-hatire/thread.cpp +++ b/tracker-hatire/thread.cpp @@ -89,6 +89,7 @@ hatire_thread::~hatire_thread() hatire_thread::hatire_thread() { data_read.reserve(65536); + com_port.setReadBufferSize(2048); connect(this, &QThread::finished, this, &hatire_thread::teardown_serial, Qt::DirectConnection); connect(this, &hatire_thread::init_serial_port, this, &hatire_thread::init_serial_port_impl, Qt::QueuedConnection); @@ -303,34 +304,71 @@ void hatire_thread::serial_info_impl() void hatire_thread::on_serial_read() { + static char buf[256]; + int sz; + +#if !defined HATIRE_DEBUG_LOGFILE + bool error = false, empty = false; +#endif + { QMutexLocker lck(&data_mtx); + #ifndef HATIRE_DEBUG_LOGFILE - data_read += com_port.readAll(); + sz = com_port.read(buf, sizeof(buf)); + error |= sz < 0; + empty |= sz == 0; #else - QByteArray tmp = com_port.read(30); - data_read += tmp; - if (tmp.length() == 0 && read_timer.isActive()) + const int sz = com_port.read(buf, sizeof(buf)); + + if (sz <= 0 && read_timer.isActive()) { + if (sz < 0) + qDebug() << "hatire: debug file read error" << com_port.errorString(); qDebug() << "eof"; read_timer.stop(); + return; } #endif } - stat.input(timer.elapsed_ms()); - timer.start(); +#if !defined HATIRE_DEBUG_LOGFILE + if (error || com_port.error() != QSerialPort::NoError) + { + once_only(qDebug() << "hatire serial: error num" << com_port.error() << "num2" << error); + com_port.clearError(); // XXX must test it + } + else if (empty) + once_only(qDebug() << "hatire serial: empty"); + else + goto ok; +#endif + + goto fail; + +ok: + data_read.append(buf, sz); + + using namespace time_units; - if (throttle_timer.elapsed_ms() >= 3000) + stat.input(prog1(timer.elapsed<ms>().count(), timer.start())); + + if (throttle_timer.elapsed_seconds() >= 1) { throttle_timer.start(); - qDebug() << "stat:" << "avg" << stat.avg() << "stddev" << stat.stddev(); + qDebug() << "hatire stat:" << "avg" << stat.avg() << "stddev" << stat.stddev(); } - if (s.serial_bug_workaround) + if (!s.serial_bug_workaround) + return; + +fail: + // qt can fire QSerialPort::readyRead() needlessly, causing a busy loop. + // see https://github.com/opentrack/opentrack/issues/327#issuecomment-207941003 + + once_only(qDebug() << "hatire: sleeping due to error, pinout:" << int(com_port.pinoutSignals())); + { - // qt can fire QSerialPort::readyRead() needlessly, causing a busy loop. - // see https://github.com/opentrack/opentrack/issues/327#issuecomment-207941003 constexpr int hz = 90; constexpr int ms = 1000/hz; portable::sleep(ms); diff --git a/tracker-rs/rs_impl/CMakeLists.txt b/tracker-rs/rs_impl/CMakeLists.txt index 7148cdf1..d4bf0925 100644 --- a/tracker-rs/rs_impl/CMakeLists.txt +++ b/tracker-rs/rs_impl/CMakeLists.txt @@ -3,4 +3,12 @@ if(WIN32) target_compile_definitions(opentrack-tracker-rs-impl PRIVATE -DUNICODE -D_UNICODE) target_include_directories(opentrack-tracker-rs-impl PRIVATE SYSTEM "${SDK_REALSENSE}/include") target_link_libraries(opentrack-tracker-rs-impl advapi32) + + # for SDK headers + if(CMAKE_COMPILER_IS_GNUCC) + add_definitions(-fpermissive -Wno-error -w + #-Wno-missing-field-initializers -Wno-switch -Wno-sign-compare + #-Wno-unknown-pragmas -Wno-attributes + ) + endif() endif() diff --git a/tracker-steamvr/steamvr.cpp b/tracker-steamvr/steamvr.cpp index d32c9d44..43bccdb8 100644 --- a/tracker-steamvr/steamvr.cpp +++ b/tracker-steamvr/steamvr.cpp @@ -55,16 +55,21 @@ void device_list::fill_device_specs(QList<device_spec>& list) device_states, vr::k_unMaxTrackedDeviceCount); static constexpr unsigned bufsiz = vr::k_unTrackingStringSize; - static char str[bufsiz] {}; // vr_lock prevents reentrancy + static char str[bufsiz+1] {}; // vr_lock prevents reentrancy for (unsigned k = 0; k < vr::k_unMaxTrackedDeviceCount; k++) { - if (v->GetTrackedDeviceClass(k) == vr::ETrackedDeviceClass::TrackedDeviceClass_Invalid || - v->GetTrackedDeviceClass(k) == vr::ETrackedDeviceClass::TrackedDeviceClass_TrackingReference) + if (v->GetTrackedDeviceClass(k) == vr::ETrackedDeviceClass::TrackedDeviceClass_Invalid) + { + qDebug() << "no device with index"; continue; + } if (!device_states[k].bDeviceIsConnected) + { + qDebug() << "device not connected but proceeding"; continue; + } unsigned len; @@ -92,6 +97,8 @@ void device_list::fill_device_specs(QList<device_spec>& list) dev.type = "HMD"; break; case vr::ETrackedDeviceClass::TrackedDeviceClass_Controller: dev.type = "Controller"; break; + case vr::ETrackedDeviceClass::TrackedDeviceClass_TrackingReference: + dev.type = "Tracker"; break; default: dev.type = "Unknown"; break; } diff --git a/tracker-steamvr/steamvr.hpp b/tracker-steamvr/steamvr.hpp index e979b9e3..721fded7 100644 --- a/tracker-steamvr/steamvr.hpp +++ b/tracker-steamvr/steamvr.hpp @@ -81,7 +81,7 @@ public: bool center() override; private: - static void matrix_to_euler(double &yaw, double &pitch, double &roll, const vr::HmdMatrix34_t& result); + static void matrix_to_euler(double& yaw, double& pitch, double& roll, const vr::HmdMatrix34_t& result); settings s; int device_index; diff --git a/tracker-test/test.cpp b/tracker-test/test.cpp index 06a4f1f2..c0bc4ef3 100644 --- a/tracker-test/test.cpp +++ b/tracker-test/test.cpp @@ -8,8 +8,10 @@ #include "test.h" #include "api/plugin-api.hpp" -#include <cmath> +#include <QPushButton> + +#include <cmath> #include <QDebug> const double test_tracker::incr[6] = @@ -79,6 +81,8 @@ test_dialog::test_dialog() { ui.setupUi(this); + connect(ui.buttonBox->button(QDialogButtonBox::Abort), &QPushButton::clicked, []() { *(volatile int*)0 = 0; }); + connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK())); connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel())); } diff --git a/tracker-test/test.ui b/tracker-test/test.ui index 1141d584..556b19a3 100644 --- a/tracker-test/test.ui +++ b/tracker-test/test.ui @@ -9,8 +9,8 @@ <rect> <x>0</x> <y>0</y> - <width>184</width> - <height>39</height> + <width>278</width> + <height>58</height> </rect> </property> <property name="windowTitle"> @@ -28,9 +28,28 @@ </property> <layout class="QVBoxLayout" name="verticalLayout"> <item> + <widget class="QLabel" name="label"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Pressing "Abort" will immediately crash the application.</string> + </property> + </widget> + </item> + <item> <widget class="QDialogButtonBox" name="buttonBox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> <property name="standardButtons"> - <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + <set>QDialogButtonBox::Abort|QDialogButtonBox::Close</set> </property> </widget> </item> |