diff options
Diffstat (limited to 'compat')
| -rw-r--r-- | compat/CMakeLists.txt | 8 | ||||
| -rw-r--r-- | compat/camera-names.cpp | 111 | ||||
| -rw-r--r-- | compat/camera-names.hpp | 18 | ||||
| -rw-r--r-- | compat/export.hpp | 27 | ||||
| -rw-r--r-- | compat/make-unique.hpp | 43 | ||||
| -rw-r--r-- | compat/nan.cpp | 17 | ||||
| -rw-r--r-- | compat/nan.hpp | 11 | ||||
| -rw-r--r-- | compat/options.cpp | 239 | ||||
| -rw-r--r-- | compat/options.hpp | 481 | ||||
| -rw-r--r-- | compat/pi-constant.hpp | 3 | ||||
| -rw-r--r-- | compat/process-list.hpp | 169 | ||||
| -rw-r--r-- | compat/qcopyable-mutex.hpp | 37 | ||||
| -rw-r--r-- | compat/shm.cpp | 199 | ||||
| -rw-r--r-- | compat/shm.h | 48 | ||||
| -rw-r--r-- | compat/sleep.hpp | 22 | ||||
| -rw-r--r-- | compat/slider.cpp | 73 | ||||
| -rw-r--r-- | compat/slider.hpp | 62 | ||||
| -rw-r--r-- | compat/timer.hpp | 100 | ||||
| -rw-r--r-- | compat/util.hpp | 93 | ||||
| -rw-r--r-- | compat/win32-com.cpp | 60 | ||||
| -rw-r--r-- | compat/win32-com.hpp | 18 | 
21 files changed, 1839 insertions, 0 deletions
| 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 | 
