summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.clang-format7
-rw-r--r--.clang-tidy164
-rw-r--r--.gitattributes2
-rw-r--r--.github/workflows/build-windows.bat3
-rw-r--r--.github/workflows/cmake.yml70
-rw-r--r--.github/workflows/linux-package.yml77
-rw-r--r--3rdparty-notices/EIGEN-COPYING.txt379
-rw-r--r--3rdparty-notices/LIBUSB-COPYING.txt504
-rw-r--r--3rdparty-notices/ONNXRUNTIME.txt24
-rw-r--r--CMakeLists.txt24
-rw-r--r--README.md40
-rw-r--r--api/lang/de_DE.ts11
-rw-r--r--api/lang/zh_CN.ts2
-rw-r--r--api/plugin-api.cpp25
-rw-r--r--api/plugin-api.hpp58
-rw-r--r--api/plugin-support.hpp22
-rw-r--r--cmake/FindEigen3.cmake106
-rw-r--r--cmake/FindONNXRuntime.cmake104
-rw-r--r--cmake/apple.cmake13
-rw-r--r--cmake/msvc-clang.cmake131
-rw-r--r--cmake/msvc.cmake160
-rw-r--r--cmake/opentrack-boilerplate.cmake47
-rw-r--r--cmake/opentrack-hier.cmake16
-rw-r--r--cmake/opentrack-i18n.cmake25
-rw-r--r--cmake/opentrack-install.cmake65
-rw-r--r--cmake/opentrack-load-user-settings.cmake7
-rw-r--r--cmake/opentrack-opencv.cmake10
-rw-r--r--cmake/opentrack-org.cmake21
-rw-r--r--cmake/opentrack-pkg-config.cmake33
-rw-r--r--cmake/opentrack-platform.cmake28
-rw-r--r--cmake/opentrack-policy.cmake4
-rw-r--r--cmake/opentrack-qt.cmake35
-rw-r--r--cmake/opentrack-variant.cmake53
-rw-r--r--cmake/translation-stub.ts2
-rw-r--r--compat/CMakeLists.txt19
-rw-r--r--compat/camera-names.cpp54
-rw-r--r--compat/check-visible.cpp2
-rw-r--r--compat/check-visible.hpp2
-rw-r--r--compat/correlation-calibrator.cpp3
-rw-r--r--compat/hamilton-tools.cpp (renamed from filter-hamilton/hamilton-tools.cpp)36
-rw-r--r--compat/hamilton-tools.h76
-rw-r--r--compat/lang/de_DE.ts (renamed from ext-falcon-bms-linear-acc/lang/nl_NL.ts)2
-rw-r--r--compat/lang/zh_CN.ts2
-rw-r--r--compat/macros.h (renamed from compat/macros1.h)19
-rw-r--r--compat/macros.hpp25
-rw-r--r--compat/math.hpp59
-rw-r--r--compat/mutex.cpp30
-rw-r--r--compat/mutex.hpp18
-rw-r--r--compat/process-list.cpp195
-rw-r--r--compat/process-list.hpp161
-rw-r--r--compat/qt-dpi.hpp17
-rw-r--r--compat/qt-signal.cpp12
-rw-r--r--compat/qt-signal.hpp59
-rw-r--r--compat/run-in-thread.hpp108
-rw-r--r--compat/shm.cpp3
-rw-r--r--compat/shm.h4
-rw-r--r--compat/timer.cpp24
-rw-r--r--compat/timer.hpp2
-rw-r--r--contrib-noinst/important-stuff/game-data.exebin0 -> 367104 bytes
-rw-r--r--contrib-noinst/important-stuff/game_data.c1
-rw-r--r--contrib/libusbK-dev-kit/libusbK-3.1.0.0-setup.exebin0 -> 7904435 bytes
-rw-r--r--contrib/libusbK-inf-wizard/libusbK-inf-wizard.exebin0 -> 9474560 bytes
-rw-r--r--contrib/libusbK-inf-wizard/license/AUTHORS-libusb0.txt20
-rw-r--r--contrib/libusbK-inf-wizard/license/AUTHORS-libusbK.txt40
-rw-r--r--contrib/libusbK-inf-wizard/license/LICENSE-bsd.txt33
-rw-r--r--contrib/libusbK-inf-wizard/license/LICENSE-gpl3.txt686
-rw-r--r--contrib/libusbK-inf-wizard/license/LICENSE-lgpl3.txt165
-rw-r--r--csv/csv.cpp136
-rw-r--r--csv/csv.h25
-rw-r--r--csv/lang/de_DE.ts (renamed from ext-falcon-bms-linear-acc/lang/ru_RU.ts)2
-rw-r--r--csv/lang/zh_CN.ts2
-rw-r--r--cv/CMakeLists.txt2
-rw-r--r--cv/affine.hpp1
-rw-r--r--cv/init.cpp3
-rw-r--r--cv/lang/de_DE.ts (renamed from ext-falcon-bms-linear-acc/lang/stub.ts)2
-rw-r--r--cv/lang/zh_CN.ts2
-rw-r--r--cv/numeric.hpp4
-rw-r--r--cv/translation-calibrator.cpp2
-rw-r--r--cv/translation-calibrator.hpp2
-rw-r--r--cv/video-widget.cpp16
-rw-r--r--cv/video-widget.hpp2
-rw-r--r--dinput/dinput.cpp5
-rw-r--r--dinput/dinput.hpp16
-rw-r--r--dinput/keybinding-worker.cpp107
-rw-r--r--dinput/keybinding-worker.hpp12
-rw-r--r--dinput/lang/de_DE.ts (renamed from ext-falcon-bms-linear-acc/lang/zh_CN.ts)2
-rw-r--r--dinput/lang/zh_CN.ts2
-rw-r--r--dinput/win32-joystick.cpp21
-rw-r--r--dinput/win32-joystick.hpp17
-rw-r--r--ext-falcon-bms-linear-acc/CMakeLists.txt3
-rw-r--r--ext-falcon-bms-linear-acc/FlightData.h578
-rw-r--r--ext-falcon-bms-linear-acc/bms-shm.cpp2
-rw-r--r--ext-falcon-bms-linear-acc/bms-shm.hpp2
-rw-r--r--ext-falcon-bms-linear-acc/falcon-bms-dialog.cpp9
-rw-r--r--ext-falcon-bms-linear-acc/falcon-bms-dialog.hpp10
-rw-r--r--ext-falcon-bms-linear-acc/falcon-bms-ext.cpp15
-rw-r--r--ext-falcon-bms-linear-acc/falcon-bms-ext.hpp11
-rw-r--r--ext-falcon-bms-linear-acc/falcon-bms-metadata.cpp11
-rw-r--r--ext-falcon-bms-linear-acc/falcon-bms-metadata.hpp9
-rw-r--r--ext-falcon-bms-linear-acc/module.cpp5
-rw-r--r--filter-accela-hamilton/CMakeLists.txt2
-rw-r--r--filter-accela-hamilton/accela_hamilton.cpp121
-rw-r--r--filter-accela-hamilton/accela_hamilton.h69
-rw-r--r--filter-accela-hamilton/accela_hamilton_dialog.cpp99
-rw-r--r--filter-accela-hamilton/accela_hamilton_filtercontrols.ui485
-rw-r--r--filter-accela-hamilton/accela_hamilton_settings.hpp63
-rw-r--r--filter-accela-hamilton/lang/de_DE.ts91
-rw-r--r--filter-accela-hamilton/lang/nl_NL.ts82
-rw-r--r--filter-accela-hamilton/lang/ru_RU.ts82
-rw-r--r--filter-accela-hamilton/lang/stub.ts82
-rw-r--r--filter-accela-hamilton/lang/zh_CN.ts82
-rw-r--r--filter-accela/ftnoir_accela_filtercontrols.ui35
-rw-r--r--filter-accela/ftnoir_filter_accela.cpp21
-rw-r--r--filter-accela/ftnoir_filter_accela.h6
-rw-r--r--filter-accela/ftnoir_filter_accela_dialog.cpp18
-rw-r--r--filter-accela/lang/de_DE.ts57
-rw-r--r--filter-accela/lang/zh_CN.ts41
-rw-r--r--filter-ewma2/ftnoir_ewma_filtercontrols.ui6
-rw-r--r--filter-ewma2/ftnoir_filter_ewma2.cpp21
-rw-r--r--filter-ewma2/ftnoir_filter_ewma2.h5
-rw-r--r--filter-ewma2/lang/de_DE.ts72
-rw-r--r--filter-ewma2/lang/nl_NL.ts2
-rw-r--r--filter-ewma2/lang/ru_RU.ts2
-rw-r--r--filter-ewma2/lang/stub.ts2
-rw-r--r--filter-ewma2/lang/zh_CN.ts23
-rw-r--r--filter-hamilton/ftnoir_filter_hamilton.cpp31
-rw-r--r--filter-hamilton/ftnoir_filter_hamilton.h4
-rw-r--r--filter-hamilton/ftnoir_hamilton_filtercontrols.ui1509
-rw-r--r--filter-hamilton/hamilton-tools.h27
-rw-r--r--filter-hamilton/lang/de_DE.ts78
-rw-r--r--filter-hamilton/lang/nl_NL.ts26
-rw-r--r--filter-hamilton/lang/ru_RU.ts26
-rw-r--r--filter-hamilton/lang/stub.ts26
-rw-r--r--filter-hamilton/lang/zh_CN.ts42
-rw-r--r--filter-kalman/CMakeLists.txt5
-rw-r--r--filter-kalman/ftnoir_kalman_filtercontrols.ui170
-rw-r--r--filter-kalman/kalman.cpp323
-rw-r--r--filter-kalman/kalman.h159
-rw-r--r--filter-kalman/kalman_simulation.py76
-rw-r--r--filter-nm/CMakeLists.txt1
-rw-r--r--filter-nm/ftnoir_filter_nm.cpp67
-rw-r--r--filter-nm/ftnoir_filter_nm.h81
-rw-r--r--filter-nm/ftnoir_filter_nm_dialog.cpp56
-rw-r--r--filter-nm/ftnoir_nm_filtercontrols.ui312
-rw-r--r--filter-nm/lang/de_DE.ts62
-rw-r--r--filter-nm/lang/nl_NL.ts (renamed from trackmouse/lang/zh_CN.ts)48
-rw-r--r--filter-nm/lang/ru_RU.ts62
-rw-r--r--filter-nm/lang/stub.ts (renamed from trackmouse/lang/stub.ts)48
-rw-r--r--filter-nm/lang/zh_CN.ts62
-rw-r--r--gui/CMakeLists.txt7
-rw-r--r--gui/images/english.pngbin283 -> 259 bytes
-rw-r--r--gui/images/settings16.pngbin711 -> 1136 bytes
-rw-r--r--gui/init.cpp83
-rw-r--r--gui/init.hpp2
-rw-r--r--gui/lang/de_DE.ts428
-rw-r--r--gui/lang/nl_NL.ts70
-rw-r--r--gui/lang/ru_RU.ts77
-rw-r--r--gui/lang/stub.ts76
-rw-r--r--gui/lang/zh_CN.ts76
-rw-r--r--gui/mapping-dialog.cpp23
-rw-r--r--gui/mapping-dialog.hpp1
-rw-r--r--gui/mapping-dialog.ui69
-rw-r--r--gui/options-dialog.cpp (renamed from gui/settings.cpp)223
-rw-r--r--gui/options-dialog.hpp57
-rw-r--r--gui/options-dialog.ui2364
-rw-r--r--gui/process_detector.cpp8
-rw-r--r--gui/process_widget.ui13
-rw-r--r--gui/settings-dialog.ui2202
-rw-r--r--gui/settings.hpp33
-rw-r--r--installer/.gitignore1
-rw-r--r--installer/Output/.gitignore1
-rw-r--r--installer/Output/.gitkeep0
-rw-r--r--installer/opentrack-installer.iss6
-rw-r--r--logic/extensions.cpp74
-rw-r--r--logic/extensions.hpp44
-rw-r--r--logic/lang/de_DE.ts64
-rw-r--r--logic/lang/zh_CN.ts23
-rw-r--r--logic/main-settings.hpp12
-rw-r--r--logic/pipeline.cpp106
-rw-r--r--logic/pipeline.hpp16
-rw-r--r--logic/shortcuts.cpp11
-rw-r--r--logic/state.cpp22
-rw-r--r--logic/state.hpp3
-rw-r--r--logic/win32-shortcuts.cpp29
-rw-r--r--logic/work.cpp4
-rw-r--r--logic/work.hpp22
-rwxr-xr-xmacosx/make-app-bundle.sh5
-rw-r--r--main-window/CMakeLists.txt6
-rw-r--r--main-window/export.hpp11
-rw-r--r--main-window/lang/nl_NL.ts4
-rw-r--r--main-window/lang/ru_RU.ts4
-rw-r--r--main-window/lang/stub.ts4
-rw-r--r--main-window/lang/zh_CN.ts4
-rw-r--r--main-window/mixin-traits.cpp62
-rw-r--r--main-window/mixin-traits.hpp42
-rw-r--r--main-window/mixins.hpp13
-rw-r--r--main-window/module-mixin.cpp126
-rw-r--r--main-window/module-mixin.hpp52
-rw-r--r--migration/20220105_00-pt-grayscale.cpp38
-rw-r--r--migration/20220126_00-camera-name.cpp79
-rw-r--r--migration/lang/de_DE.ts4
-rw-r--r--migration/lang/zh_CN.ts2
-rw-r--r--migration/migration.cpp31
-rw-r--r--migration/migration.hpp3
-rw-r--r--opentrack/CMakeLists.txt16
-rw-r--r--opentrack/appnap_mac.mm47
-rw-r--r--opentrack/defs.hpp16
-rw-r--r--opentrack/lang/de_DE.ts193
-rw-r--r--opentrack/main-window.cpp383
-rw-r--r--opentrack/main-window.hpp36
-rw-r--r--opentrack/main-window.ui2424
-rw-r--r--opentrack/new_file_dialog.h1
-rw-r--r--options/base-value.cpp19
-rw-r--r--options/base-value.hpp7
-rw-r--r--options/bundle.hpp9
-rw-r--r--options/connector.cpp6
-rw-r--r--options/connector.hpp4
-rw-r--r--options/defs.hpp2
-rw-r--r--options/globals.cpp22
-rw-r--r--options/globals.hpp6
-rw-r--r--options/group.hpp2
-rw-r--r--options/lang/de_DE.ts4
-rw-r--r--options/lang/zh_CN.ts2
-rw-r--r--options/metatype.cpp2
-rw-r--r--options/scoped.cpp9
-rw-r--r--options/scoped.hpp4
-rw-r--r--options/slider.hpp1
-rw-r--r--options/tie.cpp33
-rw-r--r--options/tie.hpp28
-rw-r--r--options/value-traits.hpp81
-rw-r--r--options/value.hpp44
-rw-r--r--pose-widget/lang/de_DE.ts4
-rw-r--r--pose-widget/lang/zh_CN.ts2
-rw-r--r--pose-widget/pose-widget.cpp31
-rw-r--r--pose-widget/pose-widget.hpp5
-rw-r--r--presets/README.txt12
-rw-r--r--proto-flightgear/ftnoir_protocol_fg.cpp6
-rw-r--r--proto-flightgear/lang/de_DE.ts37
-rw-r--r--proto-flightgear/lang/zh_CN.ts2
-rw-r--r--proto-fsuipc/lang/zh_CN.ts2
-rw-r--r--proto-ft/ftnoir_ftcontrols.ui183
-rw-r--r--proto-ft/ftnoir_protocol_ft.cpp94
-rw-r--r--proto-ft/ftnoir_protocol_ft.h16
-rw-r--r--proto-ft/ftnoir_protocol_ft_dialog.cpp21
-rw-r--r--proto-ft/lang/de_DE.ts106
-rw-r--r--proto-ft/lang/nl_NL.ts40
-rw-r--r--proto-ft/lang/ru_RU.ts40
-rw-r--r--proto-ft/lang/stub.ts40
-rw-r--r--proto-ft/lang/zh_CN.ts42
-rw-r--r--proto-iokit-foohid/foohidjoystick.cpp2
-rw-r--r--proto-iokit-foohid/iokitprotocol.cpp14
-rw-r--r--proto-iokit-foohid/lang/zh_CN.ts2
-rw-r--r--proto-libevdev/ftnoir_protocol_libevdev.cpp7
-rw-r--r--proto-libevdev/ftnoir_protocol_libevdev.h1
-rw-r--r--proto-libevdev/lang/de_DE.ts37
-rw-r--r--proto-libevdev/lang/zh_CN.ts2
-rw-r--r--proto-mouse/ftnoir_protocol_mouse.cpp4
-rw-r--r--proto-mouse/lang/de_DE.ts (renamed from trackmouse/lang/nl_NL.ts)51
-rw-r--r--proto-mouse/lang/zh_CN.ts24
-rw-r--r--proto-osc/CMakeLists.txt15
-rw-r--r--proto-osc/dialog.cpp38
-rw-r--r--proto-osc/dialog.hpp29
-rw-r--r--proto-osc/dialog.ui131
-rw-r--r--proto-osc/images/osc-icon.pngbin0 -> 868 bytes
-rw-r--r--proto-osc/lang/de_DE.ts45
-rw-r--r--proto-osc/lang/nl_NL.ts45
-rw-r--r--proto-osc/lang/ru_RU.ts (renamed from filter-kalman/lang/ru_RU.ts)29
-rw-r--r--proto-osc/lang/stub.ts (renamed from filter-kalman/lang/stub.ts)29
-rw-r--r--proto-osc/lang/zh_CN.ts (renamed from filter-kalman/lang/zh_CN.ts)31
-rw-r--r--proto-osc/metadata.cpp8
-rw-r--r--proto-osc/metadata.hpp10
-rw-r--r--proto-osc/osc-res.qrc5
-rw-r--r--proto-osc/proto.cpp54
-rw-r--r--proto-osc/proto.hpp29
-rw-r--r--proto-osc/settings.hpp14
-rw-r--r--proto-simconnect/ftnoir_protocol_sc.cpp32
-rw-r--r--proto-simconnect/ftnoir_protocol_sc.h2
-rw-r--r--proto-simconnect/lang/de_DE.ts (renamed from trackmouse/lang/ru_RU.ts)49
-rw-r--r--proto-simconnect/lang/zh_CN.ts2
-rw-r--r--proto-udp/ftnoir_protocol_ftn.cpp2
-rw-r--r--proto-udp/lang/de_DE.ts37
-rw-r--r--proto-udp/lang/zh_CN.ts2
-rw-r--r--proto-vjoystick/lang/de_DE.ts70
-rw-r--r--proto-vjoystick/lang/nl_NL.ts8
-rw-r--r--proto-vjoystick/lang/ru_RU.ts8
-rw-r--r--proto-vjoystick/lang/stub.ts8
-rw-r--r--proto-vjoystick/lang/zh_CN.ts10
-rw-r--r--proto-vjoystick/vjoystick.cpp2
-rw-r--r--proto-vjoystick/vjoystick.h1
-rw-r--r--proto-vjoystick/vjoystick.ui2
-rw-r--r--proto-wine/CMakeLists.txt6
-rw-r--r--proto-wine/ftnoir_protocol_wine.cpp111
-rw-r--r--proto-wine/ftnoir_protocol_wine.h19
-rw-r--r--proto-wine/ftnoir_protocol_wine_dialog.cpp208
-rw-r--r--proto-wine/ftnoir_winecontrols.ui256
-rw-r--r--proto-wine/lang/de_DE.ts140
-rw-r--r--proto-wine/lang/nl_NL.ts75
-rw-r--r--proto-wine/lang/ru_RU.ts75
-rw-r--r--proto-wine/lang/stub.ts75
-rw-r--r--proto-wine/lang/zh_CN.ts77
-rw-r--r--proto-wine/proton.cpp41
-rw-r--r--proto-wine/proton.h7
-rw-r--r--qxt-mini/lang/de_DE.ts4
-rw-r--r--qxt-mini/lang/zh_CN.ts2
-rw-r--r--qxt-mini/powerset.hpp2
-rw-r--r--qxt-mini/qxtglobalshortcut.cpp4
-rw-r--r--qxt-mini/qxtglobalshortcut_mac.cpp19
-rw-r--r--qxt-mini/qxtglobalshortcut_p.h6
-rw-r--r--qxt-mini/qxtglobalshortcut_x11.cpp2
-rw-r--r--sdk-paths-runner@GNU-Linux.cmake3
-rw-r--r--sdk-paths-runneradmin@MSVC-windows.cmake33
-rw-r--r--sdk-paths-sthalik@Clang-windows.cmake2
-rw-r--r--sdk-paths-sthalik@GNU-windows.cmake2
-rw-r--r--sdk-paths-sthalik@MSVC-windows.cmake51
-rw-r--r--settings/facetracknoir supported games.csv47
-rw-r--r--spline/axis-opts.cpp3
-rw-r--r--spline/axis-opts.hpp10
-rw-r--r--spline/lang/de_DE.ts4
-rw-r--r--spline/lang/zh_CN.ts2
-rw-r--r--spline/spline-widget.cpp22
-rw-r--r--spline/spline.cpp97
-rw-r--r--spline/spline.hpp10
-rw-r--r--tracker-aruco/CMakeLists.txt11
-rw-r--r--tracker-aruco/aruco-trackercontrols.ui93
-rw-r--r--tracker-aruco/ftnoir_tracker_aruco.cpp19
-rw-r--r--tracker-aruco/ftnoir_tracker_aruco.h3
-rw-r--r--tracker-aruco/lang/de_DE.ts82
-rw-r--r--tracker-aruco/lang/nl_NL.ts8
-rw-r--r--tracker-aruco/lang/ru_RU.ts8
-rw-r--r--tracker-aruco/lang/stub.ts8
-rw-r--r--tracker-aruco/lang/zh_CN.ts32
-rw-r--r--tracker-easy/CMakeLists.txt6
-rw-r--r--tracker-easy/lang/de_DE.ts250
-rw-r--r--tracker-easy/preview.cpp1
-rw-r--r--tracker-easy/tracker-easy.cpp8
-rw-r--r--tracker-eyeware-beam/CMakeLists.txt27
-rw-r--r--tracker-eyeware-beam/eyeware_beam.cpp116
-rw-r--r--tracker-eyeware-beam/eyeware_beam.h70
-rw-r--r--tracker-eyeware-beam/eyeware_beam.qrc5
-rw-r--r--tracker-eyeware-beam/eyeware_beam.ui90
-rw-r--r--tracker-eyeware-beam/eyeware_beam_logo.pngbin0 -> 2032 bytes
-rw-r--r--tracker-eyeware-beam/lang/de_DE.ts20
-rw-r--r--tracker-eyeware-beam/lang/nl_NL.ts20
-rw-r--r--tracker-eyeware-beam/lang/ru_RU.ts20
-rw-r--r--tracker-eyeware-beam/lang/stub.ts20
-rw-r--r--tracker-eyeware-beam/lang/zh_CN.ts20
-rw-r--r--tracker-freepie-udp/ftnoir_tracker_freepie-udp.cpp6
-rw-r--r--tracker-freepie-udp/lang/de_DE.ts86
-rw-r--r--tracker-freepie-udp/lang/zh_CN.ts2
-rw-r--r--tracker-fusion/fusion.cpp3
-rw-r--r--tracker-fusion/lang/de_DE.ts56
-rw-r--r--tracker-fusion/lang/zh_CN.ts2
-rw-r--r--tracker-hatire/CMakeLists.txt3
-rw-r--r--tracker-hatire/ftnoir_hatcontrols.ui84
-rw-r--r--tracker-hatire/ftnoir_tracker_hat.cpp2
-rw-r--r--tracker-hatire/ftnoir_tracker_hat.h3
-rw-r--r--tracker-hatire/ftnoir_tracker_hat_dialog.cpp5
-rw-r--r--tracker-hatire/ftnoir_tracker_hat_settings.h3
-rw-r--r--tracker-hatire/lang/de_DE.ts353
-rw-r--r--tracker-hatire/lang/nl_NL.ts4
-rw-r--r--tracker-hatire/lang/ru_RU.ts4
-rw-r--r--tracker-hatire/lang/stub.ts4
-rw-r--r--tracker-hatire/lang/zh_CN.ts6
-rw-r--r--tracker-hatire/thread.cpp17
-rw-r--r--tracker-hatire/thread.hpp1
-rw-r--r--tracker-hydra/lang/de_DE.ts15
-rw-r--r--tracker-hydra/lang/zh_CN.ts2
-rw-r--r--tracker-joystick/ftnoir_tracker_joystick.cpp4
-rw-r--r--tracker-joystick/lang/de_DE.ts86
-rw-r--r--tracker-joystick/lang/zh_CN.ts2
-rw-r--r--tracker-kinect-face/CMakeLists.txt12
-rw-r--r--tracker-kinect-face/camera_kinect_ir.cpp8
-rw-r--r--tracker-kinect-face/kinect_face_settings.h1
-rw-r--r--tracker-kinect-face/kinect_face_tracker.cpp26
-rw-r--r--tracker-kinect-face/kinect_face_tracker.h1
-rw-r--r--tracker-kinect-face/lang/de_DE.ts29
-rw-r--r--tracker-kinect-face/lang/zh_CN.ts2
-rw-r--r--tracker-linux-joystick/ftnoir_tracker_linux_joystick.cpp4
-rw-r--r--tracker-linux-joystick/lang/de_DE.ts86
-rw-r--r--tracker-linux-joystick/lang/zh_CN.ts2
-rw-r--r--tracker-linux-joystick/linux_joystick.cpp3
-rw-r--r--tracker-neuralnet/BUILD.md24
-rw-r--r--tracker-neuralnet/CMakeLists.txt54
-rw-r--r--tracker-neuralnet/deadzone_filter.cpp173
-rw-r--r--tracker-neuralnet/deadzone_filter.h37
-rw-r--r--tracker-neuralnet/ftnoir_tracker_neuralnet.cpp1106
-rw-r--r--tracker-neuralnet/ftnoir_tracker_neuralnet.h230
-rw-r--r--tracker-neuralnet/lang/de_DE.ts172
-rw-r--r--tracker-neuralnet/lang/nl_NL.ts82
-rw-r--r--tracker-neuralnet/lang/ru_RU.ts121
-rw-r--r--tracker-neuralnet/lang/stub.ts82
-rw-r--r--tracker-neuralnet/lang/zh_CN.ts113
-rw-r--r--tracker-neuralnet/model_adapters.cpp416
-rw-r--r--tracker-neuralnet/model_adapters.h105
-rw-r--r--tracker-neuralnet/models/head-pose-0.2-big.onnxbin0 -> 44760256 bytes
-rw-r--r--tracker-neuralnet/models/head-pose-0.2-small.onnx (renamed from tracker-neuralnet/models/head-pose.onnx)bin13047683 -> 12926209 bytes
-rw-r--r--tracker-neuralnet/models/head-pose-0.3-big-quantized.onnxbin0 -> 11385815 bytes
-rw-r--r--tracker-neuralnet/neuralnet-trackercontrols.ui842
-rw-r--r--tracker-neuralnet/opencv_contrib.h120
-rw-r--r--tracker-neuralnet/preview.cpp135
-rw-r--r--tracker-neuralnet/preview.h60
-rw-r--r--tracker-neuralnet/redist/vcomp140.dllbin0 -> 151976 bytes
-rw-r--r--tracker-neuralnet/tests.cpp58
-rw-r--r--tracker-neuralnet/unscented_trafo.h132
-rw-r--r--tracker-pt/CMakeLists.txt5
-rw-r--r--tracker-pt/FTNoIR_PT_Controls.ui746
-rw-r--r--tracker-pt/ftnoir_tracker_pt.cpp96
-rw-r--r--tracker-pt/ftnoir_tracker_pt.h12
-rw-r--r--tracker-pt/ftnoir_tracker_pt_dialog.cpp72
-rw-r--r--tracker-pt/ftnoir_tracker_pt_dialog.h9
-rw-r--r--tracker-pt/lang/de_DE.ts378
-rw-r--r--tracker-pt/lang/nl_NL.ts79
-rw-r--r--tracker-pt/lang/ru_RU.ts79
-rw-r--r--tracker-pt/lang/stub.ts79
-rw-r--r--tracker-pt/lang/zh_CN.ts79
-rw-r--r--tracker-pt/module/CMakeLists.txt5
-rw-r--r--tracker-pt/module/camera.cpp21
-rw-r--r--tracker-pt/module/camera.h4
-rw-r--r--tracker-pt/module/frame.cpp53
-rw-r--r--tracker-pt/module/frame.hpp8
-rw-r--r--tracker-pt/module/lang/de_DE.ts11
-rw-r--r--tracker-pt/module/lang/zh_CN.ts2
-rw-r--r--tracker-pt/module/point_extractor.cpp157
-rw-r--r--tracker-pt/module/point_extractor.h18
-rw-r--r--tracker-pt/point-filter.cpp72
-rw-r--r--tracker-pt/point-filter.hpp32
-rw-r--r--tracker-pt/point_tracker.cpp24
-rw-r--r--tracker-pt/point_tracker.h17
-rw-r--r--tracker-pt/pt-api.cpp7
-rw-r--r--tracker-pt/pt-api.hpp28
-rw-r--r--tracker-pt/pt-settings.hpp14
-rw-r--r--tracker-rift-140/lang/de_DE.ts18
-rw-r--r--tracker-rift-140/lang/zh_CN.ts2
-rw-r--r--tracker-rs/ftnoir_tracker_rs.cpp23
-rw-r--r--tracker-rs/ftnoir_tracker_rs.h3
-rw-r--r--tracker-rs/ftnoir_tracker_rs_controls.cpp6
-rw-r--r--tracker-rs/ftnoir_tracker_rs_controls.h3
-rw-r--r--tracker-rs/ftnoir_tracker_rs_controls.ui42
-rw-r--r--tracker-rs/lang/de_DE.ts55
-rw-r--r--tracker-rs/lang/nl_NL.ts34
-rw-r--r--tracker-rs/lang/ru_RU.ts34
-rw-r--r--tracker-rs/lang/stub.ts34
-rw-r--r--tracker-rs/lang/zh_CN.ts36
-rw-r--r--tracker-rs/rs_impl/ftnoir_tracker_rs_impl.cpp15
-rw-r--r--tracker-s2bot/ftnoir_tracker_s2bot.cpp13
-rw-r--r--tracker-s2bot/lang/de_DE.ts90
-rw-r--r--tracker-s2bot/lang/zh_CN.ts36
-rw-r--r--tracker-steamvr/lang/de_DE.ts33
-rw-r--r--tracker-steamvr/lang/zh_CN.ts2
-rw-r--r--tracker-steamvr/steamvr.cpp48
-rw-r--r--tracker-steamvr/steamvr.hpp4
-rw-r--r--tracker-test/lang/de_DE.ts22
-rw-r--r--tracker-test/lang/zh_CN.ts2
-rw-r--r--tracker-test/test.h1
-rw-r--r--tracker-tobii/CMakeLists.txt27
-rw-r--r--tracker-tobii/ftnoir_tracker_tobii.cpp65
-rw-r--r--tracker-tobii/ftnoir_tracker_tobii.h50
-rw-r--r--tracker-tobii/ftnoir_tracker_tobii_controls.ui56
-rw-r--r--tracker-tobii/ftnoir_tracker_tobii_dialog.cpp19
-rw-r--r--tracker-tobii/lang/de_DE.ts22
-rw-r--r--tracker-tobii/lang/nl_NL.ts12
-rw-r--r--tracker-tobii/lang/ru_RU.ts12
-rw-r--r--tracker-tobii/lang/stub.ts12
-rw-r--r--tracker-tobii/lang/zh_CN.ts14
-rw-r--r--tracker-tobii/thread.cpp109
-rw-r--r--tracker-tobii/thread.hpp35
-rw-r--r--tracker-tobii/tobii.cpp121
-rw-r--r--tracker-tobii/tobii.h61
-rw-r--r--tracker-tobii/tobii.qrc5
-rw-r--r--tracker-tobii/tobii.ui67
-rw-r--r--tracker-tobii/tobii_dialog.cpp20
-rw-r--r--tracker-tobii/tobii_logo.pngbin0 -> 1768 bytes
-rw-r--r--tracker-trackhat/CMakeLists.txt16
-rw-r--r--tracker-trackhat/camera.cpp140
-rw-r--r--tracker-trackhat/dialog.cpp165
-rw-r--r--tracker-trackhat/dialog.hpp36
-rw-r--r--tracker-trackhat/dialog.ui602
-rw-r--r--tracker-trackhat/extractor.cpp21
-rw-r--r--tracker-trackhat/frame.cpp128
-rw-r--r--tracker-trackhat/handle.cpp72
-rw-r--r--tracker-trackhat/images/trackhat-64x64.pngbin0 -> 1430 bytes
-rw-r--r--tracker-trackhat/images/trackhat.icobin0 -> 3758 bytes
-rw-r--r--tracker-trackhat/images/trackhat.pngbin0 -> 14581 bytes
-rw-r--r--tracker-trackhat/lang/nl_NL.ts154
-rw-r--r--tracker-trackhat/lang/ru_RU.ts154
-rw-r--r--tracker-trackhat/lang/stub.ts154
-rw-r--r--tracker-trackhat/lang/zh_CN.ts154
-rw-r--r--tracker-trackhat/led.cpp66
-rw-r--r--tracker-trackhat/metadata.cpp39
-rw-r--r--tracker-trackhat/metadata.hpp21
-rw-r--r--tracker-trackhat/settings.cpp144
-rw-r--r--tracker-trackhat/tracker_trackhat.qrc5
-rw-r--r--tracker-trackhat/trackhat-res.qrc5
-rw-r--r--tracker-trackhat/trackhat.hpp209
-rw-r--r--tracker-udp/ftnoir_tracker_udp.cpp2
-rw-r--r--tracker-udp/lang/de_DE.ts65
-rw-r--r--tracker-udp/lang/zh_CN.ts2
-rw-r--r--tracker-wii/CMakeLists.txt5
-rw-r--r--tracker-wii/lang/de_DE.ts11
-rw-r--r--tracker-wii/lang/zh_CN.ts2
-rw-r--r--tracker-wii/wii_camera.cpp66
-rw-r--r--tracker-wii/wii_camera.h40
-rw-r--r--tracker-wii/wii_frame.cpp7
-rw-r--r--tracker-wii/wii_frame.hpp4
-rw-r--r--tracker-wii/wii_module.cpp6
-rw-r--r--tracker-wii/wii_point_extractor.cpp74
-rw-r--r--tracker-wii/wii_point_extractor.h8
-rw-r--r--tracker-wii/wiiyourself/lang/zh_CN.ts2
-rw-r--r--trackmouse/CMakeLists.txt9
-rw-r--r--trackmouse/images/start.pngbin1400 -> 0 bytes
-rw-r--r--trackmouse/images/stop.pngbin5790 -> 0 bytes
-rw-r--r--trackmouse/main.cpp18
-rw-r--r--trackmouse/trackmouse-res.qrc6
-rw-r--r--trackmouse/trackmouse-settings.cpp132
-rw-r--r--trackmouse/trackmouse.icobin67134 -> 0 bytes
-rw-r--r--trackmouse/trackmouse.rc2
-rw-r--r--trackmouse/window.cpp388
-rw-r--r--trackmouse/window.hpp94
-rw-r--r--trackmouse/window.ui458
-rw-r--r--variant/default/_variant.cmake32
-rw-r--r--variant/trackmouse/_variant.cmake22
-rw-r--r--video-opencv/CMakeLists.txt5
-rw-r--r--video-opencv/impl-camera.cpp7
-rw-r--r--video-opencv/impl-metadata.cpp5
-rw-r--r--video-opencv/impl.hpp5
-rw-r--r--video-opencv/lang/de_DE.ts4
-rw-r--r--video-opencv/lang/nl_NL.ts2
-rw-r--r--video-opencv/lang/ru_RU.ts2
-rw-r--r--video-opencv/lang/zh_CN.ts2
-rw-r--r--video-ps3eye/CMakeLists.txt4
-rw-r--r--video-ps3eye/dialog.ui14
-rw-r--r--video-ps3eye/lang/de_DE.ts (renamed from filter-kalman/lang/nl_NL.ts)24
-rw-r--r--video-ps3eye/lang/nl_NL.ts17
-rw-r--r--video-ps3eye/lang/ru_RU.ts17
-rw-r--r--video-ps3eye/lang/stub.ts15
-rw-r--r--video-ps3eye/lang/zh_CN.ts21
-rw-r--r--video-ps3eye/module.cpp42
-rw-r--r--video-ps3eye/module.hpp9
m---------video-ps3eye/ps3eye-driver0
-rw-r--r--video-ps3eye/shm-layout.hpp16
-rw-r--r--video-ps3eye/wrapper.cxx9
-rw-r--r--video/camera.cpp14
-rw-r--r--video/camera.hpp19
-rw-r--r--video/lang/de_DE.ts4
-rw-r--r--video/lang/nl_NL.ts2
-rw-r--r--video/lang/ru_RU.ts2
-rw-r--r--video/lang/zh_CN.ts2
-rw-r--r--video/video-widget.cpp7
-rw-r--r--video/video-widget.hpp6
-rw-r--r--x-plane-plugin/plugin.c4
550 files changed, 24120 insertions, 12383 deletions
diff --git a/.clang-format b/.clang-format
index 3cc08007..eb080eb9 100644
--- a/.clang-format
+++ b/.clang-format
@@ -1,8 +1,9 @@
BasedOnStyle: LLVM
AccessModifierOffset: -4
AlignEscapedNewlines: Right
+AlignOperands: Align
AllowAllParametersOfDeclarationOnNextLine: false
-AllowShortBlocksOnASingleLine: false
+AllowShortBlocksOnASingleLine: Never
AllowShortFunctionsOnASingleLine: InlineOnly
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
@@ -13,7 +14,7 @@ BreakBeforeBinaryOperators: NonAssignment
BreakBeforeBraces: Allman
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
-ColumnLimit: 100
+ColumnLimit: 150
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
@@ -41,6 +42,6 @@ SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
-Standard: Cpp11
+Standard: Latest
TabWidth: 4
UseTab: Never
diff --git a/.clang-tidy b/.clang-tidy
index 89ad1d49..df2a6e10 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -1,61 +1,107 @@
-Checks:
- #-*,
- *,
- clang-analyzer-*,clang-diagnostic-*,
- cppcoreguidelines-*,performance-*,bugprone-*,
- readability-*,misc-*,hicpp-*,cert-*,modernize-*,
- -clang-diagnostic-nonportable-include-path,
- -clang-analyzer-deadcode.DeadStores,
- -clang-analyze-core.CallAndMessage,
- -cppcoreguidelines-special-member-functions,-hicpp-special-member-functions,
- -hicpp-braces-around-statements,-readability-braces-around-statements,
- -hicpp-use-equals-default -modernize-use-equals-default,
- -hicpp-use-auto,
- -hicpp-vararg,
- -hicpp-member-init,
- -cert-err58-cpp,
- -readability-implicit-bool-conversion,
- -readability-else-after-return,
- -modernize-raw-string-literal,
- -hicpp-no-array-decay,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
- -cert-err34-c,
- -hicpp-signed-bitwise,
- -modernize-use-auto,-hicpp-use-auto,
- -readability-named-parameter,
- -cert-dcl54-cpp,
- -hicpp-new-delete-operators,
- -modernize-pass-by-value,
- -cppcoreguidelines-pro-type-union-access,
- -hicpp-use-equals-default,
- -cppcoreguidelines-pro-bounds-pointer-arithmetic,
- -cppcoreguidelines-pro-bounds-constant-array-index,
- -cppcoreguidelines-pro-bounds-array-to-pointer-decay,
- -cppcoreguidelines-pro-type-vararg,
- -cppcoreguidelines-pro-type-cstyle-cast,
- -cppcoreguidelines-pro-type-reinterpret-cast,
- -cppcoreguidelines-owning-memory,
- -cppcoreguidelines-pro-type-const-cast,
- -hicpp-function-size,
- -cppcoreguidelines-pro-type-static-cast-downcast,
- -hicpp-use-override,
- -hicpp-use-equals-delete,
- -readability-static-accessed-through-instance,
- -cert-flp30-c,
- -modernize-redundant-void-arg,
- -hicpp-explicit-conversions,
- -readability-function-size,
- -cppcoreguidelines-avoid-goto,
- -modernize-use-using,
- -cppcoreguidelines-c-copy-assignment-signature,
- -hicpp-avoid-goto,
- -readability-inconsistent-declaration-parameter-name,
- -cppcoreguidelines-avoid-magic-numbers,
- -readability-magic-numbers,
- -modernize-loop-convert,
- -modernize-use-nodiscard,
+Checks: '
+*,
+-altera-*,
+-android-*,
+-clang-analyzer-osx.*,
+-fuchsia-*,
+-google-*,
+-llvm-*,
+-llvmlibc-*,
+-objc-*,
+-bugprone-assignment-in-if-condition,
+-bugprone-easily-swappable-parameters,
+bugprone-exception-escape,
+-bugprone-implicit-widening-of-multiplication-result,
+-bugprone-narrowing-conversions,
+-cert-*,
+llvm-namespace-comment,
+clang-analyzer-core.CallAndMessage,
+clang-analyzer-deadcode.DeadStores,
+-clang-diagnostic-nonportable-include-path,
+-cppcoreguidelines-avoid-c-arrays,
+-cppcoreguidelines-avoid-capture-default-when-capturing-this,
+-cppcoreguidelines-avoid-do-while,
+-cppcoreguidelines-avoid-goto,
+-cppcoreguidelines-avoid-magic-numbers,
+-cppcoreguidelines-c-copy-assignment-signature,
+-cppcoreguidelines-init-variables,
+-cppcoreguidelines-macro-usage,
+-cppcoreguidelines-narrowing-conversions,
+-cppcoreguidelines-non-private-member-variables-in-classes,
+-cppcoreguidelines-owning-memory,
+-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
+-cppcoreguidelines-pro-bounds-constant-array-index,
+-cppcoreguidelines-pro-bounds-pointer-arithmetic,
+-cppcoreguidelines-pro-type-const-cast,
+-cppcoreguidelines-pro-type-cstyle-cast,
+-cppcoreguidelines-pro-type-reinterpret-cast,
+-cppcoreguidelines-pro-type-static-cast-downcast,
+-cppcoreguidelines-pro-type-union-access,
+-cppcoreguidelines-pro-type-vararg,
+-cppcoreguidelines-special-member-functions,
+-google-readability-braces-around-statements,
+-google-readability-casting,
+-hicpp-avoid-c-arrays,
+-hicpp-avoid-goto,
+-hicpp-braces-around-statements,
+-hicpp-exception-baseclass,
+-hicpp-explicit-conversions,
+-hicpp-function-size,
+-hicpp-invalid-access-moved,
+-hicpp-member-init,
+-hicpp-move-const-arg,
+-hicpp-named-parameter,
+-hicpp-new-delete-operators,
+-hicpp-no-array-decay,
+-hicpp-signed-bitwise,
+-hicpp-special-member-functions,
+-hicpp-uppercase-literal-suffix,
+-hicpp-use-auto,
+-hicpp-use-equals-default,
+-hicpp-use-equals-delete,
+-hicpp-use-override,
+-hicpp-vararg,
+-misc-non-private-member-variables-in-classes,
+-misc-use-anonymous-namespace,
+-modernize-avoid-c-arrays,
+-modernize-loop-convert,
+-modernize-pass-by-value,
+-modernize-raw-string-literal,
+-modernize-redundant-void-arg,
+-modernize-return-braced-init-list,
+-modernize-use-auto,
+modernize-use-equals-default,
+-modernize-use-nodiscard,
+-modernize-use-trailing-return-type,
+-modernize-use-using,
+-performance-no-int-to-ptr,
+-readability-braces-around-statements,
+-readability-else-after-return,
+readability-function-cognitive-complexity,
+-readability-function-size,
+-readability-identifier-length,
+-readability-implicit-bool-conversion,
+-readability-inconsistent-declaration-parameter-name,
+-readability-isolate-declaration,
+-readability-magic-numbers,
+readability-make-member-function-const,
+-readability-named-parameter,
+-readability-redundant-access-specifiers,
+-readability-simplify-boolean-expr,
+-readability-static-accessed-through-instance,
+-readability-uppercase-literal-suffix,
+-readability-use-anyofallof,
+-*-avoid-unconditional-preprocessor-if,
+-*-braces-around-statements,
+-*-incorrect-roundings,
+-*-uppercase-literal-suffix,
+-*-use-emplace,
+-*-use-std-print,
+-*-use-trailing-return-type,
+-*-vararg,
+'
AnalyzeTemporaryDtors: true
-#HeaderFilterRegex: '/dev/opentrack'
CheckOptions:
- key: cert-dcl59-cpp.HeaderFileExtensions
@@ -78,5 +124,11 @@ CheckOptions:
value: '90'
- key: readability-function-size.VariableThreshold
value: '10'
+ - key: readability-function-cognitive-complexity.IgnoreMacros
+ value: '1'
+ - key: readability-function-cognitive-complexity.DescribeBasicIncrements
+ value: '1'
+ - key: misc-const-correctness.AnalyzeValues
+ value: '0'
# vim: ft=yaml
diff --git a/.gitattributes b/.gitattributes
index 05e51885..462acc88 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -19,4 +19,4 @@
*.in text eol=lf
*.cmd text eol=crlf
-*.bat text eol=crlf \ No newline at end of file
+*.bat text eol=crlf
diff --git a/.github/workflows/build-windows.bat b/.github/workflows/build-windows.bat
new file mode 100644
index 00000000..4571683e
--- /dev/null
+++ b/.github/workflows/build-windows.bat
@@ -0,0 +1,3 @@
+set path=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files\CMake\bin;C:\Program Files\Git\cmd;%GITHUB_WORKSPACE%/ninja-build
+"C:/Program Files/Microsoft Visual Studio/2022/Enterprise/VC/Auxiliary/Build/vcvars64.bat" >nul && %*
+exit /b %ERRORLEVEL%
diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml
new file mode 100644
index 00000000..6c8fac10
--- /dev/null
+++ b/.github/workflows/cmake.yml
@@ -0,0 +1,70 @@
+name: CMake
+
+on:
+ - push
+ - pull_request
+
+env:
+ # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
+ BUILD_TYPE: Release
+
+jobs:
+ build:
+ # The CMake configure and build commands are platform agnostic and should work equally
+ # well on Windows or Mac. You can convert this to a matrix build if you need
+ # cross-platform coverage.
+ # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [macos-latest, ubuntu-latest, windows-latest]
+ include:
+ - os: macos-latest
+ cmake: cmake
+ - os: ubuntu-latest
+ cmake: cmake
+ - os: windows-latest
+ cmake: .\.github\workflows\build-windows.bat cmake
+
+ steps:
+ - name: clone opentrack/opentrack
+ uses: actions/checkout@v2
+
+ - name: clone opentrack/depends
+ uses: actions/checkout@v2
+ with:
+ repository: opentrack/opentrack-depends
+ submodules: true
+ path: opentrack-depends
+
+ - uses: seanmiddleditch/gha-setup-ninja@master
+
+ - name: Install Linux Dependencies
+ run: |
+ sudo apt-get update
+ sudo apt-get install libproc2-dev libopencv-dev libopencv-dev wine64-tools
+ sudo apt-get install qt6-tools-dev qt6-serialport-dev qt6-base-private-dev
+ if: matrix.os == 'ubuntu-latest'
+
+ - name: Install Qt
+ uses: jurplel/install-qt-action@v4
+ with:
+ dir: "${{ github.workspace }}/qt"
+ version: '6.8.3'
+ modules: qtserialport qtmultimedia qtimageformats
+ target: 'desktop'
+ use-official: true
+ if: matrix.os != 'ubuntu-latest'
+
+ - name: Configure
+ run: ${{matrix.cmake}} -GNinja -S ${{github.workspace}}/ -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DQt6_DIR="${{env.QT_ROOT_DIR}}/lib/cmake/Qt6"
+
+ - name: Build
+ run: ${{matrix.cmake}} --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --target install
+
+# - name: Upload build
+# uses: actions/upload-artifact@v3
+# with:
+# name: buildoutput
+# path: ${{github.workspace}}/build/
diff --git a/.github/workflows/linux-package.yml b/.github/workflows/linux-package.yml
new file mode 100644
index 00000000..bc622b6f
--- /dev/null
+++ b/.github/workflows/linux-package.yml
@@ -0,0 +1,77 @@
+name: Linuax Package
+on:
+ push:
+ tags:
+ - 'opentrack-*'
+ workflow_dispatch:
+env:
+ build_type: Release
+ onnx_version: 1.21.0
+ onnx_prefix: onnxruntime-linux-x64
+ file_name: '' # to disable warning in case of a linter
+ Qt5_DIR: '' # to disable warning in case of a linter
+jobs:
+ Package:
+ runs-on: ubuntu-22.04
+ steps:
+ - name: Clone opentrack/opentrack
+ uses: actions/checkout@v4
+ - name: Clone opentrack/depends
+ uses: actions/checkout@v4
+ with:
+ repository: opentrack/opentrack-depends
+ submodules: true
+ path: opentrack-depends
+
+ - name: Install Dependencies
+ run: |
+ sudo apt-get update
+ sudo apt-get install libprocps-dev libopencv-dev wine64-tools
+ wget -q https://github.com/microsoft/onnxruntime/releases/download/v${{env.onnx_version}}/${{env.onnx_prefix}}-${{env.onnx_version}}.tgz
+ tar xzf ${{env.onnx_prefix}}-${{env.onnx_version}}.tgz
+ - name: Install Qt
+ uses: jurplel/install-qt-action@v4
+ with:
+ version: 5.15
+ archives: icu qtbase qttools qtserialport
+
+ - name: Configure and Build
+ run: |
+ cmake -S . -B build \
+ -DCMAKE_BUILD_TYPE=${{env.build_type}} \
+ -DONNXRuntime_DIR=${{env.onnx_prefix}}-${{env.onnx_version}} \
+ -DQt5_DIR=${{env.Qt5_DIR}}
+ cmake --build build --config ${{env.build_type}} --target install -- -j $(nproc)
+
+ - name: Pack
+ run: |
+ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$(pwd)/${{env.onnx_prefix}}-${{env.onnx_version}}/lib
+
+ # required by linuxdeployqt
+ sudo apt-get install libfuse2
+ wget -q https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage
+ chmod +x linuxdeployqt-*.AppImage
+ cd build/install/
+ ln -s $(pwd)/libexec/opentrack $(pwd)/lib
+ ../../linuxdeployqt-*.AppImage bin/opentrack -bundle-non-qt-libs -unsupported-allow-new-glibc
+ rm lib
+
+ echo '#!/bin/sh' >> opentrack
+ echo 'CURRENT_PATH="$(dirname "$(readlink -f "$0")")"' >> opentrack
+ echo 'export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$CURRENT_PATH/libexec/opentrack' >> opentrack
+ echo '"$CURRENT_PATH/bin/opentrack" "$@"' >> opentrack
+ chmod +x opentrack
+
+ if [[ ${{github.ref_name}} =~ ^opentrack.*$ ]]; then
+ file_name=${{github.ref_name}}-linux.tar.gz
+ else
+ file_name=opentrack-dev-linux.tar.gz
+ fi
+ echo "file_name=$file_name" >> $GITHUB_ENV
+
+ tar czf ../../$file_name .
+ - name: Upload
+ uses: actions/upload-artifact@v4
+ with:
+ name: ${{env.file_name}}
+ path: ${{env.file_name}}
diff --git a/3rdparty-notices/EIGEN-COPYING.txt b/3rdparty-notices/EIGEN-COPYING.txt
deleted file mode 100644
index c203fff6..00000000
--- a/3rdparty-notices/EIGEN-COPYING.txt
+++ /dev/null
@@ -1,379 +0,0 @@
-The opentrack project is using the Eigen3 linear algebra library with site at
-<http://eigen.tuxfamily.org/>
-
-We're only using the MPL2-licensed bits. This is to avoid copyleft. License
-follows:
-
-Mozilla Public License Version 2.0
-==================================
-
-1. Definitions
---------------
-
-1.1. "Contributor"
- means each individual or legal entity that creates, contributes to
- the creation of, or owns Covered Software.
-
-1.2. "Contributor Version"
- means the combination of the Contributions of others (if any) used
- by a Contributor and that particular Contributor's Contribution.
-
-1.3. "Contribution"
- means Covered Software of a particular Contributor.
-
-1.4. "Covered Software"
- means Source Code Form to which the initial Contributor has attached
- the notice in Exhibit A, the Executable Form of such Source Code
- Form, and Modifications of such Source Code Form, in each case
- including portions thereof.
-
-1.5. "Incompatible With Secondary Licenses"
- means
-
- (a) that the initial Contributor has attached the notice described
- in Exhibit B to the Covered Software; or
-
- (b) that the Covered Software was made available under the terms of
- version 1.1 or earlier of the License, but not also under the
- terms of a Secondary License.
-
-1.6. "Executable Form"
- means any form of the work other than Source Code Form.
-
-1.7. "Larger Work"
- means a work that combines Covered Software with other material, in
- a separate file or files, that is not Covered Software.
-
-1.8. "License"
- means this document.
-
-1.9. "Licensable"
- means having the right to grant, to the maximum extent possible,
- whether at the time of the initial grant or subsequently, any and
- all of the rights conveyed by this License.
-
-1.10. "Modifications"
- means any of the following:
-
- (a) any file in Source Code Form that results from an addition to,
- deletion from, or modification of the contents of Covered
- Software; or
-
- (b) any new file in Source Code Form that contains any Covered
- Software.
-
-1.11. "Patent Claims" of a Contributor
- means any patent claim(s), including without limitation, method,
- process, and apparatus claims, in any patent Licensable by such
- Contributor that would be infringed, but for the grant of the
- License, by the making, using, selling, offering for sale, having
- made, import, or transfer of either its Contributions or its
- Contributor Version.
-
-1.12. "Secondary License"
- means either the GNU General Public License, Version 2.0, the GNU
- Lesser General Public License, Version 2.1, the GNU Affero General
- Public License, Version 3.0, or any later versions of those
- licenses.
-
-1.13. "Source Code Form"
- means the form of the work preferred for making modifications.
-
-1.14. "You" (or "Your")
- means an individual or a legal entity exercising rights under this
- License. For legal entities, "You" includes any entity that
- controls, is controlled by, or is under common control with You. For
- purposes of this definition, "control" means (a) the power, direct
- or indirect, to cause the direction or management of such entity,
- whether by contract or otherwise, or (b) ownership of more than
- fifty percent (50%) of the outstanding shares or beneficial
- ownership of such entity.
-
-2. License Grants and Conditions
---------------------------------
-
-2.1. Grants
-
-Each Contributor hereby grants You a world-wide, royalty-free,
-non-exclusive license:
-
-(a) under intellectual property rights (other than patent or trademark)
- Licensable by such Contributor to use, reproduce, make available,
- modify, display, perform, distribute, and otherwise exploit its
- Contributions, either on an unmodified basis, with Modifications, or
- as part of a Larger Work; and
-
-(b) under Patent Claims of such Contributor to make, use, sell, offer
- for sale, have made, import, and otherwise transfer either its
- Contributions or its Contributor Version.
-
-2.2. Effective Date
-
-The licenses granted in Section 2.1 with respect to any Contribution
-become effective for each Contribution on the date the Contributor first
-distributes such Contribution.
-
-2.3. Limitations on Grant Scope
-
-The licenses granted in this Section 2 are the only rights granted under
-this License. No additional rights or licenses will be implied from the
-distribution or licensing of Covered Software under this License.
-Notwithstanding Section 2.1(b) above, no patent license is granted by a
-Contributor:
-
-(a) for any code that a Contributor has removed from Covered Software;
- or
-
-(b) for infringements caused by: (i) Your and any other third party's
- modifications of Covered Software, or (ii) the combination of its
- Contributions with other software (except as part of its Contributor
- Version); or
-
-(c) under Patent Claims infringed by Covered Software in the absence of
- its Contributions.
-
-This License does not grant any rights in the trademarks, service marks,
-or logos of any Contributor (except as may be necessary to comply with
-the notice requirements in Section 3.4).
-
-2.4. Subsequent Licenses
-
-No Contributor makes additional grants as a result of Your choice to
-distribute the Covered Software under a subsequent version of this
-License (see Section 10.2) or under the terms of a Secondary License (if
-permitted under the terms of Section 3.3).
-
-2.5. Representation
-
-Each Contributor represents that the Contributor believes its
-Contributions are its original creation(s) or it has sufficient rights
-to grant the rights to its Contributions conveyed by this License.
-
-2.6. Fair Use
-
-This License is not intended to limit any rights You have under
-applicable copyright doctrines of fair use, fair dealing, or other
-equivalents.
-
-2.7. Conditions
-
-Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
-in Section 2.1.
-
-3. Responsibilities
--------------------
-
-3.1. Distribution of Source Form
-
-All distribution of Covered Software in Source Code Form, including any
-Modifications that You create or to which You contribute, must be under
-the terms of this License. You must inform recipients that the Source
-Code Form of the Covered Software is governed by the terms of this
-License, and how they can obtain a copy of this License. You may not
-attempt to alter or restrict the recipients' rights in the Source Code
-Form.
-
-3.2. Distribution of Executable Form
-
-If You distribute Covered Software in Executable Form then:
-
-(a) such Covered Software must also be made available in Source Code
- Form, as described in Section 3.1, and You must inform recipients of
- the Executable Form how they can obtain a copy of such Source Code
- Form by reasonable means in a timely manner, at a charge no more
- than the cost of distribution to the recipient; and
-
-(b) You may distribute such Executable Form under the terms of this
- License, or sublicense it under different terms, provided that the
- license for the Executable Form does not attempt to limit or alter
- the recipients' rights in the Source Code Form under this License.
-
-3.3. Distribution of a Larger Work
-
-You may create and distribute a Larger Work under terms of Your choice,
-provided that You also comply with the requirements of this License for
-the Covered Software. If the Larger Work is a combination of Covered
-Software with a work governed by one or more Secondary Licenses, and the
-Covered Software is not Incompatible With Secondary Licenses, this
-License permits You to additionally distribute such Covered Software
-under the terms of such Secondary License(s), so that the recipient of
-the Larger Work may, at their option, further distribute the Covered
-Software under the terms of either this License or such Secondary
-License(s).
-
-3.4. Notices
-
-You may not remove or alter the substance of any license notices
-(including copyright notices, patent notices, disclaimers of warranty,
-or limitations of liability) contained within the Source Code Form of
-the Covered Software, except that You may alter any license notices to
-the extent required to remedy known factual inaccuracies.
-
-3.5. Application of Additional Terms
-
-You may choose to offer, and to charge a fee for, warranty, support,
-indemnity or liability obligations to one or more recipients of Covered
-Software. However, You may do so only on Your own behalf, and not on
-behalf of any Contributor. You must make it absolutely clear that any
-such warranty, support, indemnity, or liability obligation is offered by
-You alone, and You hereby agree to indemnify every Contributor for any
-liability incurred by such Contributor as a result of warranty, support,
-indemnity or liability terms You offer. You may include additional
-disclaimers of warranty and limitations of liability specific to any
-jurisdiction.
-
-4. Inability to Comply Due to Statute or Regulation
----------------------------------------------------
-
-If it is impossible for You to comply with any of the terms of this
-License with respect to some or all of the Covered Software due to
-statute, judicial order, or regulation then You must: (a) comply with
-the terms of this License to the maximum extent possible; and (b)
-describe the limitations and the code they affect. Such description must
-be placed in a text file included with all distributions of the Covered
-Software under this License. Except to the extent prohibited by statute
-or regulation, such description must be sufficiently detailed for a
-recipient of ordinary skill to be able to understand it.
-
-5. Termination
---------------
-
-5.1. The rights granted under this License will terminate automatically
-if You fail to comply with any of its terms. However, if You become
-compliant, then the rights granted under this License from a particular
-Contributor are reinstated (a) provisionally, unless and until such
-Contributor explicitly and finally terminates Your grants, and (b) on an
-ongoing basis, if such Contributor fails to notify You of the
-non-compliance by some reasonable means prior to 60 days after You have
-come back into compliance. Moreover, Your grants from a particular
-Contributor are reinstated on an ongoing basis if such Contributor
-notifies You of the non-compliance by some reasonable means, this is the
-first time You have received notice of non-compliance with this License
-from such Contributor, and You become compliant prior to 30 days after
-Your receipt of the notice.
-
-5.2. If You initiate litigation against any entity by asserting a patent
-infringement claim (excluding declaratory judgment actions,
-counter-claims, and cross-claims) alleging that a Contributor Version
-directly or indirectly infringes any patent, then the rights granted to
-You by any and all Contributors for the Covered Software under Section
-2.1 of this License shall terminate.
-
-5.3. In the event of termination under Sections 5.1 or 5.2 above, all
-end user license agreements (excluding distributors and resellers) which
-have been validly granted by You or Your distributors under this License
-prior to termination shall survive termination.
-
-************************************************************************
-* *
-* 6. Disclaimer of Warranty *
-* ------------------------- *
-* *
-* Covered Software is provided under this License on an "as is" *
-* basis, without warranty of any kind, either expressed, implied, or *
-* statutory, including, without limitation, warranties that the *
-* Covered Software is free of defects, merchantable, fit for a *
-* particular purpose or non-infringing. The entire risk as to the *
-* quality and performance of the Covered Software is with You. *
-* Should any Covered Software prove defective in any respect, You *
-* (not any Contributor) assume the cost of any necessary servicing, *
-* repair, or correction. This disclaimer of warranty constitutes an *
-* essential part of this License. No use of any Covered Software is *
-* authorized under this License except under this disclaimer. *
-* *
-************************************************************************
-
-************************************************************************
-* *
-* 7. Limitation of Liability *
-* -------------------------- *
-* *
-* Under no circumstances and under no legal theory, whether tort *
-* (including negligence), contract, or otherwise, shall any *
-* Contributor, or anyone who distributes Covered Software as *
-* permitted above, be liable to You for any direct, indirect, *
-* special, incidental, or consequential damages of any character *
-* including, without limitation, damages for lost profits, loss of *
-* goodwill, work stoppage, computer failure or malfunction, or any *
-* and all other commercial damages or losses, even if such party *
-* shall have been informed of the possibility of such damages. This *
-* limitation of liability shall not apply to liability for death or *
-* personal injury resulting from such party's negligence to the *
-* extent applicable law prohibits such limitation. Some *
-* jurisdictions do not allow the exclusion or limitation of *
-* incidental or consequential damages, so this exclusion and *
-* limitation may not apply to You. *
-* *
-************************************************************************
-
-8. Litigation
--------------
-
-Any litigation relating to this License may be brought only in the
-courts of a jurisdiction where the defendant maintains its principal
-place of business and such litigation shall be governed by laws of that
-jurisdiction, without reference to its conflict-of-law provisions.
-Nothing in this Section shall prevent a party's ability to bring
-cross-claims or counter-claims.
-
-9. Miscellaneous
-----------------
-
-This License represents the complete agreement concerning the subject
-matter hereof. If any provision of this License is held to be
-unenforceable, such provision shall be reformed only to the extent
-necessary to make it enforceable. Any law or regulation which provides
-that the language of a contract shall be construed against the drafter
-shall not be used to construe this License against a Contributor.
-
-10. Versions of the License
----------------------------
-
-10.1. New Versions
-
-Mozilla Foundation is the license steward. Except as provided in Section
-10.3, no one other than the license steward has the right to modify or
-publish new versions of this License. Each version will be given a
-distinguishing version number.
-
-10.2. Effect of New Versions
-
-You may distribute the Covered Software under the terms of the version
-of the License under which You originally received the Covered Software,
-or under the terms of any subsequent version published by the license
-steward.
-
-10.3. Modified Versions
-
-If you create software not governed by this License, and you want to
-create a new license for such software, you may create and use a
-modified version of this License if you rename the license and remove
-any references to the name of the license steward (except to note that
-such modified license differs from this License).
-
-10.4. Distributing Source Code Form that is Incompatible With Secondary
-Licenses
-
-If You choose to distribute Source Code Form that is Incompatible With
-Secondary Licenses under the terms of this version of the License, the
-notice described in Exhibit B of this License must be attached.
-
-Exhibit A - Source Code Form License Notice
--------------------------------------------
-
- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-If it is not possible or desirable to put the notice in a particular
-file, then You may include the notice in a location (such as a LICENSE
-file in a relevant directory) where a recipient would be likely to look
-for such a notice.
-
-You may add additional accurate notices of copyright ownership.
-
-Exhibit B - "Incompatible With Secondary Licenses" Notice
----------------------------------------------------------
-
- This Source Code Form is "Incompatible With Secondary Licenses", as
- defined by the Mozilla Public License, v. 2.0.
diff --git a/3rdparty-notices/LIBUSB-COPYING.txt b/3rdparty-notices/LIBUSB-COPYING.txt
new file mode 100644
index 00000000..5ab7695a
--- /dev/null
+++ b/3rdparty-notices/LIBUSB-COPYING.txt
@@ -0,0 +1,504 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/3rdparty-notices/ONNXRUNTIME.txt b/3rdparty-notices/ONNXRUNTIME.txt
new file mode 100644
index 00000000..7ebf5147
--- /dev/null
+++ b/3rdparty-notices/ONNXRUNTIME.txt
@@ -0,0 +1,24 @@
+The neuralnet tracker uses the ONNX library.
+See: <https://github.com/microsoft/onnxruntime>
+
+MIT License
+
+Copyright (c) Microsoft Corporation
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE. \ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 601175b5..c28ead74 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -29,8 +29,8 @@ set(CMAKE_CXX_LINKER_PREFERENCE_PROPAGATES ON)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/")
include(opentrack-policy NO_POLICY_SCOPE)
-project(opentrack)
cmake_minimum_required(VERSION 3.13 FATAL_ERROR)
+project(opentrack)
# must be prior to CMakeDetermineCXXCompiler due to rpath
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT OR NOT CMAKE_INSTALL_PREFIX)
@@ -60,14 +60,13 @@ endif()
include(CMakeParseArguments)
-include(opentrack-policy)
include(opentrack-load-user-settings)
include(opentrack-mrproper)
set_property(GLOBAL PROPERTY opentrack-all-modules "")
set_property(GLOBAL PROPERTY opentrack-all-source-dirs "")
-set(opentrack_all-translations "nl_NL;ru_RU;stub;zh_CN")
+set(opentrack_all-translations "de_DE;nl_NL;ru_RU;stub;zh_CN")
include(opentrack-hier)
include(opentrack-platform)
@@ -88,7 +87,7 @@ add_custom_target(mrproper
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}")
function(otr_add_subdirs)
- otr_dist_select_variant()
+ otr_init_variant()
get_property(_globs GLOBAL PROPERTY opentrack-subprojects)
otr_add_target_dirs(_globbed ${_globs})
@@ -109,27 +108,28 @@ otr_add_subdirs()
otr_merge_translations()
include(opentrack-install)
-otr_install_sources()
+message("Install directory:")
+message(" ${CMAKE_INSTALL_PREFIX}")
string(TOUPPER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE)
-message(STATUS "-- Compile flags:")
+message("Compile flags:")
#foreach(j C CXX)
foreach(j CXX)
foreach(i "" "_${CMAKE_BUILD_TYPE}")
- message(STATUS " ${j}${i}: ${CMAKE_${j}_FLAGS${i}}")
+ message(" ${j}${i}: ${CMAKE_${j}_FLAGS${i}}")
endforeach()
endforeach()
-message(STATUS "-- Link flags:")
+message("Link flags:")
foreach(j "" "_${CMAKE_BUILD_TYPE}")
#foreach(i EXE SHARED)
foreach(i SHARED)
- message(STATUS " LINK_${i}${j}: ${CMAKE_${i}_LINKER_FLAGS${j}}")
+ message(" LINK_${i}${j}: ${CMAKE_${i}_LINKER_FLAGS${j}}")
endforeach()
endforeach()
-message(STATUS "-- Static archive flags:")
+message("Static archive flags:")
foreach(k "" "_${CMAKE_BUILD_TYPE}")
- message(STATUS " STATIC${k}: ${CMAKE_STATIC_LINKER_FLAGS${k}}")
+ message(" STATIC${k}: ${CMAKE_STATIC_LINKER_FLAGS${k}}")
endforeach()
-message(STATUS "--")
+message("--")
diff --git a/README.md b/README.md
index adfa8f29..f04200b4 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,10 @@
+[<img src="https://github.com/opentrack/opentrack/actions/workflows/cmake.yml/badge.svg">](https://github.com/opentrack/opentrack/actions/workflows/cmake.yml)
+
## Intro
-[<img src="https://ci.appveyor.com/api/projects/status/n0j9h38jnif5qbe9/branch/unstable?svg=true"/>](https://ci.appveyor.com/project/sthalik/opentrack/branch/unstable)
+opentrack is a program for tracking user's head rotation and transmitting it to flight simulation software and military-themed video games. Project home is located at <<https://github.com/opentrack/opentrack>>.
-opentrack project home is located at <<http://github.com/opentrack/opentrack>>.
+Looking for **railway planning software**? <<https://opentrack.ch>> had the name `opentrack` first. Apologies for the long-standing naming conflict.
For the latest **downloads** visit <<https://github.com/opentrack/opentrack/releases>> Download an `.exe` installer or a `.7z` archive. Currently installers and portable versions for Windows are available for each release. It supports [USB stick truly "portable" installations](https://github.com/opentrack/opentrack/wiki/portable-mode-for-USB-sticks)
@@ -10,10 +12,6 @@ Please first refer to <<https://github.com/opentrack/opentrack/wiki>>
for [new user guide](https://github.com/opentrack/opentrack/wiki/Quick-Start-Guide-(WIP)), [frequent answers](https://github.com/opentrack/opentrack/wiki/common-issues), specific tracker/filter
documentation. See also the [gameplay video](https://www.youtube.com/watch?v=XI73ul_FnBI) with opentrack set up.
-## Looking for railway planning software?
-
-**Railway planning software** <<http://opentrack.ch>> had the name `opentrack` first. Apologies for the long-standing naming conflict.
-
## Usage
`opentrack` is an application dedicated to tracking user's head
@@ -26,20 +24,22 @@ Don't be afraid to submit an **issue/feature request** if you have any problems!
## Tracking input
-- PointTracker by Patrick Ruoff, freetrack-like light sources
-- Oculus Rift DK1, DK2, CV, and legacy/knockoff versions (Windows only)
-- Paper [marker support](https://github.com/opentrack/opentrack/wiki/Aruco-tracker)
- via the ArUco library <<https://github.com/opentrack/aruco>>
+- PointTracker by Patrick Ruoff, FreeTrack-like light points
+- Oculus Rift (Windows only)
+- Paper [marker](https://github.com/opentrack/opentrack/wiki/Aruco-tracker) via the Aruco<sup>[[1](https://github.com/opentrack/aruco)]</sup> library
- Razer Hydra
- Relaying via UDP from a different computer
-- Relaying UDP via FreePIE-specific Android app
+- Relaying UDP via the FreePIE<sup>[[1](https://andersmalmgren.github.io/FreePIE/)]</sup> Android [apps](https://github.com/opentrack/opentrack/tree/master/contrib/freepie-udp)
- Joystick analog axes (Windows)
- Windows Phone [tracker](https://github.com/ZanderAdam/OpenTrack.WindowsPhone/wiki) over opentrack UDP protocol
-- Arduino with custom firmware
-- Intel RealSense 3D cameras (Windows)
+- Arduino with custom Hatire firmware
+- Intel RealSense 3D camera (Windows)
- BBC micro:bit, LEGO, sensortag support via Smalltalk<sup>[(1)](https://en.wikipedia.org/wiki/Smalltalk)[(2)](https://en.wikipedia.org/wiki/Alan_Kay)</sup>
- [S2Bot](http://www.picaxe.com/Teaching/Other-Software/Scratch-Helper-Apps/)
+ [S2Bot](https://www.picaxe.com/Teaching/Other-Software/Scratch-Helper-Apps/)
- Wiimote (Windows)
+- NeuralNet face tracker
+- Eyeware Beam<sup>[[1](https://beam.eyeware.tech/)]</sup>
+- Tobii eye tracker
## Output protocols
@@ -48,7 +48,7 @@ Don't be afraid to submit an **issue/feature request** if you have any problems!
- Relaying UDP to another computer
- Virtual joystick output (Windows, Linux, OSX)
- Wine freetrack glue protocol (Linux, OSX)
-- X-Plane plugin (Linux)
+- X-Plane plugin (Linux; uses the Wine output option)
- Tablet-like mouse output (Windows)
- FlightGear
- FSUIPC for Microsoft Flight Simulator 2002/2004 (Windows)
@@ -57,7 +57,7 @@ Don't be afraid to submit an **issue/feature request** if you have any problems!
## Credits, in chronological order
- Stanisław Halik (maintainer)
-- Wim Vriend -- author of [FaceTrackNoIR](http://facetracknoir.sourceforge.net/) that served as the initial codebase for `opentrack`. While the code was almost entirely rewritten, we still hold on to many of `FaceTrackNoIR`'s ideas.
+- Wim Vriend -- author of [FaceTrackNoIR](https://facetracknoir.sourceforge.net/) that served as the initial codebase for `opentrack`. While the code was almost entirely rewritten, we still hold on to many of `FaceTrackNoIR`'s ideas.
- Chris Thompson (aka mm0zct, Rift and Razer Hydra author and maintainer)
- Patrick Ruoff (PT tracker author)
- Xavier Hallade (Intel RealSense tracker author and maintainer)
@@ -69,10 +69,12 @@ Don't be afraid to submit an **issue/feature request** if you have any problems!
- Wei Shuai (Wiimote tracker)
- Stéphane Lenclud (Kinect Face Tracker, Easy Tracker)
- GO63-samara (Hamilton Filter, Pose-widget improvement)
+- Davide Mameli (Eyeware Beam tracker)
+- Khoa Nguyen (Tobii eye tracker)
## Thanks
-- uglyDwarf (high CON)
+- uglyDwarf (of [linuxtrack](https://github.com/uglyDwarf/linuxtrack/))
- Andrzej Czarnowski (FreePIE tracker and
[Google Cardboard](https://github.com/opentrack/opentrack/wiki/VR-HMD-goggles-setup-----google-cardboard,-colorcross,-opendive)
assistance, testing)
@@ -86,9 +88,9 @@ Don't be afraid to submit an **issue/feature request** if you have any problems!
## Contributing
-Code, translations,
+See guides for writing new modules\[[1](https://github.com/opentrack/opentrack/blob/master/api/plugin-api.hpp)\]\[[2](https://github.com/opentrack/opentrack/blob/master/tracker-test/test.h)\], and for [working with core code](https://github.com/opentrack/opentrack/wiki/Hacking-opentrack).
-Please see [basic rules for contributing](https://github.com/opentrack/opentrack/blob/unstable/CONTRIBUTING.md). There's also a guide for [working with core code](https://github.com/opentrack/opentrack/wiki/Hacking-opentrack). For writing input and output modules you don't need this guide except for
+To edit the wiki, send pull requests to the [opentrack/wiki](https://github.com/opentrack/wiki) repository. The [user-facing wiki](https://github.com/opentrack/opentrack/wiki) will automatically update itself once the commit is merged.
## License and warranty
diff --git a/api/lang/de_DE.ts b/api/lang/de_DE.ts
new file mode 100644
index 00000000..688fe72b
--- /dev/null
+++ b/api/lang/de_DE.ts
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>module_status_mixin</name>
+ <message>
+ <source>Unknown error</source>
+ <translation>Unbekannter Fehler</translation>
+ </message>
+</context>
+</TS>
diff --git a/api/lang/zh_CN.ts b/api/lang/zh_CN.ts
index d29cce1b..26bf67a5 100644
--- a/api/lang/zh_CN.ts
+++ b/api/lang/zh_CN.ts
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
<context>
<name>module_status_mixin</name>
<message>
diff --git a/api/plugin-api.cpp b/api/plugin-api.cpp
index 79f9b464..4d8d90e9 100644
--- a/api/plugin-api.cpp
+++ b/api/plugin-api.cpp
@@ -1,28 +1,28 @@
#include "plugin-api.hpp"
-
-#include <utility>
+#include <QCloseEvent>
+#include <QDebug>
namespace plugin_api::detail {
BaseDialog::BaseDialog() = default;
-void BaseDialog::closeEvent(QCloseEvent*)
+void BaseDialog::closeEvent(QCloseEvent* e)
{
if (isVisible())
- {
- hide();
emit closing();
- }
+ e->accept();
}
void BaseDialog::done(int)
{
if (isVisible())
- {
- hide();
close();
- }
}
+bool BaseDialog::embeddable() noexcept { return false; }
+void BaseDialog::save() {}
+void BaseDialog::reload() {}
+void BaseDialog::set_buttons_visible(bool) {}
+
} // ns plugin_api::detail
// these exist so that vtable is emitted in a single compilation unit, not all of them.
@@ -36,10 +36,14 @@ IFilter::IFilter() = default;
IFilter::~IFilter() = default;
IFilterDialog::IFilterDialog() = default;
IFilterDialog::~IFilterDialog() = default;
+void IFilterDialog::register_filter(IFilter*) {}
+void IFilterDialog::unregister_filter() {}
IProtocol::IProtocol() = default;
IProtocol::~IProtocol() = default;
IProtocolDialog::IProtocolDialog() = default;
IProtocolDialog::~IProtocolDialog() = default;
+void IProtocolDialog::register_protocol(IProtocol*){}
+void IProtocolDialog::unregister_protocol() {}
ITracker::ITracker() = default;
ITracker::~ITracker() = default;
bool ITracker::center() { return false; }
@@ -47,14 +51,11 @@ ITrackerDialog::ITrackerDialog() = default;
ITrackerDialog::~ITrackerDialog() = default;
void ITrackerDialog::register_tracker(ITracker*) {}
void ITrackerDialog::unregister_tracker() {}
-IExtension::~IExtension() = default;
-IExtensionDialog::~IExtensionDialog() = default;
bool module_status::is_ok() const
{
return error.isNull();
}
-
module_status_mixin::~module_status_mixin() = default;
module_status::module_status(const QString& error) : error(error) {}
module_status::module_status() = default;
diff --git a/api/plugin-api.hpp b/api/plugin-api.hpp
index 263ee475..2d77bdf4 100644
--- a/api/plugin-api.hpp
+++ b/api/plugin-api.hpp
@@ -42,6 +42,10 @@ protected:
BaseDialog();
public:
void closeEvent(QCloseEvent *) override;
+ virtual bool embeddable() noexcept;
+ virtual void set_buttons_visible(bool x); // XXX TODO remove it once all modules are converted
+ virtual void save(); // XXX HACK should be pure virtual
+ virtual void reload(); // XXX HACK should be pure virtual -sh 20211214
signals:
void closing();
private slots:
@@ -55,15 +59,18 @@ private slots:
#define OPENTRACK_DECLARE_PLUGIN_INTERNAL(ctor_class, ctor_ret_class, metadata_class, dialog_class, dialog_ret_class) \
extern "C" \
{ \
- OTR_PLUGIN_EXPORT ctor_ret_class* GetConstructor(void) \
+ OTR_PLUGIN_EXPORT ctor_ret_class* GetConstructor(void); \
+ ctor_ret_class* GetConstructor(void) \
{ \
return new ctor_class; \
} \
- OTR_PLUGIN_EXPORT Metadata_* GetMetadata(void) \
+ OTR_PLUGIN_EXPORT Metadata_* GetMetadata(void); \
+ Metadata_* GetMetadata(void) \
{ \
return new metadata_class; \
} \
- OTR_PLUGIN_EXPORT dialog_ret_class* GetDialog(void) \
+ OTR_PLUGIN_EXPORT dialog_ret_class* GetDialog(void); \
+ dialog_ret_class* GetDialog(void) \
{ \
return new dialog_class; \
} \
@@ -221,48 +228,3 @@ struct OTR_API_EXPORT ITrackerDialog : public plugin_api::detail::BaseDialog
#define OPENTRACK_DECLARE_TRACKER(tracker_class, dialog_class, metadata_class) \
OPENTRACK_DECLARE_PLUGIN_INTERNAL(tracker_class, ITracker, metadata_class, dialog_class, ITrackerDialog)
-struct OTR_API_EXPORT IExtension : module_status_mixin
-{
- enum event_mask : unsigned
- {
- none = 0u,
- on_raw = 1 << 0,
- on_before_filter = 1 << 1,
- on_before_mapping = 1 << 2,
- on_finished = 1 << 3,
- };
-
- enum event_ordinal : unsigned
- {
- ev_raw = 0,
- ev_before_filter = 1,
- ev_before_mapping = 2,
- ev_finished = 3,
-
- event_count = 4,
- };
-
- IExtension() = default;
- ~IExtension() override;
-
- virtual event_mask hook_types() = 0;
-
- virtual void process_raw(Pose&) {}
- virtual void process_before_filter(Pose&) {}
- virtual void process_before_mapping(Pose&) {}
- virtual void process_finished(Pose&) {}
-
- IExtension(const IExtension&) = delete;
- IExtension& operator=(const IExtension&) = delete;
-};
-
-struct OTR_API_EXPORT IExtensionDialog : public plugin_api::detail::BaseDialog
-{
- ~IExtensionDialog() override;
-
- virtual void register_extension(IExtension& ext) = 0;
- virtual void unregister_extension() = 0;
-};
-
-#define OPENTRACK_DECLARE_EXTENSION(ext_class, dialog_class, metadata_class) \
- OPENTRACK_DECLARE_PLUGIN_INTERNAL(ext_class, IExtension, metadata_class, dialog_class, IExtensionDialog)
diff --git a/api/plugin-support.hpp b/api/plugin-support.hpp
index 9c0a3ae0..f577f62f 100644
--- a/api/plugin-support.hpp
+++ b/api/plugin-support.hpp
@@ -17,6 +17,8 @@
#include <QDebug>
#include <QString>
+#include <QStringView>
+#include <QLatin1StringView>
#include <QLibrary>
#include <QDir>
#include <QIcon>
@@ -38,7 +40,6 @@ enum class dylib_type : unsigned
Filter = 0xdeadbabe,
Tracker = 0xcafebeef,
Protocol = 0xdeadf00d,
- Extension = 0xcafebabe,
Video = 0xbadf00d,
Invalid = (unsigned)-1,
};
@@ -54,7 +55,7 @@ struct dylib final
return;
handle.setFileName(filename_);
- handle.setLoadHints(QLibrary::DeepBindHint | QLibrary::PreventUnloadHint | QLibrary::ResolveAllSymbolsHint);
+ handle.setLoadHints(QLibrary::DeepBindHint | QLibrary::ResolveAllSymbolsHint);
#ifdef __clang__
# pragma clang diagnostic push
@@ -132,16 +133,16 @@ private:
static QString trim_filename(const QString& in_)
{
- QStringRef in(&in_);
+ auto in = QStringView{in_};
- const int idx = in.lastIndexOf("/");
+ const int idx = in.lastIndexOf(QLatin1StringView{"/"});
if (idx != -1)
{
in = in.mid(idx + 1);
- if (in.startsWith(OPENTRACK_LIBRARY_PREFIX) &&
- in.endsWith("." OPENTRACK_LIBRARY_EXTENSION))
+ if (in.startsWith(QLatin1StringView{OPENTRACK_LIBRARY_PREFIX}) &&
+ in.endsWith(QLatin1StringView{"." OPENTRACK_LIBRARY_EXTENSION}))
{
constexpr unsigned pfx_len = sizeof(OPENTRACK_LIBRARY_PREFIX) - 1;
constexpr unsigned rst_len = sizeof("." OPENTRACK_LIBRARY_EXTENSION) - 1;
@@ -154,14 +155,13 @@ private:
OPENTRACK_LIBRARY_PREFIX "opentrack-tracker-",
OPENTRACK_LIBRARY_PREFIX "opentrack-proto-",
OPENTRACK_LIBRARY_PREFIX "opentrack-filter-",
- OPENTRACK_LIBRARY_PREFIX "opentrack-ext-",
OPENTRACK_LIBRARY_PREFIX "opentrack-video-",
};
for (auto name : names)
{
- if (in.startsWith(name))
- return in.mid(std::strlen(name)).toString();
+ if (in.startsWith(QLatin1StringView{name}))
+ return in.mid((int)std::strlen(name)).toString();
}
}
}
@@ -180,20 +180,17 @@ struct Modules final
filter_modules(filter(type::Filter)),
tracker_modules(filter(type::Tracker)),
protocol_modules(filter(type::Protocol)),
- extension_modules(filter(type::Extension)),
video_modules(filter(type::Video))
{}
dylib_list& filters() { return filter_modules; }
dylib_list& trackers() { return tracker_modules; }
dylib_list& protocols() { return protocol_modules; }
- dylib_list& extensions() { return extension_modules; }
private:
dylib_list module_list;
dylib_list filter_modules;
dylib_list tracker_modules;
dylib_list protocol_modules;
- dylib_list extension_modules;
dylib_list video_modules;
static dylib_list& sorted(dylib_list& xs)
@@ -229,7 +226,6 @@ private:
{ type::Filter, OPENTRACK_LIBRARY_PREFIX "opentrack-filter-*." OPENTRACK_LIBRARY_EXTENSION, },
{ type::Tracker, OPENTRACK_LIBRARY_PREFIX "opentrack-tracker-*." OPENTRACK_LIBRARY_EXTENSION, },
{ type::Protocol, OPENTRACK_LIBRARY_PREFIX "opentrack-proto-*." OPENTRACK_LIBRARY_EXTENSION, },
- { type::Extension, OPENTRACK_LIBRARY_PREFIX "opentrack-ext-*." OPENTRACK_LIBRARY_EXTENSION, },
{ type::Video, OPENTRACK_LIBRARY_PREFIX "opentrack-video-*." OPENTRACK_LIBRARY_EXTENSION, dylib_load_none, },
};
diff --git a/cmake/FindEigen3.cmake b/cmake/FindEigen3.cmake
deleted file mode 100644
index bbad2298..00000000
--- a/cmake/FindEigen3.cmake
+++ /dev/null
@@ -1,106 +0,0 @@
-# - Try to find Eigen3 lib
-#
-# This module supports requiring a minimum version, e.g. you can do
-# find_package(Eigen3 3.1.2)
-# to require version 3.1.2 or newer of Eigen3.
-#
-# Once done this will define
-#
-# EIGEN3_FOUND - system has eigen lib with correct version
-# EIGEN3_INCLUDE_DIR - the eigen include directory
-# EIGEN3_VERSION - eigen version
-#
-# This module reads hints about search locations from
-# the following enviroment variables:
-#
-# EIGEN3_ROOT
-# EIGEN3_ROOT_DIR
-
-# Copyright (c) 2006, 2007 Montel Laurent, <montel@kde.org>
-# Copyright (c) 2008, 2009 Gael Guennebaud, <g.gael@free.fr>
-# Copyright (c) 2009 Benoit Jacob <jacob.benoit.1@gmail.com>
-# Redistribution and use is allowed according to the terms of the 2-clause BSD license.
-
-if(NOT Eigen3_FIND_VERSION)
- if(NOT Eigen3_FIND_VERSION_MAJOR)
- set(Eigen3_FIND_VERSION_MAJOR 2)
- endif(NOT Eigen3_FIND_VERSION_MAJOR)
- if(NOT Eigen3_FIND_VERSION_MINOR)
- set(Eigen3_FIND_VERSION_MINOR 91)
- endif(NOT Eigen3_FIND_VERSION_MINOR)
- if(NOT Eigen3_FIND_VERSION_PATCH)
- set(Eigen3_FIND_VERSION_PATCH 0)
- endif(NOT Eigen3_FIND_VERSION_PATCH)
-
- set(Eigen3_FIND_VERSION "${Eigen3_FIND_VERSION_MAJOR}.${Eigen3_FIND_VERSION_MINOR}.${Eigen3_FIND_VERSION_PATCH}")
-endif(NOT Eigen3_FIND_VERSION)
-
-macro(_eigen3_check_version)
- file(READ "${EIGEN3_INCLUDE_DIR}/Eigen/src/Core/util/Macros.h" _eigen3_version_header)
-
- string(REGEX MATCH "define[ \t]+EIGEN_WORLD_VERSION[ \t]+([0-9]+)" _eigen3_world_version_match "${_eigen3_version_header}")
- set(EIGEN3_WORLD_VERSION "${CMAKE_MATCH_1}")
- string(REGEX MATCH "define[ \t]+EIGEN_MAJOR_VERSION[ \t]+([0-9]+)" _eigen3_major_version_match "${_eigen3_version_header}")
- set(EIGEN3_MAJOR_VERSION "${CMAKE_MATCH_1}")
- string(REGEX MATCH "define[ \t]+EIGEN_MINOR_VERSION[ \t]+([0-9]+)" _eigen3_minor_version_match "${_eigen3_version_header}")
- set(EIGEN3_MINOR_VERSION "${CMAKE_MATCH_1}")
-
- set(EIGEN3_VERSION ${EIGEN3_WORLD_VERSION}.${EIGEN3_MAJOR_VERSION}.${EIGEN3_MINOR_VERSION})
- if(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})
- set(EIGEN3_VERSION_OK FALSE)
- else(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})
- set(EIGEN3_VERSION_OK TRUE)
- endif(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})
-
- if(NOT EIGEN3_VERSION_OK)
-
- message(STATUS "Eigen3 version ${EIGEN3_VERSION} found in ${EIGEN3_INCLUDE_DIR}, "
- "but at least version ${Eigen3_FIND_VERSION} is required")
- endif(NOT EIGEN3_VERSION_OK)
-endmacro(_eigen3_check_version)
-
-if (EIGEN3_INCLUDE_DIR)
-
- # in cache already
- _eigen3_check_version()
- set(EIGEN3_FOUND ${EIGEN3_VERSION_OK})
-
-else (EIGEN3_INCLUDE_DIR)
-
- # search first if an Eigen3Config.cmake is available in the system,
- # if successful this would set EIGEN3_INCLUDE_DIR and the rest of
- # the script will work as usual
- find_package(Eigen3 ${Eigen3_FIND_VERSION} NO_MODULE QUIET)
-
- if(NOT EIGEN3_INCLUDE_DIR)
- find_path(EIGEN3_INCLUDE_DIR NAMES signature_of_eigen3_matrix_library
- HINTS
- ENV EIGEN3_ROOT
- ENV EIGEN3_ROOT_DIR
- PATHS
- ${CMAKE_INSTALL_PREFIX}/include
- ${KDE4_INCLUDE_DIR}
- PATH_SUFFIXES eigen3 eigen
- )
- endif(NOT EIGEN3_INCLUDE_DIR)
-
- if(EIGEN3_INCLUDE_DIR)
- _eigen3_check_version()
- endif(EIGEN3_INCLUDE_DIR)
-
- include(FindPackageHandleStandardArgs)
- find_package_handle_standard_args(Eigen3 DEFAULT_MSG EIGEN3_INCLUDE_DIR EIGEN3_VERSION_OK)
-
- mark_as_advanced(EIGEN3_INCLUDE_DIR)
-
-endif(EIGEN3_INCLUDE_DIR)
-
-if(EIGEN3_FOUND)
- add_definitions(
- -DEIGEN_MPL2_ONLY=1
- -DEIGEN_MAX_CPP_VER=17
- -DEIGEN_DEFAULT_DENSE_INDEX_TYPE=int
- -DEIGEN_NO_DEBUG=1
- -DEIGEN_DONT_PARALLELIZE=1
- )
-endif()
diff --git a/cmake/FindONNXRuntime.cmake b/cmake/FindONNXRuntime.cmake
new file mode 100644
index 00000000..60ddfc2b
--- /dev/null
+++ b/cmake/FindONNXRuntime.cmake
@@ -0,0 +1,104 @@
+# FindONNXRuntime
+# ===============
+#
+# Find an ONNX Runtime installation.
+# ONNX Runtime is a cross-platform inference and training machine-learning
+# accelerator.
+#
+# Input variables
+# ---------------
+#
+# ONNXRuntime_ROOT Set root installation. This is an environment
+# variable
+# ONNXRuntime_DIR CMake variable to set the installation root.
+#
+# Output variable
+# ---------------
+#
+# ONNXRuntime_FOUND True if headers and requested libraries were found
+# ONNXRuntime_LIBRARIES Component libraries to be linked.
+# ONNXRuntime_INCLUDE_DIRS Include directories.
+
+set(ONNXRuntime_DIR CACHE PATH "Root directory of the ONNX Runtime installation")
+
+# This script is mostly inspired by the guide there:
+# https://gitlab.kitware.com/cmake/community/-/wikis/doc/tutorials/How-To-Find-Libraries
+# Finding of the DLLs was added so we can install them with the application.
+# We adhere to modern CMake standards and also define an import library.
+
+# Determine architecture string for subdir in nuget package
+# This is only relevant when we point `ONNXRuntime_DIR` to the
+# extracted nuget package content.
+if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
+ if (CMAKE_SIZEOF_VOID_P GREATER_EQUAL 8)
+ set(ONNXRuntime_Arch "linux-x64")
+ else()
+ message(FATAL_ERROR "32 Bit Linux builds are not supported")
+ endif()
+elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
+ if (CMAKE_SIZEOF_VOID_P GREATER_EQUAL 8)
+ set(ONNXRuntime_Arch "win-x64")
+ else()
+ set(ONNXRuntime_Arch "win-x86")
+ endif()
+endif()
+
+
+find_library(ONNXRuntime_LIBRARY onnxruntime
+ HINTS ${ONNXRuntime_DIR}
+ PATH_SUFFIXES
+ # For a "normal" installation
+ "lib" "lib64" "bin"
+ # For the nuget package
+ "runtimes/${ONNXRuntime_Arch}/native"
+ )
+
+if(WIN32)
+ SET(CMAKE_FIND_LIBRARY_SUFFIXES ".dll" ".DLL")
+ find_library(ONNXRuntime_RUNTIME onnxruntime
+ HINTS ${ONNXRuntime_DIR}
+ PATH_SUFFIXES
+ # For a "normal" installation
+ "lib" "lib64" "bin"
+ # For the nuget package
+ "runtimes/${ONNXRuntime_Arch}/native"
+ )
+else()
+ SET(ONNXRuntime_RUNTIME ${ONNXRuntime_LIBRARY})
+endif()
+
+find_path(ONNXRuntime_INCLUDE_DIR onnxruntime_cxx_api.h
+ HINTS ${ONNXRuntime_DIR}
+ PATH_SUFFIXES
+ # For .nuget packages
+ "build/native/include"
+ # For when the directory structure of the onnx source repo is preserved
+ "include/onnxruntime/core/session"
+ "include/onnxruntime"
+ # For when we copy the files somewhere
+ "include"
+ )
+
+
+include(FindPackageHandleStandardArgs)
+# handle the QUIETLY and REQUIRED arguments and set ONNXRuntime_FOUND to TRUE
+# if all listed variables are TRUE
+find_package_handle_standard_args(ONNXRuntime DEFAULT_MSG
+ ONNXRuntime_LIBRARY ONNXRuntime_INCLUDE_DIR ONNXRuntime_RUNTIME)
+
+if(ONNXRuntime_FOUND)
+ if(NOT TARGET onnxruntime::onnxruntime)
+ # Following this quide:
+ # https://cmake.org/cmake/help/git-stage/guide/importing-exporting/index.html#importing-libraries
+ add_library(onnxruntime::onnxruntime SHARED IMPORTED)
+ set_target_properties(onnxruntime::onnxruntime PROPERTIES
+ IMPORTED_LOCATION "${ONNXRuntime_RUNTIME}"
+ INTERFACE_INCLUDE_DIRECTORIES "${ONNXRuntime_INCLUDE_DIR}"
+ IMPORTED_IMPLIB "${ONNXRuntime_LIBRARY}")
+ endif()
+endif()
+
+mark_as_advanced(ONNXRuntime_INCLUDE_DIR ONNXRuntime_LIBRARY ONNXRuntime_RUNTIME)
+
+set(ONNXRuntime_INCLUDE_DIRS ${ONNXRuntime_INCLUDE_DIR})
+set(ONNXRuntime_LIBRARIES ${ONNXRuntime_LIBRARY}) \ No newline at end of file
diff --git a/cmake/apple.cmake b/cmake/apple.cmake
index f6b83382..64e4d628 100644
--- a/cmake/apple.cmake
+++ b/cmake/apple.cmake
@@ -1,12 +1,19 @@
set(CMAKE_BUILD_TYPE_INIT RELEASE)
-set(CMAKE_CXX_FLAGS "-arch x86_64 -stdlib=libc++" CACHE STRING "" FORCE)
-set(CMAKE_C_FLAGS "-arch x86_64" CACHE STRING "" FORCE)
+if("$ENV{OTR_OSX_ARCH}" STREQUAL "arm64")
+ set(CMAKE_OSX_ARCHITECTURES "arm64" CACHE STRING "" FORCE)
+ set(opentrack-intel FALSE)
+else()
+ set(CMAKE_OSX_ARCHITECTURES "x86_64" CACHE STRING "" FORCE)
+ set(opentrack-intel TRUE)
+endif()
+
+set(CMAKE_CXX_FLAGS "-stdlib=libc++" CACHE STRING "" FORCE)
set(CMAKE_C_FLAGS_RELEASE "-ffast-math -O3 -flto -g" CACHE STRING "" FORCE)
set(CMAKE_CXX_FLAGS_RELEASE " ${CMAKE_C_FLAGS_RELEASE}" CACHE STRING "" FORCE)
-set(cmake-link-common "-stdlib=libc++ -arch x86_64")
+set(cmake-link-common "-stdlib=libc++")
set(CMAKE_EXE_LINKER_FLAGS "${cmake-link-common} -Wl,-stack_size,0x4000000" CACHE STRING "" FORCE)
set(CMAKE_SHARED_LINKER_FLAGS ${cmake-link-common} CACHE STRING "" FORCE)
set(CMAKE_MODULE_LINKER_FLAGS ${cmake-link-common} CACHE STRING "" FORCE)
diff --git a/cmake/msvc-clang.cmake b/cmake/msvc-clang.cmake
deleted file mode 100644
index 419a3652..00000000
--- a/cmake/msvc-clang.cmake
+++ /dev/null
@@ -1,131 +0,0 @@
-# this file only serves as toolchain file when specified so explicitly
-# when building the software. from repository's root directory:
-# mkdir build && cmake -DCMAKE_TOOLCHAIN_FILE=$(pwd)/../cmake/msvc.cmake build/
-
-include("${CMAKE_CURRENT_LIST_DIR}/opentrack-policy.cmake")
-
-SET(CMAKE_SYSTEM_NAME Windows)
-SET(CMAKE_SYSTEM_VERSION 1)
-
-# search for programs in the host directories
-SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
-# don't poison with system compile-time data
-SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
-SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
-
-set(cc "")
-
-set(cc "${cc} -W3 -Wall -Wextra -Wno-unused-command-line-argument -Wno-missing-braces")
-set(cc "${cc} -Wno-inconsistent-missing-override")
-set(cc "${cc} -Wno-return-type-c-linkage")
-
-set(cc "${cc} -Xclang -std=c++14 -Xclang -fms-compatibility-version=1912 -fms-compatibility")
-
-set(cc "${cc} -U __clang__ -U__clang")
-
-set(cc "${cc} -O3")
-set(cc "${cc} -Xclang -O3 -Xclang -flto -Qvec -Oit -Oy -Ob2 -fp:fast -GS- -GF -GL -Gw -Gy -Gm -Zc:inline")
-set(cc "${cc} -Zo -FS -Zc:threadSafeInit -arch:SSE2 -D_HAS_EXCEPTIONS=0")
-set(cc "${cc} -bigobj")
-#set(cc "${cc} -Wno-unknown-argument -Wno-unknown-pragmas -Wno-invalid-noreturn")
-
-#set(CMAKE_CXX_SIMULATE_ID "MSVC" CACHE INTERNAL "" FORCE)
-#set(CMAKE_C_SIMULATE_ID "MSVC" CACHE INTERNAL "" FORCE)
-#set(CMAKE_CXX_SIMULATE_VERSION 19.0 CACHE INTERNAL "" FORCE)
-#set(CMAKE_C_SIMULATE_VERSION 19.0 CACHE INTERNAL "" FORCE)
-
-#set(CMAKE_INCLUDE_SYSTEM_FLAG_C "-Xclang -isystem")
-#set(CMAKE_INCLUDE_SYSTEM_FLAG_CXX "-Xclang -isystem")
-
-#set(CMAKE_C_USE_RESPONSE_FILE_FOR_INCLUDES TRUE)
-#set(CMAKE_CXX_USE_RESPONSE_FILE_FOR_INCLUDES TRUE)
-
-set(CMAKE_CXX_COMPILER "d:/llvm/msbuild-bin/cl.exe")
-set(CMAKE_C_COMPILER "d:/llvm/msbuild-bin/cl.exe")
-
-#set(CMAKE_CXX_COMPILER_ID "MSVC" CACHE INTERNAL "" FORCE)
-#set(CMAKE_C_COMPILER_ID "MSVC" CACHE INTERNAL "" FORCE)
-
-#set(CMAKE_CXX_COMPILER_VERSION "19.0" CACHE INTERNAL "" FORCE)
-#set(CMAKE_C_COMPILER_VERSION "19.0" CACHE INTERNAL "" FORCE)
-
-#set(CMAKE_VS_PLATFORM_TOOLSET "v150_clang_4_0")
-
-set(CMAKE_CXX_FLAGS_RELEASE_INIT " ")
-set(CMAKE_C_FLAGS_RELEASE_INIT " ")
-
-set(CMAKE_CXX_STANDARD_REQUIRED FALSE)
-set(CMAKE_CXX_EXTENSIONS FALSE)
-
-set(warns_ "")
-
-set(warns-disable 4530 4577 4789 4244 4702 4530 4244 4127 4458 4456 4251)
-
-foreach(i ${warns-disable})
- set(warns_ "${warns_} -wd${i}")
-endforeach()
-if(CMAKE_PROJECT_NAME STREQUAL "opentrack")
- #C4263 - member function does not override any base class virtual member function
- #C4264 - no override available for virtual member function from base class, function is hidden
- #C4265 - class has virtual functions, but destructor is not virtual
- #C4266 - no override available for virtual member function from base type, function is hidden
- #C4928 - illegal copy-initialization, more than one user-defined conversion has been implicitly applied
-
- set(warns 4263 4264 4266 4928)
- set(warns-noerr 4265)
-
- foreach(i ${warns})
- set(warns_ "${warns_} -w1${i} -we${i}")
- endforeach()
-
- foreach(i ${warns-noerr})
- set(warns_ "${warns_} -w1${i}")
- endforeach()
- set(cc "${cc} -GR-")
-endif()
-
-set(base-cflags "${warns_} -MT -Zi -cgthreads8 -W4")
-
-set(_CFLAGS "${base-cflags}")
-set(_CXXFLAGS "${base-cflags}")
-set(_CFLAGS_RELEASE "${cc}")
-set(_CFLAGS_DEBUG "-GS -sdl -Gs -guard:cf")
-set(_CXXFLAGS_RELEASE "${cc}")
-set(_CXXFLAGS_DEBUG "${_CFLAGS_DEBUG}")
-
-set(_LDFLAGS_COMMON "-ignore:4217 -ignore:4221")
-set(_LDFLAGS "-machine:X86 -DEBUG ${_LDFLAGS_COMMON}")
-set(_LDFLAGS_RELEASE "-OPT:REF -OPT:ICF=10")
-set(_LDFLAGS_DEBUG "")
-
-set(_LDFLAGS_STATIC "-machine:X86 ${_LDFLAGS_COMMON}")
-set(_LDFLAGS_STATIC_RELEASE "")
-set(_LDFLAGS_STATIC_DEBUG "")
-
-foreach(j C CXX)
- foreach(i "" _DEBUG _RELEASE)
- set(CMAKE_${j}_FLAGS${i} "${CMAKE_${j}_FLAGS${i}} ${_${j}FLAGS${i}}")
- endforeach()
- set(CMAKE_${j}_FLAGS "${CMAKE_${j}_FLAGS} ${_${j}FLAGS}")
-endforeach()
-
-foreach(j "" _DEBUG _RELEASE)
- foreach(i MODULE EXE SHARED)
- set(CMAKE_${i}_LINKER_FLAGS${j} "${_LDFLAGS${j}} ${CMAKE_${i}_LINKER_FLAGS${j}}")
- endforeach()
-endforeach()
-
-set(CMAKE_STATIC_LINKER_FLAGS "${_LDFLAGS_STATIC} ${CMAKE_STATIC_LINKER_FLAGS}")
-set(CMAKE_STATIC_LINKER_FLAGS_RELEASE "${_LDFLAGS_STATIC_RELEASE} ${CMAKE_STATIC_LINKER_FLAGS_RELEASE}")
-set(CMAKE_STATIC_LINKER_FLAGS_DEBUG "${_LDFLAGS_STATIC_DEBUG} ${CMAKE_STATIC_LINKER_FLAGS_DEBUG}")
-
-set(CMAKE_RC_FLAGS "-nologo -DWIN32")
-
-if(NOT CMAKE_INSTALL_PREFIX)
- set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/install" CACHE PATH "" FORCE)
-endif()
-
-if(NOT CMAKE_BUILD_TYPE)
- set(CMAKE_BUILD_TYPE "RELEASE" CACHE STRING "" FORCE)
-endif()
-
diff --git a/cmake/msvc.cmake b/cmake/msvc.cmake
index cd967dd9..b43ac7f1 100644
--- a/cmake/msvc.cmake
+++ b/cmake/msvc.cmake
@@ -3,8 +3,25 @@
# mkdir build && cmake -DCMAKE_TOOLCHAIN_FILE=$(pwd)/../cmake/msvc.cmake build/
SET(CMAKE_SYSTEM_NAME Windows)
-SET(CMAKE_SYSTEM_VERSION 5.01)
-set(CMAKE_SYSTEM_PROCESSOR x86)
+if(opentrack-64bit)
+ set(CMAKE_SYSTEM_PROCESSOR AMD64)
+ SET(CMAKE_SYSTEM_VERSION 5.02)
+else()
+ set(CMAKE_SYSTEM_PROCESSOR x86)
+ SET(CMAKE_SYSTEM_VERSION 5.01)
+endif()
+
+if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT OR CMAKE_INSTALL_PREFIX STREQUAL "")
+ set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/install" CACHE PATH "" FORCE)
+endif()
+
+if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/opentrack-policy.cmake")
+ include("${CMAKE_CURRENT_LIST_DIR}/opentrack-policy.cmake" NO_POLICY_SCOPE)
+endif()
+
+#set(CMAKE_GENERATOR Ninja)
+#set(CMAKE_MAKE_PROGRAM ninja.exe)
+set(CMAKE_ASM_NASM_COMPILER nasm.exe)
# search for programs in the host directories
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
@@ -12,15 +29,46 @@ SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
-set(CMAKE_GENERATOR "Ninja")
#add_definitions(-D_ITERATOR_DEBUG_LEVEL=0)
-add_definitions(-diagnostics:caret)
#add_compile_options(-Qvec-report:2)
#add_compile_options(-d2cgsummary)
+add_definitions(-D_HAS_EXCEPTIONS=0)
+
+if(DEFINED CMAKE_TOOLCHAIN_FILE)
+ # ignore cmake warning: Manually-specified variable not used by the project
+ set(CMAKE_TOOLCHAIN_FILE "${CMAKE_TOOLCHAIN_FILE}")
+endif()
+
+include("${CMAKE_CURRENT_LIST_DIR}/opentrack-policy.cmake" NO_POLICY_SCOPE)
+set(CMAKE_POLICY_DEFAULT_CMP0069 NEW CACHE INTERNAL "" FORCE)
+#set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON)
+
+set(CMAKE_C_EXTENSIONS FALSE)
+set(CMAKE_CXX_EXTENSIONS FALSE)
+
+set(CMAKE_CROSSCOMPILING 0)
+
+function(sets type)
+ set(i 0)
+ list(LENGTH ARGN max)
+ math(EXPR foo "${max} % 2")
+ if(NOT foo EQUAL 0)
+ message(FATAL_ERROR "argument count not even")
+ endif()
+ while(i LESS max)
+ list(GET ARGN "${i}" name)
+ math(EXPR i "${i} + 1")
+ list(GET ARGN "${i}" value)
+ math(EXPR i "${i} + 1")
+ set(${name} "${value}" CACHE "${type}" "" FORCE)
+ endwhile()
+endfunction()
+
+set(OpenCV_STATIC 1)
if(CMAKE_PROJECT_NAME STREQUAL "opentrack")
- include("${CMAKE_CURRENT_LIST_DIR}/opentrack-policy.cmake" NO_POLICY_SCOPE)
+ #include("${CMAKE_CURRENT_LIST_DIR}/opentrack-policy.cmake" NO_POLICY_SCOPE)
add_compile_options("-W4")
@@ -34,46 +82,102 @@ if(CMAKE_PROJECT_NAME STREQUAL "opentrack")
endif()
if(CMAKE_PROJECT_NAME STREQUAL "QtBase")
- unset(CMAKE_CROSSCOMPILING)
- set(QT_BUILD_TOOLS_WHEN_CROSSCOMPILING TRUE CACHE BOOL "" FORCE)
- set(QT_DEBUG_OPTIMIZATION_FLAGS TRUE CACHE BOOL "" FORCE)
- set(QT_USE_DEFAULT_CMAKE_OPTIMIZATION_FLAGS TRUE CACHE BOOL "" FORCE)
-
- set(FEATURE_debug OFF)
- set(FEATURE_debug_and_release OFF)
- set(FEATURE_force_debug_info ON)
- set(FEATURE_ltcg ON)
- set(FEATURE_shared ON)
+ set(QT_BUILD_TOOLS_WHEN_CROSSCOMPILING TRUE CACHE BOOL "" FORCE)
+ set(QT_DEBUG_OPTIMIZATION_FLAGS TRUE CACHE BOOL "" FORCE)
+ set(QT_USE_DEFAULT_CMAKE_OPTIMIZATION_FLAGS TRUE CACHE BOOL "" FORCE)
+
+ set(FEATURE_debug OFF)
+ set(FEATURE_debug_and_release OFF)
+ set(FEATURE_force_debug_info ON)
+ set(FEATURE_ltcg ON)
+ set(FEATURE_shared ON)
endif()
-if(CMAKE_PROJECT_NAME STREQUAL "opencv")
+if(CMAKE_PROJECT_NAME STREQUAL "OpenCV")
+ set(PARALLEL_ENABLE_PLUGINS FALSE)
+ set(HIGHGUI_ENABLE_PLUGINS FALSE)
+ set(VIDEOIO_ENABLE_PLUGINS FALSE)
+
set(OPENCV_SKIP_MSVC_EXCEPTIONS_FLAG TRUE)
+ set(OPENCV_DISABLE_THREAD_SUPPORT TRUE)
+
+ set(ENABLE_FAST_MATH ON)
+ set(BUILD_TESTS OFF)
+ set(BUILD_PERF_TESTS OFF)
+ set(BUILD_opencv_apps OFF)
+ set(BUILD_opencv_gapi OFF)
+
+ set(OPENCV_SKIP_MSVC_PARALLEL 1)
+ set(OPENCV_DISABLE_THREAD_SUPPORT 1)
+endif()
+
+if(CMAKE_PROJECT_NAME STREQUAL "TestOscpack")
+ add_compile_definitions(OSC_HOST_LITTLE_ENDIAN)
endif()
-set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
-set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON)
+set(opentrack-simd "SSE2")
+
+if(CMAKE_PROJECT_NAME STREQUAL "onnxruntime")
+ sets(BOOL
+ ONNX_USE_MSVC_STATIC_RUNTIME ON
+ protobuf_MSVC_STATIC_RUNTIME ON
+ ABSL_MSVC_STATIC_RUNTIME ON
+ onnxruntime_USE_AVX ON
+ onnxruntime_USE_AVX2 OFF
+ onnxruntime_USE_AVX512 OFF
+ onnxruntime_BUILD_BENCHMARKS OFF
+ onnxruntime_BUILD_FOR_NATIVE_MACHINE OFF
+ onnxruntime_BUILD_SHARED_LIB ON
+ onnxruntime_BUILD_UNIT_TESTS OFF
+ protobuf_BUILD_EXAMPLES OFF
+ protobuf_BUILD_SHARED_LIBS OFF
+ ONNX_BUILD_BENCHMARKS OFF
+ ONNX_BUILD_TESTS OFF
+ ONNX_DISABLE_EXCEPTIONS OFF # important!
+ ONNX_GEN_PB_TYPE_STUBS OFF
+ onnxruntime_DISABLE_CONTRIB_OPS ON
+ BUILD_TESTING OFF
+ BUILD_SHARED_LIBS OFF
+ )
+ if(opentrack-64bit)
+ sets(BOOL
+ onnxruntime_USE_AVX ON
+ )
+ endif()
+endif()
+
+if(opentrack-64bit)
+ set(opentrack-simd "AVX")
+endif()
+
+set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
+add_compile_options(-MT)
-add_compile_options(-Zi -Zf -Zo -bigobj -cgthreads1 -vd0)
add_link_options(-cgthreads:1)
-set(_CFLAGS "")
-set(_CXXFLAGS "")
-set(_CFLAGS_RELEASE "-O2 -Oit -Oy- -Ob3 -fp:fast -GS- -GF -GL -Gw -Gy -arch:SSE2 -MT")
+set(_CFLAGS "-diagnostics:caret -Zc:inline -Zc:preprocessor -wd4117 -Zi -Zf -Zo -bigobj -cgthreads1 -vd0 -permissive-")
+#if(NOT opentrack-no-static-crt)
+# set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded" CACHE INTERNAL "" FORCE)
+#else()
+# set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreadedDLL" CACHE INTERNAL "" FORCE)
+#endif()
+set(_CXXFLAGS "${_CFLAGS} -Zc:throwingNew -Zc:lambda")
+set(_CFLAGS_RELEASE "-O2 -Oit -Oy- -Ob3 -fp:fast -GS- -GF -GL -Gw -Gy")
+if(NOT opentrack-simd STREQUAL "")
+ set(_CFLAGS_RELEASE "${_CFLAGS_RELEASE} -arch:${opentrack-simd}")
+endif()
set(_CFLAGS_DEBUG "-guard:cf -MTd -Gs0 -RTCs")
set(_CXXFLAGS_RELEASE "${_CFLAGS_RELEASE}")
set(_CXXFLAGS_DEBUG "${_CFLAGS_DEBUG}")
-set(_LDFLAGS_RELEASE "-OPT:REF -OPT:ICF=10 -LTCG:INCREMENTAL -DEBUG:FULL")
+set(_LDFLAGS "")
+set(_LDFLAGS_RELEASE "-OPT:REF,ICF=10 -LTCG -DEBUG:FULL")
set(_LDFLAGS_DEBUG "-DEBUG:FULL")
set(_LDFLAGS_STATIC "")
-set(_LDFLAGS_STATIC_RELEASE "-LTCG:INCREMENTAL")
+set(_LDFLAGS_STATIC_RELEASE "-LTCG")
set(_LDFLAGS_STATIC_DEBUG "")
-if(CMAKE_INSTALL_PREFIX STREQUAL "")
- set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/install" CACHE STRING "" FORCE)
-endif()
-
set(CMAKE_BUILD_TYPE_INIT "RELEASE" CACHE INTERNAL "")
string(TOUPPER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE)
diff --git a/cmake/opentrack-boilerplate.cmake b/cmake/opentrack-boilerplate.cmake
index a78e0ed0..3c66cc61 100644
--- a/cmake/opentrack-boilerplate.cmake
+++ b/cmake/opentrack-boilerplate.cmake
@@ -1,5 +1,9 @@
include_guard(GLOBAL)
+if(POLICY CMP0177)
+ cmake_policy(SET CMP0177 NEW)
+endif()
+
set(opentrack-perms-file WORLD_READ OWNER_WRITE OWNER_READ GROUP_READ)
set(opentrack-perms-dir WORLD_READ WORLD_EXECUTE OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE)
set(opentrack-perms-exec "${opentrack-perms-dir}")
@@ -44,15 +48,15 @@ function(otr_glob_sources var)
endforeach()
foreach(dir ${ARGN})
set(dir "${basedir}/${dir}")
- file(GLOB cxx "${dir}/*.cpp")
- file(GLOB cc "${dir}/*.c")
- file(GLOB hh "${dir}/*.h" "${dir}/*.hpp" "${dir}/*.inc")
- file(GLOB res "${dir}/*.rc")
+ file(GLOB cxx CONFIGURE_DEPENDS "${dir}/*.cpp")
+ file(GLOB cc CONFIGURE_DEPENDS "${dir}/*.c")
+ file(GLOB hh CONFIGURE_DEPENDS "${dir}/*.h" "${dir}/*.hpp" "${dir}/*.inc")
+ file(GLOB res CONFIGURE_DEPENDS "${dir}/*.rc")
foreach(f res)
set_source_files_properties(${f} PROPERTIES LANGUAGE RC)
endforeach()
- file(GLOB ui "${dir}/*.ui")
- file(GLOB rc "${dir}/*.qrc")
+ file(GLOB ui CONFIGURE_DEPENDS "${dir}/*.ui")
+ file(GLOB rc CONFIGURE_DEPENDS "${dir}/*.qrc")
set(all ${cc} ${cxx} ${hh} ${res})
foreach(i ui rc res cc cxx hh all)
set(${var}-${i} "${${var}-${i}}" ${${i}} PARENT_SCOPE)
@@ -84,7 +88,7 @@ function(otr_compat target)
endif()
get_property(type TARGET "${n}" PROPERTY TYPE)
- if (".${TYPE}" STREQUAL ".EXECUTABLE")
+ if (type STREQUAL "EXECUTABLE")
otr_fixup_subsystem(${target})
endif()
endfunction()
@@ -145,7 +149,7 @@ function(otr_module n_)
if(arg_EXECUTABLE)
if (APPLE)
- set(subsys "MACOSX_BUNDLE")
+ set(subsys "MACOSX_BUNDLE")
elseif(NOT WIN32)
set(subsys "")
elseif(arg_WIN32-CONSOLE)
@@ -177,7 +181,7 @@ function(otr_module n_)
set(self "${n}" PARENT_SCOPE)
- if(NOT arg_RELINK)
+ if(NOT arg_RELINK AND FALSE)
set_property(TARGET ${n} PROPERTY LINK_DEPENDS_NO_SHARED TRUE)
else()
set_property(TARGET ${n} PROPERTY LINK_DEPENDS_NO_SHARED FALSE)
@@ -194,12 +198,7 @@ function(otr_module n_)
string(REPLACE "-" "_" build-n ${n_})
string(TOUPPER "${build-n}" build-n)
target_compile_definitions(${n} PRIVATE "BUILD_${build-n}")
-
- get_property(ident GLOBAL PROPERTY opentrack-ident)
- if (".${ident}" STREQUAL ".")
- message(FATAL_ERROR "must set global property `opentrack-ident' in `opentrack-variant.cmake'")
- endif()
- target_compile_definitions(${n} PRIVATE OPENTRACK_ORG=\"${ident}\")
+ include(opentrack-org)
if(arg_STATIC)
set(arg_NO-INSTALL TRUE)
@@ -222,20 +221,22 @@ function(otr_module n_)
if(arg_BIN)
if (APPLE)
install(TARGETS "${n}"
- RUNTIME DESTINATION ${opentrack-bin}
- BUNDLE DESTINATION ${opentrack-bin}
- LIBRARY DESTINATION ${opentrack-bin}/Library
- RESOURCE DESTINATION ${opentrack-bin}/opentrack.app/Resource
+ RUNTIME DESTINATION "${opentrack-bin}"
+ BUNDLE DESTINATION "${opentrack-bin}"
+ LIBRARY DESTINATION "${opentrack-bin}/Library"
+ RESOURCE DESTINATION "${opentrack-bin}/opentrack.app/Resource"
PERMISSIONS ${opentrack-perms-exec})
else()
install(TARGETS "${n}"
- RUNTIME DESTINATION ${opentrack-bin}
- LIBRARY DESTINATION ${opentrack-libexec}
+ RUNTIME DESTINATION "${opentrack-bin}"
+ LIBRARY DESTINATION "${opentrack-libexec}"
PERMISSIONS ${opentrack-perms-exec})
endif()
else()
# Plugins
- install(TARGETS "${n}" ${opentrack-install-src}
+ install(TARGETS "${n}"
+ RUNTIME DESTINATION "${opentrack-libexec}"
+ LIBRARY DESTINATION "${opentrack-libexec}"
PERMISSIONS ${opentrack-perms-exec})
endif()
@@ -263,7 +264,7 @@ function(otr_add_target_dirs var)
list(APPEND globs "${k}/CMakeLists.txt")
endforeach()
set(projects "")
- file(GLOB projects ${globs})
+ file(GLOB projects CONFIGURE_DEPENDS ${globs})
list(SORT projects)
set("${var}" "${projects}" PARENT_SCOPE)
endfunction()
diff --git a/cmake/opentrack-hier.cmake b/cmake/opentrack-hier.cmake
index 16694d1f..bb1a435d 100644
--- a/cmake/opentrack-hier.cmake
+++ b/cmake/opentrack-hier.cmake
@@ -14,8 +14,7 @@ if(APPLE)
set(opentrack-runtime-libexec "/Plugins/") # MUST HAVE A TRAILING BACKSLASH, Used in APP
set(opentrack-runtime-doc "/") # MUST HAVE A TRAILING BACKSLASH
set(opentrack-bin "${CMAKE_INSTALL_PREFIX}")
- set(opentrack-doc "./doc")
- set(opentrack-src "./source-code")
+ set(opentrack-doc "doc")
set(opentrack-i18n "opentrack.app/Contents/Resources") # used during install
set(opentrack-runtime-i18n "../Resources/i18n") # used in application
set(opentrack-install-rpath "${CMAKE_INSTALL_PREFIX}/Library")
@@ -24,24 +23,21 @@ elseif(WIN32)
set(opentrack-runtime-libexec "/${opentrack-libexec}/") # MUST HAVE A TRAILING BACKSLASH
set(opentrack-runtime-doc "/doc/") # MUST HAVE A TRAILING BACKSLASH
set(opentrack-bin ".")
- set(opentrack-doc "./doc")
- set(opentrack-src "./source-code")
- set(opentrack-i18n "./i18n")
+ set(opentrack-doc "doc")
+ set(opentrack-i18n "i18n")
set(opentrack-runtime-i18n "./i18n")
set(opentrack-debug "./debug")
set(opentrack-install-rpath "")
else()
set(opentrack-libexec "libexec/opentrack")
set(opentrack-runtime-libexec "/../${opentrack-libexec}/") # MUST HAVE A TRAILING BACKSLASH
- set(opentrack-runtime-doc "/share/doc/opentrack/") # MUST HAVE A TRAILING BACKSLASH
+ set(opentrack-runtime-doc "/../share/doc/opentrack/") # MUST HAVE A TRAILING BACKSLASH
set(opentrack-bin "bin")
- set(opentrack-doc "./share/doc/opentrack")
- set(opentrack-src "./share/doc/opentrack/source-code")
+ set(opentrack-doc "share/doc/opentrack")
set(opentrack-install-rpath "${CMAKE_INSTALL_PREFIX}/${opentrack-libexec}")
- set(opentrack-i18n "./share/opentrack/i18n")
+ set(opentrack-i18n "share/opentrack/i18n")
set(opentrack-runtime-i18n "../share/opentrack/i18n")
endif()
-set(opentrack-install-src RUNTIME DESTINATION ${opentrack-libexec} LIBRARY DESTINATION ${opentrack-libexec})
function(otr_escape_string var str)
string(REGEX REPLACE "([^_A-Za-z0-9./:-])" "\\\\\\1" str "${str}")
diff --git a/cmake/opentrack-i18n.cmake b/cmake/opentrack-i18n.cmake
index 297307b9..fca83248 100644
--- a/cmake/opentrack-i18n.cmake
+++ b/cmake/opentrack-i18n.cmake
@@ -1,21 +1,23 @@
include_guard(GLOBAL)
+if(POLICY CMP0177)
+ cmake_policy(SET CMP0177 NEW)
+endif()
+
add_custom_target(i18n-lupdate)
-set_property(TARGET i18n-lupdate PROPERTY FOLDER "i18n")
add_custom_target(i18n-lrelease DEPENDS i18n-lupdate)
-set_property(TARGET i18n-lrelease PROPERTY FOLDER "i18n")
add_custom_target(i18n ALL DEPENDS i18n-lrelease)
function(otr_i18n_for_target_directory n)
set(k "opentrack-${n}")
- get_property(lupdate-binary TARGET "Qt6::lupdate" PROPERTY IMPORTED_LOCATION)
+ get_property(lupdate-binary TARGET Qt6::lupdate PROPERTY IMPORTED_LOCATION)
#make_directory("${CMAKE_CURRENT_BINARY_DIR}/lang")
set(ts-files "")
foreach(k ${opentrack_all-translations})
- list(APPEND ts-files "lang/${k}.ts")
+ list(APPEND ts-files "${CMAKE_CURRENT_SOURCE_DIR}/lang/${k}.ts")
set_property(GLOBAL APPEND PROPERTY "opentrack-ts-files-${k}" "${CMAKE_CURRENT_SOURCE_DIR}/lang/${k}.ts")
endforeach()
set(stamp "${CMAKE_CURRENT_BINARY_DIR}/lupdate.stamp")
@@ -26,6 +28,7 @@ function(otr_i18n_for_target_directory n)
if (NOT EXISTS "${t}")
file(MAKE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/lang")
file(READ "${CMAKE_SOURCE_DIR}/cmake/translation-stub.ts" stub)
+ string(REPLACE "@LANG@" "${i}" stub "${stub}")
file(WRITE "${t}" "${stub}")
endif()
endforeach()
@@ -48,22 +51,23 @@ function(otr_i18n_for_target_directory n)
-ts ${ts-files}
${to-null}
COMMAND "${CMAKE_COMMAND}" -E touch "${stamp}"
- DEPENDS ${${k}-cc} ${${k}-hh} ${${k}-uih} ${${k}-moc}
+ #BYPRODUCTS ${ts-files}
+ DEPENDS "opentrack-${n}"
COMMENT "Running lupdate for ${n}"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}")
set(target-name "i18n-module-${n}")
add_custom_target(${target-name} DEPENDS "${stamp}" COMMENT "")
- set_property(TARGET ${target-name} PROPERTY FOLDER "i18n")
add_dependencies(i18n-lupdate ${target-name})
endfunction()
function(otr_merge_translations)
otr_escape_string(i18n-pfx "${opentrack-i18n}")
- install(CODE "file(REMOVE_RECURSE \"\${CMAKE_INSTALL_PREFIX}/${i18n-pfx}\")")
+ #install(CODE "message(FATAL_ERROR \"foo \${CMAKE_INSTALL_PREFIX}\")")
+ #install(CODE "file(REMOVE_RECURSE \"\${CMAKE_INSTALL_PREFIX}/${i18n-pfx}\")")
foreach(i ${opentrack_all-translations})
get_property(ts-files GLOBAL PROPERTY "opentrack-ts-files-${i}")
- get_property(lrelease-binary TARGET "Qt6::lrelease" PROPERTY IMPORTED_LOCATION)
+ get_property(lrelease-binary TARGET Qt6::lrelease PROPERTY IMPORTED_LOCATION)
set(qm-output "${CMAKE_BINARY_DIR}/${i}.qm")
@@ -82,17 +86,16 @@ function(otr_merge_translations)
${ts-files}
-qm "${qm-output}"
${to-null}
- DEPENDS ${ts-files} i18n-lupdate
+ DEPENDS i18n-lupdate ${ts-files}
COMMENT "Running lrelease for ${i}"
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}")
set(target-name i18n-qm-${i})
add_custom_target(${target-name} DEPENDS "${qm-output}")
- set_property(TARGET ${target-name} PROPERTY FOLDER "i18n")
add_dependencies(i18n-lrelease ${target-name})
install(FILES "${qm-output}"
- DESTINATION "${CMAKE_INSTALL_PREFIX}/${opentrack-i18n}"
+ DESTINATION "${opentrack-i18n}"
PERMISSIONS ${opentrack-perms-file})
endforeach()
endfunction()
diff --git a/cmake/opentrack-install.cmake b/cmake/opentrack-install.cmake
index 94f3e29d..ebef95bc 100644
--- a/cmake/opentrack-install.cmake
+++ b/cmake/opentrack-install.cmake
@@ -1,4 +1,9 @@
include_guard(GLOBAL)
+
+if(POLICY CMP0177)
+ cmake_policy(SET CMP0177 NEW)
+endif()
+
macro(otr_install_misc path)
install(${ARGN} DESTINATION "${path}" PERMISSIONS ${opentrack-perms-file})
endmacro()
@@ -16,53 +21,29 @@ macro(otr_install_dir path)
)
endmacro()
-function(otr_setup_refresh_install_dir)
- if((NOT CMAKE_INSTALL_PREFIX STREQUAL "") AND (NOT opentrack-src STREQUAL ""))
- otr_escape_string(dir "${CMAKE_INSTALL_PREFIX}/${opentrack-src}/")
- install(CODE "file(REMOVE_RECURSE \"${dir}\")")
- endif()
-endfunction()
-
-function(otr_install_sources)
- otr_setup_refresh_install_dir()
- get_property(source-dirs GLOBAL PROPERTY opentrack-all-source-dirs)
- foreach(k ${source-dirs})
- file(RELATIVE_PATH dest "${CMAKE_SOURCE_DIR}" "${k}")
- otr_install_dir("${opentrack-src}" "${dest}")
- endforeach()
-
- otr_install_dir("${opentrack-src}" "${CMAKE_SOURCE_DIR}/cmake")
- otr_install_dir("${opentrack-src}" "${CMAKE_SOURCE_DIR}/bin")
-
- otr_install_misc("${opentrack-src}" FILES "${CMAKE_SOURCE_DIR}/CMakeLists.txt")
- otr_install_misc("${opentrack-doc}" FILES "${CMAKE_SOURCE_DIR}/README.md")
- otr_install_misc("${opentrack-doc}" FILES "${CMAKE_SOURCE_DIR}/.github/CONTRIBUTING.md")
- otr_install_misc("${opentrack-doc}" FILES "${CMAKE_SOURCE_DIR}/WARRANTY.txt")
- otr_install_misc("${opentrack-doc}" FILES "${CMAKE_SOURCE_DIR}/OPENTRACK-LICENSING.txt")
- otr_install_misc("${opentrack-doc}" FILES "${CMAKE_SOURCE_DIR}/AUTHORS.md")
-endfunction()
-
function(cleanup_visual_studio_debug)
otr_escape_string(pfx "${CMAKE_INSTALL_PREFIX}")
install(CODE "file(REMOVE_RECURSE \"${pfx}/.vs\")")
endfunction()
-otr_install_dir("${opentrack-doc}" ${CMAKE_SOURCE_DIR}/3rdparty-notices)
-otr_install_dir("${opentrack-doc}" "${CMAKE_SOURCE_DIR}/settings" "${CMAKE_SOURCE_DIR}/contrib")
+otr_install_dir("${opentrack-doc}" "3rdparty-notices")
+otr_install_dir("${opentrack-doc}" "settings" "${CMAKE_SOURCE_DIR}/contrib")
+otr_install_dir("${opentrack-libexec}" "presets")
if(WIN32)
- otr_install_misc(. FILES "${CMAKE_SOURCE_DIR}/bin/qt.conf")
- otr_install_misc(. FILES "${CMAKE_SOURCE_DIR}/bin/cleye.config")
- otr_install_misc(${opentrack-libexec} FILES "${CMAKE_SOURCE_DIR}/bin/cleye.config")
+ if(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ otr_install_misc(. FILES "bin/cleye.config")
+ otr_install_misc(${opentrack-libexec} FILES "bin/cleye.config")
+ endif()
endif()
-otr_install_misc("${opentrack-doc}" FILES ${CMAKE_SOURCE_DIR}/README.md)
+otr_install_misc("${opentrack-doc}" FILES README.md)
-otr_install_misc("${opentrack-doc}" FILES "${CMAKE_SOURCE_DIR}/README.md")
-otr_install_misc("${opentrack-doc}" FILES "${CMAKE_SOURCE_DIR}/.github/CONTRIBUTING.md")
-otr_install_misc("${opentrack-doc}" FILES "${CMAKE_SOURCE_DIR}/WARRANTY.txt")
-otr_install_misc("${opentrack-doc}" FILES "${CMAKE_SOURCE_DIR}/OPENTRACK-LICENSING.txt")
-otr_install_misc("${opentrack-doc}" FILES "${CMAKE_SOURCE_DIR}/AUTHORS.md")
+otr_install_misc("${opentrack-doc}" FILES "README.md")
+otr_install_misc("${opentrack-doc}" FILES ".github/CONTRIBUTING.md")
+otr_install_misc("${opentrack-doc}" FILES "WARRANTY.txt")
+otr_install_misc("${opentrack-doc}" FILES "OPENTRACK-LICENSING.txt")
+otr_install_misc("${opentrack-doc}" FILES "AUTHORS.md")
# this must be done last because the files may be in use already
# do it last so in case of file-in-use failure, the rest is installed
@@ -75,9 +56,9 @@ endif()
if (APPLE)
set(OSX_POST_INSTALL_DIR "/../thirdparty")
endif()
-otr_install_exec("${opentrack-libexec}${OSX_POST_INSTALL_DIR}" FILES "${CMAKE_SOURCE_DIR}/bin/freetrackclient.dll")
-otr_install_exec("${opentrack-libexec}${OSX_POST_INSTALL_DIR}" FILES "${CMAKE_SOURCE_DIR}/bin/freetrackclient64.dll")
+otr_install_exec("${opentrack-libexec}${OSX_POST_INSTALL_DIR}" FILES "bin/freetrackclient.dll")
+otr_install_exec("${opentrack-libexec}${OSX_POST_INSTALL_DIR}" FILES "bin/freetrackclient64.dll")
otr_install_exec("${opentrack-libexec}${OSX_POST_INSTALL_DIR}" FILES
- "${CMAKE_SOURCE_DIR}/bin/NPClient.dll"
- "${CMAKE_SOURCE_DIR}/bin/NPClient64.dll"
- "${CMAKE_SOURCE_DIR}/bin/TrackIR.exe")
+ "bin/NPClient.dll"
+ "bin/NPClient64.dll"
+ "bin/TrackIR.exe")
diff --git a/cmake/opentrack-load-user-settings.cmake b/cmake/opentrack-load-user-settings.cmake
index 1baf3702..fdb63936 100644
--- a/cmake/opentrack-load-user-settings.cmake
+++ b/cmake/opentrack-load-user-settings.cmake
@@ -26,12 +26,11 @@ else()
set(__sdk_host_os "")
endif()
-set(__sdk_paths_basename "sdk-paths-${__sdk_username}@${CMAKE_CXX_COMPILER_ID}-${__sdk_host_os}${__sdk_target_os}.cmake")
-set(__sdk_paths_filename "${CMAKE_SOURCE_DIR}/${__sdk_paths_basename}")
+set(__sdk_paths_filename "${CMAKE_CURRENT_SOURCE_DIR}/sdk-paths-${__sdk_username}@${CMAKE_CXX_COMPILER_ID}-${__sdk_host_os}${__sdk_target_os}.cmake")
if(EXISTS "${__sdk_paths_filename}")
- message(STATUS "Loading user settings '${__sdk_paths_basename}'")
+ message(STATUS "Loading user settings '${__sdk_paths_filename}'")
include("${__sdk_paths_filename}")
else()
- message(STATUS "User settings file '${__sdk_paths_basename}' doesn't exist")
+ message(STATUS "User settings file '${__sdk_paths_filename}' doesn't exist")
endif()
diff --git a/cmake/opentrack-opencv.cmake b/cmake/opentrack-opencv.cmake
index 40356418..3acadf4a 100644
--- a/cmake/opentrack-opencv.cmake
+++ b/cmake/opentrack-opencv.cmake
@@ -2,13 +2,3 @@ include_guard(GLOBAL)
include(opentrack-boilerplate)
find_package(OpenCV QUIET)
-
-function(otr_install_opencv_libs)
- foreach(k core features2d calib3d flann imgcodecs imgproc videoio)
- otr_install_lib("opencv_${k}" "${opentrack-libexec}")
- endforeach()
-endfunction()
-
-if(TARGET opencv_core)
- otr_install_opencv_libs()
-endif()
diff --git a/cmake/opentrack-org.cmake b/cmake/opentrack-org.cmake
new file mode 100644
index 00000000..107c829b
--- /dev/null
+++ b/cmake/opentrack-org.cmake
@@ -0,0 +1,21 @@
+include_guard(GLOBAL)
+function(otr_write_org)
+ get_property(ident GLOBAL PROPERTY opentrack-ident)
+ if (ident STREQUAL "")
+ message(FATAL_ERROR "must set global property `opentrack-ident' in `opentrack-variant.cmake'")
+ endif()
+ otr_escape_string(ident "${ident}")
+ set(new-str "#pragma once
+#define OPENTRACK_ORG \"${ident}\"
+")
+
+ set(filename "${CMAKE_BINARY_DIR}/opentrack-org.hxx")
+ set(old-str "")
+ if(EXISTS "${filename}")
+ file(READ "${filename}" old-str)
+ endif()
+ if(NOT old-str STREQUAL new-str)
+ file(WRITE "${filename}" "${new-str}")
+ endif()
+endfunction()
+otr_write_org()
diff --git a/cmake/opentrack-pkg-config.cmake b/cmake/opentrack-pkg-config.cmake
index 5b84cf40..0ea92e56 100644
--- a/cmake/opentrack-pkg-config.cmake
+++ b/cmake/opentrack-pkg-config.cmake
@@ -1,19 +1,34 @@
include_guard(GLOBAL)
include(FindPkgConfig)
-function(otr_pkgconfig target)
+function(_otr_pkgconfig_add target k)
+ target_compile_options(${target} PRIVATE "${${k}_CFLAGS}")
+ target_link_options(${target} PRIVATE ${${k}_LDFLAGS})
+ target_include_directories(${target} SYSTEM PRIVATE ${${k}_INCLUDE_DIRS} ${${k}_INCLUDEDIR})
+ target_link_libraries(${target} ${${k}_LIBRARIES})
+endfunction()
+
+
+function(otr_pkgconfig_ ret target)
+ set(${ret} 1 PARENT_SCOPE)
foreach(i ${ARGN})
set(k pkg-config_${i})
- pkg_check_modules(${k} QUIET ${i})
- if(${${k}_FOUND})
- target_compile_options(${target} PRIVATE "${${k}_CFLAGS}")
- target_link_options(${target} PRIVATE ${${k}_LDFLAGS})
- target_include_directories(${target} SYSTEM PRIVATE ${${k}_INCLUDE_DIRS} ${${k}_INCLUDEDIR})
- target_link_libraries(${target} ${${k}_LIBRARIES})
+ pkg_check_modules(${k} ${i})
+ if(${k}_FOUND)
+ _otr_pkgconfig_add(${target} ${k})
else()
- message(FATAL_ERROR "Can't find '${i}'. Please install development files for this package.")
+ set(${ret} 0 PARENT_SCOPE)
endif()
endforeach()
endfunction()
-
+function(otr_pkgconfig target)
+ foreach(i ${ARGN})
+ set(k pkg-config_${i})
+ pkg_check_modules(${k} ${i})
+ if(NOT ${k}_FOUND)
+ message(FATAL_ERROR "can't find ${i} for ${target}")
+ endif()
+ _otr_pkgconfig_add(${target} ${k})
+ endforeach()
+endfunction()
diff --git a/cmake/opentrack-platform.cmake b/cmake/opentrack-platform.cmake
index 23f684f3..43d9c7a0 100644
--- a/cmake/opentrack-platform.cmake
+++ b/cmake/opentrack-platform.cmake
@@ -34,12 +34,10 @@ set(CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}" CACHE STRING "" FORCE)
include_directories("${CMAKE_SOURCE_DIR}")
-set(opentrack_maintainer-mode FALSE CACHE INTERNAL "Select if developing core code (not modules)")
-
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
-set(CMAKE_CXX_STANDARD 17)
-set(CMAKE_CXX_STANDARD_DEFAULT 17)
+set(CMAKE_CXX_STANDARD 20)
+set(CMAKE_CXX_STANDARD_DEFAULT 20)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
set(CMAKE_CXX_EXTENSIONS FALSE)
@@ -97,6 +95,7 @@ if(CMAKE_COMPILER_IS_GNUCXX AND NOT APPLE)
add_link_options(-Wl,--exclude-libs,ALL)
add_link_options(-Wl,-z,relro,-z,now)
add_link_options(-Wl,--as-needed)
+ add_link_options(-Wl,-z,noexecstack)
add_compile_options(-fno-plt)
endif()
endif()
@@ -118,15 +117,15 @@ if(MSVC)
add_definitions(-D_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1)
add_definitions(-D_SCL_SECURE_NO_WARNINGS)
- add_compile_options(-EHsc)
- #add_definitions(-D_HAS_EXCEPTIONS=0)
+ #add_compile_options(-EHsc)
+ add_definitions(-D_HAS_EXCEPTIONS=0)
add_definitions(-D_ENABLE_EXTENDED_ALIGNED_STORAGE)
add_definitions(-D_ENABLE_ATOMIC_ALIGNMENT_FIX)
add_definitions(-D_SILENCE_CXX17_NEGATORS_DEPRECATION_WARNING)
add_definitions(-D_SILENCE_CXX17_ADAPTOR_TYPEDEFS_DEPRECATION_WARNING)
+ add_definitions(-D_SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING)
- add_compile_options(-diagnostics:caret)
add_compile_options(-permissive-)
if(opentrack-64bit)
@@ -134,10 +133,20 @@ if(MSVC)
endif()
add_link_options(-DYNAMICBASE -NXCOMPAT)
- add_link_options(-WX)
+ #add_link_options(-WX)
add_link_options(-ignore:4020)
add_link_options(-ignore:4217) # debug build
+
+ if(MSVC_VERSION GREATER_EQUAL 1913)
+ if(NOT MSVC_VERSION GREATER_EQUAL 1929)
+ add_compile_options(-experimental:external)
+ endif()
+ add_compile_options(-external:W0 -external:anglebrackets)
+ endif()
+ add_compile_options(-Zc:preprocessor)
+ #add_compile_options(-Zc:inline)
+
#C4457: declaration of 'id' hides function parameter
#C4456: declaration of 'i' hides previous local declaration
#C4263 - member function does not override any base class virtual member function
@@ -146,8 +155,9 @@ if(MSVC)
#C4266 - no override available for virtual member function from base type, function is hidden
#C4928 - illegal copy-initialization, more than one user-defined conversion has been implicitly applied
#C4200: nonstandard extension used: zero-sized array in struct/union
+ #C4459: declaration of 'eps' hides global declaration
- set(warns-disable 4530 4577 4789 4244 4702 4530 4244 4127 4458 4456 4251 4100 4702 4457 4200)
+ set(warns-disable 4530 4577 4789 4244 4702 4530 4244 4127 4458 4456 4251 4100 4702 4457 4200 4459)
foreach(i ${warns-disable})
add_compile_options(-wd${i})
diff --git a/cmake/opentrack-policy.cmake b/cmake/opentrack-policy.cmake
index 6426cf87..5fd8647f 100644
--- a/cmake/opentrack-policy.cmake
+++ b/cmake/opentrack-policy.cmake
@@ -13,10 +13,12 @@ set(_policies
CMP0012
CMP0069
CMP0063
- CMP0082
+ CMP0074
)
foreach(k ${_policies})
if(POLICY ${k})
cmake_policy(SET ${k} NEW)
endif()
endforeach()
+
+set(CMAKE_INSTALL_MESSAGE LAZY)
diff --git a/cmake/opentrack-qt.cmake b/cmake/opentrack-qt.cmake
index ab0b751b..39aa6361 100644
--- a/cmake/opentrack-qt.cmake
+++ b/cmake/opentrack-qt.cmake
@@ -1,38 +1,39 @@
include_guard(GLOBAL)
set(qt-required-components Core Network Widgets LinguistTools Gui)
set(qt-optional-components SerialPort)
-set(qt-imported-targets Qt6::Core Qt6::Gui Qt6::Network Qt6::SerialPort Qt6::Widgets)
+set(qt-imported-targets Qt6::Core Qt6::Gui Qt6::Network Qt6::Widgets)
if(APPLE)
- list(APPEND qt-required-components "DBus")
- list(APPEND qt-optional-components "Multimedia")
- list(APPEND qt-imported-targets Qt6::DBus Qt6::Multimedia)
-elseif(WIN32)
- list(APPEND qt-required-components)
+ list(APPEND qt-required-components Multimedia)
+ list(APPEND qt-imported-targets Multimedia)
endif()
find_package(Qt6 REQUIRED COMPONENTS ${qt-required-components} QUIET)
find_package(Qt6 COMPONENTS ${qt-optional-components} QUIET)
-set(MY_QT_LIBS ${Qt6Core_LIBRARIES} ${Qt6Gui_LIBRARIES} ${Qt6Widgets_LIBRARIES} ${Qt6Network_LIBRARIES})
+if(WIN32)
+ find_package(Qt6Gui REQUIRED COMPONENTS QWindowsIntegrationPlugin)
+endif()
+
+set(MY_QT_LIBS Qt6::Core Qt6::Gui Qt6::Widgets Qt6::Network)
if(APPLE)
- list(APPEND MY_QT_LIBS ${Qt6Multimedia_LIBRARIES} ${Qt6DBus_LIBRARIES})
+ list(APPEND MY_QT_LIBS Qt6::Multimedia)
endif()
function(otr_install_qt_libs)
foreach(i ${qt-imported-targets})
- if(NOT TARGET "${i}")
- continue()
- endif()
- otr_install_lib(${i} "platforms")
+ #if(NOT TARGET "${i}")
+ # continue()
+ #endif()
+ otr_install_lib(${i} ".")
endforeach()
if(WIN32)
- get_property(foo TARGET Qt6::Core PROPERTY IMPORTED_LOCATION)
- get_filename_component(foo "${foo}" DIRECTORY)
- otr_install_lib("${foo}/../platforms/qwindows.dll" "platforms")
+ otr_install_lib(Qt6::QWindowsIntegrationPlugin "./platforms")
endif()
endfunction()
-otr_install_qt_libs()
+if(WIN32 OR APPLE)
+ otr_install_qt_libs()
+endif()
function(otr_qt n)
if(".${${n}-cc}${${n}-cxx}${${n}-hh}" STREQUAL ".")
@@ -50,7 +51,7 @@ function(otr_qt n)
endfunction()
function(otr_qt2 n)
- target_include_directories("${n}" PRIVATE SYSTEM
+ target_include_directories("${n}" SYSTEM PRIVATE
${Qt6Core_INCLUDE_DIRS} ${Qt6Gui_INCLUDE_DIRS} ${Qt6Widgets_INCLUDE_DIRS} ${Qt6Network_INCLUDE_DIRS}
)
target_compile_definitions("${n}" PRIVATE
diff --git a/cmake/opentrack-variant.cmake b/cmake/opentrack-variant.cmake
index 2cf39fcd..5cb4bd57 100644
--- a/cmake/opentrack-variant.cmake
+++ b/cmake/opentrack-variant.cmake
@@ -1,29 +1,32 @@
include_guard(GLOBAL)
-function(otr_dist_select_variant)
- file(GLOB variants "variant/*")
+function(otr_init_variant)
+ set_property(GLOBAL PROPERTY opentrack-variant "default")
+ set_property(GLOBAL PROPERTY opentrack-ident "opentrack-2.3")
- set(variant-list "")
-
- foreach(k ${variants})
- get_filename_component(name "${k}" NAME)
- if(EXISTS "${k}/_variant.cmake")
- list(APPEND variant-list "${name}")
- else()
- message(FATAL_ERROR "stray variant dir '${name}'")
- endif()
- endforeach()
-
- set(opentrack_variant "default" CACHE STRING "")
- set(dir "${CMAKE_SOURCE_DIR}/variant/${opentrack_variant}")
-
- if(NOT EXISTS "${dir}/_variant.cmake")
- set(opentrack_variant "default" CACHE STRING "" FORCE)
- set(dir "${CMAKE_SOURCE_DIR}/variant/${opentrack_variant}")
- endif()
-
- set_property(CACHE opentrack_variant PROPERTY STRINGS "${variant-list}")
-
- include("${dir}/_variant.cmake")
- otr_init_variant()
+ set(subprojects
+ "tracker-*"
+ "proto-*"
+ "filter-*"
+ "options"
+ "api"
+ "compat"
+ "logic"
+ "dinput"
+ "gui"
+ "main"
+ "x-plane-plugin"
+ "csv"
+ "pose-widget"
+ "spline"
+ "qxt-mini"
+ "macosx"
+ "cv"
+ "migration"
+ "main-window"
+ "video"
+ "video-*"
+ "opentrack"
+ )
+ set_property(GLOBAL PROPERTY opentrack-subprojects "${subprojects}")
endfunction()
diff --git a/cmake/translation-stub.ts b/cmake/translation-stub.ts
index 6401616d..9d616636 100644
--- a/cmake/translation-stub.ts
+++ b/cmake/translation-stub.ts
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="@LANG@">
</TS>
diff --git a/compat/CMakeLists.txt b/compat/CMakeLists.txt
index fb498fb6..87b191cc 100644
--- a/compat/CMakeLists.txt
+++ b/compat/CMakeLists.txt
@@ -1,7 +1,3 @@
-if(CMAKE_COMPILER_IS_GNUCXX)
- add_compile_options(-fno-lto -fno-fast-math -fno-finite-math-only -O0)
-endif()
-
otr_module(compat NO-COMPAT BIN)
if(NOT WIN32 AND NOT APPLE)
@@ -11,3 +7,18 @@ endif()
if(WIN32)
target_link_libraries(${self} strmiids)
endif()
+
+
+if(APPLE)
+ target_link_libraries(${self} proc)
+elseif(LINUX)
+ otr_pkgconfig_(has-libproc2 ${self} libproc2)
+ if(has-libproc2)
+ target_compile_definitions(${self} PUBLIC -DOTR_HAS_LIBPROC2)
+ else()
+ otr_pkgconfig_(has-libprocps ${self} libprocps)
+ if(NOT has-libprocps)
+ message(FATAL_ERROR "install libproc2 or libprocps development files")
+ endif()
+ endif()
+endif()
diff --git a/compat/camera-names.cpp b/compat/camera-names.cpp
index 5d190943..82776584 100644
--- a/compat/camera-names.cpp
+++ b/compat/camera-names.cpp
@@ -12,7 +12,8 @@
#endif
#ifdef __APPLE__
-# include <QCameraInfo>
+# include <QCameraDevice>
+# include <QMediaDevices>
#endif
#ifdef __linux__
@@ -41,6 +42,36 @@ int camera_name_to_index(const QString &name)
return -1;
}
+#ifdef _WIN32
+# include <QRegularExpression>
+
+static QString prop_to_qstring(IPropertyBag* pPropBag, const wchar_t* name)
+{
+ QString ret{};
+ VARIANT var;
+ VariantInit(&var);
+ HRESULT hr = pPropBag->Read(name, &var, nullptr);
+ if (SUCCEEDED(hr))
+ ret = QString{(const QChar*)var.bstrVal, int(std::wcslen(var.bstrVal))};
+ VariantClear(&var);
+ return ret;
+}
+
+static QString device_path_from_qstring(const QString& str)
+{
+ // language=RegExp prefix=R"/( suffix=)/"
+ static const QRegularExpression regexp{R"/(#vid_([0-9a-f]{4})&pid_([0-9a-f]{4})&mi_([0-9a-f]{2})#([^#]+))/",
+ QRegularExpression::CaseInsensitiveOption};
+ auto match = regexp.match(str);
+ if (!match.hasMatch())
+ return {};
+ QString id = match.captured(4);
+ id.replace('&', '_');
+ return id;
+}
+
+#endif
+
std::vector<std::tuple<QString, int>> get_camera_names()
{
std::vector<std::tuple<QString, int>> ret;
@@ -67,26 +98,26 @@ std::vector<std::tuple<QString, int>> get_camera_names()
{
IPropertyBag *pPropBag;
hr = pMoniker->BindToStorage(nullptr, nullptr, IID_IPropertyBag, (void **)&pPropBag);
- if (SUCCEEDED(hr)) {
+ if (SUCCEEDED(hr)) {
// To retrieve the filter's friendly name, do the following:
- VARIANT var;
- VariantInit(&var);
- hr = pPropBag->Read(L"FriendlyName", &var, nullptr);
if (SUCCEEDED(hr))
{
- // Display the name in your UI somehow.
- QString str((QChar*)var.bstrVal, int(std::wcslen(var.bstrVal)));
- ret.push_back({ str, ret.size() });
+ QString str = prop_to_qstring(pPropBag, L"FriendlyName");
+ QString path = device_path_from_qstring(prop_to_qstring(pPropBag, L"DevicePath"));
+ if (!path.isNull())
+ str += QStringLiteral(" [%1]").arg(path);
+ ret.push_back({ str, (int)ret.size() });
}
- VariantClear(&var);
pPropBag->Release();
}
pMoniker->Release();
}
pEnumCat->Release();
}
+#if 0
else
qDebug() << "failed CLSID_VideoInputDeviceCategory" << hr;
+#endif
pSysDevEnum->Release();
#endif
@@ -113,9 +144,8 @@ std::vector<std::tuple<QString, int>> get_camera_names()
}
#endif
#ifdef __APPLE__
- QList<QCameraInfo> cameras = QCameraInfo::availableCameras();
- for (const QCameraInfo &cameraInfo : cameras)
- ret.push_back({ cameraInfo.description(), ret.size() });
+ for (const QCameraDevice& camera_info : QMediaDevices::videoInputs())
+ ret.push_back({ camera_info.description(), ret.size() });
#endif
return ret;
diff --git a/compat/check-visible.cpp b/compat/check-visible.cpp
index 4da649c7..621d941a 100644
--- a/compat/check-visible.cpp
+++ b/compat/check-visible.cpp
@@ -10,7 +10,7 @@ static bool visible = true;
#if defined _WIN32
#include "timer.hpp"
-#include "macros.hpp"
+#include "macros.h"
static Timer timer;
diff --git a/compat/check-visible.hpp b/compat/check-visible.hpp
index e24260ab..6669b84d 100644
--- a/compat/check-visible.hpp
+++ b/compat/check-visible.hpp
@@ -1,7 +1,7 @@
#pragma once
#include "export.hpp"
-#include "macros1.h"
+#include "macros.h"
class QWidget;
diff --git a/compat/correlation-calibrator.cpp b/compat/correlation-calibrator.cpp
index 01f3b14f..07ef7d3d 100644
--- a/compat/correlation-calibrator.cpp
+++ b/compat/correlation-calibrator.cpp
@@ -2,6 +2,7 @@
#include "variance.hpp"
#include "compat/math.hpp"
#include "compat/meta.hpp"
+#include "compat/macros.h"
#include <cmath>
#include <iterator>
@@ -49,7 +50,7 @@ bool correlation_calibrator::check_buckets(const vec6& data)
for (unsigned k = 0; k < 6; k++)
{
- const double val = clamp(data[k], min[k], max[k]);
+ const double val = std::clamp(data[k], min[k], max[k]);
pos[k] = unsigned((val-min[k])/spacing[k]);
if (pos[k] >= nbuckets[k])
diff --git a/filter-hamilton/hamilton-tools.cpp b/compat/hamilton-tools.cpp
index e18082a8..eb0ef984 100644
--- a/filter-hamilton/hamilton-tools.cpp
+++ b/compat/hamilton-tools.cpp
@@ -1,28 +1,7 @@
#include "hamilton-tools.h"
#include <cmath>
-double VectorLength(const tVector v)
-{
- return(sqrt(v.v[0]*v.v[0] + v.v[1]*v.v[1] + v.v[2]*v.v[2]));
-}
-
-double sqr(const double v) { return(v*v); }
-
-double VectorDistance(const double v1[], const tVector v2)
-{
- return(sqrt(sqr(v2.v[0]-v1[0])+sqr(v2.v[1]-v1[1])+sqr(v2.v[2]-v1[2])));
-}
-
-tVector Lerp(const tVector s, const double d[], const double alpha)
-{
- tVector V;
- V.v[0] = s.v[0] + (d[0] - s.v[0]) * alpha;
- V.v[1] = s.v[1] + (d[1] - s.v[1]) * alpha;
- V.v[2] = s.v[2] + (d[2] - s.v[2]) * alpha;
- return(V);
-}
-
-tQuat QuatFromAngleAxe(const double angle, const tVector axe)
+tQuat QuatFromAngleAxe(const double angle, const tVector& axe)
{
double a = TO_RAD * 0.5 * angle;
double d = sin(a) / VectorLength(axe);
@@ -35,7 +14,7 @@ tQuat QuatFromAngleAxe(const double angle, const tVector axe)
);
}
-tQuat QuatMultiply(const tQuat qL, const tQuat qR)
+tQuat QuatMultiply(const tQuat& qL, const tQuat& qR)
{
tQuat Q;
Q.x = qL.w*qR.x + qL.x*qR.w + qL.y*qR.z - qL.z*qR.y;
@@ -45,11 +24,6 @@ tQuat QuatMultiply(const tQuat qL, const tQuat qR)
return(Q);
}
-double AngleBetween(const tQuat S, const tQuat D)
-{
- return( TO_DEG * 2*acos(fabs(S.x*D.x + S.y*D.y + S.z*D.z + S.w*D.w)) );
-}
-
tQuat QuatFromYPR(const double YPR[])
{
tQuat Q, Qp, Qy;
@@ -61,7 +35,7 @@ tQuat QuatFromYPR(const double YPR[])
return(QuatMultiply(Qy, Q));
}
-void Normalize(tQuat Q)
+void Normalize(tQuat& Q)
{
double m = sqrt(Q.x*Q.x + Q.y*Q.y + Q.z*Q.z + Q.w*Q.w);
if (m > EPSILON)
@@ -75,7 +49,7 @@ void Normalize(tQuat Q)
else Q = tQuat(0, 0, 0, 1);
}
-tQuat Slerp(const tQuat S, const tQuat D, const double alpha)
+tQuat Slerp(const tQuat& S, const tQuat& D, const double alpha)
{
// calc cosine of half angle
double cosin = S.x*D.x + S.y*D.y + S.z*D.z + S.w*D.w;
@@ -117,7 +91,7 @@ tQuat Slerp(const tQuat S, const tQuat D, const double alpha)
return( Q );
}
-void QuatToYPR(const tQuat Q, double YPR[])
+void QuatToYPR(const tQuat& Q, double YPR[])
{
const double xx = Q.x * Q.x;
const double xy = Q.x * Q.y;
diff --git a/compat/hamilton-tools.h b/compat/hamilton-tools.h
new file mode 100644
index 00000000..885e9f79
--- /dev/null
+++ b/compat/hamilton-tools.h
@@ -0,0 +1,76 @@
+#pragma once
+
+#include <algorithm>
+#include "export.hpp"
+#include "compat/math.hpp"
+
+constexpr double TO_RAD = (M_PI / 180);
+constexpr double TO_DEG = (180 / M_PI);
+constexpr double EPSILON = 1e-30;
+
+struct tVector
+{
+ double v[3];
+ tVector(double X = 0, double Y = 0, double Z = 0) {v[0]=X; v[1]=Y; v[2]=Z;}
+ tVector(const double V[]) {v[0]=V[0]; v[1]=V[1]; v[2]=V[2];}
+
+ void operator=(const tVector& other)
+ {
+ std::copy(other.v, other.v + 3, v);
+ }
+ inline const tVector operator+(const tVector& other) const
+ {
+ return tVector(v[0] + other.v[0], v[1] + other.v[1], v[2] + other.v[2]);
+ }
+ void operator+=(const tVector& other)
+ {
+ v[0] += other.v[0];
+ v[1] += other.v[1];
+ v[2] += other.v[2];
+ }
+ const tVector operator-(const tVector& other) const
+ {
+ return tVector(v[0] - other.v[0], v[1] - other.v[1], v[2] - other.v[2]);
+ }
+ void operator-=(const tVector& other)
+ {
+ v[0] -= other.v[0];
+ v[1] -= other.v[1];
+ v[2] -= other.v[2];
+ }
+ const tVector operator*(double scalar) const
+ {
+ return tVector(v[0] * scalar, v[1] * scalar, v[2] * scalar);
+ }
+ void operator*=(double scalar)
+ {
+ v[0] *= scalar;
+ v[1] *= scalar;
+ v[2] *= scalar;
+ }
+ const tVector operator/(double scalar) const
+ {
+ return *this * (1.0 / scalar);
+ }
+ void operator/= (double scalar)
+ {
+ *this *= 1.0 / scalar;
+ }
+};
+
+struct tQuat
+{
+ double x, y, z, w;
+ tQuat(double X = 0, double Y = 0, double Z = 0, double W = 1)
+ {x = X; y = Y; z = Z; w = W;}
+};
+
+inline double VectorLength(const tVector& v) { return sqrt(v.v[0] * v.v[0] + v.v[1] * v.v[1] + v.v[2] * v.v[2]); }
+inline double VectorDistance(const tVector& v1, const tVector& v2) { return VectorLength(v2 - v1); }
+inline tVector Lerp(const tVector& s, const tVector& d, const double alpha) { return s + (d - s) * alpha; }
+tQuat OTR_COMPAT_EXPORT QuatFromYPR(const double YPR[]);
+tQuat OTR_COMPAT_EXPORT QuatMultiply(const tQuat& qL, const tQuat& qR);
+inline tQuat QuatDivide(const tQuat& qL, const tQuat& qR) { return QuatMultiply(qL, tQuat(-qR.x, -qR.y, -qR.z, qR.w)); }
+inline double AngleBetween(const tQuat& S, const tQuat& D) { return TO_DEG * 2 * acos(fabs(S.x * D.x + S.y * D.y + S.z * D.z + S.w * D.w)); }
+tQuat OTR_COMPAT_EXPORT Slerp(const tQuat& S, const tQuat& D, const double alpha);
+void OTR_COMPAT_EXPORT QuatToYPR(const tQuat& Q, double YPR[]);
diff --git a/ext-falcon-bms-linear-acc/lang/nl_NL.ts b/compat/lang/de_DE.ts
index 9e739505..1552582e 100644
--- a/ext-falcon-bms-linear-acc/lang/nl_NL.ts
+++ b/compat/lang/de_DE.ts
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1" language="nl_NL">
+<TS version="2.1" language="de_DE">
</TS>
diff --git a/compat/lang/zh_CN.ts b/compat/lang/zh_CN.ts
index 6401616d..e5ca8aa9 100644
--- a/compat/lang/zh_CN.ts
+++ b/compat/lang/zh_CN.ts
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
</TS>
diff --git a/compat/macros1.h b/compat/macros.h
index 2ebfc4df..7de7fb86 100644
--- a/compat/macros1.h
+++ b/compat/macros.h
@@ -9,7 +9,7 @@
#if defined _MSC_VER
# define force_inline __forceinline
#else
-# define force_inline __attribute__((always_inline))
+# define force_inline __attribute__((always_inline)) inline
#endif
#if !defined likely
@@ -38,5 +38,20 @@
# define progn(...) ([&]() -> decltype(auto) { __VA_ARGS__ }())
# define eval_once2(expr, ctr) eval_once3(expr, ctr)
# define eval_once3(expr, ctr) ([&] { [[maybe_unused]] static const char init_ ## ctr = ((void)(expr), 0); }())
-# define eval_once(expr) eval_once2(expr, __COUNTER__)
+# ifdef QT_NO_DEBUG_OUTPUT
+# define eval_once(expr) void()
+# else
+# define eval_once(expr) eval_once2(expr, __COUNTER__)
+# endif
+
+#define OTR_DISABLE_MOVE_COPY(type) \
+ type(const type&) = delete; \
+ type(type&&) = delete; \
+ type& operator=(const type&) = delete; \
+ type& operator=(type&&) = delete
+#endif
+
+#ifdef _MSC_VER
+# define strncasecmp _strnicmp
+# define strcasecmp _stricmp
#endif
diff --git a/compat/macros.hpp b/compat/macros.hpp
deleted file mode 100644
index 497933cf..00000000
--- a/compat/macros.hpp
+++ /dev/null
@@ -1,25 +0,0 @@
-#pragma once
-
-#include "macros1.h"
-
-#include <utility>
-#include <type_traits>
-
-// before C++20
-namespace cxx20_compat {
- template<typename T>
- struct remove_cvref {
- using type = std::remove_cv_t<std::remove_reference_t<T>>;
- };
-} // ns cxx20_compat
-
-template<typename t>
-using remove_cvref_t = typename cxx20_compat::remove_cvref<t>::type;
-
-template<typename t>
-using to_const_ref_t = std::add_lvalue_reference_t<std::add_const_t<remove_cvref_t<t>>>;
-
-template<typename t>
-using cv_qualified = std::conditional_t<std::is_fundamental_v<remove_cvref_t<t>>,
- remove_cvref_t<t>,
- to_const_ref_t<t>>;
diff --git a/compat/math.hpp b/compat/math.hpp
index 34428cbd..c5981cc2 100644
--- a/compat/math.hpp
+++ b/compat/math.hpp
@@ -1,72 +1,15 @@
#pragma once
-#include "macros.hpp"
-
#include <cmath>
#include <type_traits>
-namespace util_detail {
-
-template<typename n>
-inline auto clamp_float(n val, n min_, n max_)
-{
- return std::fmin(std::fmax(val, min_), max_);
-}
-
-template<typename t>
-struct clamp final
-{
- static inline auto clamp_(t val, t min_, t max_)
- {
- if (unlikely(val > max_))
- return max_;
- if (unlikely(val < min_))
- return min_;
- return val;
- }
-};
-
-template<>
-struct clamp<float>
-{
- static inline auto clamp_(float val, float min_, float max_)
- {
- return clamp_float(val, min_, max_);
- }
-};
-
-template<>
-struct clamp<double>
-{
- static inline auto clamp_(double val, double min_, double max_)
- {
- return clamp_float(val, min_, max_);
- }
-};
-
-} // ns util_detail
-
-template<typename t, typename u, typename v>
-inline auto clamp(const t& val, const u& min, const v& max)
-{
- using w = cv_qualified<decltype(val + min + max)>;
- return util_detail::clamp<w>::clamp_(val, min, max);
-}
-
template<typename t>
-inline auto iround(t val) -> std::enable_if_t<std::is_floating_point_v<remove_cvref_t<t>>, int>
+inline auto iround(t val) -> std::enable_if_t<std::is_floating_point_v<std::decay_t<t>>, int>
{
return (int)std::round(val);
}
-template<typename t>
-inline auto uround(t val) -> std::enable_if_t<std::is_floating_point_v<remove_cvref_t<t>>, unsigned>
-{
- return (unsigned)std::fmax(0, std::round(val));
-}
-
template <typename t>
-force_inline
constexpr int signum(const t& x)
{
return x < t{0} ? -1 : 1;
diff --git a/compat/mutex.cpp b/compat/mutex.cpp
index 664677ea..71f42329 100644
--- a/compat/mutex.cpp
+++ b/compat/mutex.cpp
@@ -1,33 +1,31 @@
+#include "export.hpp"
#include "mutex.hpp"
#include <cstdlib>
+#include <QMutex>
+#include <QRecursiveMutex>
-mutex& mutex::operator=(const mutex& rhs)
+template<typename M>
+mutex<M>& mutex<M>::operator=(const mutex& rhs)
{
- if (rhs->isRecursive() != inner.isRecursive())
- std::abort();
-
return *this;
}
-mutex::mutex(const mutex& datum) : mutex{datum.inner.isRecursive() ? Recursive : NonRecursive}
-{
-}
+template<typename MutexType> MutexType* mutex<MutexType>::operator&() const { return &inner; }
-mutex::mutex(RecursionMode m) : inner{m}
-{
-}
+template<typename M> mutex<M>::mutex(const mutex<M>& datum) {}
+template<typename M> mutex<M>::mutex() = default;
-QMutex* mutex::operator&() const noexcept
+template<typename M>
+mutex<M>::operator M*() const noexcept
{
return &inner;
}
-mutex::operator QMutex*() const noexcept
+template<typename M>
+M* mutex<M>::operator->() const noexcept
{
return &inner;
}
-QMutex* mutex::operator->() const noexcept
-{
- return &inner;
-}
+template class OTR_COMPAT_EXPORT mutex<QMutex>;
+template class OTR_COMPAT_EXPORT mutex<QRecursiveMutex>;
diff --git a/compat/mutex.hpp b/compat/mutex.hpp
index 54758a08..6ba4fa8c 100644
--- a/compat/mutex.hpp
+++ b/compat/mutex.hpp
@@ -1,24 +1,18 @@
#pragma once
-#include <QMutex>
-
#include "export.hpp"
+template<typename MutexType>
class OTR_COMPAT_EXPORT mutex
{
- mutable QMutex inner;
+ mutable MutexType inner{};
public:
- using RecursionMode = QMutex::RecursionMode;
- static constexpr RecursionMode Recursive = RecursionMode::Recursive;
- static constexpr RecursionMode NonRecursive = RecursionMode::NonRecursive;
-
mutex& operator=(const mutex& datum);
+ MutexType* operator&() const;
mutex(const mutex& datum);
- explicit mutex(RecursionMode m);
- mutex() : mutex{NonRecursive} {}
+ explicit mutex();
- QMutex* operator&() const noexcept;
- explicit operator QMutex*() const noexcept;
- QMutex* operator->() const noexcept;
+ explicit operator MutexType*() const noexcept;
+ MutexType* operator->() const noexcept;
};
diff --git a/compat/process-list.cpp b/compat/process-list.cpp
new file mode 100644
index 00000000..34c83b06
--- /dev/null
+++ b/compat/process-list.cpp
@@ -0,0 +1,195 @@
+#include "process-list.hpp"
+
+#include <vector>
+#include <QStringList>
+#include <QDebug>
+
+#ifdef _WIN32
+
+#include <windows.h>
+#include <tlhelp32.h>
+
+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(QString{e.szExeFile});
+ } while (Process32Next(h, &e) == TRUE);
+
+ CloseHandle(h);
+
+ return ret;
+}
+
+#elif defined __APPLE__
+
+#include <sys/sysctl.h>
+#include <libproc.h>
+
+QStringList get_all_executable_names()
+{
+ QStringList ret; ret.reserve(512);
+ QList<int> vec; vec.reserve(512);
+
+ 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 == QStringLiteral("wine.bin") || tmp == QStringLiteral("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 <cerrno>
+
+#ifdef OTR_HAS_LIBPROC2
+#include <libproc2/pids.h>
+QStringList get_all_executable_names()
+{
+ QStringList ret;
+ enum pids_item items[] = { PIDS_ID_PID, PIDS_CMD, PIDS_CMDLINE_V };
+
+ enum rel_items { rel_pid, rel_cmd, rel_cmdline };
+ struct pids_info *info = NULL;
+ struct pids_stack *stack;
+ QString tmp; tmp.reserve(64);
+
+ procps_pids_new(&info, items, 3);
+
+ while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY)))
+ {
+ char **p_cmdline = PIDS_VAL(rel_cmdline, strv, stack, info);
+
+ // note, wine sets argv[0] so no parsing like in OSX case
+ if (p_cmdline && p_cmdline[0] && p_cmdline[0][0] &&
+ !(p_cmdline[0][0] == '-' && !p_cmdline[0][1]))
+ {
+ tmp = QString{p_cmdline[0]};
+ const int idx = std::max(tmp.lastIndexOf('\\'), tmp.lastIndexOf('/'));
+ if (idx != -1)
+ tmp = tmp.mid(idx+1);
+ //qDebug() << "procps" << tmp;
+ ret.append(tmp);
+ }
+ }
+ //qDebug() << "-- procps end";
+
+ procps_pids_unref(&info);
+
+ return ret;
+}
+#else
+#include <proc/readproc.h>
+#include <cerrno>
+
+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;
+}
+#endif
+
+#else
+QStringList get_all_executable_names() { return {}; }
+
+#endif
diff --git a/compat/process-list.hpp b/compat/process-list.hpp
index 782dc0a4..323829bf 100644
--- a/compat/process-list.hpp
+++ b/compat/process-list.hpp
@@ -7,164 +7,7 @@
#pragma once
-#include <QDebug>
+#include "export.hpp"
#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 == QStringLiteral("wine.bin") || tmp == QStringLiteral("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>
-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
+OTR_COMPAT_EXPORT QStringList get_all_executable_names();
diff --git a/compat/qt-dpi.hpp b/compat/qt-dpi.hpp
index 01659c8a..fc17a062 100644
--- a/compat/qt-dpi.hpp
+++ b/compat/qt-dpi.hpp
@@ -3,17 +3,22 @@
#include <algorithm>
#include <QWidget>
+static inline double screen_dpi(const QPaintDevice* widget)
+{
+ const auto& x = *widget;
+#ifdef _WIN32
+ return std::max(x.devicePixelRatioF(), 1.);
+#else
+ return std::max(std::max(x.logicalDpiX()/(double)x.physicalDpiX(), x.devicePixelRatioF()), 1.);
+#endif
+}
+
template<typename self>
struct screen_dpi_mixin
{
protected:
double screen_dpi() const
{
- const auto& x = *static_cast<const self*>(this);
-#ifdef _WIN32
- return std::max(x.devicePixelRatioF(), 1.);
-#else
- return std::max(std::max(x.logicalDpiX()/(double)x.physicalDpiX(), x.devicePixelRatioF()), 1.);
-#endif
+ return ::screen_dpi(static_cast<const self*>(this));
}
};
diff --git a/compat/qt-signal.cpp b/compat/qt-signal.cpp
index 08aac663..8c23866b 100644
--- a/compat/qt-signal.cpp
+++ b/compat/qt-signal.cpp
@@ -1,13 +1,7 @@
+#define OTR_GENERATE_SIGNAL3(type) void sig_##type::operator()(const type& x) const { notify(x); }
#include "qt-signal.hpp"
+namespace _qt_sig_impl {
-namespace qt_sig {
+sig_void::sig_void(QObject* parent) : QObject(parent) {}
-nullary::nullary(QObject* parent) : QObject(parent) {}
-nullary::~nullary() = default;
-
-void nullary::operator()() const
-{
- notify();
}
-
-} // ns qt_sig
diff --git a/compat/qt-signal.hpp b/compat/qt-signal.hpp
index 119e063c..92ab7e6f 100644
--- a/compat/qt-signal.hpp
+++ b/compat/qt-signal.hpp
@@ -4,27 +4,66 @@
#include "export.hpp"
#include <QObject>
+#include <QList>
+#include <QPointF>
+#include <QVariant>
-namespace qt_sig {
+namespace _qt_sig_impl {
-class OTR_COMPAT_EXPORT nullary : public QObject
+template<typename t> struct sig;
+
+class OTR_COMPAT_EXPORT sig_void final : public QObject
{
Q_OBJECT
public:
template<typename t, typename F>
- nullary(t* datum, F&& f, Qt::ConnectionType conntype = Qt::AutoConnection) : QObject(datum)
+ sig_void(t* datum, F&& f, Qt::ConnectionType conntype = Qt::AutoConnection) : QObject(datum)
{
- connect(this, &nullary::notify, datum, f, conntype);
+ connect(this, &sig_void::notify, datum, f, conntype);
}
-
- nullary(QObject* parent = nullptr);
- ~nullary() override;
-
- void operator()() const;
+ explicit sig_void(QObject* parent = nullptr);
+ void operator()() const { notify(); }
signals:
void notify() const;
};
-} // ns qt_sig
+template<> struct sig<void> { using t = sig_void; };
+
+#ifndef OTR_GENERATE_SIGNAL3
+# define OTR_GENERATE_SIGNAL3(x)
+#endif
+#define OTR_GENERATE_SIGNAL2(type) \
+ class OTR_COMPAT_EXPORT sig_##type final : public QObject \
+ { \
+ Q_OBJECT \
+ public: \
+ explicit sig_##type(QObject* parent = nullptr) : QObject(parent) {} \
+ void operator()(const type& x) const; \
+ Q_SIGNALS: \
+ void notify(const type& x) const; \
+ }; \
+ OTR_GENERATE_SIGNAL3(type)
+
+# define OTR_GENERATE_SIGNAL(type) \
+ OTR_GENERATE_SIGNAL2(type); \
+ using qlist##type = QList<type>; \
+ OTR_GENERATE_SIGNAL2(qlist##type); \
+ template<> struct sig<type> { using t = sig_##type; }; \
+ template<> struct sig<qlist##type> { using t = qlist##type; }
+
+OTR_GENERATE_SIGNAL(int);
+OTR_GENERATE_SIGNAL(double);
+OTR_GENERATE_SIGNAL(float);
+OTR_GENERATE_SIGNAL(bool);
+OTR_GENERATE_SIGNAL(QString);
+OTR_GENERATE_SIGNAL(QPointF);
+OTR_GENERATE_SIGNAL(QVariant);
+
+} // namespace _qt_sig_impl
+
+#undef OTR_GENERATE_SIGNAL2
+#undef OTR_GENERATE_SIGNAL
+
+template<typename t> using qt_signal = typename _qt_sig_impl::sig<t>::t;
diff --git a/compat/run-in-thread.hpp b/compat/run-in-thread.hpp
index b8ffc179..c552c600 100644
--- a/compat/run-in-thread.hpp
+++ b/compat/run-in-thread.hpp
@@ -7,9 +7,6 @@
* copyright notice and this permission notice appear in all copies.
*/
-#include "macros.hpp"
-
-#include <cassert>
#include <thread>
#include <condition_variable>
#include <utility>
@@ -17,99 +14,56 @@
#include <QObject>
#include <QThread>
-namespace qt_impl_detail {
-
-template<typename t>
-struct run_in_thread_traits
+namespace impl_run_in_thread {
+struct semaphore final
{
- using type = t;
- using ret_type = t;
- static inline void assign(t& lvalue, const t& rvalue) { lvalue = rvalue; }
- static inline t pass(const t& val) { return val; }
- template<typename F> static inline t call(F&& fun) { return std::move(fun()); }
-};
+ using lock_guard = std::unique_lock<std::mutex>;
+ std::mutex mtx;
+ std::condition_variable cvar;
+ bool flag = false;
-template<typename u>
-struct run_in_thread_traits<u&&>
-{
- using t = typename std::remove_reference<u>::type;
- using type = t;
- using ret_type = u;
- static inline void assign(t& lvalue, t&& rvalue) { lvalue = rvalue; }
- static inline t&& pass(t&& val) { return val; }
- template<typename F> static inline t&& call(F&& fun) { return std::move(fun()); }
-};
+ semaphore() = default;
-template<>
-struct run_in_thread_traits<void>
-{
- using type = unsigned char;
- using ret_type = void;
- static inline void assign(unsigned char&, unsigned char&&) {}
- static inline void pass(type&&) {}
- template<typename F> static type call(F&& fun) { fun(); return type(0); }
-};
+ void wait()
+ {
+ lock_guard guard(mtx);
+ while (!flag)
+ cvar.wait(guard);
+ }
+ void notify()
+ {
+ lock_guard guard(mtx);
+ flag = true;
+ cvar.notify_one();
+ }
+};
}
template<typename F>
-auto never_inline
-run_in_thread_sync(QObject* obj, F&& fun)
- -> typename qt_impl_detail::run_in_thread_traits<decltype(fun())>::ret_type
+void run_in_thread_sync(QObject* obj, F&& fun)
{
- using lock_guard = std::unique_lock<std::mutex>;
+ if (obj->thread() == QThread::currentThread())
+ return (void)fun();
- using traits = qt_impl_detail::run_in_thread_traits<decltype(fun())>;
-
- typename traits::type ret;
-
- struct semaphore final
- {
- std::mutex mtx;
- std::condition_variable cvar;
- bool flag;
-
- semaphore() : flag(false) {}
-
- void wait()
- {
- lock_guard guard(mtx);
- while (!flag)
- cvar.wait(guard);
- }
-
- void notify()
- {
- lock_guard guard(mtx);
- flag = true;
- cvar.notify_one();
- }
- };
-
- semaphore sem;
+ impl_run_in_thread::semaphore sem;
{
QObject src;
- QObject::connect(&src,
- &QObject::destroyed,
- obj,
- [&] {
- traits::assign(ret, traits::call(fun));
- sem.notify();
- },
- Qt::AutoConnection);
+ QObject::connect(&src, &QObject::destroyed,
+ obj, [&] { fun(); sem.notify(); },
+ Qt::QueuedConnection);
}
sem.wait();
- return traits::pass(std::move(ret));
}
template<typename F>
void run_in_thread_async(QObject* obj, F&& fun)
{
+ if (obj->thread() == QThread::currentThread())
+ return (void)fun();
+
QObject src;
- QThread* t = obj->thread();
- if (!t) abort();
- src.moveToThread(t);
- QObject::connect(&src, &QObject::destroyed, obj, std::move(fun), Qt::AutoConnection);
+ QObject::connect(&src, &QObject::destroyed, obj, std::forward<F>(fun), Qt::QueuedConnection);
}
diff --git a/compat/shm.cpp b/compat/shm.cpp
index f59469dc..341de40f 100644
--- a/compat/shm.cpp
+++ b/compat/shm.cpp
@@ -17,7 +17,7 @@
#ifdef QT_CORE_LIB
# include <QDebug>
-# define warn(str, ...) (qDebug() << "shm:" str ": " << __VA_ARGS__)
+# define warn(str, ...) (qDebug() << "shm " str ": " << __VA_ARGS__)
#else
# define warn(str, ...) (void)0
#endif
@@ -134,4 +134,3 @@ bool shm_wrapper::success()
return mem != nullptr;
#endif
}
-
diff --git a/compat/shm.h b/compat/shm.h
index d1363219..5036b335 100644
--- a/compat/shm.h
+++ b/compat/shm.h
@@ -20,7 +20,7 @@
#endif
#include "export.hpp"
-#include "macros1.h"
+#include "macros.h"
class OTR_COMPAT_EXPORT shm_wrapper final
{
@@ -39,4 +39,6 @@ public:
bool unlock();
bool success();
inline void* ptr() { return mem; }
+
+ OTR_DISABLE_MOVE_COPY(shm_wrapper);
};
diff --git a/compat/timer.cpp b/compat/timer.cpp
index f60e4f62..fdea185a 100644
--- a/compat/timer.cpp
+++ b/compat/timer.cpp
@@ -23,26 +23,24 @@ void Timer::start()
gettime(&state);
}
-struct timespec Timer::gettime_() const
+struct timespec Timer::get_delta() const
{
struct timespec ts; // NOLINT
gettime(&ts);
- unsigned long long a = ts.tv_sec, b = state.tv_sec;
- int c = ts.tv_nsec, d = state.tv_nsec;
- return { (time_t)(a - b), (long)(c - d) };
+ return { ts.tv_sec - state.tv_sec, ts.tv_nsec - state.tv_nsec };
}
// milliseconds
double Timer::elapsed_ms() const
{
- struct timespec delta = gettime_();
+ struct timespec delta = get_delta();
return delta.tv_sec * 1000 + delta.tv_nsec * 1e-6;
}
double Timer::elapsed_seconds() const
{
- struct timespec delta = gettime_();
+ struct timespec delta = get_delta();
return delta.tv_sec + delta.tv_nsec * 1e-9;
}
@@ -63,20 +61,14 @@ static auto otr_get_clock_frequency()
void Timer::gettime(timespec* state)
{
- static const unsigned long long freq = otr_get_clock_frequency();
+ static const auto freq = otr_get_clock_frequency();
LARGE_INTEGER d;
BOOL ret = QueryPerformanceCounter(&d);
assert(ret && "QueryPerformanceCounter failed");
- constexpr int usec = 1000000;
- unsigned long long tm = d.QuadPart;
- tm *= usec;
- tm /= freq;
- tm %= usec;
- tm *= 1000;
-
- state->tv_sec = (time_t)((unsigned long long)d.QuadPart/freq);
- state->tv_nsec = (long)tm;
+ constexpr int nsec = 1'000'000 * 1000;
+ state->tv_sec = (time_t)(d.QuadPart/freq);
+ state->tv_nsec = (decltype(state->tv_nsec))(d.QuadPart % freq * nsec / freq);
}
#elif defined __MACH__
diff --git a/compat/timer.hpp b/compat/timer.hpp
index f7791a1a..47072226 100644
--- a/compat/timer.hpp
+++ b/compat/timer.hpp
@@ -23,6 +23,6 @@ struct OTR_COMPAT_EXPORT Timer final
private:
struct timespec state {};
static void gettime(struct timespec* state);
- struct timespec gettime_() const;
+ struct timespec get_delta() const;
using ns = time_units::ns;
};
diff --git a/contrib-noinst/important-stuff/game-data.exe b/contrib-noinst/important-stuff/game-data.exe
new file mode 100644
index 00000000..4e43a540
--- /dev/null
+++ b/contrib-noinst/important-stuff/game-data.exe
Binary files differ
diff --git a/contrib-noinst/important-stuff/game_data.c b/contrib-noinst/important-stuff/game_data.c
index 48774187..4947fc61 100644
--- a/contrib-noinst/important-stuff/game_data.c
+++ b/contrib-noinst/important-stuff/game_data.c
@@ -2,6 +2,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <mxml.h>
+#include <mxml-private.h>
#include <stdbool.h>
#include <stdint.h>
#include <sys/stat.h>
diff --git a/contrib/libusbK-dev-kit/libusbK-3.1.0.0-setup.exe b/contrib/libusbK-dev-kit/libusbK-3.1.0.0-setup.exe
new file mode 100644
index 00000000..17d13b16
--- /dev/null
+++ b/contrib/libusbK-dev-kit/libusbK-3.1.0.0-setup.exe
Binary files differ
diff --git a/contrib/libusbK-inf-wizard/libusbK-inf-wizard.exe b/contrib/libusbK-inf-wizard/libusbK-inf-wizard.exe
new file mode 100644
index 00000000..47c9f175
--- /dev/null
+++ b/contrib/libusbK-inf-wizard/libusbK-inf-wizard.exe
Binary files differ
diff --git a/contrib/libusbK-inf-wizard/license/AUTHORS-libusb0.txt b/contrib/libusbK-inf-wizard/license/AUTHORS-libusb0.txt
new file mode 100644
index 00000000..3ad12f72
--- /dev/null
+++ b/contrib/libusbK-inf-wizard/license/AUTHORS-libusb0.txt
@@ -0,0 +1,20 @@
+This software is distributed under the following licenses:
+Driver: GNU General Public License (GPL)
+Library: GNU Lesser General Public (LGPL)
+Test Files: GNU Lesser General Public (LGPL)
+Filter Installer: GNU Lesser General Public (LGPL)
+
+Library, Test Programs:
+
+Stephan Meyer, <ste_meyer@web.de>
+Johannes Erdfelt, <johannes@erdfelt.com>
+Thomas Sailer, <sailer@ife.ee.ethz.ch>
+
+Drivers, Installer:
+
+Stephan Meyer, <ste_meyer@web.de>
+Travis Robinson, <libusbdotnet@gmail.com>
+
+Testing, Technical support:
+
+Xiaofan Chen, <xiaofanc@gmail.com>
diff --git a/contrib/libusbK-inf-wizard/license/AUTHORS-libusbK.txt b/contrib/libusbK-inf-wizard/license/AUTHORS-libusbK.txt
new file mode 100644
index 00000000..99f27a45
--- /dev/null
+++ b/contrib/libusbK-inf-wizard/license/AUTHORS-libusbK.txt
@@ -0,0 +1,40 @@
+CONTRIBUTORS
+-----------------------------------------------------------------------
+Development : Travis Lee Robinson (libusbdotnet@gmail.com)
+Testing : Xiaofan Chen (xiaofanc@gmail.com)
+
+LICENSE
+-----------------------------------------------------------------------
+Copyright (c) 2011-2012 Travis Lee Robinson. All rights reserved.
+
+APPLICABLE FOR ALL LIBUSBK BINARIES AND SOURCE CODE UNLESS OTHERWISE
+SPECIFIED. PLEASE SEE INDIVIDUAL COMPONENTS LICENSING TERMS FOR DETAILS.
+
+NOTE: Portions of dpscat use source code from libwdi which is licensed
+for LGPL use only. (See dpscat.c)
+
+NOTE: libusbK-inf-wizard.exe is linked to libwdi which is licensed for
+LGPL use only.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of Travis Lee Robinson nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL TRAVIS ROBINSON BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/contrib/libusbK-inf-wizard/license/LICENSE-bsd.txt b/contrib/libusbK-inf-wizard/license/LICENSE-bsd.txt
new file mode 100644
index 00000000..b80c667f
--- /dev/null
+++ b/contrib/libusbK-inf-wizard/license/LICENSE-bsd.txt
@@ -0,0 +1,33 @@
+Copyright (c) 2011-2012 Travis Lee Robinson. All rights reserved.
+
+APPLICABLE FOR ALL LIBUSBK BINARIES AND SOURCE CODE UNLESS OTHERWISE
+SPECIFIED. PLEASE SEE INDIVIDUAL COMPONENTS LICENSING TERMS FOR DETAILS.
+
+NOTE: Portions of dpscat use source code from libwdi which is licensed
+for LGPL use only. (See dpscat.c)
+
+NOTE: libusbK-inf-wizard.exe is linked to libwdi which is licensed for
+LGPL use only.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of Travis Lee Robinson nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL TRAVIS ROBINSON BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/contrib/libusbK-inf-wizard/license/LICENSE-gpl3.txt b/contrib/libusbK-inf-wizard/license/LICENSE-gpl3.txt
new file mode 100644
index 00000000..eff3f9ac
--- /dev/null
+++ b/contrib/libusbK-inf-wizard/license/LICENSE-gpl3.txt
@@ -0,0 +1,686 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL TRAVIS ROBINSON BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/contrib/libusbK-inf-wizard/license/LICENSE-lgpl3.txt b/contrib/libusbK-inf-wizard/license/LICENSE-lgpl3.txt
new file mode 100644
index 00000000..65c5ca88
--- /dev/null
+++ b/contrib/libusbK-inf-wizard/license/LICENSE-lgpl3.txt
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/csv/csv.cpp b/csv/csv.cpp
index a1f62dc0..79841bea 100644
--- a/csv/csv.cpp
+++ b/csv/csv.cpp
@@ -11,84 +11,42 @@
#include "csv.h"
#include "compat/library-path.hpp"
-#include <QTextDecoder>
-#include <QFile>
+#include <utility>
+#include <cstdio>
+#include <QByteArray>
#include <QString>
+#include <QStringDecoder>
+#include <QFile>
+#include <QByteArrayView>
#include <QDebug>
-#include <utility>
-#include <algorithm>
-
-const QTextCodec* const CSV::m_codec = QTextCodec::codecForName("System");
-const QRegExp CSV::m_rx = QRegExp(QString("((?:(?:[^;\\n]*;?)|(?:\"[^\"]*\";?))*)?\\n?"));
-const QRegExp CSV::m_rx2 = QRegExp(QString("(?:\"([^\"]*)\";?)|(?:([^;]*);?)?"));
+namespace {
-CSV::CSV(QIODevice* device) :
- m_device(device),
- m_pos(0)
+void chomp(QString& str)
{
- if (m_device && m_device->isReadable())
+ if (!str.isEmpty() && str.back() == '\n')
{
- QTextDecoder dec(m_codec);
- m_string = dec.toUnicode(m_device->readAll());
+ str.chop(1);
+ if (!str.isEmpty() && str.back() == '\r')
+ str.chop(1);
}
}
-QString CSV::readLine()
+auto do_scanf(QLatin1StringView s, unsigned(&tmp)[8])
{
- QString line;
-
- if ((m_pos = m_rx.indexIn(m_string,m_pos)) != -1)
- {
- line = m_rx.cap(1);
- m_pos += m_rx.matchedLength();
- }
- else
- {
- const QChar lf(QChar::LineFeed);
-
- while (m_pos < m_string.length())
- if (m_string[m_pos++] == lf)
- break;
- }
- return line;
-}
-
-bool CSV::parseLine(QStringList& ret)
-{
- QString line(readLine());
-
- QStringList list;
- int pos2 = 0;
+ unsigned fuzz[3];
+ return std::sscanf(s.constData(),
+ "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
+ fuzz + 2,
+ fuzz + 0,
+ tmp + 3, tmp + 2, tmp + 1, tmp + 0,
+ tmp + 7, tmp + 6, tmp + 5, tmp + 4,
+ fuzz + 1);
+};
- if (line.size() == 0)
- {
- ret = QStringList();
- return m_device->size() > m_pos;
- }
- else
- {
- while (line.size() > pos2 && (pos2 = m_rx2.indexIn(line, pos2)) != -1)
- {
- QString col;
- if (m_rx2.cap(1).size() > 0)
- col = m_rx2.cap(1);
- else if (m_rx2.cap(2).size() > 0)
- col = m_rx2.cap(2);
-
- list << col;
-
- if (col.size())
- pos2 += m_rx2.matchedLength();
- else
- pos2++;
- }
- }
- ret = std::move(list);
- return true;
}
-bool CSV::getGameData(int id, unsigned char* table, QString& gamename)
+bool getGameData(int id, unsigned char* table, QString& gamename)
{
for (int i = 0; i < 8; i++)
table[i] = 0;
@@ -96,28 +54,34 @@ bool CSV::getGameData(int id, unsigned char* table, QString& gamename)
if (id != 0)
qDebug() << "csv: lookup game id" << id;
- QString id_str(QString::number(id));
-
static const QString csv_path(OPENTRACK_BASE_PATH +
OPENTRACK_DOC_PATH "settings/facetracknoir supported games.csv");
+ auto id_str = QString::number(id);
QFile file(csv_path);
-
- if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
+ if (!file.open(QIODevice::ReadOnly))
{
qDebug() << "csv: can't open game list for freetrack protocol!";
return false;
}
+ QStringDecoder decoder{QStringConverter::Encoding::Utf8};
+ QStringList gameLine; gameLine.reserve(8);
+ unsigned lineno = 0;
+ // TODO QIODevice::readLineInto() is Qt 6.9 - sh 20250515
+ char buf[256];
- CSV csv(&file);
+ while (auto sz = file.readLine(buf, sizeof(buf)))
+ {
+ QString line = decoder.decode(QByteArrayView{buf, sz});
- unsigned tmp[8];
- unsigned fuzz[3];
+ if (line.isEmpty())
+ break;
+ chomp(line);
+ if (line.isEmpty())
+ continue;
- QStringList gameLine;
+ gameLine = line.split(';', Qt::SplitBehaviorFlags::KeepEmptyParts);
- for (int lineno = 0; csv.parseLine(gameLine); lineno++)
- {
//qDebug() << "Column 0: " << gameLine.at(0); // No.
//qDebug() << "Column 1: " << gameLine.at(1); // Game Name
//qDebug() << "Column 2: " << gameLine.at(2); // Game Protocol
@@ -127,28 +91,18 @@ bool CSV::getGameData(int id, unsigned char* table, QString& gamename)
//qDebug() << "Column 6: " << gameLine.at(6); // International ID
//qDebug() << "Column 7: " << gameLine.at(7); // FaceTrackNoIR ID
- if (gameLine.count() == 8)
+ if (gameLine.size() == 8)
{
- if (gameLine.at(6).compare(id_str, Qt::CaseInsensitive) == 0)
+ if (gameLine[6] == id_str)
{
const QString& proto = gameLine[3];
QString& name = gameLine[1];
-
const QByteArray id_cstr = gameLine[7].toLatin1();
-
- auto do_scanf = [&]() {
- return sscanf(id_cstr.constData(),
- "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
- fuzz + 2,
- fuzz + 0,
- tmp + 3, tmp + 2, tmp + 1, tmp + 0,
- tmp + 7, tmp + 6, tmp + 5, tmp + 4,
- fuzz + 1);
- };
+ unsigned tmp[8] {};
if (proto == QStringLiteral("V160") || id_cstr.length() != 22)
(void)0;
- else if (id_cstr.length() != 22 || do_scanf() != 11)
+ else if (id_cstr.length() != 22 || do_scanf(QLatin1StringView(id_cstr), tmp) != 11)
qDebug() << "scanf failed" << lineno;
else
{
@@ -161,7 +115,9 @@ bool CSV::getGameData(int id, unsigned char* table, QString& gamename)
}
}
else
- qDebug() << "malformed csv line" << lineno;
+ qDebug() << "csv wrong column count" << gameLine.size();
+
+ lineno++;
}
if (id)
diff --git a/csv/csv.h b/csv/csv.h
index 9e72b2bb..15f279d6 100644
--- a/csv/csv.h
+++ b/csv/csv.h
@@ -1,26 +1,5 @@
#pragma once
-#include <QtGlobal>
-#include <QObject>
-#include <QStringList>
-#include <QIODevice>
-#include <QTextCodec>
-#include <QRegExp>
-#include <QtGlobal>
-class CSV
-{
-public:
- QString readLine();
- bool parseLine(QStringList& ret);
+#include <QString>
- static bool getGameData(int gameID, unsigned char* table, QString& gamename);
-private:
- CSV(QIODevice* device);
-
- QIODevice* m_device;
- QString m_string;
- int m_pos;
-
- static QTextCodec const* const m_codec;
- static const QRegExp m_rx, m_rx2;
-};
+bool getGameData(int gameID, unsigned char* table, QString& gamename);
diff --git a/ext-falcon-bms-linear-acc/lang/ru_RU.ts b/csv/lang/de_DE.ts
index f62cf2e1..1552582e 100644
--- a/ext-falcon-bms-linear-acc/lang/ru_RU.ts
+++ b/csv/lang/de_DE.ts
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1" language="ru_RU">
+<TS version="2.1" language="de_DE">
</TS>
diff --git a/csv/lang/zh_CN.ts b/csv/lang/zh_CN.ts
index 6401616d..e5ca8aa9 100644
--- a/csv/lang/zh_CN.ts
+++ b/csv/lang/zh_CN.ts
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
</TS>
diff --git a/cv/CMakeLists.txt b/cv/CMakeLists.txt
index d5f33d44..3a8798cc 100644
--- a/cv/CMakeLists.txt
+++ b/cv/CMakeLists.txt
@@ -4,7 +4,7 @@ if(OpenCV_FOUND)
try_compile(cv_use-ipp "${CMAKE_CURRENT_BINARY_DIR}"
SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/ocv-check.cxx"
CMAKE_FLAGS "-DINCLUDE_DIRECTORIES=${OpenCV_INCLUDE_DIRS}"
- "-DCXX_STANDARD=17" "-DCXX_STANDARD_REQUIRED=1"
+ "-DCXX_STANDARD=20" "-DCXX_STANDARD_REQUIRED=1"
OUTPUT_VARIABLE krap)
otr_module(cv STATIC)
target_link_libraries(${self} opencv_core opentrack-video)
diff --git a/cv/affine.hpp b/cv/affine.hpp
index 882a1145..4640e24e 100644
--- a/cv/affine.hpp
+++ b/cv/affine.hpp
@@ -7,7 +7,6 @@
#pragma once
-#include <opencv2/core.hpp>
#include "numeric.hpp"
namespace affine_impl {
diff --git a/cv/init.cpp b/cv/init.cpp
index c9c4650f..d883365b 100644
--- a/cv/init.cpp
+++ b/cv/init.cpp
@@ -1,6 +1,7 @@
#include "init.hpp"
#include <type_traits>
-#include <opencv2/core.hpp>
+#include <opencv2/core/base.hpp>
+#include <opencv2/core/utility.hpp>
[[noreturn]]
static
diff --git a/ext-falcon-bms-linear-acc/lang/stub.ts b/cv/lang/de_DE.ts
index 6401616d..1552582e 100644
--- a/ext-falcon-bms-linear-acc/lang/stub.ts
+++ b/cv/lang/de_DE.ts
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="de_DE">
</TS>
diff --git a/cv/lang/zh_CN.ts b/cv/lang/zh_CN.ts
index 6401616d..e5ca8aa9 100644
--- a/cv/lang/zh_CN.ts
+++ b/cv/lang/zh_CN.ts
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
</TS>
diff --git a/cv/numeric.hpp b/cv/numeric.hpp
index ce8f7e82..25ee078b 100644
--- a/cv/numeric.hpp
+++ b/cv/numeric.hpp
@@ -1,10 +1,10 @@
#pragma once
#include <type_traits>
-#include <opencv2/core.hpp>
+#include <opencv2/core/matx.hpp>
namespace numeric_types {
- using f = float;
+ using f = double;
static_assert(std::is_floating_point_v<f>);
diff --git a/cv/translation-calibrator.cpp b/cv/translation-calibrator.cpp
index dd520ce6..6fb3e638 100644
--- a/cv/translation-calibrator.cpp
+++ b/cv/translation-calibrator.cpp
@@ -8,6 +8,8 @@
#include "translation-calibrator.hpp"
#include "compat/euler.hpp"
#include "compat/math.hpp"
+#include "compat/macros.h"
+#include <opencv2/core.hpp>
#include <tuple>
diff --git a/cv/translation-calibrator.hpp b/cv/translation-calibrator.hpp
index 406edb72..3912c938 100644
--- a/cv/translation-calibrator.hpp
+++ b/cv/translation-calibrator.hpp
@@ -7,7 +7,7 @@
#pragma once
-#include <opencv2/core.hpp>
+#include <opencv2/core/matx.hpp>
#include <vector>
//-----------------------------------------------------------------------------
diff --git a/cv/video-widget.cpp b/cv/video-widget.cpp
index 289b9cee..7accd275 100644
--- a/cv/video-widget.cpp
+++ b/cv/video-widget.cpp
@@ -1,5 +1,5 @@
#include "video-widget.hpp"
-
+#include "compat/macros.h"
#include <opencv2/imgproc.hpp>
void cv_video_widget::update_image(const cv::Mat& frame)
@@ -13,15 +13,8 @@ void cv_video_widget::update_image(const cv::Mat& frame)
return;
cv::Mat const* __restrict scaled = nullptr;
-
- if (frame3.cols != W || frame3.rows != H)
- {
- frame3 = cv::Mat(H, W, frame.type());
- frame2 = cv::Mat(H, W, CV_8UC4);
-
- if (!frame2.isContinuous() || !frame3.isContinuous())
- std::abort();
- }
+ frame3.create(H, W, frame.type());
+ frame2.create(H, W, CV_8UC4);
if (frame.cols != W || frame.rows != H)
{
@@ -37,7 +30,6 @@ void cv_video_widget::update_image(const cv::Mat& frame)
scaled = &frame;
int color_cvt = cv::COLOR_COLORCVT_MAX;
- constexpr int nchannels = 4;
switch (scaled->channels())
{
@@ -47,7 +39,7 @@ void cv_video_widget::update_image(const cv::Mat& frame)
case 3:
color_cvt = cv::COLOR_BGR2BGRA;
break;
- case nchannels:
+ case 4:
break;
default:
unreachable();
diff --git a/cv/video-widget.hpp b/cv/video-widget.hpp
index 54316d32..24f8d7f3 100644
--- a/cv/video-widget.hpp
+++ b/cv/video-widget.hpp
@@ -8,7 +8,7 @@
#pragma once
#include "video/video-widget.hpp"
-#include <opencv2/core.hpp>
+#include <opencv2/core/mat.hpp>
struct cv_video_widget final : video_widget
{
diff --git a/dinput/dinput.cpp b/dinput/dinput.cpp
index 31de40e7..b9713b8a 100644
--- a/dinput/dinput.cpp
+++ b/dinput/dinput.cpp
@@ -1,10 +1,11 @@
#undef NDEBUG
#include "dinput.hpp"
-#include "compat/macros.hpp"
+#include "compat/macros.h"
#include <cassert>
#include <cstdlib>
+#include <dinput.h>
#include <QDebug>
@@ -60,7 +61,7 @@ diptr di_t::init_di_()
di_t::di_t() = default;
-bool di_t::poll_device(LPDIRECTINPUTDEVICE8 dev)
+bool di_t::poll_device(IDirectInputDevice8A* dev)
{
HRESULT hr;
assert(handle);
diff --git a/dinput/dinput.hpp b/dinput/dinput.hpp
index e9908a94..09c9a30b 100644
--- a/dinput/dinput.hpp
+++ b/dinput/dinput.hpp
@@ -15,7 +15,19 @@
#undef DIRECTINPUT_VERSION
#define DIRECTINPUT_VERSION 0x800
-#include <dinput.h>
+struct IDirectInputDevice8A;
+typedef struct IDirectInputDevice8A IDirectInputDevice8A;
+struct IDirectInput8A;
+typedef struct IDirectInput8A IDirectInput8A;
+struct _GUID;
+typedef struct _GUID GUID;
+struct _DIDATAFORMAT;
+typedef struct _DIDATAFORMAT DIDATAFORMAT;
+typedef int BOOL;
+struct DIDEVICEINSTANCEA;
+typedef struct DIDEVICEINSTANCEA DIDEVICEINSTANCEA;
+struct DIDEVICEOBJECTINSTANCEA;
+typedef struct DIDEVICEOBJECTINSTANCEA DIDEVICEOBJECTINSTANCEA;
// XXX TODO -sh 20190209
// keybinding_worker and joystick context are badly named
@@ -39,5 +51,5 @@ public:
operator bool() const;
operator diptr() const;
- static bool poll_device(LPDIRECTINPUTDEVICE8 dev);
+ static bool poll_device(IDirectInputDevice8A* dev);
};
diff --git a/dinput/keybinding-worker.cpp b/dinput/keybinding-worker.cpp
index 0ceca789..7cd8d663 100644
--- a/dinput/keybinding-worker.cpp
+++ b/dinput/keybinding-worker.cpp
@@ -9,13 +9,20 @@
#ifdef _WIN32
#include "keybinding-worker.hpp"
-#include "compat/macros.hpp"
+#include "compat/macros.h"
#include "compat/thread-name.hpp"
#include <QDebug>
#include <QMutexLocker>
-#include <windows.h>
+#include <dinput.h>
+
+static void destroy(IDirectInputDevice8A*& dev)
+{
+ if (dev)
+ dev->Release();
+ dev = nullptr;
+}
Key::Key() = default;
@@ -35,16 +42,13 @@ KeybindingWorker::~KeybindingWorker()
requestInterruption();
wait();
- if (dinkeyboard)
- {
- dinkeyboard->Unacquire();
- dinkeyboard->Release();
- }
+ destroy(dinkeyboard);
+ destroy(dinmouse);
}
-bool KeybindingWorker::init()
+bool KeybindingWorker::init_(IDirectInputDevice8A*& dev, const char* name, const GUID& guid, const DIDATAFORMAT& fmt)
{
- if (dinkeyboard)
+ if (dev)
return true;
if (!din)
@@ -53,24 +57,39 @@ bool KeybindingWorker::init()
goto fail;
}
- if (din->CreateDevice(GUID_SysKeyboard, &dinkeyboard, nullptr) != DI_OK)
+ if (auto hr = din->CreateDevice(guid, &dev, nullptr); hr != DI_OK)
{
- qDebug() << "dinput: create keyboard failed" << GetLastError();
+ qDebug() << "dinput: create" << name << "failed" << (void*)hr;
goto fail;
}
- if (dinkeyboard->SetDataFormat(&c_dfDIKeyboard) != DI_OK)
+ if (auto hr = dev->SetDataFormat(&fmt); hr != DI_OK)
{
- qDebug() << "dinput: keyboard SetDataFormat" << GetLastError();
+ qDebug() << "dinput:" << name << "SetDataFormat" << (void*)hr;
goto fail;
}
- if (dinkeyboard->SetCooperativeLevel((HWND) fake_main_window.winId(), DISCL_NONEXCLUSIVE | DISCL_BACKGROUND) != DI_OK)
+ if (auto hr = dev->SetCooperativeLevel((HWND) fake_main_window.winId(), DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
+ hr != DI_OK)
{
- qDebug() << "dinput: keyboard SetCooperativeLevel" << GetLastError();
+ qDebug() << "dinput:" << name << "SetCooperativeLevel" << (void*)hr;
goto fail;
}
+ return true;
+fail:
+ destroy(dev);
+ return false;
+}
+
+bool KeybindingWorker::init()
+{
+ bool ret = init_(dinkeyboard, "keyboard", GUID_SysKeyboard, c_dfDIKeyboard) &&
+ init_(dinmouse, "mouse", GUID_SysMouse, c_dfDIMouse2);
+
+ if (!ret)
+ goto fail;
+
{
DIPROPDWORD dipdw;
dipdw.dwData = num_keyboard_states;
@@ -79,9 +98,9 @@ bool KeybindingWorker::init()
dipdw.diph.dwObj = 0;
dipdw.diph.dwSize = sizeof(dipdw);
- if (dinkeyboard->SetProperty(DIPROP_BUFFERSIZE, &dipdw.diph) != DI_OK)
+ if (auto hr = dinkeyboard->SetProperty(DIPROP_BUFFERSIZE, &dipdw.diph); hr != DI_OK)
{
- qDebug() << "dinput: DIPROP_BUFFERSIZE";
+ qDebug() << "dinput: keyboard DIPROP_BUFFERSIZE" << (void*)hr;
goto fail;
}
}
@@ -89,11 +108,8 @@ bool KeybindingWorker::init()
return true;
fail:
- if (dinkeyboard)
- {
- dinkeyboard->Release();
- dinkeyboard = nullptr;
- }
+ destroy(dinkeyboard);
+ destroy(dinmouse);
return false;
}
@@ -125,6 +141,7 @@ void KeybindingWorker::run()
bool ok = true;
ok &= run_keyboard_nolock();
+ ok &= run_mouse_nolock();
ok &= run_joystick_nolock();
if (!ok)
@@ -136,6 +153,38 @@ void KeybindingWorker::run()
}
}
+bool KeybindingWorker::run_mouse_nolock()
+{
+ DIMOUSESTATE2 state;
+
+ if (!di_t::poll_device(dinmouse))
+ eval_once(qDebug() << "dinput: mouse poll failed");
+
+ if (auto hr = dinmouse->GetDeviceState(sizeof(state), &state); hr != DI_OK)
+ {
+ eval_once(qDebug() << "dinput: mouse GetDeviceState failed" << (void*) hr << GetLastError);
+ return false;
+ }
+
+ Key k;
+ k.guid = QStringLiteral("mouse");
+
+ for (int i = first_mouse_button; i < num_mouse_buttons; i++)
+ {
+ const bool new_state = state.rgbButtons[i] & 0x80;
+ k.held = new_state;
+ k.keycode = i;
+ bool& old_state = mouse_state[i - first_mouse_button];
+ if (old_state != new_state)
+ {
+ for (auto& r : receivers)
+ (*r)(k);
+ }
+ old_state = new_state;
+ }
+ return true;
+}
+
bool KeybindingWorker::run_keyboard_nolock()
{
/* There are some problems reported on various forums
@@ -153,6 +202,8 @@ bool KeybindingWorker::run_keyboard_nolock()
if (!di_t::poll_device(dinkeyboard))
eval_once(qDebug() << "dinput: keyboard poll failed");
+ DIDEVICEOBJECTDATA keyboard_states[num_keyboard_states];
+
DWORD sz = num_keyboard_states;
HRESULT hr = dinkeyboard->GetDeviceData(sizeof(*keyboard_states), keyboard_states, &sz, 0);
@@ -164,9 +215,13 @@ bool KeybindingWorker::run_keyboard_nolock()
for (unsigned k = 0; k < sz; k++)
{
- const unsigned idx = keyboard_states[k].dwOfs & 0xff; // defensive programming
+ const int idx = keyboard_states[k].dwOfs & 0xff; // defensive programming
const bool held = !!(keyboard_states[k].dwData & 0x80);
+ if (held == keystate[idx])
+ continue;
+ keystate[idx] = held;
+
switch (idx)
{
case DIK_LCONTROL:
@@ -192,8 +247,6 @@ bool KeybindingWorker::run_keyboard_nolock()
}
break;
}
-
- keystate[idx] = held;
}
return true;
@@ -225,7 +278,7 @@ KeybindingWorker::fun* KeybindingWorker::add_receiver(fun& receiver)
{
QMutexLocker l(&mtx);
receivers.push_back(std::make_unique<fun>(receiver));
- fun* f = receivers[receivers.size() - 1].get();
+ fun* f = &*receivers[receivers.size() - 1];
//qDebug() << "add receiver" << (long) f;
joy_ctx.refresh();
return f;
@@ -241,7 +294,7 @@ void KeybindingWorker::remove_receiver(KeybindingWorker::fun* pos)
for (int i = s(receivers.size()) - 1; i >= 0; i--)
{
using u = unsigned;
- if (receivers[u(i)].get() == pos)
+ if (&*receivers[u(i)] == pos)
{
ok = true;
//qDebug() << "remove receiver" << (long) pos;
diff --git a/dinput/keybinding-worker.hpp b/dinput/keybinding-worker.hpp
index 335a5c11..1c22ca17 100644
--- a/dinput/keybinding-worker.hpp
+++ b/dinput/keybinding-worker.hpp
@@ -45,7 +45,11 @@ struct OTR_DINPUT_EXPORT KeybindingWorker : private QThread
KeybindingWorker& operator=(KeybindingWorker&) = delete;
private:
- IDirectInputDevice8A* dinkeyboard = nullptr;
+ static constexpr int num_keyboard_states = 64;
+ static constexpr int num_mouse_buttons = 8;
+ static constexpr int first_mouse_button = 3;
+
+ IDirectInputDevice8A* dinkeyboard = nullptr, *dinmouse = nullptr;
win32_joy_ctx joy_ctx;
std::vector<std::unique_ptr<fun>> receivers;
QMutex mtx;
@@ -53,21 +57,21 @@ private:
di_t din;
bool keystate[256] {};
+ bool mouse_state[num_mouse_buttons - first_mouse_button] = {};
void run() override;
bool run_keyboard_nolock();
bool run_joystick_nolock();
+ bool run_mouse_nolock();
bool init();
+ bool init_(IDirectInputDevice8A*& dev, const char* name, const GUID& guid, const DIDATAFORMAT& fmt);
KeybindingWorker();
static KeybindingWorker& make();
fun* add_receiver(fun& receiver);
void remove_receiver(fun* pos);
~KeybindingWorker() override;
-
- static constexpr int num_keyboard_states = 64;
- DIDEVICEOBJECTDATA keyboard_states[num_keyboard_states] {};
public:
class Token
{
diff --git a/ext-falcon-bms-linear-acc/lang/zh_CN.ts b/dinput/lang/de_DE.ts
index 6401616d..1552582e 100644
--- a/ext-falcon-bms-linear-acc/lang/zh_CN.ts
+++ b/dinput/lang/de_DE.ts
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="de_DE">
</TS>
diff --git a/dinput/lang/zh_CN.ts b/dinput/lang/zh_CN.ts
index 6401616d..e5ca8aa9 100644
--- a/dinput/lang/zh_CN.ts
+++ b/dinput/lang/zh_CN.ts
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
</TS>
diff --git a/dinput/win32-joystick.cpp b/dinput/win32-joystick.cpp
index 0f2687fe..06c1f89f 100644
--- a/dinput/win32-joystick.cpp
+++ b/dinput/win32-joystick.cpp
@@ -1,7 +1,7 @@
#ifdef _WIN32
#include "win32-joystick.hpp"
-#include "compat/macros.hpp"
+#include "compat/macros.h"
#include <cstddef>
#include <algorithm>
@@ -11,13 +11,13 @@
#include <QWidget>
#include <QDebug>
+#include <dinput.h>
#include <objbase.h>
namespace win32_joy_impl {
QMutex win32_joy_ctx::enum_state::mtx;
win32_joy_ctx::enum_state win32_joy_ctx::enumerator;
-DIDEVICEOBJECTDATA win32_joy_ctx::joy::keystate_buffers[num_buffers] = {};
void win32_joy_ctx::poll(fn const& f)
{
@@ -48,7 +48,7 @@ bool win32_joy_ctx::poll_axis(const QString &guid, int* axes)
auto& j = iter->second;
auto& joy_handle = j->joy_handle;
- DIJOYSTATE2 js = {};
+ DIJOYSTATE2 js;
if (!di_t::poll_device(joy_handle))
continue;
@@ -142,6 +142,8 @@ bool win32_joy_ctx::joy::poll(fn const& f)
return false;
}
+ DIDEVICEOBJECTDATA keystate_buffers[num_buffers];
+
DWORD sz = num_buffers;
if (FAILED(hr = joy_handle->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), keystate_buffers, &sz, 0)))
{
@@ -226,6 +228,9 @@ win32_joy_ctx::enum_state::~enum_state()
void win32_joy_ctx::enum_state::refresh()
{
all.clear();
+#ifdef __SANITIZE_ADDRESS__
+ return;
+#endif
if (!di)
{
@@ -255,7 +260,7 @@ void win32_joy_ctx::enum_state::refresh()
const win32_joy_ctx::joys_t& win32_joy_ctx::enum_state::get_joys() const { return joys; }
-BOOL CALLBACK win32_joy_ctx::enum_state::EnumJoysticksCallback(const DIDEVICEINSTANCE *pdidInstance, void *pContext)
+BOOL CALLBACK win32_joy_ctx::enum_state::EnumJoysticksCallback(const DIDEVICEINSTANCEA *pdidInstance, void *pContext)
{
enum_state& state = *reinterpret_cast<enum_state*>(pContext);
const QString guid = guid_to_string(pdidInstance->guidInstance);
@@ -270,7 +275,7 @@ BOOL CALLBACK win32_joy_ctx::enum_state::EnumJoysticksCallback(const DIDEVICEINS
{
HRESULT hr;
- LPDIRECTINPUTDEVICE8 h;
+ IDirectInputDevice8A* h;
if (FAILED(hr = state.di->CreateDevice(pdidInstance->guidInstance, &h, nullptr)))
{
qDebug() << "dinput: failed joystick CreateDevice" << guid << (void*)hr;
@@ -322,7 +327,7 @@ end:
return DIENUM_CONTINUE;
}
-BOOL CALLBACK win32_joy_ctx::enum_state::EnumObjectsCallback(const DIDEVICEOBJECTINSTANCE *pdidoi, void *ctx)
+BOOL CALLBACK win32_joy_ctx::enum_state::EnumObjectsCallback(const DIDEVICEOBJECTINSTANCEA* pdidoi, void *ctx)
{
if (pdidoi->dwType & DIDFT_AXIS)
{
@@ -336,7 +341,7 @@ BOOL CALLBACK win32_joy_ctx::enum_state::EnumObjectsCallback(const DIDEVICEOBJEC
HRESULT hr;
- if (FAILED(hr = reinterpret_cast<LPDIRECTINPUTDEVICE8>(ctx)->SetProperty(DIPROP_RANGE, &diprg.diph)))
+ if (FAILED(hr = reinterpret_cast<IDirectInputDevice8A*>(ctx)->SetProperty(DIPROP_RANGE, &diprg.diph)))
{
qDebug() << "dinput: failed joystick DIPROP_RANGE" << (void*)hr;
return DIENUM_STOP;
@@ -346,7 +351,7 @@ BOOL CALLBACK win32_joy_ctx::enum_state::EnumObjectsCallback(const DIDEVICEOBJEC
return DIENUM_CONTINUE;
}
-win32_joy_ctx::joy::joy(LPDIRECTINPUTDEVICE8 handle, const QString& guid, const QString &name)
+win32_joy_ctx::joy::joy(IDirectInputDevice8A* handle, const QString& guid, const QString &name)
: joy_handle(handle), guid(guid), name(name)
{
//qDebug() << "make joy" << guid << name << joy_handle;
diff --git a/dinput/win32-joystick.hpp b/dinput/win32-joystick.hpp
index ff52ad1d..8e5344d6 100644
--- a/dinput/win32-joystick.hpp
+++ b/dinput/win32-joystick.hpp
@@ -10,6 +10,7 @@
#include "dinput.hpp"
#include "compat/timer.hpp"
#include "export.hpp"
+#include "compat/qhash.hpp"
#include <memory>
#include <vector>
@@ -17,15 +18,13 @@
#include <unordered_map>
#include <iterator>
-#include "compat/qhash.hpp"
#include <QString>
#include <QMutex>
namespace win32_joy_impl {
-static constexpr unsigned max_buttons = std::size(DIJOYSTATE2().rgbButtons);
-static constexpr unsigned max_pov_hats = std::size(DIJOYSTATE2().rgdwPOV);
-
+static constexpr unsigned max_buttons = 128;
+static constexpr unsigned max_pov_hats = 4;
static constexpr unsigned pov_hat_directions = 8;
// cf. https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ee416628(v=vs.85)
@@ -48,13 +47,11 @@ struct OTR_DINPUT_EXPORT win32_joy_ctx final
struct joy final
{
- LPDIRECTINPUTDEVICE8 joy_handle;
+ IDirectInputDevice8A* joy_handle;
QString guid, name;
bool last_state[max_buttons_and_pov_hats] {};
- static DIDEVICEOBJECTDATA keystate_buffers[num_buffers];
-
- joy(LPDIRECTINPUTDEVICE8 handle, const QString& guid, const QString& name);
+ joy(IDirectInputDevice8A* handle, const QString& guid, const QString& name);
~joy();
void release();
@@ -89,8 +86,8 @@ private:
joys_t joys;
di_t di;
- static BOOL CALLBACK EnumJoysticksCallback(const DIDEVICEINSTANCE* pdidInstance, VOID* pContext);
- static BOOL CALLBACK EnumObjectsCallback(const DIDEVICEOBJECTINSTANCE* pdidoi, VOID* ctx);
+ static BOOL __stdcall EnumJoysticksCallback(const DIDEVICEINSTANCEA* pdidInstance, void* pContext);
+ static BOOL __stdcall EnumObjectsCallback(const DIDEVICEOBJECTINSTANCEA* pdidoi, void* ctx);
public:
static QMutex mtx;
diff --git a/ext-falcon-bms-linear-acc/CMakeLists.txt b/ext-falcon-bms-linear-acc/CMakeLists.txt
deleted file mode 100644
index bb284f06..00000000
--- a/ext-falcon-bms-linear-acc/CMakeLists.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-if(WIN32)
- #otr_module(ext-falcon-bms-acceleration)
-endif()
diff --git a/ext-falcon-bms-linear-acc/FlightData.h b/ext-falcon-bms-linear-acc/FlightData.h
deleted file mode 100644
index 3f7e99ab..00000000
--- a/ext-falcon-bms-linear-acc/FlightData.h
+++ /dev/null
@@ -1,578 +0,0 @@
-#pragma once
-
-#define FLIGHTDATA_VERSION 117
-// changelog:
-// 110: initial BMS 4.33 version
-// 111: added SysTest to LightBits3
-// 112: added MCAnnounced to LightBits3
-// 113: added AllLampBits2OnExceptCarapace to LightBits2 and AllLampBits3OnExceptCarapace to LightBits3
-// 114: renamed WOW LightBit to ONGROUND, added "real" (AFM) WOW to LightBits3
-// 115: renamed "real" WOW in MLGWOW, added NLGWOW
-// 116: bitfields are now unsigned instead of signed
-// 117: added ATF_Not_Engaged to LightBits3
-
-// *** "FalconSharedMemoryArea" ***
-struct FlightData
-{
- // GENERAL NOTE FOR ALL LIGHTBITS:
- //
- // The lightbits contain status about whether a lamp is activated or deactivated. A *blinking* lamp
- // is always activated, even if it is in the "off" phase of the blinking! To check whether an activated
- // lamp is blinking or just "on", use the BlinkBits in FlightData2. A blinkbit does NOT alternate on/off
- // either, it will just state *if* a lamp is blinking. This construct might seem strange at 1st sight,
- // but only like this it can be guaranteed that even low-freq shared mem readers will pick up the info
- // about blinking lamps correctly. Obviously, it is up to the external program to implement the actual
- // blinking logic/freq etc.
- //
- // Summary:
- // a) The LightBit says "lamp is active (LightBit 1) or inactive (LightBit 0)".
- // b) The BlinkBit says "if the lamp is active (see LightBit 1), is it steady (BlinkBit 0)
- // or is it blinking (BlinkBit 1)"
- // c) If a lamp has no BlinkBit, it is always assumed to be steady if active (LightBit 1).
-
- enum LightBits : unsigned
- {
- MasterCaution = 0x1, // Left eyebrow
-
- // Brow Lights
- TF = 0x2, // Left eyebrow
- OXY_BROW = 0x4, // repurposed for eyebrow OXY LOW (was OBS, unused)
- EQUIP_HOT = 0x8, // Caution light; repurposed for cooling fault (was: not used)
- ONGROUND = 0x10, // True if on ground: this is not a lamp bit!
- ENG_FIRE = 0x20, // Right eyebrow; upper half of split face lamp
- CONFIG = 0x40, // Stores config, caution panel
- HYD = 0x80, // Right eyebrow; see also OIL (this lamp is not split face)
- Flcs_ABCD = 0x100, // TEST panel FLCS channel lamps; repurposed, was OIL (see HYD; that lamp is not split face)
- FLCS = 0x200, // Right eyebrow; was called DUAL which matches block 25, 30/32 and older 40/42
- CAN = 0x400, // Right eyebrow
- T_L_CFG = 0x800, // Right eyebrow
-
- // AOA Indexers
- AOAAbove = 0x1000,
- AOAOn = 0x2000,
- AOABelow = 0x4000,
-
- // Refuel/NWS
- RefuelRDY = 0x8000,
- RefuelAR = 0x10000,
- RefuelDSC = 0x20000,
-
- // Caution Lights
- FltControlSys = 0x40000,
- LEFlaps = 0x80000,
- EngineFault = 0x100000,
- Overheat = 0x200000,
- FuelLow = 0x400000,
- Avionics = 0x800000,
- RadarAlt = 0x1000000,
- IFF = 0x2000000,
- ECM = 0x4000000,
- Hook = 0x8000000,
- NWSFail = 0x10000000,
- CabinPress = 0x20000000,
-
- AutoPilotOn = 0x40000000, // TRUE if is AP on. NB: This is not a lamp bit!
- TFR_STBY = 0x80000000, // MISC panel; lower half of split face TFR lamp
-
- // Used with the MAL/IND light code to light up "everything"
- // please update this if you add/change bits!
- AllLampBitsOn = 0xBFFFFFEF
- };
-
- enum LightBits2 : unsigned
- {
- // Threat Warning Prime
- HandOff = 0x1,
- Launch = 0x2,
- PriMode = 0x4,
- Naval = 0x8,
- Unk = 0x10,
- TgtSep = 0x20,
-
- // EWS
- Go = 0x40, // On and operating normally
- NoGo = 0x80, // On but malfunction present
- Degr = 0x100, // Status message: AUTO DEGR
- Rdy = 0x200, // Status message: DISPENSE RDY
- ChaffLo = 0x400, // Bingo chaff quantity reached
- FlareLo = 0x800, // Bingo flare quantity reached
-
- // Aux Threat Warning
- AuxSrch = 0x1000,
- AuxAct = 0x2000,
- AuxLow = 0x4000,
- AuxPwr = 0x8000,
-
- // ECM
- EcmPwr = 0x10000,
- EcmFail = 0x20000,
-
- // Caution Lights
- FwdFuelLow = 0x40000,
- AftFuelLow = 0x80000,
-
- EPUOn = 0x100000, // EPU panel; run light
- JFSOn = 0x200000, // Eng Jet Start panel; run light
-
- // Caution panel
- SEC = 0x400000,
- OXY_LOW = 0x800000,
- PROBEHEAT = 0x1000000,
- SEAT_ARM = 0x2000000,
- BUC = 0x4000000,
- FUEL_OIL_HOT = 0x8000000,
- ANTI_SKID = 0x10000000,
-
- TFR_ENGAGED = 0x20000000, // MISC panel; upper half of split face TFR lamp
- GEARHANDLE = 0x40000000, // Lamp in gear handle lights on fault or gear in motion
- ENGINE = 0x80000000, // Lower half of right eyebrow ENG FIRE/ENGINE lamp
-
- // Used with the MAL/IND light code to light up "everything"
- // please update this if you add/change bits!
- AllLampBits2On = 0xFFFFF03F,
- AllLampBits2OnExceptCarapace = AllLampBits2On ^ HandOff ^ Launch ^ PriMode ^ Naval ^ Unk ^ TgtSep ^ AuxSrch ^ AuxAct ^ AuxLow ^ AuxPwr
- };
-
- enum LightBits3 : unsigned
- {
- // Elec panel
- FlcsPmg = 0x1,
- MainGen = 0x2,
- StbyGen = 0x4,
- EpuGen = 0x8,
- EpuPmg = 0x10,
- ToFlcs = 0x20,
- FlcsRly = 0x40,
- BatFail = 0x80,
-
- // EPU panel
- Hydrazine = 0x100,
- Air = 0x200,
-
- // Caution panel
- Elec_Fault = 0x400,
- Lef_Fault = 0x800,
-
- OnGround = 0x1000, // weight-on-wheels
- FlcsBitRun = 0x2000, // FLT CONTROL panel RUN light (used to be Multi-engine fire light)
- FlcsBitFail = 0x4000, // FLT CONTROL panel FAIL light (used to be Lock light Cue; non-F-16)
- DbuWarn = 0x8000, // Right eyebrow DBU ON cell; was Shoot light cue; non-F16
- NoseGearDown = 0x10000, // Landing gear panel; on means down and locked
- LeftGearDown = 0x20000, // Landing gear panel; on means down and locked
- RightGearDown = 0x40000, // Landing gear panel; on means down and locked
- ParkBrakeOn = 0x100000, // Parking brake engaged; NOTE: not a lamp bit
- Power_Off = 0x200000, // Set if there is no electrical power. NB: not a lamp bit
-
- // Caution panel
- cadc = 0x400000,
-
- // Left Aux console
- SpeedBrake = 0x800000, // True if speed brake is in anything other than stowed position
-
- // Threat Warning Prime - additional bits
- SysTest = 0x1000000,
-
- // Master Caution WILL come up (actual lightBit has 3sec delay like in RL),
- // usable for cockpit builders with RL equipment which has a delay on its own.
- // Will be set to false again as soon as the MasterCaution bit is set.
- MCAnnounced = 0x2000000,
-
- //MLGWOW is only for AFM , it means WOW switches on MLG are triggered => FLCS switches to WOWPitchRockGain
- MLGWOW = 0x4000000,
- NLGWOW = 0x8000000,
-
- ATF_Not_Engaged = 0x10000000,
-
- // Free bits in LightBits3
- //0x20000000,
- //0x40000000,
- //0x80000000,
-
- // Used with the MAL/IND light code to light up "everything"
- // please update this if you add/change bits!
- AllLampBits3On = 0x1147EFFF,
- AllLampBits3OnExceptCarapace = AllLampBits3On ^ SysTest
- };
-
- enum HsiBits : unsigned
- {
- ToTrue = 0x01, // HSI_FLAG_TO_TRUE == 1, TO
- IlsWarning = 0x02, // HSI_FLAG_ILS_WARN
- CourseWarning = 0x04, // HSI_FLAG_CRS_WARN
- Init = 0x08, // HSI_FLAG_INIT
- TotalFlags = 0x10, // HSI_FLAG_TOTAL_FLAGS; never set
- ADI_OFF = 0x20, // ADI OFF Flag
- ADI_AUX = 0x40, // ADI AUX Flag
- ADI_GS = 0x80, // ADI GS FLAG
- ADI_LOC = 0x100, // ADI LOC FLAG
- HSI_OFF = 0x200, // HSI OFF Flag
- BUP_ADI_OFF = 0x400, // Backup ADI Off Flag
- VVI = 0x800, // VVI OFF Flag
- AOA = 0x1000, // AOA OFF Flag
- AVTR = 0x2000, // AVTR Light
- OuterMarker = 0x4000, // MARKER beacon light for outer marker
- MiddleMarker = 0x8000, // MARKER beacon light for middle marker
- FromTrue = 0x10000, // HSI_FLAG_TO_TRUE == 2, FROM
-
- Flying = 0x80000000, // true if player is attached to an aircraft (i.e. not in UI state). NOTE: Not a lamp bit
-
- // Used with the MAL/IND light code to light up "everything"
- // please update this is you add/change bits!
- AllLampHsiBitsOn = 0xE000
- };
-
- // These are outputs from the sim
- // Note: some two-engine values removed in this version for compatibility
- // reasons.
- float x; // Ownship North (Ft)
- float y; // Ownship East (Ft)
- float z; // Ownship Down (Ft) --- NOTE: use FlightData2 AAUZ for barometric altitude!
- float xDot; // Ownship North Rate (ft/sec)
- float yDot; // Ownship East Rate (ft/sec)
- float zDot; // Ownship Down Rate (ft/sec)
- float alpha; // Ownship AOA (Degrees)
- float beta; // Ownship Beta (Degrees)
- float gamma; // Ownship Gamma (Radians)
- float pitch; // Ownship Pitch (Radians)
- float roll; // Ownship Pitch (Radians)
- float yaw; // Ownship Pitch (Radians)
- float mach; // Ownship Mach number
- float kias; // Ownship Indicated Airspeed (Knots)
- float vt; // Ownship True Airspeed (Ft/Sec)
- float gs; // Ownship Normal Gs
- float windOffset; // Wind delta to FPM (Radians)
- float nozzlePos; // Ownship engine nozzle percent open (0-100)
- //float nozzlePos2; // MOVED TO FlightData2! Ownship engine nozzle2 percent open (0-100)
- float internalFuel; // Ownship internal fuel (Lbs)
- float externalFuel; // Ownship external fuel (Lbs)
- float fuelFlow; // Ownship fuel flow (Lbs/Hour)
- float rpm; // Ownship engine rpm (Percent 0-103)
- //float rpm2; // MOVED TO FlightData2! Ownship engine rpm2 (Percent 0-103)
- float ftit; // Ownship Forward Turbine Inlet Temp (Degrees C)
- //float ftit2; // MOVED TO FlightData2! Ownship Forward Turbine Inlet Temp2 (Degrees C)
- float gearPos; // Ownship Gear position 0 = up, 1 = down;
- float speedBrake; // Ownship speed brake position 0 = closed, 1 = 60 Degrees open
- float epuFuel; // Ownship EPU fuel (Percent 0-100)
- float oilPressure; // Ownship Oil Pressure (Percent 0-100)
- //float oilPressure2; // MOVED TO FlightData2! Ownship Oil Pressure2 (Percent 0-100)
- unsigned int lightBits; // Cockpit Indicator Lights, one bit per bulb. See enum
-
- // These are inputs. Use them carefully
- // NB: these do not work when TrackIR device is enabled
- // NB2: launch falcon with the '-head' command line parameter to activate !
- float headPitch; // Head pitch offset from design eye (radians)
- float headRoll; // Head roll offset from design eye (radians)
- float headYaw; // Head yaw offset from design eye (radians)
-
- // new lights
- unsigned int lightBits2; // Cockpit Indicator Lights, one bit per bulb. See enum
- unsigned int lightBits3; // Cockpit Indicator Lights, one bit per bulb. See enum
-
- // chaff/flare
- float ChaffCount; // Number of Chaff left
- float FlareCount; // Number of Flare left
-
- // landing gear
- float NoseGearPos; // Position of the nose landinggear; caution: full down values defined in dat files
- float LeftGearPos; // Position of the left landinggear; caution: full down values defined in dat files
- float RightGearPos; // Position of the right landinggear; caution: full down values defined in dat files
-
- // ADI values
- float AdiIlsHorPos; // Position of horizontal ILS bar
- float AdiIlsVerPos; // Position of vertical ILS bar
-
- // HSI states
- int courseState; // HSI_STA_CRS_STATE
- int headingState; // HSI_STA_HDG_STATE
- int totalStates; // HSI_STA_TOTAL_STATES; never set
-
- // HSI values
- float courseDeviation; // HSI_VAL_CRS_DEVIATION
- float desiredCourse; // HSI_VAL_DESIRED_CRS
- float distanceToBeacon; // HSI_VAL_DISTANCE_TO_BEACON
- float bearingToBeacon; // HSI_VAL_BEARING_TO_BEACON
- float currentHeading; // HSI_VAL_CURRENT_HEADING
- float desiredHeading; // HSI_VAL_DESIRED_HEADING
- float deviationLimit; // HSI_VAL_DEV_LIMIT
- float halfDeviationLimit; // HSI_VAL_HALF_DEV_LIMIT
- float localizerCourse; // HSI_VAL_LOCALIZER_CRS
- float airbaseX; // HSI_VAL_AIRBASE_X
- float airbaseY; // HSI_VAL_AIRBASE_Y
- float totalValues; // HSI_VAL_TOTAL_VALUES; never set
-
- float TrimPitch; // Value of trim in pitch axis, -0.5 to +0.5
- float TrimRoll; // Value of trim in roll axis, -0.5 to +0.5
- float TrimYaw; // Value of trim in yaw axis, -0.5 to +0.5
-
- // HSI flags
- unsigned int hsiBits; // HSI flags
-
- //DED Lines
- char DEDLines[5][26]; //25 usable chars
- char Invert[5][26]; //25 usable chars
-
- //PFL Lines
- char PFLLines[5][26]; //25 usable chars
- char PFLInvert[5][26]; //25 usable chars
-
- //TacanChannel
- int UFCTChan, AUXTChan;
-
- // RWR
- int RwrObjectCount;
- int RWRsymbol[40];
- float bearing[40];
- unsigned long missileActivity[40];
- unsigned long missileLaunch[40];
- unsigned long selected[40];
- float lethality[40];
- unsigned long newDetection[40];
-
- //fuel values
- float fwd, aft, total;
-
-#if 0
- void SetLightBit (unsigned int newBit) {lightBits |= newBit;};
- void ClearLightBit(unsigned int newBit) { lightBits &= ~newBit; };
- bool IsSet(unsigned int newBit) { return ((lightBits & newBit) != 0); };
-
- void SetLightBit2(unsigned int newBit) { lightBits2 |= newBit; };
- void ClearLightBit2(unsigned int newBit) { lightBits2 &= ~newBit; };
- bool IsSet2(unsigned int newBit) { return ((lightBits2 & newBit) != 0); };
-
- void SetLightBit3(unsigned int newBit) { lightBits3 |= newBit; };
- void ClearLightBit3(unsigned int newBit) { lightBits3 &= ~newBit; };
- bool IsSet3(unsigned int newBit) { return ((lightBits3 & newBit) != 0); };
-
- void SetHsiBit(unsigned int newBit) { hsiBits |= newBit; };
- void ClearHsiBit(unsigned int newBit) { hsiBits &= ~newBit; };
- bool IsSetHsi(unsigned int newBit) { return ((hsiBits & newBit) != 0); };
-#endif
-
- int VersionNum; // Version of FlightData mem area
-
- // New values added here for header file compatibility but not implemented
- // in this version of the code at present.
- float headX; // Head X offset from design eye (feet)
- float headY; // Head Y offset from design eye (feet)
- float headZ; // Head Z offset from design eye (feet)
-
- int MainPower; // Main Power switch state, 0=down, 1=middle, 2=up
-};
-
-
-// OSB capture for MFD button labeling
-
-#define OSB_STRING_LENGTH 8 // currently strings appear to be max 7 printing chars
-
-struct OsbLabel {
- char line1[OSB_STRING_LENGTH];
- char line2[OSB_STRING_LENGTH];
- bool inverted;
-};
-
-
-// *** "FalconSharedOsbMemoryArea" ***
-struct OSBData
-{
- OsbLabel leftMFD[20];
- OsbLabel rightMFD[20];
-};
-
-
-#define FLIGHTDATA2_VERSION 9
-// changelog:
-// 1: initial BMS 4.33 version
-// 2: added AltCalReading, altBits, BupUhfPreset, powerBits, blinkBits, cmdsMode
-// 3: added VersionNum, hydPressureA/B, cabinAlt, BupUhfFreq, currentTime, vehicleACD
-// 4: added fuelflow2
-// 5: added RwrInfo, lefPos, tefPos
-// 6: added vtolPos
-// 7: bit fields are now unsigned instead of signed
-// 8: increased RwrInfo size to 512
-// 9: added human pilot names and their status in a session
-
-// do NOT change these w/o crosschecking the BMS code
-#define RWRINFO_SIZE 512
-#define CALLSIGN_LEN 12
-#define MAX_CALLSIGNS 32
-
-// *** "FalconSharedMemoryArea2" ***
-struct FlightData2
-{
- // TACAN
- enum TacanSources : unsigned
- {
- UFC = 0,
- AUX = 1,
- NUMBER_OF_SOURCES = 2,
- };
- enum TacanBits : unsigned
- {
- band = 0x01, // true in this bit position if band is X
- mode = 0x02, // true in this bit position if domain is air to air
- };
-
- // ALTIMETER
- enum AltBits : unsigned
- {
- CalType = 0x01, // true if calibration in inches of Mercury (Hg), false if in hectoPascal (hPa)
- PneuFlag = 0x02, // true if PNEU flag is visible
- };
-
- // POWER
- enum PowerBits : unsigned
- {
- BusPowerBattery = 0x01, // true if at least the battery bus is powered
- BusPowerEmergency = 0x02, // true if at least the emergency bus is powered
- BusPowerEssential = 0x04, // true if at least the essential bus is powered
- BusPowerNonEssential = 0x08, // true if at least the non-essential bus is powered
- MainGenerator = 0x10, // true if the main generator is online
- StandbyGenerator = 0x20, // true if the standby generator is online
- JetFuelStarter = 0x40, // true if JFS is running, can be used for magswitch
- };
-
- // BLINKING LIGHTS - only indicating *IF* a lamp is blinking, not implementing the actual on/off/blinking pattern logic!
- enum BlinkBits : unsigned
- {
- // currently working
- OuterMarker = 0x01, // defined in HsiBits - slow flashing for outer marker
- MiddleMarker = 0x02, // defined in HsiBits - fast flashing for middle marker
- PROBEHEAT = 0x04, // defined in LightBits2 - probeheat system is tested
- AuxSrch = 0x08, // defined in LightBits2 - search function in NOT activated and a search radar is painting ownship
- Launch = 0x10, // defined in LightBits2 - missile is fired at ownship
- PriMode = 0x20, // defined in LightBits2 - priority mode is enabled but more than 5 threat emitters are detected
- Unk = 0x40, // defined in LightBits2 - unknown is not active but EWS detects unknown radar
-
- // not working yet, defined for future use
- Elec_Fault = 0x80, // defined in LightBits3 - non-resetting fault
- OXY_BROW = 0x100, // defined in LightBits - monitor fault during Obogs
- EPUOn = 0x200, // defined in LightBits3 - abnormal EPU operation
- JFSOn_Slow = 0x400, // defined in LightBits3 - slow blinking: non-critical failure
- JFSOn_Fast = 0x800, // defined in LightBits3 - fast blinking: critical failure
- };
-
- // CMDS mode state
- enum CmdsModes : unsigned
- {
- CmdsOFF = 0,
- CmdsSTBY = 1,
- CmdsMAN = 2,
- CmdsSEMI = 3,
- CmdsAUTO = 4,
- CmdsBYP = 5,
- };
-
- // HSI/eHSI mode state
- enum NavModes : unsigned
- {
- ILS_TACAN = 0,
- TACAN = 1,
- NAV = 2,
- ILS_NAV = 3,
- };
-
- // human pilot state
- enum FlyStates : unsigned
- {
- IN_UI = 0, // UI - in the UI
- LOADING = 1, // UI>3D - loading the sim data
- WAITING = 2, // UI>3D - waiting for other players
- FLYING = 3, // 3D - flying
- DEAD = 4, // 3D>Dead - dead, waiting to respawn
- UNKNOWN = 5, // ???
- };
-
- // VERSION 1
- float nozzlePos2; // Ownship engine nozzle2 percent open (0-100)
- float rpm2; // Ownship engine rpm2 (Percent 0-103)
- float ftit2; // Ownship Forward Turbine Inlet Temp2 (Degrees C)
- float oilPressure2; // Ownship Oil Pressure2 (Percent 0-100)
- unsigned char navMode; // current mode selected for HSI/eHSI, see NavModes enum for details
- float AAUZ; // Ownship barometric altitude given by AAU (depends on calibration)
- char tacanInfo[NUMBER_OF_SOURCES]; // Tacan band/mode settings for UFC and AUX COMM
-
- // VERSION 2 / 7
- int AltCalReading; // barometric altitude calibration (depends on CalType)
- unsigned int altBits; // various altimeter bits, see AltBits enum for details
- unsigned int powerBits; // Ownship power bus / generator states, see PowerBits enum for details
- unsigned int blinkBits; // Cockpit indicator lights blink status, see BlinkBits enum for details
- // NOTE: these bits indicate only *if* a lamp is blinking, in addition to the
- // existing on/off bits. It's up to the external program to implement the
- // *actual* blinking.
- int cmdsMode; // Ownship CMDS mode state, see CmdsModes enum for details
- int BupUhfPreset; // BUP UHF channel preset
-
- // VERSION 3
- int BupUhfFreq; // BUP UHF channel frequency
- float cabinAlt; // Ownship cabin altitude
- float hydPressureA; // Ownship Hydraulic Pressure A
- float hydPressureB; // Ownship Hydraulic Pressure B
- int currentTime; // Current time in seconds (max 60 * 60 * 24)
- short vehicleACD; // Ownship ACD index number, i.e. which aircraft type are we flying.
- int VersionNum; // Version of FlightData2 mem area
-
- // VERSION 4
- float fuelFlow2; // Ownship fuel flow2 (Lbs/Hour)
-
- // VERSION 5 / 8
- char RwrInfo[RWRINFO_SIZE]; // New RWR Info
- float lefPos; // Ownship LEF position
- float tefPos; // Ownship TEF position
-
- // VERSION 6
- float vtolPos; // Ownship VTOL exhaust angle
-
- // VERSION 9
- char pilotsOnline; // Number of pilots in an MP session
- char pilotsCallsign[MAX_CALLSIGNS][CALLSIGN_LEN]; // List of pilots callsign connected to an MP session
- char pilotsStatus[MAX_CALLSIGNS]; // Status of the MP pilots, see enum FlyStates
-
-#if 0
- // TACAN
- // setters for internal use only
- void SetUfcTacanToAA(bool t) { if (t) { tacanInfo[UFC] |= mode; } else { tacanInfo[UFC] &= ~mode; } }
- void SetAuxTacanToAA(bool t) { if (t) { tacanInfo[AUX] |= mode; } else { tacanInfo[AUX] &= ~mode; } }
- void SetUfcTacanToX(bool t) { if (t) { tacanInfo[UFC] |= band; } else { tacanInfo[UFC] &= ~band; } }
- void SetAuxTacanToX(bool t) { if (t) { tacanInfo[AUX] |= band; } else { tacanInfo[AUX] &= ~band; } }
-
- // getters for external reader programs
- bool UfcTacanIsAA(void) {return ((tacanInfo[UFC] & mode) != 0); }
- bool AuxTacanIsAA(void) {return ((tacanInfo[AUX] & mode) != 0); }
- bool UfcTacanIsX(void) {return ((tacanInfo[UFC] & band) != 0); }
- bool AuxTacanIsX(void) {return ((tacanInfo[AUX] & band) != 0); }
-
- // ALTIMETER
- void SetAltBit(unsigned int newBit) { altBits |= newBit; };
- void ClearAltBit(unsigned int newBit) { altBits &= ~newBit; };
- bool IsSetAlt(unsigned int newBit) { return ((altBits & newBit) != 0); };
-
- // POWER
- void SetPowerBit(unsigned int newBit) { powerBits |= newBit; };
- void ClearPowerBit(unsigned int newBit) { powerBits &= ~newBit; };
- bool IsSetPower(unsigned int newBit) { return ((powerBits & newBit) != 0); };
-
- // BLINKING LIGHTS
- void SetBlinkBit(unsigned int newBit) { blinkBits |= newBit; };
- void ClearBlinkBit(unsigned int newBit) { blinkBits &= ~newBit; };
- bool IsSetBlink(unsigned int newBit) { return ((blinkBits & newBit) != 0); };
-
- // CMDS mode state
- void SetCmdsMode(int newMode) {cmdsMode = newMode;};
- int GetCmdsMode(void) {return cmdsMode;};
-
- // HSI/eHSI mode state
- void SetNavMode(int newMode) {navMode = newMode;};
- int GetNavMode(void) {return navMode;};
-#endif
-};
-
-#define BMS_MEM_OSB "FalconSharedOsbMemoryArea"
-#define BMS_MEM_DATA1 "FalconSharedMemoryArea"
-#define BMS_MEM_DATA2 "FalconSharedMemoryArea2"
-
-#if 0
-extern OSBData cockpitOSBData; // "FalconSharedOsbMemoryArea"
-extern FlightData cockpitFlightData; // "FalconSharedMemoryArea"
-extern FlightData2 cockpitFlightData2; // "FalconSharedMemoryArea2"
-#endif
diff --git a/ext-falcon-bms-linear-acc/bms-shm.cpp b/ext-falcon-bms-linear-acc/bms-shm.cpp
deleted file mode 100644
index be7d94ff..00000000
--- a/ext-falcon-bms-linear-acc/bms-shm.cpp
+++ /dev/null
@@ -1,2 +0,0 @@
-#include "FlightData.h"
-
diff --git a/ext-falcon-bms-linear-acc/bms-shm.hpp b/ext-falcon-bms-linear-acc/bms-shm.hpp
deleted file mode 100644
index 3f59c932..00000000
--- a/ext-falcon-bms-linear-acc/bms-shm.hpp
+++ /dev/null
@@ -1,2 +0,0 @@
-#pragma once
-
diff --git a/ext-falcon-bms-linear-acc/falcon-bms-dialog.cpp b/ext-falcon-bms-linear-acc/falcon-bms-dialog.cpp
deleted file mode 100644
index d4268a06..00000000
--- a/ext-falcon-bms-linear-acc/falcon-bms-dialog.cpp
+++ /dev/null
@@ -1,9 +0,0 @@
-#include "falcon-bms-dialog.hpp"
-
-void falcon_bms_acceleration_dialog::register_extension(IExtension&)
-{
-}
-
-void falcon_bms_acceleration_dialog::unregister_extension()
-{
-}
diff --git a/ext-falcon-bms-linear-acc/falcon-bms-dialog.hpp b/ext-falcon-bms-linear-acc/falcon-bms-dialog.hpp
deleted file mode 100644
index 9aa5baa3..00000000
--- a/ext-falcon-bms-linear-acc/falcon-bms-dialog.hpp
+++ /dev/null
@@ -1,10 +0,0 @@
-#pragma once
-
-#include "falcon-bms-ext.hpp"
-
-struct falcon_bms_acceleration_dialog : IExtensionDialog
-{
-public:
- void register_extension(IExtension& ext) override;
- void unregister_extension() override;
-};
diff --git a/ext-falcon-bms-linear-acc/falcon-bms-ext.cpp b/ext-falcon-bms-linear-acc/falcon-bms-ext.cpp
deleted file mode 100644
index b6a32540..00000000
--- a/ext-falcon-bms-linear-acc/falcon-bms-ext.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-#include "falcon-bms-ext.hpp"
-
-IExtension::event_mask falcon_bms_acceleration_ext::hook_types()
-{
- return IExtension::on_finished;
-}
-
-falcon_bms_acceleration_ext::falcon_bms_acceleration_ext()
-{
-}
-
-void falcon_bms_acceleration_ext::process_finished(Pose& p)
-{
- (void)p;
-}
diff --git a/ext-falcon-bms-linear-acc/falcon-bms-ext.hpp b/ext-falcon-bms-linear-acc/falcon-bms-ext.hpp
deleted file mode 100644
index e08d5377..00000000
--- a/ext-falcon-bms-linear-acc/falcon-bms-ext.hpp
+++ /dev/null
@@ -1,11 +0,0 @@
-#pragma once
-
-#include "api/plugin-api.hpp"
-
-struct falcon_bms_acceleration_ext : IExtension
-{
- event_mask hook_types() override;
- falcon_bms_acceleration_ext();
- void process_finished(Pose&p) override;
- module_status initialize() override { return status_ok(); }
-};
diff --git a/ext-falcon-bms-linear-acc/falcon-bms-metadata.cpp b/ext-falcon-bms-linear-acc/falcon-bms-metadata.cpp
deleted file mode 100644
index fa95447c..00000000
--- a/ext-falcon-bms-linear-acc/falcon-bms-metadata.cpp
+++ /dev/null
@@ -1,11 +0,0 @@
-#include "falcon-bms-metadata.hpp"
-
-QString falcon_bms_acceleration_metadata::name()
-{
- return "Falcon BMS linear acceleration display";
-}
-
-QIcon falcon_bms_acceleration_metadata::icon()
-{
- return QIcon();
-}
diff --git a/ext-falcon-bms-linear-acc/falcon-bms-metadata.hpp b/ext-falcon-bms-linear-acc/falcon-bms-metadata.hpp
deleted file mode 100644
index eea0d6bd..00000000
--- a/ext-falcon-bms-linear-acc/falcon-bms-metadata.hpp
+++ /dev/null
@@ -1,9 +0,0 @@
-#pragma once
-
-#include "falcon-bms-ext.hpp"
-
-struct falcon_bms_acceleration_metadata : Metadata
-{
- QString name() override;
- QIcon icon() override;
-};
diff --git a/ext-falcon-bms-linear-acc/module.cpp b/ext-falcon-bms-linear-acc/module.cpp
deleted file mode 100644
index 80a9553d..00000000
--- a/ext-falcon-bms-linear-acc/module.cpp
+++ /dev/null
@@ -1,5 +0,0 @@
-#include "falcon-bms-ext.hpp"
-#include "falcon-bms-dialog.hpp"
-#include "falcon-bms-metadata.hpp"
-
-OPENTRACK_DECLARE_EXTENSION(falcon_bms_acceleration_ext, falcon_bms_acceleration_dialog, falcon_bms_acceleration_metadata)
diff --git a/filter-accela-hamilton/CMakeLists.txt b/filter-accela-hamilton/CMakeLists.txt
new file mode 100644
index 00000000..825cef5e
--- /dev/null
+++ b/filter-accela-hamilton/CMakeLists.txt
@@ -0,0 +1,2 @@
+otr_module(filter-accela-hamilton)
+target_link_libraries(opentrack-filter-accela-hamilton opentrack-spline)
diff --git a/filter-accela-hamilton/accela_hamilton.cpp b/filter-accela-hamilton/accela_hamilton.cpp
new file mode 100644
index 00000000..7fde90b8
--- /dev/null
+++ b/filter-accela-hamilton/accela_hamilton.cpp
@@ -0,0 +1,121 @@
+/* Copyright (c) 2012-2015 Stanislaw Halik
+ * Copyright (c) 2023-2024 Michael Welter
+ *
+ * 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 "accela_hamilton.h"
+#include "api/plugin-api.hpp"
+#include "opentrack/defs.hpp"
+
+#include <algorithm>
+
+#include "compat/math.hpp"
+#include "compat/hamilton-tools.h"
+#include "compat/math-imports.hpp"
+#include "compat/time.hpp"
+
+
+accela_hamilton::accela_hamilton()
+{
+ s.make_splines(spline_rot, spline_pos);
+}
+
+
+void accela_hamilton::filter(const double* input, double *output)
+{
+ constexpr double EPSILON = 1e-15F;
+
+ const tQuat current_rot = QuatFromYPR(input + Yaw);
+ const tVector current_pos(input[TX], input[TY], input[TZ]);
+
+ if (unlikely(first_run))
+ {
+ first_run = false;
+ last_rotation = current_rot;
+ last_position = current_pos;
+ t.start();
+#if defined DEBUG_ACCELA
+ debug_max = 0;
+ debug_timer.start();
+#endif
+ return;
+ }
+
+ const double pos_thres{s.pos_smoothing};
+ const double pos_dz{ s.pos_deadzone};
+
+ const double dt = t.elapsed_seconds();
+ t.start();
+
+ // Position
+ {
+ const tVector delta = current_pos - last_position;
+ const double delta_len = VectorLength(delta);
+ const tVector delta_normed = delta_len>0. ? delta/delta_len : tVector(); // Zero vector when length was zero.
+ const double gain = dt*spline_pos.get_value_no_save(std::max(0., delta_len-pos_dz) / pos_thres);
+ const tVector output_pos = last_position + (delta_normed * gain);
+ output[TX] = output_pos.v[0];
+ output[TY] = output_pos.v[1];
+ output[TZ] = output_pos.v[2];
+ last_position = output_pos;
+ }
+
+ // Zoom smoothing:
+ const double zoomed_smoothing = [this](double output_z) {
+ // Local copies because accessing settings involves thread synchronization
+ // and I don't like this in the middle of math.
+ const double max_zoomed_smoothing {s.max_zoomed_smoothing};
+ const double max_z {s.max_z};
+ // Movement toward the monitor is negative. Negate and clamp it to get a positive value
+ const double z = std::clamp(-output_z, 0., max_z);
+ return max_zoomed_smoothing * z / (max_z + EPSILON);
+ }(output[TZ]);
+
+ const double rot_dz{ s.rot_deadzone};
+ const double rot_thres = double{s.rot_smoothing} + zoomed_smoothing;
+
+ // Rotation
+ {
+ // Inter/extrapolates along the arc between the old and new orientation.
+ // It's basically a quaternion spherical linear interpolation, where the
+ // accela gain x dt is the blending parameter. Might actually overshoot
+ // the new orientation, but that's fine.
+
+ // Compute rotation angle and axis which brings the previous orientation to the current rotation
+ const double angle = AngleBetween(last_rotation, current_rot);
+ // Apply the Accela gain magic. The "gain_angle" is the desired rotation from the old orientation
+ // towards the current. Then alpha is the blending factor for the SLerp operation. It is normalized
+ // to the range [0,1] where 1 corresponds to the current orientation, i.e. it is the fractional
+ // rotation relative to the "gain_angle". EPSILON is added to prevent division by zero.
+ // Additionally we use std::min(1., ...) to clamp the blending. alpha>1 would probably not make much
+ // sense since it would mean extrapolation beyond the current orientation. And it would be a rare
+ // edge case. Secondly idk if Slerp supports alpha>1.
+ const double normalized_angle = std::max(0., angle - rot_dz) / rot_thres;
+ const double gain_angle = dt*spline_rot.get_value_no_save(std::abs(normalized_angle));
+ const double alpha = std::min(1., gain_angle / (angle + EPSILON));
+ // Rotate toward the measured orientation.
+ const tQuat output_rot = Slerp(last_rotation, current_rot, alpha);
+ // And back to Euler angles
+ QuatToYPR(output_rot, output + Yaw);
+ last_rotation = output_rot;
+ }
+}
+
+namespace detail::accela_hamilton {
+
+void settings_accela_hamilton::make_splines(spline& rot, spline& pos)
+{
+ rot.clear(); pos.clear();
+
+ for (const auto& val : rot_gains)
+ rot.add_point({ val.x, val.y });
+
+ for (const auto& val : pos_gains)
+ pos.add_point({ val.x, val.y });
+}
+
+} // ns detail::accela_hamilton
+
+OPENTRACK_DECLARE_FILTER(accela_hamilton, dialog_accela_hamilton, accela_hamiltonDll)
diff --git a/filter-accela-hamilton/accela_hamilton.h b/filter-accela-hamilton/accela_hamilton.h
new file mode 100644
index 00000000..1afbfd8c
--- /dev/null
+++ b/filter-accela-hamilton/accela_hamilton.h
@@ -0,0 +1,69 @@
+/* Copyright (c) 2012-2015 Stanislaw Halik
+ * Copyright (c) 2023-2024 Michael Welter
+ *
+ * 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 "ui_accela_hamilton_filtercontrols.h"
+
+#include "accela_hamilton_settings.hpp"
+#include "api/plugin-api.hpp"
+#include "compat/timer.hpp"
+#include "compat/variance.hpp"
+#include "compat/hamilton-tools.h"
+
+#include <QMutex>
+#include <QTimer>
+
+
+//#define DEBUG_ACCELA
+
+struct accela_hamilton : IFilter
+{
+ accela_hamilton();
+ void filter(const double* input, double *output) override;
+ void center() override { first_run = true; }
+ spline spline_rot, spline_pos;
+ module_status initialize() override { return status_ok(); }
+private:
+ settings_accela_hamilton s;
+ tVector last_position = {};
+ tQuat last_rotation = {};
+ Timer t;
+#if defined DEBUG_ACCELA
+ Timer debug_timer;
+ double debug_max;
+ variance var;
+#endif
+ bool first_run = true;
+};
+
+class dialog_accela_hamilton : public IFilterDialog
+{
+ Q_OBJECT
+public:
+ dialog_accela_hamilton();
+ void register_filter(IFilter*) override {}
+ void unregister_filter() override {}
+ void save() override;
+ void reload() override;
+ bool embeddable() noexcept override { return true; }
+ void set_buttons_visible(bool x) override;
+private:
+ Ui::AccelaUICdialog_accela ui;
+ settings_accela_hamilton s;
+private slots:
+ void doOK();
+ void doCancel();
+};
+
+class accela_hamiltonDll : public Metadata
+{
+ Q_OBJECT
+
+ QString name() override { return tr("AccelaHamilton"); }
+ QIcon icon() override { return QIcon(":/images/filter-16.png"); }
+};
diff --git a/filter-accela-hamilton/accela_hamilton_dialog.cpp b/filter-accela-hamilton/accela_hamilton_dialog.cpp
new file mode 100644
index 00000000..bf670d34
--- /dev/null
+++ b/filter-accela-hamilton/accela_hamilton_dialog.cpp
@@ -0,0 +1,99 @@
+/* Copyright (c) 2012-2015 Stanislaw Halik
+ * Copyright (c) 2023-2024 Michael Welter
+ *
+ * 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 "accela_hamilton.h"
+#include <cmath>
+#include <QDebug>
+#include <algorithm>
+#include <QDoubleSpinBox>
+#include "api/plugin-api.hpp"
+#include "spline/spline-widget.hpp"
+#include <QDialog>
+
+using namespace options;
+
+dialog_accela_hamilton::dialog_accela_hamilton()
+{
+ ui.setupUi(this);
+
+ connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK()));
+ connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel()));
+
+ tie_setting(s.rot_smoothing, ui.rotation_slider);
+ tie_setting(s.pos_smoothing, ui.translation_slider);
+ tie_setting(s.rot_deadzone, ui.rot_dz_slider);
+ tie_setting(s.pos_deadzone, ui.trans_dz_slider);
+
+ tie_setting(s.rot_smoothing, ui.rot_gain, [](const slider_value& s) { return tr("%1°").arg(s.cur(), 0, 'g', 4); });
+ tie_setting(s.pos_smoothing, ui.trans_gain, [](const slider_value& s) { return tr("%1mm").arg(s.cur(), 0, 'g', 4); });
+ tie_setting(s.rot_deadzone, ui.rot_dz, [](const slider_value& s) { return tr("%1°").arg(s.cur(), 0, 'g', 4); });
+ tie_setting(s.pos_deadzone, ui.trans_dz, [](const slider_value& s) { return tr("%1mm").arg(s.cur()); });
+
+ tie_setting(s.max_zoomed_smoothing, ui.max_zoomed_smoothing);
+ tie_setting(s.max_zoomed_smoothing, ui.lb_max_zoomed_smoothing, [](double x)
+ { return tr("%1°").arg(x, 0, 'g', 3);});
+
+ tie_setting(s.max_z, ui.max_z);
+ tie_setting(s.max_z, ui.lb_max_z, [](double x)
+ { return tr("%1mm").arg(x, 0, 'g', 3);});
+
+//#define SPLINE_ROT_DEBUG
+//#define SPLINE_TRANS_DEBUG
+
+#if defined SPLINE_ROT_DEBUG || defined SPLINE_TRANS_DEBUG
+ {
+ spline rot, pos;
+ s.make_splines(rot, pos);
+
+#ifdef SPLINE_ROT_DEBUG
+ QDialog dr;
+ spline_widget r(&dr);
+ dr.setWindowTitle("Accela rotation gain"); r.set_preview_only(true); r.setEnabled(true);
+ r.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); r.set_config(&rot);
+ r.setFixedSize(1024, 600);
+ dr.show();
+ dr.exec();
+#endif
+
+#ifdef SPLINE_TRANS_DEBUG
+ QDialog dt;
+ spline_widget t(&dt);
+ dt.setWindowTitle("Accela translation gain"); t.set_preview_only(true); t.setEnabled(true);
+ dt.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); t.set_config(&pos);
+ t.setFixedSize(1024, 600);
+ dt.show();
+ dt.exec();
+#endif
+ }
+#endif
+}
+
+void dialog_accela_hamilton::doOK()
+{
+ save();
+ close();
+}
+
+void dialog_accela_hamilton::doCancel()
+{
+ close();
+}
+
+void dialog_accela_hamilton::save()
+{
+ s.b->save();
+}
+
+void dialog_accela_hamilton::reload()
+{
+ s.b->reload();
+}
+
+void dialog_accela_hamilton::set_buttons_visible(bool x)
+{
+ ui.buttonBox->setVisible(x);
+}
diff --git a/filter-accela-hamilton/accela_hamilton_filtercontrols.ui b/filter-accela-hamilton/accela_hamilton_filtercontrols.ui
new file mode 100644
index 00000000..f6049444
--- /dev/null
+++ b/filter-accela-hamilton/accela_hamilton_filtercontrols.ui
@@ -0,0 +1,485 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>AccelaUICdialog_accela</class>
+ <widget class="QWidget" name="AccelaUICdialog_accela">
+ <property name="windowModality">
+ <enum>Qt::NonModal</enum>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>632</width>
+ <height>628</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>550</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="windowTitle">
+ <string>Filter settings</string>
+ </property>
+ <property name="windowIcon">
+ <iconset resource="../gui/opentrack-res.qrc">
+ <normaloff>:/images/filter-16.png</normaloff>:/images/filter-16.png</iconset>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_3">
+ <property name="topMargin">
+ <number>5</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QTextBrowser" name="textBrowser">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>130</height>
+ </size>
+ </property>
+ <property name="acceptDrops">
+ <bool>false</bool>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <property name="verticalScrollBarPolicy">
+ <enum>Qt::ScrollBarAlwaysOff</enum>
+ </property>
+ <property name="horizontalScrollBarPolicy">
+ <enum>Qt::ScrollBarAlwaysOff</enum>
+ </property>
+ <property name="sizeAdjustPolicy">
+ <enum>QAbstractScrollArea::AdjustToContents</enum>
+ </property>
+ <property name="html">
+ <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:10pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Accela + Hamilton Filter&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-weight:600;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;For both rotations and positions: No movement occurs within the dead zone.&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;For larger movements, rotation and translation occurs toward the new measurement. The strength is determined by the Smoothing setting and the builtin Accela gain curves.&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;When you lean forward, smoothing is added up to the set maximum at the distance of Max Z.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QGroupBox" name="groupBox_3">
+ <property name="title">
+ <string>Rotation Smoothing (Added when zoomed)</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_5">
+ <property name="topMargin">
+ <number>4</number>
+ </property>
+ <property name="bottomMargin">
+ <number>4</number>
+ </property>
+ <property name="horizontalSpacing">
+ <number>9</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>2</number>
+ </property>
+ <item row="1" column="2">
+ <widget class="QSlider" name="max_z">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimum">
+ <number>0</number>
+ </property>
+ <property name="maximum">
+ <number>25</number>
+ </property>
+ <property name="pageStep">
+ <number>1</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Max Added</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QSlider" name="max_zoomed_smoothing">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimum">
+ <number>0</number>
+ </property>
+ <property name="maximum">
+ <number>49</number>
+ </property>
+ <property name="pageStep">
+ <number>1</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="tickPosition">
+ <enum>QSlider::TicksAbove</enum>
+ </property>
+ <property name="tickInterval">
+ <number>40</number>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="lb_max_zoomed_smoothing">
+ <property name="minimumSize">
+ <size>
+ <width>50</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>0° </string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLabel" name="lb_max_z">
+ <property name="minimumSize">
+ <size>
+ <width>50</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>0mm</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_8">
+ <property name="text">
+ <string>Max Z</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="5" column="0">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QGroupBox" name="groupBox_2">
+ <property name="title">
+ <string>Position filtering (X, Y, Z - translation)</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <property name="topMargin">
+ <number>4</number>
+ </property>
+ <property name="bottomMargin">
+ <number>4</number>
+ </property>
+ <property name="horizontalSpacing">
+ <number>9</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>2</number>
+ </property>
+ <item row="1" column="2">
+ <widget class="QSlider" name="trans_dz_slider">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimum">
+ <number>0</number>
+ </property>
+ <property name="maximum">
+ <number>20</number>
+ </property>
+ <property name="pageStep">
+ <number>1</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Smoothing</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QSlider" name="translation_slider">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimum">
+ <number>0</number>
+ </property>
+ <property name="maximum">
+ <number>29</number>
+ </property>
+ <property name="pageStep">
+ <number>1</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="tickPosition">
+ <enum>QSlider::TicksAbove</enum>
+ </property>
+ <property name="tickInterval">
+ <number>40</number>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="trans_gain">
+ <property name="minimumSize">
+ <size>
+ <width>50</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>0mm</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLabel" name="trans_dz">
+ <property name="minimumSize">
+ <size>
+ <width>50</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>0mm</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_6">
+ <property name="text">
+ <string>Deadzone</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ <zorder>trans_dz_slider</zorder>
+ <zorder>translation_slider</zorder>
+ <zorder>trans_dz</zorder>
+ <zorder>label_6</zorder>
+ <zorder>trans_gain</zorder>
+ <zorder>label</zorder>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QGroupBox" name="groupBox44">
+ <property name="title">
+ <string>Rotation filtering (Yaw, pitch, and roll)</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <property name="topMargin">
+ <number>4</number>
+ </property>
+ <property name="bottomMargin">
+ <number>4</number>
+ </property>
+ <property name="horizontalSpacing">
+ <number>9</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>2</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QLabel" name="lblSensYaw_6">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Smoothing</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="rot_gain">
+ <property name="minimumSize">
+ <size>
+ <width>50</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>0°</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QSlider" name="rotation_slider">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimum">
+ <number>0</number>
+ </property>
+ <property name="maximum">
+ <number>49</number>
+ </property>
+ <property name="pageStep">
+ <number>1</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="tickPosition">
+ <enum>QSlider::TicksAbove</enum>
+ </property>
+ <property name="tickInterval">
+ <number>50</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Deadzone</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLabel" name="rot_dz">
+ <property name="minimumSize">
+ <size>
+ <width>50</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>0°</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QSlider" name="rot_dz_slider">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimum">
+ <number>0</number>
+ </property>
+ <property name="maximum">
+ <number>20</number>
+ </property>
+ <property name="pageStep">
+ <number>1</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <tabstops>
+ <tabstop>rotation_slider</tabstop>
+ <tabstop>rot_dz_slider</tabstop>
+ <tabstop>translation_slider</tabstop>
+ <tabstop>trans_dz_slider</tabstop>
+ </tabstops>
+ <resources>
+ <include location="../gui/opentrack-res.qrc"/>
+ </resources>
+ <connections/>
+ <slots>
+ <slot>startEngineClicked()</slot>
+ <slot>stopEngineClicked()</slot>
+ <slot>cameraSettingsClicked()</slot>
+ </slots>
+</ui>
diff --git a/filter-accela-hamilton/accela_hamilton_settings.hpp b/filter-accela-hamilton/accela_hamilton_settings.hpp
new file mode 100644
index 00000000..71ddf479
--- /dev/null
+++ b/filter-accela-hamilton/accela_hamilton_settings.hpp
@@ -0,0 +1,63 @@
+#pragma once
+
+#include "spline/spline.hpp"
+#include "options/options.hpp"
+
+namespace detail::accela_hamilton {
+
+using namespace options;
+
+// ------------------------------------
+// debug knobs
+// ------------------------------------
+
+//#define DEBUG_ACCELA
+//#define SPLINE_ROT_DEBUG
+//#define SPLINE_TRANS_DEBUG
+
+struct settings_accela_hamilton : opts
+{
+ struct gains
+ {
+ double x, y;
+ };
+
+ static constexpr gains const rot_gains[] {
+ { 9, 300 },
+ { 8, 200 },
+ { 5, 100 },
+ { 2.5, 35 },
+ { 1.5, 8 },
+ { 1, 1.5 },
+ { .5, .4 },
+ };
+
+ static constexpr gains const pos_gains[] {
+ { 9, 200 },
+ { 8, 150 },
+ { 7, 110 },
+ { 5, 60 },
+ { 3, 24 },
+ { 2, 7.5 },
+ { 1.66, 4.5 },
+ { 1.33, 2.25 },
+ { .66, .75 },
+ { .33, .375 },
+ { 0, 0 },
+ };
+
+ static void make_splines(spline& rot, spline& pos);
+
+ value<slider_value> rot_smoothing { b, "rotation-sensitivity", { 1.5, .05, 2.5 } },
+ pos_smoothing { b, "translation-sensitivity", { 1., .05, 1.5 } },
+ rot_deadzone { b, "rotation-deadzone", { .03, 0, .2 } },
+ pos_deadzone { b, "translation-deadzone", { .1, 0, 1 } },
+ max_zoomed_smoothing { b, "max_zoomed_smoothing", { .0, .0, 10. } },
+ max_z { b, "max-z", { 10., .0, 30. } };
+
+ settings_accela_hamilton() : opts("accela-hamilton-sliders") {}
+};
+
+} // ns detail::accela_hamilton
+
+using detail::accela_hamilton::settings_accela_hamilton;
diff --git a/filter-accela-hamilton/lang/de_DE.ts b/filter-accela-hamilton/lang/de_DE.ts
new file mode 100644
index 00000000..1a27c27b
--- /dev/null
+++ b/filter-accela-hamilton/lang/de_DE.ts
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>AccelaUICdialog_accela</name>
+ <message>
+ <source>Filter settings</source>
+ <translation>Filter-Einstellungen</translation>
+ </message>
+ <message>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;Ubuntu&apos;; font-size:10pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Accela + Hamilton Filter&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-weight:600;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;For both rotations and positions: No movement occurs within the dead zone.&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;For larger movements, rotation and translation occurs toward the new measurement. The strength is determined by the Smoothing setting and the builtin Accela gain curves.&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;When you lean forward, smoothing is added up to the set maximum at the distance of Max Z.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;Ubuntu&apos;; font-size:10pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Accela + Hamilton Filter&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-weight:600;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Für Rotation und Position: Es erfolgt keine Bewegung innerhalb des Totbereichs.&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Bei größeren Bewegungen erfolgen Rotation und Übersetzung zugunsten der neuen Messung. Die Stärke wird durch die Glättungseinstellung und die vorgegebenen Accela-Verstärkungskurven bestimmt.&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Beim Vorbeugen wird eine Glättung bis zum eingestellten Maximum der Distanz Max Z hinzugefügt.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <source>Rotation Smoothing (Added when zoomed)</source>
+ <translation>Rotationsglättung (hinzugefügt beim Zoomen)</translation>
+ </message>
+ <message>
+ <source>Max Added</source>
+ <translation>Maximal hinzugefügt</translation>
+ </message>
+ <message>
+ <source>0° </source>
+ <translation>0° </translation>
+ </message>
+ <message>
+ <source>0mm</source>
+ <translation>0 mm</translation>
+ </message>
+ <message>
+ <source>Max Z</source>
+ <translation>Maximales Z</translation>
+ </message>
+ <message>
+ <source>Position filtering (X, Y, Z - translation)</source>
+ <translation>Positionsfilterung (X, Y, Z-Übersetzung)</translation>
+ </message>
+ <message>
+ <source>Smoothing</source>
+ <translation>Glättung</translation>
+ </message>
+ <message>
+ <source>Deadzone</source>
+ <translation>Totbereich</translation>
+ </message>
+ <message>
+ <source>Rotation filtering (Yaw, pitch, and roll)</source>
+ <translation>Rotationsfilterung (Gieren, Nicken und Rollen)</translation>
+ </message>
+ <message>
+ <source>0°</source>
+ <translation>0°</translation>
+ </message>
+</context>
+<context>
+ <name>accela_hamiltonDll</name>
+ <message>
+ <source>AccelaHamilton</source>
+ <translation>AccelaHamilton</translation>
+ </message>
+</context>
+<context>
+ <name>dialog_accela_hamilton</name>
+ <message>
+ <source>%1°</source>
+ <translation>%1°</translation>
+ </message>
+ <message>
+ <source>%1mm</source>
+ <translation>%1 mm</translation>
+ </message>
+</context>
+</TS>
diff --git a/filter-accela-hamilton/lang/nl_NL.ts b/filter-accela-hamilton/lang/nl_NL.ts
new file mode 100644
index 00000000..13315922
--- /dev/null
+++ b/filter-accela-hamilton/lang/nl_NL.ts
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="nl_NL">
+<context>
+ <name>AccelaUICdialog_accela</name>
+ <message>
+ <source>Filter settings</source>
+ <translation type="unfinished">Filter-instellingen</translation>
+ </message>
+ <message>
+ <source>Smoothing</source>
+ <translation type="unfinished">Verzachten</translation>
+ </message>
+ <message>
+ <source>Position filtering (X, Y, Z - translation)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>0mm</source>
+ <translation type="unfinished">0mm</translation>
+ </message>
+ <message>
+ <source>Deadzone</source>
+ <translation type="unfinished">Deadzone</translation>
+ </message>
+ <message>
+ <source>Rotation filtering (Yaw, pitch, and roll)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>0°</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Rotation Smoothing (Added when zoomed)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>0° </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Max Z</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Max Added</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;Ubuntu&apos;; font-size:10pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Accela + Hamilton Filter&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-weight:600;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;For both rotations and positions: No movement occurs within the dead zone.&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;For larger movements, rotation and translation occurs toward the new measurement. The strength is determined by the Smoothing setting and the builtin Accela gain curves.&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;When you lean forward, smoothing is added up to the set maximum at the distance of Max Z.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>accela_hamiltonDll</name>
+ <message>
+ <source>AccelaHamilton</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>dialog_accela_hamilton</name>
+ <message>
+ <source>%1°</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1mm</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/filter-accela-hamilton/lang/ru_RU.ts b/filter-accela-hamilton/lang/ru_RU.ts
new file mode 100644
index 00000000..7dd3e1eb
--- /dev/null
+++ b/filter-accela-hamilton/lang/ru_RU.ts
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="ru_RU">
+<context>
+ <name>AccelaUICdialog_accela</name>
+ <message>
+ <source>Filter settings</source>
+ <translation>Настройка фильтра</translation>
+ </message>
+ <message>
+ <source>Smoothing</source>
+ <translation>Сглаживание</translation>
+ </message>
+ <message>
+ <source>Position filtering (X, Y, Z - translation)</source>
+ <translation>Фильтрация смещений (X, Y, Z)</translation>
+ </message>
+ <message>
+ <source>0mm</source>
+ <translation>0мм</translation>
+ </message>
+ <message>
+ <source>Deadzone</source>
+ <translation>Мертвая зона</translation>
+ </message>
+ <message>
+ <source>Rotation filtering (Yaw, pitch, and roll)</source>
+ <translation>Фильтрация поворотов (Рысканье, тангаж, крен)</translation>
+ </message>
+ <message>
+ <source>0°</source>
+ <translation></translation>
+ </message>
+ <message>
+ <source>Rotation Smoothing (Added when zoomed)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>0° </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Max Z</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Max Added</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;Ubuntu&apos;; font-size:10pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Accela + Hamilton Filter&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-weight:600;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;For both rotations and positions: No movement occurs within the dead zone.&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;For larger movements, rotation and translation occurs toward the new measurement. The strength is determined by the Smoothing setting and the builtin Accela gain curves.&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;When you lean forward, smoothing is added up to the set maximum at the distance of Max Z.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>accela_hamiltonDll</name>
+ <message>
+ <source>AccelaHamilton</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>dialog_accela_hamilton</name>
+ <message>
+ <source>%1°</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1mm</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/filter-accela-hamilton/lang/stub.ts b/filter-accela-hamilton/lang/stub.ts
new file mode 100644
index 00000000..2cd62df5
--- /dev/null
+++ b/filter-accela-hamilton/lang/stub.ts
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1">
+<context>
+ <name>AccelaUICdialog_accela</name>
+ <message>
+ <source>Filter settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Smoothing</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Position filtering (X, Y, Z - translation)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>0mm</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Deadzone</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Rotation filtering (Yaw, pitch, and roll)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>0°</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Rotation Smoothing (Added when zoomed)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>0° </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Max Z</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Max Added</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;Ubuntu&apos;; font-size:10pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Accela + Hamilton Filter&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-weight:600;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;For both rotations and positions: No movement occurs within the dead zone.&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;For larger movements, rotation and translation occurs toward the new measurement. The strength is determined by the Smoothing setting and the builtin Accela gain curves.&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;When you lean forward, smoothing is added up to the set maximum at the distance of Max Z.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>accela_hamiltonDll</name>
+ <message>
+ <source>AccelaHamilton</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>dialog_accela_hamilton</name>
+ <message>
+ <source>%1°</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1mm</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/filter-accela-hamilton/lang/zh_CN.ts b/filter-accela-hamilton/lang/zh_CN.ts
new file mode 100644
index 00000000..43a12f5a
--- /dev/null
+++ b/filter-accela-hamilton/lang/zh_CN.ts
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="zh_CN">
+<context>
+ <name>AccelaUICdialog_accela</name>
+ <message>
+ <source>Filter settings</source>
+ <translation>过滤器设置</translation>
+ </message>
+ <message>
+ <source>Rotation filtering (Yaw, pitch, and roll)</source>
+ <translation>旋转过滤器 (偏航, 俯仰, 滚转)</translation>
+ </message>
+ <message>
+ <source>Smoothing</source>
+ <translation type="unfinished">平滑</translation>
+ </message>
+ <message>
+ <source>0°</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Deadzone</source>
+ <translation>死区</translation>
+ </message>
+ <message>
+ <source>Position filtering (X, Y, Z - translation)</source>
+ <translation>方位过滤器 (X, Y, Z - 变换)</translation>
+ </message>
+ <message>
+ <source>0mm</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Rotation Smoothing (Added when zoomed)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>0° </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Max Z</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Max Added</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;Ubuntu&apos;; font-size:10pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Accela + Hamilton Filter&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-weight:600;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;For both rotations and positions: No movement occurs within the dead zone.&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;For larger movements, rotation and translation occurs toward the new measurement. The strength is determined by the Smoothing setting and the builtin Accela gain curves.&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;When you lean forward, smoothing is added up to the set maximum at the distance of Max Z.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>accela_hamiltonDll</name>
+ <message>
+ <source>AccelaHamilton</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>dialog_accela_hamilton</name>
+ <message>
+ <source>%1°</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1mm</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/filter-accela/ftnoir_accela_filtercontrols.ui b/filter-accela/ftnoir_accela_filtercontrols.ui
index ce9f52a2..899f4920 100644
--- a/filter-accela/ftnoir_accela_filtercontrols.ui
+++ b/filter-accela/ftnoir_accela_filtercontrols.ui
@@ -10,7 +10,7 @@
<x>0</x>
<y>0</y>
<width>550</width>
- <height>275</height>
+ <height>279</height>
</rect>
</property>
<property name="sizePolicy">
@@ -276,6 +276,19 @@
<zorder>label</zorder>
</widget>
</item>
+ <item row="3" column="0">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
<item row="2" column="0">
<widget class="QLabel" name="label_9">
<property name="sizePolicy">
@@ -321,18 +334,18 @@
</property>
</widget>
</item>
- <item row="3" column="0">
- <widget class="QDialogButtonBox" name="buttonBox">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
+ <item row="4" column="0">
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
</property>
- <property name="standardButtons">
- <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>1</height>
+ </size>
</property>
- </widget>
+ </spacer>
</item>
</layout>
</widget>
diff --git a/filter-accela/ftnoir_filter_accela.cpp b/filter-accela/ftnoir_filter_accela.cpp
index b70f08cf..940ac50c 100644
--- a/filter-accela/ftnoir_filter_accela.cpp
+++ b/filter-accela/ftnoir_filter_accela.cpp
@@ -7,6 +7,7 @@
#include "ftnoir_filter_accela.h"
#include "compat/math.hpp"
#include "api/plugin-api.hpp"
+#include "opentrack/defs.hpp"
#include <algorithm>
#include <QDebug>
@@ -37,7 +38,7 @@ static void do_deltas(const double* deltas, double* output, F&& fun)
for (unsigned k = 0; k < N; k++)
{
- const double c = dist > 1e-6 ? clamp((fabs(deltas[k]) / dist), 0., 1.) : 0;
+ const double c = dist > 1e-6 ? std::clamp((fabs(deltas[k]) / dist), 0., 1.) : 0;
norm[k] = c;
}
@@ -62,6 +63,14 @@ static void do_deltas(const double* deltas, double* output, F&& fun)
}
}
+template<typename F>
+[[maybe_unused]]
+never_inline
+static void do_delta(double delta, double* output, F&& fun)
+{
+ *output = fun(fabs(delta)) * signum(delta);
+}
+
void accela::filter(const double* input, double *output)
{
static constexpr double full_turn = 360.0;
@@ -111,9 +120,16 @@ void accela::filter(const double* input, double *output)
deltas[i] = d / rot_thres;
}
+#ifdef UI_ACCELA_OLD_STAIRCASE
+ for (int i = Yaw; i <= Roll; i++)
+ do_delta(deltas[i], &output[i], [this](double x) {
+ return spline_rot.get_value_no_save(x);
+ });
+#else
do_deltas(&deltas[Yaw], &output[Yaw], [this](double x) {
return spline_rot.get_value_no_save(x);
});
+#endif
// pos
@@ -138,7 +154,8 @@ void accela::filter(const double* input, double *output)
{
output[k] *= dt;
output[k] += last_output[k];
- if (fabs(output[k]) > half_turn) output[k] -= copysign(full_turn, output[k]);
+ if (k >= Yaw && fabs(output[k]) > half_turn)
+ output[k] -= copysign(full_turn, output[k]);
last_output[k] = output[k];
}
diff --git a/filter-accela/ftnoir_filter_accela.h b/filter-accela/ftnoir_filter_accela.h
index 6471a7b8..0d6dca1b 100644
--- a/filter-accela/ftnoir_filter_accela.h
+++ b/filter-accela/ftnoir_filter_accela.h
@@ -12,7 +12,6 @@
#include "api/plugin-api.hpp"
#include "compat/timer.hpp"
#include "compat/variance.hpp"
-#include "compat/macros.hpp"
#include <QMutex>
#include <QTimer>
@@ -45,9 +44,12 @@ public:
dialog_accela();
void register_filter(IFilter*) override {}
void unregister_filter() override {}
+ void save() override;
+ void reload() override;
+ bool embeddable() noexcept override { return true; }
+ void set_buttons_visible(bool x) override;
private:
Ui::AccelaUICdialog_accela ui;
- void save();
settings_accela s;
private slots:
void doOK();
diff --git a/filter-accela/ftnoir_filter_accela_dialog.cpp b/filter-accela/ftnoir_filter_accela_dialog.cpp
index 37f265e1..e87fdc03 100644
--- a/filter-accela/ftnoir_filter_accela_dialog.cpp
+++ b/filter-accela/ftnoir_filter_accela_dialog.cpp
@@ -27,10 +27,10 @@ dialog_accela::dialog_accela()
tie_setting(s.rot_deadzone, ui.rot_dz_slider);
tie_setting(s.pos_deadzone, ui.trans_dz_slider);
- tie_setting(s.rot_smoothing, ui.rot_gain, [](const slider_value& s) { return tr("%1°").arg(s, 0, 'g', 4); });
- tie_setting(s.pos_smoothing, ui.trans_gain, [](const slider_value& s) { return tr("%1mm").arg(s, 0, 'g', 4); });
- tie_setting(s.rot_deadzone, ui.rot_dz, [](const slider_value& s) { return tr("%1°").arg(s, 0, 'g', 4); });
- tie_setting(s.pos_deadzone, ui.trans_dz, [](const slider_value& s) { return tr("%1mm").arg(s); });
+ tie_setting(s.rot_smoothing, ui.rot_gain, [](const slider_value& s) { return tr("%1°").arg(s.cur(), 0, 'g', 4); });
+ tie_setting(s.pos_smoothing, ui.trans_gain, [](const slider_value& s) { return tr("%1mm").arg(s.cur(), 0, 'g', 4); });
+ tie_setting(s.rot_deadzone, ui.rot_dz, [](const slider_value& s) { return tr("%1°").arg(s.cur(), 0, 'g', 4); });
+ tie_setting(s.pos_deadzone, ui.trans_dz, [](const slider_value& s) { return tr("%1mm").arg(s.cur()); });
//#define SPLINE_ROT_DEBUG
//#define SPLINE_TRANS_DEBUG
@@ -78,3 +78,13 @@ void dialog_accela::save()
{
s.b->save();
}
+
+void dialog_accela::reload()
+{
+ s.b->reload();
+}
+
+void dialog_accela::set_buttons_visible(bool x)
+{
+ ui.buttonBox->setVisible(x);
+}
diff --git a/filter-accela/lang/de_DE.ts b/filter-accela/lang/de_DE.ts
new file mode 100644
index 00000000..9ab4e8c5
--- /dev/null
+++ b/filter-accela/lang/de_DE.ts
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>AccelaUICdialog_accela</name>
+ <message>
+ <source>Filter settings</source>
+ <translation>Filter-Einstellungen</translation>
+ </message>
+ <message>
+ <source>Rotation filtering (Yaw, pitch, and roll)</source>
+ <translation>Rotationsfilterung (Gieren, Nicken und Rollen)</translation>
+ </message>
+ <message>
+ <source>Smoothing</source>
+ <translation>Glättung</translation>
+ </message>
+ <message>
+ <source>0°</source>
+ <translation>0°</translation>
+ </message>
+ <message>
+ <source>Deadzone</source>
+ <translation>Totbereich</translation>
+ </message>
+ <message>
+ <source>Position filtering (X, Y, Z - translation)</source>
+ <translation>Positionsfilterung (X, Y, Z-Übersetzung)</translation>
+ </message>
+ <message>
+ <source>0mm</source>
+ <translation>0 mm</translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;right&quot;&gt;&lt;br/&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Accela by &lt;/span&gt;&lt;a href=&quot;https://github.com/sthalik&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0057ae;&quot;&gt;Stanisław Halik&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;&lt;br/&gt;Thanks to &lt;/span&gt;&lt;a href=&quot;https://github.com/dbaarda&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0057ae;&quot;&gt;Donovan Baarda&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;2012-2017&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;br/&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Visit &lt;/span&gt;&lt;a href=&quot;https://github.com/opentrack/opentrack/wiki/Accela-in-opentrack-2.3&quot;&gt;&lt;span style=&quot; font-size:8pt; text-decoration: underline; color:#0000ff;&quot;&gt;our wiki&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; for description of the settings.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;right&quot;&gt;&lt;br/&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Accela by &lt;/span&gt;&lt;a href=&quot;https://github.com/sthalik&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0057ae;&quot;&gt;Stanisław Halik&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;&lt;br/&gt;Thanks to &lt;/span&gt;&lt;a href=&quot;https://github.com/dbaarda&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0057ae;&quot;&gt;Donovan Baarda&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;2012-2017&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;br/&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;In &lt;/span&gt;&lt;a href=&quot;https://github.com/opentrack/opentrack/wiki/Accela-in-opentrack-2.3&quot;&gt;&lt;span style=&quot; font-size:8pt; text-decoration: underline; color:#0000ff;&quot;&gt;unserem Wiki&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; gibt es eine Beschreibung der Einstellungen.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+</context>
+<context>
+ <name>accelaDll</name>
+ <message>
+ <source>Accela</source>
+ <translation>Accela</translation>
+ </message>
+</context>
+<context>
+ <name>dialog_accela</name>
+ <message>
+ <source>%1°</source>
+ <translation>%1°</translation>
+ </message>
+ <message>
+ <source>%1mm</source>
+ <translation>%1 mm</translation>
+ </message>
+</context>
+</TS>
diff --git a/filter-accela/lang/zh_CN.ts b/filter-accela/lang/zh_CN.ts
index 4b77b3d7..1deb8288 100644
--- a/filter-accela/lang/zh_CN.ts
+++ b/filter-accela/lang/zh_CN.ts
@@ -1,19 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
<context>
<name>AccelaUICdialog_accela</name>
<message>
<source>Filter settings</source>
- <translation type="unfinished"></translation>
+ <translation>过滤器设置</translation>
</message>
<message>
<source>Rotation filtering (Yaw, pitch, and roll)</source>
- <translation type="unfinished"></translation>
+ <translation>旋转过滤器 (偏航, 俯仰, 滚转)</translation>
</message>
<message>
<source>Smoothing</source>
- <translation type="unfinished"></translation>
+ <translation type="unfinished">平滑</translation>
</message>
<message>
<source>0°</source>
@@ -21,11 +21,11 @@
</message>
<message>
<source>Deadzone</source>
- <translation type="unfinished"></translation>
+ <translation>死区</translation>
</message>
<message>
<source>Position filtering (X, Y, Z - translation)</source>
- <translation type="unfinished"></translation>
+ <translation>方位过滤器 (X, Y, Z - 变换)</translation>
</message>
<message>
<source>0mm</source>
@@ -33,14 +33,39 @@
</message>
<message>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;right&quot;&gt;&lt;br/&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Accela by &lt;/span&gt;&lt;a href=&quot;https://github.com/sthalik&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0057ae;&quot;&gt;Stanisław Halik&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;&lt;br/&gt;Thanks to &lt;/span&gt;&lt;a href=&quot;https://github.com/dbaarda&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0057ae;&quot;&gt;Donovan Baarda&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;2012-2017&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;br/&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Visit &lt;/span&gt;&lt;a href=&quot;https://github.com/opentrack/opentrack/wiki/Accela-in-opentrack-2.3&quot;&gt;&lt;span style=&quot; font-size:8pt; text-decoration: underline; color:#0000ff;&quot;&gt;our wiki&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; for description of the settings.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
- <translation type="unfinished"></translation>
+ <translation type="unfinished">&lt;html&gt;
+
+&lt;body&gt;
+ &lt;p align=&quot;right&quot;&gt;&lt;br /&gt;
+ &lt;span style=&quot; font-size:10pt;&quot;&gt;Accela by &lt;/span&gt;
+ &lt;a href=&quot;https://github.com/sthalik&quot;&gt;
+ &lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0057ae;&quot;&gt;Stanisław Halik&lt;/span&gt;
+ &lt;/a&gt;
+ &lt;span style=&quot; font-size:10pt;&quot;&gt;&lt;br /&gt;Thanks to &lt;/span&gt;
+ &lt;a href=&quot;https://github.com/dbaarda&quot;&gt;
+ &lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0057ae;&quot;&gt;Donovan Baarda&lt;/span&gt;
+ &lt;/a&gt;
+ &lt;/p&gt;
+ &lt;p align=&quot;right&quot;&gt;
+ &lt;span style=&quot; font-size:10pt;&quot;&gt;2012-2017&lt;/span&gt;
+ &lt;/p&gt;
+ &lt;p align=&quot;right&quot;&gt;&lt;br /&gt;
+ &lt;span style=&quot; font-size:8pt;&quot;&gt;访问&lt;/span&gt;
+ &lt;a href=&quot;https://github.com/opentrack/opentrack/wiki/Accela-in-opentrack-2.3&quot;&gt;
+ &lt;span style=&quot; font-size:8pt; text-decoration: underline; color:#0066bb;&quot;&gt;我们的 wiki&lt;/span&gt;
+ &lt;/a&gt;
+ &lt;span style=&quot; font-size:8pt;&quot;&gt; 了解设置的描述.&lt;/span&gt;
+ &lt;/p&gt;
+&lt;/body&gt;
+
+&lt;/html&gt;</translation>
</message>
</context>
<context>
<name>accelaDll</name>
<message>
<source>Accela</source>
- <translation type="unfinished"></translation>
+ <translation></translation>
</message>
</context>
<context>
diff --git a/filter-ewma2/ftnoir_ewma_filtercontrols.ui b/filter-ewma2/ftnoir_ewma_filtercontrols.ui
index e0a98174..513a8ec3 100644
--- a/filter-ewma2/ftnoir_ewma_filtercontrols.ui
+++ b/filter-ewma2/ftnoir_ewma_filtercontrols.ui
@@ -245,10 +245,6 @@
</item>
<item>
<widget class="QLabel" name="label_4">
- <property name="styleSheet">
- <string notr="true">background-color: rgb(214, 214, 214);
-border-color: rgb(0, 0, 0);</string>
- </property>
<property name="frameShape">
<enum>QFrame::Box</enum>
</property>
@@ -257,7 +253,7 @@ border-color: rgb(0, 0, 0);</string>
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;&quot;&gt;
-&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Sans Serif'; font-size:10pt; font-weight:600;&quot;&gt;Give the filter up to 60 seconds to warm up and stop shaking.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Sans Serif'; font-size:10pt; font-weight:600;&quot;&gt;Give the filter a few seconds to warm up and stop shaking.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Min:&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Defines the way the filter responds to fast movements;&lt;/span&gt;&lt;/p&gt;
diff --git a/filter-ewma2/ftnoir_filter_ewma2.cpp b/filter-ewma2/ftnoir_filter_ewma2.cpp
index 25902280..686552ba 100644
--- a/filter-ewma2/ftnoir_filter_ewma2.cpp
+++ b/filter-ewma2/ftnoir_filter_ewma2.cpp
@@ -33,17 +33,17 @@ void ewma::filter(const double *input, double *output)
{
first_run = false;
timer.start();
- for (int i=0;i<6;i++)
- {
- last_output[i] = input[i];
- last_delta[i] = 0;
- last_noise[i] = 0;
- }
+ noise_RC = 0.0;
+ std::copy(input, input + 6, last_output);
+ std::fill(last_delta, last_delta + 6, 0.0);
+ std::fill(last_noise, last_noise + 6, 0.0);
+ return;
}
// Get the time in seconds since last run and restart the timer.
const double dt = timer.elapsed_seconds();
timer.start();
// Calculate delta_alpha and noise_alpha from dt.
+ noise_RC = std::min(noise_RC + dt, noise_RC_max);
double delta_alpha = dt/(dt + delta_RC);
double noise_alpha = dt/(dt + noise_RC);
@@ -61,12 +61,12 @@ void ewma::filter(const double *input, double *output)
// Calculate the current and smoothed delta.
double input_value = input[i];
double delta = input_value - last_output[i];
- if (fabs(delta) > half_turn)
+ if (i >= 3 && fabs(delta) > half_turn)
{
delta -= copysign(full_turn, delta);
input_value -= copysign(full_turn, input_value);
}
- last_delta[i] = delta_alpha*delta + (1.0-delta_alpha)*last_delta[i];
+ last_delta[i] += delta_alpha * (delta - last_delta[i]);
// Calculate the current and smoothed noise variance.
double noise = last_delta[i]*last_delta[i];
last_noise[i] = noise_alpha*noise + (1.0-noise_alpha)*last_noise[i];
@@ -78,9 +78,8 @@ void ewma::filter(const double *input, double *output)
// Calculate the dynamic alpha.
double alpha = dt/(dt + RC);
// Calculate the new output position.
- output[i] = alpha*input_value + (1.0-alpha)*last_output[i];
- if (fabs(output[i]) > half_turn) output[i] -= copysign(full_turn, output[i]);
- last_output[i] = output[i];
+ last_output[i] += alpha * (input_value - last_output[i]);
+ output[i] = last_output[i];
}
}
diff --git a/filter-ewma2/ftnoir_filter_ewma2.h b/filter-ewma2/ftnoir_filter_ewma2.h
index 1cd30a6b..5698157d 100644
--- a/filter-ewma2/ftnoir_filter_ewma2.h
+++ b/filter-ewma2/ftnoir_filter_ewma2.h
@@ -28,9 +28,10 @@ public:
module_status initialize() override { return status_ok(); }
private:
// Deltas are smoothed over the last 1/60sec.
- const double delta_RC = 1./60;
+ static constexpr double delta_RC = 1./60;
// Noise is smoothed over the last 60sec.
- const double noise_RC = 60.0;
+ static constexpr double noise_RC_max = 60.0;
+ double noise_RC = 0.0;
double last_delta[6];
double last_noise[6];
double last_output[6];
diff --git a/filter-ewma2/lang/de_DE.ts b/filter-ewma2/lang/de_DE.ts
new file mode 100644
index 00000000..be6712e6
--- /dev/null
+++ b/filter-ewma2/lang/de_DE.ts
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>UICdialog_ewma</name>
+ <message>
+ <source>EWMA filter settings</source>
+ <translation>EWMA-Filtereinstellungen</translation>
+ </message>
+ <message>
+ <source>Max</source>
+ <translation>Maximum</translation>
+ </message>
+ <message>
+ <source>Min</source>
+ <translation>Minimum</translation>
+ </message>
+ <message>
+ <source>Curve</source>
+ <translation>Kurve</translation>
+ </message>
+ <message>
+ <source>100%</source>
+ <translation>100%</translation>
+ </message>
+ <message>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:8pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Sans Serif&apos;; font-size:10pt; font-weight:600;&quot;&gt;Give the filter a few seconds to warm up and stop shaking.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Min:&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Defines the way the filter responds to fast movements;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Higher value: slower response;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Max:&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Defines the way the filter responds to slow movements;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Higher value: slower response;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Pow:&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Defines the filters &apos;readiness&apos; to respond to speed changes;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Higher value = &lt;/span&gt;&lt;span style=&quot; font-size:10pt; font-weight:600;&quot;&gt;faster&lt;/span&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt; response;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:8pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Sans Serif&apos;; font-size:10pt; font-weight:600;&quot;&gt;Bitte einige Sekunden warten, bis der Filter warm gelaufen ist und nicht mehr zittert.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Minimum:&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Definiert, wie der Filter auf schnelle Bewegungen reagiert;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Höherer Wert: langsameres Ansprechen;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Maximum:&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Definiert, wie der Filter auf langsame Bewegungen reagiert;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Höherer Wert: langsameres Ansprechen;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Stärke:&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Definiert die &apos;Bereitschaft&apos; des Filters, auf Geschwindigkeitsänderungen zu reagieren;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Höherer Wert = &lt;/span&gt;&lt;span style=&quot; font-size:10pt; font-weight:600;&quot;&gt;schnelleres&lt;/span&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt; Ansprechen;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+</context>
+<context>
+ <name>ewmaDll</name>
+ <message>
+ <source>EWMA</source>
+ <translation>EWMA</translation>
+ </message>
+</context>
+</TS>
diff --git a/filter-ewma2/lang/nl_NL.ts b/filter-ewma2/lang/nl_NL.ts
index b4153053..8c718b7d 100644
--- a/filter-ewma2/lang/nl_NL.ts
+++ b/filter-ewma2/lang/nl_NL.ts
@@ -28,7 +28,7 @@
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:8pt; font-weight:400; font-style:normal;&quot;&gt;
-&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Sans Serif&apos;; font-size:10pt; font-weight:600;&quot;&gt;Give the filter up to 60 seconds to warm up and stop shaking.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Sans Serif&apos;; font-size:10pt; font-weight:600;&quot;&gt;Give the filter a few seconds to warm up and stop shaking.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Min:&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Defines the way the filter responds to fast movements;&lt;/span&gt;&lt;/p&gt;
diff --git a/filter-ewma2/lang/ru_RU.ts b/filter-ewma2/lang/ru_RU.ts
index f8517c87..14afb40e 100644
--- a/filter-ewma2/lang/ru_RU.ts
+++ b/filter-ewma2/lang/ru_RU.ts
@@ -28,7 +28,7 @@
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:8pt; font-weight:400; font-style:normal;&quot;&gt;
-&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Sans Serif&apos;; font-size:10pt; font-weight:600;&quot;&gt;Give the filter up to 60 seconds to warm up and stop shaking.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Sans Serif&apos;; font-size:10pt; font-weight:600;&quot;&gt;Give the filter a few seconds to warm up and stop shaking.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Min:&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Defines the way the filter responds to fast movements;&lt;/span&gt;&lt;/p&gt;
diff --git a/filter-ewma2/lang/stub.ts b/filter-ewma2/lang/stub.ts
index dc39f91f..fcce862e 100644
--- a/filter-ewma2/lang/stub.ts
+++ b/filter-ewma2/lang/stub.ts
@@ -28,7 +28,7 @@
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:8pt; font-weight:400; font-style:normal;&quot;&gt;
-&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Sans Serif&apos;; font-size:10pt; font-weight:600;&quot;&gt;Give the filter up to 60 seconds to warm up and stop shaking.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Sans Serif&apos;; font-size:10pt; font-weight:600;&quot;&gt;Give the filter a few seconds to warm up and stop shaking.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Min:&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Defines the way the filter responds to fast movements;&lt;/span&gt;&lt;/p&gt;
diff --git a/filter-ewma2/lang/zh_CN.ts b/filter-ewma2/lang/zh_CN.ts
index dc39f91f..b709f5c3 100644
--- a/filter-ewma2/lang/zh_CN.ts
+++ b/filter-ewma2/lang/zh_CN.ts
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
<context>
<name>UICdialog_ewma</name>
<message>
@@ -28,7 +28,7 @@
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:8pt; font-weight:400; font-style:normal;&quot;&gt;
-&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Sans Serif&apos;; font-size:10pt; font-weight:600;&quot;&gt;Give the filter up to 60 seconds to warm up and stop shaking.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Sans Serif&apos;; font-size:10pt; font-weight:600;&quot;&gt;Give the filter a few seconds to warm up and stop shaking.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Min:&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Defines the way the filter responds to fast movements;&lt;/span&gt;&lt;/p&gt;
@@ -42,7 +42,24 @@ p, li { white-space: pre-wrap; }
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Defines the filters &apos;readiness&apos; to respond to speed changes;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Higher value = &lt;/span&gt;&lt;span style=&quot; font-size:10pt; font-weight:600;&quot;&gt;faster&lt;/span&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt; response;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
- <translation type="unfinished"></translation>
+ <translation type="unfinished">&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:8pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Sans Serif&apos;; font-size:10pt; font-weight:600;&quot;&gt;给过滤器一点时间来完成预热与消除抖动. &lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Min:&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;定义滤波器对快速运动的响应方式; &lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;数值越大, 响应越慢; &lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Max:&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;定义滤波器对慢速运动的响应方式; &lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;数值越大, 响应越慢; &lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Pow:&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;定义滤波器&apos;readiness&apos; to respond to speed changes;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;更大数值 = &lt;/span&gt;&lt;span style=&quot; font-size:10pt; font-weight:600;&quot;&gt;更快&lt;/span&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt; 响应;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
</context>
<context>
diff --git a/filter-hamilton/ftnoir_filter_hamilton.cpp b/filter-hamilton/ftnoir_filter_hamilton.cpp
index be3faa7f..c4429ecc 100644
--- a/filter-hamilton/ftnoir_filter_hamilton.cpp
+++ b/filter-hamilton/ftnoir_filter_hamilton.cpp
@@ -9,7 +9,7 @@
#include <cmath>
#include <QMutexLocker>
#include "api/plugin-api.hpp"
-#include "hamilton-tools.h"
+#include "compat/hamilton-tools.h"
hamilton::hamilton() = default;
@@ -34,11 +34,15 @@ void hamilton::filter(const double *input, double *output)
double dist = VectorDistance( &input[TX], pos_last);
double alpha = (dist - pos_deadzone) / (pos_max + pos_deadzone + EPSILON);
- alpha = fmin(alpha, 1.0);
- alpha = fmax(alpha, 0.0);
- alpha = pow (alpha, pos_pow);
- alpha = alpha * (dist - pos_deadzone) / (dist + EPSILON);
-
+ alpha = std::min(1.0, std::max(0.0, alpha));
+ if (alpha > 0.0)
+ alpha = pow(alpha, pos_pow);
+ // Scale alpha so that alpha * dist <= dist - pos_deadzone. This ensures that
+ // the center of the deadzone will never move closer to the input position than
+ // distance dist. And this ensures that the view never jumps ahead of head
+ // movements.
+ alpha *= (dist - pos_deadzone) / (dist + EPSILON);
+
pos_last = Lerp(pos_last, input, alpha);
output[TX] = pos_last.v[0];
@@ -48,11 +52,11 @@ void hamilton::filter(const double *input, double *output)
// zoom smoothing:
const double pow_zoom {s.kPowZoom};
const double max_z {s.kMaxZ};
- double rot_zoom = pow_zoom;
+ double rot_zoom = pow_zoom;
if (output[TZ] > 0) rot_zoom = 0;
- else rot_zoom *= -output[TZ] / (max_z + EPSILON);
- rot_zoom = fmin( rot_zoom, pow_zoom );
+ else rot_zoom *= -output[TZ] / (max_z + EPSILON);
+ rot_zoom = fmin( rot_zoom, pow_zoom );
// rotations:
const double rot_max {s.kMaxRot};
@@ -62,10 +66,11 @@ void hamilton::filter(const double *input, double *output)
double angle = AngleBetween(quat_input, quat_last);
alpha = (angle - rot_deadzone) / (rot_max + rot_deadzone + EPSILON);
- alpha = fmin(alpha, 1.0);
- alpha = fmax(alpha, 0.0);
- alpha = pow (alpha, rot_pow + rot_zoom);
- alpha = alpha * (angle - rot_deadzone) / (angle + EPSILON);
+ alpha = std::min(1.0, std::max(0.0, alpha));
+ if (alpha > 0.0)
+ alpha = pow(alpha, rot_pow + rot_zoom);
+ // see comment in earlier alpha calculation above
+ alpha *= (angle - rot_deadzone) / (angle + EPSILON);
quat_last = Slerp(quat_last, quat_input, alpha);
diff --git a/filter-hamilton/ftnoir_filter_hamilton.h b/filter-hamilton/ftnoir_filter_hamilton.h
index 0756c216..199eef80 100644
--- a/filter-hamilton/ftnoir_filter_hamilton.h
+++ b/filter-hamilton/ftnoir_filter_hamilton.h
@@ -13,7 +13,7 @@
#include <QMutex>
#include "options/options.hpp"
//#include "compat/timer.hpp"
-#include "hamilton-tools.h"
+#include "compat/hamilton-tools.h"
using namespace options;
@@ -43,7 +43,7 @@ public:
module_status initialize() override { return status_ok(); }
private:
tQuat quat_last;
- tVector pos_last;
+ tVector pos_last;
settings s;
bool first_run = true;
};
diff --git a/filter-hamilton/ftnoir_hamilton_filtercontrols.ui b/filter-hamilton/ftnoir_hamilton_filtercontrols.ui
index 71cdb6da..4c8b1536 100644
--- a/filter-hamilton/ftnoir_hamilton_filtercontrols.ui
+++ b/filter-hamilton/ftnoir_hamilton_filtercontrols.ui
@@ -13,7 +13,7 @@
<x>0</x>
<y>0</y>
<width>514</width>
- <height>491</height>
+ <height>563</height>
</rect>
</property>
<property name="sizePolicy">
@@ -50,8 +50,8 @@
<property name="styleSheet">
<string notr="true"/>
</property>
- <layout class="QVBoxLayout" name="verticalLayout">
- <item>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="2" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
@@ -65,322 +65,286 @@
<height>150</height>
</size>
</property>
- <property name="font">
- <font>
- <pointsize>8</pointsize>
- <weight>50</weight>
- <bold>false</bold>
- </font>
- </property>
<property name="title">
<string>Rotations: </string>
</property>
<property name="flat">
<bool>false</bool>
</property>
- <widget class="QSlider" name="maxRot">
- <property name="geometry">
- <rect>
- <x>103</x>
- <y>30</y>
- <width>311</width>
- <height>20</height>
- </rect>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="focusPolicy">
- <enum>Qt::StrongFocus</enum>
- </property>
- <property name="minimum">
- <number>0</number>
- </property>
- <property name="maximum">
- <number>250</number>
- </property>
- <property name="singleStep">
- <number>1</number>
- </property>
- <property name="pageStep">
- <number>50</number>
- </property>
- <property name="value">
- <number>100</number>
- </property>
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="tickPosition">
- <enum>QSlider::TicksBothSides</enum>
- </property>
- <property name="tickInterval">
- <number>50</number>
- </property>
- </widget>
- <widget class="QLabel" name="lbRmax">
- <property name="geometry">
- <rect>
- <x>7</x>
- <y>30</y>
- <width>91</width>
- <height>20</height>
- </rect>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- <property name="styleSheet">
- <string notr="true"/>
- </property>
- <property name="text">
- <string> Max distance:</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- </widget>
- <widget class="QLabel" name="lbMaxRot">
- <property name="geometry">
- <rect>
- <x>424</x>
- <y>30</y>
- <width>61</width>
- <height>20</height>
- </rect>
- </property>
- <property name="minimumSize">
- <size>
- <width>45</width>
- <height>0</height>
- </size>
- </property>
- <property name="toolTip">
- <string notr="true"/>
- </property>
- <property name="text">
- <string>10,00</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- </widget>
- <widget class="QLabel" name="lbRdz">
- <property name="geometry">
- <rect>
- <x>7</x>
- <y>110</y>
- <width>91</width>
- <height>20</height>
- </rect>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- <property name="styleSheet">
- <string notr="true"/>
- </property>
- <property name="text">
- <string> Dead Zone:</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- </widget>
- <widget class="QSlider" name="dzRot">
- <property name="geometry">
- <rect>
- <x>103</x>
- <y>110</y>
- <width>311</width>
- <height>20</height>
- </rect>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="focusPolicy">
- <enum>Qt::StrongFocus</enum>
- </property>
- <property name="minimum">
- <number>0</number>
- </property>
- <property name="maximum">
- <number>50</number>
- </property>
- <property name="singleStep">
- <number>1</number>
- </property>
- <property name="pageStep">
- <number>5</number>
- </property>
- <property name="value">
- <number>1</number>
- </property>
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="tickPosition">
- <enum>QSlider::TicksBothSides</enum>
- </property>
- <property name="tickInterval">
- <number>10</number>
- </property>
- </widget>
- <widget class="QLabel" name="lbDZRot">
- <property name="geometry">
- <rect>
- <x>424</x>
- <y>110</y>
- <width>61</width>
- <height>20</height>
- </rect>
- </property>
- <property name="minimumSize">
- <size>
- <width>45</width>
- <height>0</height>
- </size>
- </property>
- <property name="toolTip">
- <string notr="true"/>
- </property>
- <property name="text">
- <string>0,01</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- </widget>
- <widget class="QSlider" name="powRot">
- <property name="geometry">
- <rect>
- <x>103</x>
- <y>70</y>
- <width>311</width>
- <height>20</height>
- </rect>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="focusPolicy">
- <enum>Qt::StrongFocus</enum>
- </property>
- <property name="autoFillBackground">
- <bool>false</bool>
- </property>
- <property name="minimum">
- <number>0</number>
- </property>
- <property name="maximum">
- <number>400</number>
- </property>
- <property name="singleStep">
- <number>1</number>
- </property>
- <property name="pageStep">
- <number>50</number>
- </property>
- <property name="value">
- <number>200</number>
- </property>
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="tickPosition">
- <enum>QSlider::TicksBothSides</enum>
- </property>
- <property name="tickInterval">
- <number>100</number>
- </property>
- </widget>
- <widget class="QLabel" name="lbPowRot">
- <property name="geometry">
- <rect>
- <x>430</x>
- <y>70</y>
- <width>45</width>
- <height>20</height>
- </rect>
- </property>
- <property name="minimumSize">
- <size>
- <width>45</width>
- <height>0</height>
- </size>
- </property>
- <property name="toolTip">
- <string notr="true"/>
- </property>
- <property name="text">
- <string>2,00</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- </widget>
- <widget class="QLabel" name="lbRpow">
- <property name="geometry">
- <rect>
- <x>7</x>
- <y>70</y>
- <width>91</width>
- <height>20</height>
- </rect>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- <property name="styleSheet">
- <string notr="true"/>
- </property>
- <property name="text">
- <string> Smoothing:</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- </widget>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="8" column="2">
+ <widget class="QSlider" name="powRot">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="focusPolicy">
+ <enum>Qt::StrongFocus</enum>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="minimum">
+ <number>0</number>
+ </property>
+ <property name="maximum">
+ <number>400</number>
+ </property>
+ <property name="singleStep">
+ <number>1</number>
+ </property>
+ <property name="pageStep">
+ <number>50</number>
+ </property>
+ <property name="value">
+ <number>200</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="tickPosition">
+ <enum>QSlider::TicksBothSides</enum>
+ </property>
+ <property name="tickInterval">
+ <number>100</number>
+ </property>
+ </widget>
+ </item>
+ <item row="8" column="3">
+ <widget class="QLabel" name="lbPowRot">
+ <property name="minimumSize">
+ <size>
+ <width>45</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="toolTip">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string>2,00</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="2">
+ <widget class="QSlider" name="dzRot">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="focusPolicy">
+ <enum>Qt::StrongFocus</enum>
+ </property>
+ <property name="minimum">
+ <number>0</number>
+ </property>
+ <property name="maximum">
+ <number>50</number>
+ </property>
+ <property name="singleStep">
+ <number>1</number>
+ </property>
+ <property name="pageStep">
+ <number>5</number>
+ </property>
+ <property name="value">
+ <number>1</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="tickPosition">
+ <enum>QSlider::TicksBothSides</enum>
+ </property>
+ <property name="tickInterval">
+ <number>10</number>
+ </property>
+ </widget>
+ </item>
+ <item row="8" column="0">
+ <widget class="QLabel" name="lbRpow">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string>Smoothing power:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="0">
+ <widget class="QLabel" name="lbRdz">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string>Dead Zone:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="3">
+ <widget class="QLabel" name="lbDZRot">
+ <property name="minimumSize">
+ <size>
+ <width>45</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="toolTip">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string>0,01</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="0">
+ <widget class="QLabel" name="lbRmax">
+ <property name="text">
+ <string>Max distance:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="2">
+ <widget class="QSlider" name="maxRot">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="focusPolicy">
+ <enum>Qt::StrongFocus</enum>
+ </property>
+ <property name="minimum">
+ <number>0</number>
+ </property>
+ <property name="maximum">
+ <number>250</number>
+ </property>
+ <property name="singleStep">
+ <number>1</number>
+ </property>
+ <property name="pageStep">
+ <number>50</number>
+ </property>
+ <property name="value">
+ <number>100</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="tickPosition">
+ <enum>QSlider::TicksBothSides</enum>
+ </property>
+ <property name="tickInterval">
+ <number>50</number>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="3">
+ <widget class="QLabel" name="lbMaxRot">
+ <property name="minimumSize">
+ <size>
+ <width>45</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="toolTip">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string>10,00</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="6" column="0">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ <property name="centerButtons">
+ <bool>true</bool>
+ </property>
</widget>
</item>
- <item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>For both rotations and positions: No movement occurs within the dead zone. Smoothing scales down to minimum at max distance + 2 x dead zone.</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
<widget class="QGroupBox" name="groupBox_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
@@ -397,300 +361,248 @@
<property name="title">
<string>Positions: </string>
</property>
- <widget class="QSlider" name="maxDist">
- <property name="geometry">
- <rect>
- <x>103</x>
- <y>30</y>
- <width>311</width>
- <height>20</height>
- </rect>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="focusPolicy">
- <enum>Qt::StrongFocus</enum>
- </property>
- <property name="minimum">
- <number>0</number>
- </property>
- <property name="maximum">
- <number>200</number>
- </property>
- <property name="singleStep">
- <number>1</number>
- </property>
- <property name="pageStep">
- <number>50</number>
- </property>
- <property name="value">
- <number>100</number>
- </property>
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="tickPosition">
- <enum>QSlider::TicksBothSides</enum>
- </property>
- <property name="tickInterval">
- <number>50</number>
- </property>
- </widget>
- <widget class="QLabel" name="lbRpow_3">
- <property name="geometry">
- <rect>
- <x>7</x>
- <y>70</y>
- <width>91</width>
- <height>20</height>
- </rect>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- <property name="styleSheet">
- <string notr="true"/>
- </property>
- <property name="text">
- <string> Smoothing:</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- </widget>
- <widget class="QSlider" name="powDist">
- <property name="geometry">
- <rect>
- <x>103</x>
- <y>70</y>
- <width>311</width>
- <height>20</height>
- </rect>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="focusPolicy">
- <enum>Qt::StrongFocus</enum>
- </property>
- <property name="autoFillBackground">
- <bool>false</bool>
- </property>
- <property name="minimum">
- <number>0</number>
- </property>
- <property name="maximum">
- <number>400</number>
- </property>
- <property name="singleStep">
- <number>1</number>
- </property>
- <property name="pageStep">
- <number>50</number>
- </property>
- <property name="value">
- <number>100</number>
- </property>
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="tickPosition">
- <enum>QSlider::TicksBothSides</enum>
- </property>
- <property name="tickInterval">
- <number>100</number>
- </property>
- </widget>
- <widget class="QLabel" name="lbRdz_3">
- <property name="geometry">
- <rect>
- <x>7</x>
- <y>110</y>
- <width>91</width>
- <height>20</height>
- </rect>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- <property name="styleSheet">
- <string notr="true"/>
- </property>
- <property name="text">
- <string> Dead Zone:</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- </widget>
- <widget class="QLabel" name="lbPowDist">
- <property name="geometry">
- <rect>
- <x>430</x>
- <y>70</y>
- <width>51</width>
- <height>20</height>
- </rect>
- </property>
- <property name="minimumSize">
- <size>
- <width>40</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string>1,00</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- </widget>
- <widget class="QLabel" name="lbDmax">
- <property name="geometry">
- <rect>
- <x>7</x>
- <y>30</y>
- <width>91</width>
- <height>20</height>
- </rect>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- <property name="styleSheet">
- <string notr="true"/>
- </property>
- <property name="text">
- <string> Max distance:</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- </widget>
- <widget class="QSlider" name="dzDist">
- <property name="geometry">
- <rect>
- <x>103</x>
- <y>110</y>
- <width>311</width>
- <height>20</height>
- </rect>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="focusPolicy">
- <enum>Qt::StrongFocus</enum>
- </property>
- <property name="minimum">
- <number>0</number>
- </property>
- <property name="maximum">
- <number>50</number>
- </property>
- <property name="singleStep">
- <number>1</number>
- </property>
- <property name="pageStep">
- <number>10</number>
- </property>
- <property name="value">
- <number>2</number>
- </property>
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="tickPosition">
- <enum>QSlider::TicksBothSides</enum>
- </property>
- <property name="tickInterval">
- <number>10</number>
- </property>
- </widget>
- <widget class="QLabel" name="lbMaxDist">
- <property name="geometry">
- <rect>
- <x>420</x>
- <y>30</y>
- <width>71</width>
- <height>20</height>
- </rect>
- </property>
- <property name="minimumSize">
- <size>
- <width>45</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string>10,00</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- </widget>
- <widget class="QLabel" name="lbDZDist">
- <property name="geometry">
- <rect>
- <x>420</x>
- <y>110</y>
- <width>71</width>
- <height>20</height>
- </rect>
- </property>
- <property name="minimumSize">
- <size>
- <width>45</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string>0,02</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- </widget>
+ <layout class="QGridLayout" name="gridLayout_4">
+ <item row="1" column="2">
+ <widget class="QLabel" name="lbMaxDist">
+ <property name="minimumSize">
+ <size>
+ <width>45</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>10,00</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="lbRpow_3">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string>Smoothing power:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="lbDmax">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string>Max distance:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QSlider" name="maxDist">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="focusPolicy">
+ <enum>Qt::StrongFocus</enum>
+ </property>
+ <property name="minimum">
+ <number>0</number>
+ </property>
+ <property name="maximum">
+ <number>200</number>
+ </property>
+ <property name="singleStep">
+ <number>1</number>
+ </property>
+ <property name="pageStep">
+ <number>50</number>
+ </property>
+ <property name="value">
+ <number>100</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="tickPosition">
+ <enum>QSlider::TicksBothSides</enum>
+ </property>
+ <property name="tickInterval">
+ <number>50</number>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QSlider" name="powDist">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="focusPolicy">
+ <enum>Qt::StrongFocus</enum>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="minimum">
+ <number>0</number>
+ </property>
+ <property name="maximum">
+ <number>400</number>
+ </property>
+ <property name="singleStep">
+ <number>1</number>
+ </property>
+ <property name="pageStep">
+ <number>50</number>
+ </property>
+ <property name="value">
+ <number>100</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="tickPosition">
+ <enum>QSlider::TicksBothSides</enum>
+ </property>
+ <property name="tickInterval">
+ <number>100</number>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QLabel" name="lbPowDist">
+ <property name="minimumSize">
+ <size>
+ <width>40</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>1,00</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="lbRdz_3">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string>Dead Zone:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QSlider" name="dzDist">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="focusPolicy">
+ <enum>Qt::StrongFocus</enum>
+ </property>
+ <property name="minimum">
+ <number>0</number>
+ </property>
+ <property name="maximum">
+ <number>50</number>
+ </property>
+ <property name="singleStep">
+ <number>1</number>
+ </property>
+ <property name="pageStep">
+ <number>10</number>
+ </property>
+ <property name="value">
+ <number>2</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="tickPosition">
+ <enum>QSlider::TicksBothSides</enum>
+ </property>
+ <property name="tickInterval">
+ <number>10</number>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QLabel" name="lbDZDist">
+ <property name="minimumSize">
+ <size>
+ <width>45</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>0,02</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
</widget>
</item>
- <item>
+ <item row="5" column="0">
<widget class="QGroupBox" name="groupBox_3">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
@@ -707,227 +619,194 @@
<property name="title">
<string>Zoom smoothing: </string>
</property>
- <widget class="QLabel" name="lbPowZoom">
- <property name="geometry">
- <rect>
- <x>434</x>
- <y>30</y>
- <width>45</width>
- <height>20</height>
- </rect>
- </property>
- <property name="minimumSize">
- <size>
- <width>45</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string>2,00</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- </widget>
- <widget class="QSlider" name="powZoom">
- <property name="geometry">
- <rect>
- <x>103</x>
- <y>30</y>
- <width>311</width>
- <height>20</height>
- </rect>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="focusPolicy">
- <enum>Qt::StrongFocus</enum>
- </property>
- <property name="autoFillBackground">
- <bool>false</bool>
- </property>
- <property name="minimum">
- <number>0</number>
- </property>
- <property name="maximum">
- <number>400</number>
- </property>
- <property name="singleStep">
- <number>1</number>
- </property>
- <property name="pageStep">
- <number>50</number>
- </property>
- <property name="value">
- <number>200</number>
- </property>
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="tickPosition">
- <enum>QSlider::TicksBothSides</enum>
- </property>
- <property name="tickInterval">
- <number>100</number>
- </property>
- </widget>
- <widget class="QLabel" name="lbZpow">
- <property name="geometry">
- <rect>
- <x>7</x>
- <y>30</y>
- <width>91</width>
- <height>20</height>
- </rect>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- <property name="styleSheet">
- <string notr="true"/>
- </property>
- <property name="text">
- <string> Smoothing :</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- </widget>
- <widget class="QSlider" name="maxZ">
- <property name="geometry">
- <rect>
- <x>103</x>
- <y>60</y>
- <width>311</width>
- <height>20</height>
- </rect>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="focusPolicy">
- <enum>Qt::StrongFocus</enum>
- </property>
- <property name="autoFillBackground">
- <bool>false</bool>
- </property>
- <property name="minimum">
- <number>0</number>
- </property>
- <property name="maximum">
- <number>1000</number>
- </property>
- <property name="singleStep">
- <number>1</number>
- </property>
- <property name="pageStep">
- <number>50</number>
- </property>
- <property name="value">
- <number>150</number>
- </property>
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="tickPosition">
- <enum>QSlider::TicksBothSides</enum>
- </property>
- <property name="tickInterval">
- <number>100</number>
- </property>
- </widget>
- <widget class="QLabel" name="lbZpow_2">
- <property name="geometry">
- <rect>
- <x>7</x>
- <y>60</y>
- <width>91</width>
- <height>20</height>
- </rect>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- <property name="styleSheet">
- <string notr="true"/>
- </property>
- <property name="text">
- <string>Max Z:</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- </widget>
- <widget class="QLabel" name="lbMaxZ">
- <property name="geometry">
- <rect>
- <x>434</x>
- <y>60</y>
- <width>45</width>
- <height>20</height>
- </rect>
- </property>
- <property name="minimumSize">
- <size>
- <width>45</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string>15,00</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- </widget>
+ <layout class="QGridLayout" name="gridLayout_3">
+ <item row="2" column="0">
+ <widget class="QLabel" name="lbZpow">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string>Smoothing power:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QLabel" name="lbPowZoom">
+ <property name="minimumSize">
+ <size>
+ <width>45</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>2,00</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QSlider" name="powZoom">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="focusPolicy">
+ <enum>Qt::StrongFocus</enum>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="minimum">
+ <number>0</number>
+ </property>
+ <property name="maximum">
+ <number>400</number>
+ </property>
+ <property name="singleStep">
+ <number>1</number>
+ </property>
+ <property name="pageStep">
+ <number>50</number>
+ </property>
+ <property name="value">
+ <number>200</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="tickPosition">
+ <enum>QSlider::TicksBothSides</enum>
+ </property>
+ <property name="tickInterval">
+ <number>100</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="lbZpow_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string>Max Z:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QSlider" name="maxZ">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="focusPolicy">
+ <enum>Qt::StrongFocus</enum>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="minimum">
+ <number>0</number>
+ </property>
+ <property name="maximum">
+ <number>1000</number>
+ </property>
+ <property name="singleStep">
+ <number>1</number>
+ </property>
+ <property name="pageStep">
+ <number>50</number>
+ </property>
+ <property name="value">
+ <number>150</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="tickPosition">
+ <enum>QSlider::TicksBothSides</enum>
+ </property>
+ <property name="tickInterval">
+ <number>100</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QLabel" name="lbMaxZ">
+ <property name="minimumSize">
+ <size>
+ <width>45</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>15,00</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
</widget>
</item>
- <item>
- <widget class="QDialogButtonBox" name="buttonBox">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>When you lean forward, zoom smoothing increases the smoothing power for rotations. Maximum additonal smoothing power occurs when you lean forward by a distance of Max Z.</string>
</property>
- <property name="layoutDirection">
- <enum>Qt::LeftToRight</enum>
- </property>
- <property name="standardButtons">
- <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
- </property>
- <property name="centerButtons">
+ <property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
+ <tabstops>
+ <tabstop>dzRot</tabstop>
+ <tabstop>maxRot</tabstop>
+ <tabstop>powRot</tabstop>
+ <tabstop>dzDist</tabstop>
+ <tabstop>maxDist</tabstop>
+ <tabstop>powDist</tabstop>
+ <tabstop>maxZ</tabstop>
+ <tabstop>powZoom</tabstop>
+ </tabstops>
<resources>
<include location="../gui/opentrack-res.qrc"/>
</resources>
diff --git a/filter-hamilton/hamilton-tools.h b/filter-hamilton/hamilton-tools.h
deleted file mode 100644
index 2e288225..00000000
--- a/filter-hamilton/hamilton-tools.h
+++ /dev/null
@@ -1,27 +0,0 @@
-#pragma once
-
-#include "compat/math.hpp"
-constexpr double TO_RAD = (M_PI / 180);
-constexpr double TO_DEG = (180 / M_PI);
-constexpr double EPSILON = 1e-30;
-
-struct tVector
-{
- double v[3];
- tVector(double X = 0, double Y = 0, double Z = 0) {v[0]=X; v[1]=Y; v[2]=Z;}
- tVector(double V[]) {v[0]=V[0]; v[1]=V[1]; v[2]=V[2];}
-};
-
-struct tQuat
-{
- double x, y, z, w;
- tQuat(double X = 0, double Y = 0, double Z = 0, double W = 1)
- {x = X; y = Y; z = Z; w = W;}
-};
-
-double VectorDistance(const double v1[], const tVector v2);
-tVector Lerp (const tVector s, const double d[], const double alpha);
-tQuat QuatFromYPR (const double YPR[]);
-double AngleBetween (const tQuat S, const tQuat D);
-tQuat Slerp (const tQuat S, const tQuat D, const double alpha);
-void QuatToYPR (const tQuat Q, double YPR[]);
diff --git a/filter-hamilton/lang/de_DE.ts b/filter-hamilton/lang/de_DE.ts
new file mode 100644
index 00000000..6ea1f913
--- /dev/null
+++ b/filter-hamilton/lang/de_DE.ts
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>UICdialog_hamilton</name>
+ <message>
+ <source>Hamilton filter settings</source>
+ <translation>Hamilton-Filtereinstellungen</translation>
+ </message>
+ <message>
+ <source>Rotations: </source>
+ <translation>Rotationen: </translation>
+ </message>
+ <message>
+ <source>2,00</source>
+ <translation>2,00</translation>
+ </message>
+ <message>
+ <source>Smoothing power:</source>
+ <translation>Glättungsstärke:</translation>
+ </message>
+ <message>
+ <source>Dead Zone:</source>
+ <translation>Totbereich:</translation>
+ </message>
+ <message>
+ <source>0,01</source>
+ <translation>0,01</translation>
+ </message>
+ <message>
+ <source>Max distance:</source>
+ <translation>Maximale Distanz:</translation>
+ </message>
+ <message>
+ <source>10,00</source>
+ <translation>10,00</translation>
+ </message>
+ <message>
+ <source>For both rotations and positions: No movement occurs within the dead zone. Smoothing scales down to minimum at max distance + 2 x dead zone.</source>
+ <translation>Für alle Rotationen und Positionen: Es findet keine Bewegung innerhalb des Totbereichs statt. Die Glättung skaliert bis zum Minimum an der maximalen Distanz + 2x der Totbereich.</translation>
+ </message>
+ <message>
+ <source>Positions: </source>
+ <translation>Positionen: </translation>
+ </message>
+ <message>
+ <source>1,00</source>
+ <translation>1,00</translation>
+ </message>
+ <message>
+ <source>0,02</source>
+ <translation>0,02</translation>
+ </message>
+ <message>
+ <source>Zoom smoothing: </source>
+ <translation>Zoom-Glättung: </translation>
+ </message>
+ <message>
+ <source>Max Z:</source>
+ <translation>Maximales Z:</translation>
+ </message>
+ <message>
+ <source>15,00</source>
+ <translation>15,00</translation>
+ </message>
+ <message>
+ <source>When you lean forward, zoom smoothing increases the smoothing power for rotations. Maximum additonal smoothing power occurs when you lean forward by a distance of Max Z.</source>
+ <translation>Beim Vorbeugen erhöht die Zoom-Glättung die Glättungsstärke für Rotationen. Die maximal zusätzliche Glättungsstärke tritt auf beim Vorbeugen mit der Distanz Max Z.</translation>
+ </message>
+</context>
+<context>
+ <name>hamiltonDll</name>
+ <message>
+ <source>Hamilton</source>
+ <translation>Hamilton</translation>
+ </message>
+</context>
+</TS>
diff --git a/filter-hamilton/lang/nl_NL.ts b/filter-hamilton/lang/nl_NL.ts
index b03e4c0b..1c720540 100644
--- a/filter-hamilton/lang/nl_NL.ts
+++ b/filter-hamilton/lang/nl_NL.ts
@@ -8,18 +8,10 @@
<translation type="unfinished"></translation>
</message>
<message>
- <source> Smoothing :</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
<source>Positions: </source>
<translation type="unfinished"></translation>
</message>
<message>
- <source> Max distance:</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
<source>Zoom smoothing: </source>
<translation type="unfinished"></translation>
</message>
@@ -52,15 +44,27 @@
<translation type="unfinished"></translation>
</message>
<message>
- <source> Dead Zone:</source>
+ <source>15,00</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source> Smoothing:</source>
+ <source>Smoothing power:</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>15,00</source>
+ <source>Dead Zone:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Max distance:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>For both rotations and positions: No movement occurs within the dead zone. Smoothing scales down to minimum at max distance + 2 x dead zone.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>When you lean forward, zoom smoothing increases the smoothing power for rotations. Maximum additonal smoothing power occurs when you lean forward by a distance of Max Z.</source>
<translation type="unfinished"></translation>
</message>
</context>
diff --git a/filter-hamilton/lang/ru_RU.ts b/filter-hamilton/lang/ru_RU.ts
index dfbb2268..f0681b21 100644
--- a/filter-hamilton/lang/ru_RU.ts
+++ b/filter-hamilton/lang/ru_RU.ts
@@ -8,18 +8,10 @@
<translation type="unfinished"></translation>
</message>
<message>
- <source> Smoothing :</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
<source>Positions: </source>
<translation type="unfinished"></translation>
</message>
<message>
- <source> Max distance:</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
<source>Zoom smoothing: </source>
<translation type="unfinished"></translation>
</message>
@@ -52,15 +44,27 @@
<translation type="unfinished"></translation>
</message>
<message>
- <source> Dead Zone:</source>
+ <source>15,00</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source> Smoothing:</source>
+ <source>Smoothing power:</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>15,00</source>
+ <source>Dead Zone:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Max distance:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>For both rotations and positions: No movement occurs within the dead zone. Smoothing scales down to minimum at max distance + 2 x dead zone.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>When you lean forward, zoom smoothing increases the smoothing power for rotations. Maximum additonal smoothing power occurs when you lean forward by a distance of Max Z.</source>
<translation type="unfinished"></translation>
</message>
</context>
diff --git a/filter-hamilton/lang/stub.ts b/filter-hamilton/lang/stub.ts
index a8af9f98..2b767312 100644
--- a/filter-hamilton/lang/stub.ts
+++ b/filter-hamilton/lang/stub.ts
@@ -8,18 +8,10 @@
<translation type="unfinished"></translation>
</message>
<message>
- <source> Smoothing :</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
<source>Positions: </source>
<translation type="unfinished"></translation>
</message>
<message>
- <source> Max distance:</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
<source>Zoom smoothing: </source>
<translation type="unfinished"></translation>
</message>
@@ -52,15 +44,27 @@
<translation type="unfinished"></translation>
</message>
<message>
- <source> Dead Zone:</source>
+ <source>15,00</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source> Smoothing:</source>
+ <source>Smoothing power:</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>15,00</source>
+ <source>Dead Zone:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Max distance:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>For both rotations and positions: No movement occurs within the dead zone. Smoothing scales down to minimum at max distance + 2 x dead zone.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>When you lean forward, zoom smoothing increases the smoothing power for rotations. Maximum additonal smoothing power occurs when you lean forward by a distance of Max Z.</source>
<translation type="unfinished"></translation>
</message>
</context>
diff --git a/filter-hamilton/lang/zh_CN.ts b/filter-hamilton/lang/zh_CN.ts
index a8af9f98..a299023d 100644
--- a/filter-hamilton/lang/zh_CN.ts
+++ b/filter-hamilton/lang/zh_CN.ts
@@ -1,35 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
<context>
<name>UICdialog_hamilton</name>
<message>
<source>Rotations: </source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source> Smoothing :</source>
- <translation type="unfinished"></translation>
+ <translation type="unfinished">旋转: </translation>
</message>
<message>
<source>Positions: </source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source> Max distance:</source>
- <translation type="unfinished"></translation>
+ <translation type="unfinished">位置: </translation>
</message>
<message>
<source>Zoom smoothing: </source>
- <translation type="unfinished"></translation>
+ <translation type="unfinished">缩放时平滑: </translation>
</message>
<message>
<source>Hamilton filter settings</source>
- <translation type="unfinished"></translation>
+ <translation>Hamilton 过滤器设置</translation>
</message>
<message>
<source>Max Z:</source>
- <translation type="unfinished"></translation>
+ <translation></translation>
</message>
<message>
<source>10,00</source>
@@ -52,16 +44,28 @@
<translation type="unfinished"></translation>
</message>
<message>
- <source> Dead Zone:</source>
+ <source>15,00</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source> Smoothing:</source>
- <translation type="unfinished"></translation>
+ <source>Smoothing power:</source>
+ <translation>平滑强度:</translation>
</message>
<message>
- <source>15,00</source>
- <translation type="unfinished"></translation>
+ <source>Dead Zone:</source>
+ <translation>死区:</translation>
+ </message>
+ <message>
+ <source>Max distance:</source>
+ <translation type="unfinished">最大距离:</translation>
+ </message>
+ <message>
+ <source>For both rotations and positions: No movement occurs within the dead zone. Smoothing scales down to minimum at max distance + 2 x dead zone.</source>
+ <translation type="unfinished">旋转和位置在死区范围内都不会发生移动. 在最大距离 +2x死区时, smoothing缩小至最小值.&lt;a href=&quot;https://github.com/opentrack/opentrack/pull/1667&quot;&gt;阅读此处Pull#1167&lt;/a&gt;</translation>
+ </message>
+ <message>
+ <source>When you lean forward, zoom smoothing increases the smoothing power for rotations. Maximum additonal smoothing power occurs when you lean forward by a distance of Max Z.</source>
+ <translation type="unfinished">当您向前倾时, &quot;缩放时平滑&quot; 功能会增强旋转的平滑强度. 前倾到 Max Z 距离时, 增加的平滑强度最大.</translation>
</message>
</context>
<context>
diff --git a/filter-kalman/CMakeLists.txt b/filter-kalman/CMakeLists.txt
deleted file mode 100644
index b6f8100e..00000000
--- a/filter-kalman/CMakeLists.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-find_package(Eigen3 QUIET)
-if(EIGEN3_FOUND)
- otr_module(filter-kalman)
- target_include_directories(opentrack-filter-kalman SYSTEM PUBLIC ${EIGEN3_INCLUDE_DIR})
-endif()
diff --git a/filter-kalman/ftnoir_kalman_filtercontrols.ui b/filter-kalman/ftnoir_kalman_filtercontrols.ui
deleted file mode 100644
index 83ee8e96..00000000
--- a/filter-kalman/ftnoir_kalman_filtercontrols.ui
+++ /dev/null
@@ -1,170 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>KalmanUICdialog_kalman</class>
- <widget class="QWidget" name="KalmanUICdialog_kalman">
- <property name="windowModality">
- <enum>Qt::NonModal</enum>
- </property>
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>438</width>
- <height>141</height>
- </rect>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="windowTitle">
- <string>Kalman settings</string>
- </property>
- <property name="windowIcon">
- <iconset resource="../gui/opentrack-res.qrc">
- <normaloff>:/images/filter-16.png</normaloff>:/images/filter-16.png</iconset>
- </property>
- <property name="layoutDirection">
- <enum>Qt::LeftToRight</enum>
- </property>
- <property name="autoFillBackground">
- <bool>false</bool>
- </property>
- <property name="styleSheet">
- <string notr="true"/>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout">
- <item>
- <widget class="QGroupBox" name="groupBox_2">
- <property name="title">
- <string>Measurement noise</string>
- </property>
- <layout class="QGridLayout" name="gridLayout_2">
- <item row="0" column="0">
- <widget class="QLabel" name="label">
- <property name="text">
- <string>Rotation</string>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QSlider" name="noiseRotSlider">
- <property name="maximum">
- <number>400</number>
- </property>
- <property name="singleStep">
- <number>1</number>
- </property>
- <property name="pageStep">
- <number>100</number>
- </property>
- <property name="tracking">
- <bool>true</bool>
- </property>
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="invertedAppearance">
- <bool>false</bool>
- </property>
- <property name="tickPosition">
- <enum>QSlider::TicksBelow</enum>
- </property>
- <property name="tickInterval">
- <number>100</number>
- </property>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QLabel" name="label_4">
- <property name="text">
- <string>Position</string>
- </property>
- </widget>
- </item>
- <item row="1" column="1">
- <widget class="QSlider" name="noisePosSlider">
- <property name="maximum">
- <number>400</number>
- </property>
- <property name="pageStep">
- <number>100</number>
- </property>
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="tickPosition">
- <enum>QSlider::TicksBelow</enum>
- </property>
- <property name="tickInterval">
- <number>100</number>
- </property>
- </widget>
- </item>
- <item row="0" column="2">
- <widget class="QLabel" name="noiseRotLabel">
- <property name="minimumSize">
- <size>
- <width>65</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string>°</string>
- </property>
- </widget>
- </item>
- <item row="1" column="2">
- <widget class="QLabel" name="noisePosLabel">
- <property name="minimumSize">
- <size>
- <width>65</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string>-</string>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <widget class="QDialogButtonBox" name="buttonBox">
- <property name="standardButtons">
- <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- <resources>
- <include location="../gui/opentrack-res.qrc"/>
- </resources>
- <connections/>
- <designerdata>
- <property name="gridDeltaX">
- <number>10</number>
- </property>
- <property name="gridDeltaY">
- <number>10</number>
- </property>
- <property name="gridSnapX">
- <bool>false</bool>
- </property>
- <property name="gridSnapY">
- <bool>false</bool>
- </property>
- <property name="gridVisible">
- <bool>true</bool>
- </property>
- </designerdata>
- <slots>
- <slot>startEngineClicked()</slot>
- <slot>stopEngineClicked()</slot>
- <slot>cameraSettingsClicked()</slot>
- </slots>
-</ui>
diff --git a/filter-kalman/kalman.cpp b/filter-kalman/kalman.cpp
deleted file mode 100644
index 1960e0f6..00000000
--- a/filter-kalman/kalman.cpp
+++ /dev/null
@@ -1,323 +0,0 @@
-/* Copyright (c) 2016 Michael Welter <mw.pub@welter-4d.de>
- *
- * 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 "kalman.h"
-#include <cmath>
-#include <QDebug>
-
-void KalmanFilter::init()
-{
- // allocate and initialize matrices
- measurement_noise_cov = MeasureMatrix::Zero();
- process_noise_cov = StateMatrix::Zero();
- state_cov = StateMatrix::Zero();
- state_cov_prior = StateMatrix::Zero();
- transition_matrix = StateMatrix::Zero();
- measurement_matrix = StateToMeasureMatrix::Zero();
- kalman_gain = MeasureToStateMatrix::Zero();
- // initialize state variables
- state = StateVector::Zero();
- state_prior = StateVector::Zero();
- innovation = PoseVector::Zero();
-}
-
-
-void KalmanFilter::time_update()
-{
- state_prior = transition_matrix * state;
- state_cov_prior = transition_matrix * state_cov * transition_matrix.transpose() + process_noise_cov;
-}
-
-
-void KalmanFilter::measurement_update(const PoseVector &measurement)
-{
- MeasureMatrix tmp = measurement_matrix * state_cov_prior * measurement_matrix.transpose() + measurement_noise_cov;
- MeasureMatrix tmp_inv = tmp.inverse();
- kalman_gain = state_cov_prior * measurement_matrix.transpose() * tmp_inv;
- innovation = measurement - measurement_matrix * state_prior;
- state = state_prior + kalman_gain * innovation;
- state_cov = state_cov_prior - kalman_gain * measurement_matrix * state_cov_prior;
-}
-
-
-
-void KalmanProcessNoiseScaler::init()
-{
- base_cov = StateMatrix::Zero(NUM_STATE_DOF, NUM_STATE_DOF);
- innovation_cov_estimate = MeasureMatrix::Zero(NUM_MEASUREMENT_DOF, NUM_MEASUREMENT_DOF);
-}
-
-
-/* Uses
- innovation, measurement_matrix, measurement_noise_cov, and state_cov_prior
- found in KalmanFilter. It sets
- process_noise_cov
-*/
-void KalmanProcessNoiseScaler::update(KalmanFilter &kf, double dt)
-{
- MeasureMatrix ddT = kf.innovation * kf.innovation.transpose();
- double f = dt / (dt + settings::adaptivity_window_length);
- innovation_cov_estimate =
- f * ddT + (1. - f) * innovation_cov_estimate;
-
- double T1 = (innovation_cov_estimate - kf.measurement_noise_cov).trace();
- double T2 = (kf.measurement_matrix * kf.state_cov_prior * kf.measurement_matrix.transpose()).trace();
- double alpha = 0.001;
- if (T2 > 0. && T1 > 0.)
- {
- alpha = T1 / T2;
- alpha = std::sqrt(alpha);
- alpha = std::fmin(1000., std::fmax(0.001, alpha));
- }
- kf.process_noise_cov = alpha * base_cov;
- //qDebug() << "alpha = " << alpha;
-}
-
-void DeadzoneFilter::reset()
-{
- last_output = PoseVector::Zero();
-}
-
-PoseVector DeadzoneFilter::filter(const PoseVector &input)
-{
- PoseVector out;
- for (int i = 0; i < input.rows(); ++i)
- {
- const double dz = dz_size[i];
- if (dz > 0.)
- {
- const double delta = input[i] - last_output[i];
- const double f = std::pow(std::fabs(delta) / dz, settings::deadzone_exponent);
- const double response = f / (f + 1.) * delta;
- out[i] = last_output[i] + response;
- }
- else
- out[i] = input[i];
- last_output[i] = out[i];
- }
- return out;
-}
-
-
-void kalman::fill_transition_matrix(double dt)
-{
- for (int i = 0; i < 6; ++i)
- {
- kf.transition_matrix(i, i + 6) = dt;
- }
-}
-
-void kalman::fill_process_noise_cov_matrix(StateMatrix &target, double dt) const
-{
- // This model is like movement at fixed velocity plus superimposed
- // brownian motion. Unlike standard models for tracking of objects
- // with a very well predictable trajectory (e.g.
- // https://en.wikipedia.org/wiki/Kalman_filter#Example_application.2C_technical)
- double sigma_pos = settings::process_sigma_pos;
- double sigma_angle = settings::process_sigma_rot;
- double a_pos = sigma_pos * sigma_pos * dt;
- double a_ang = sigma_angle * sigma_angle * dt;
- constexpr double b = 20;
- constexpr double c = 1.;
- for (int i = 0; i < 3; ++i)
- {
- target(i, i) = a_pos;
- target(i, i + 6) = a_pos * c;
- target(i + 6, i) = a_pos * c;
- target(i + 6, i + 6) = a_pos * b;
- }
- for (int i = 3; i < 6; ++i)
- {
- target(i, i) = a_ang;
- target(i, i + 6) = a_ang * c;
- target(i + 6, i) = a_ang * c;
- target(i + 6, i + 6) = a_ang * b;
- }
-}
-
-
-PoseVector kalman::do_kalman_filter(const PoseVector &input, double dt, bool new_input)
-{
- if (new_input)
- {
- dt = dt_since_last_input;
- fill_transition_matrix(dt);
- fill_process_noise_cov_matrix(kf_adaptive_process_noise_cov.base_cov, dt);
- kf_adaptive_process_noise_cov.update(kf, dt);
- kf.time_update();
- kf.measurement_update(input);
- }
- return kf.state.head(6);
-}
-
-
-
-kalman::kalman()
-{
- reset();
-}
-
-// The original code was written by Donovan Baarda <abo@minkirri.apana.org.au>
-// https://sourceforge.net/p/facetracknoir/discussion/1150909/thread/418615e1/?limit=25#af75/084b
-void kalman::reset()
-{
- kf.init();
- kf_adaptive_process_noise_cov.init();
- for (int i = 0; i < 6; ++i)
- {
- // initialize part of the transition matrix that do not change.
- kf.transition_matrix(i, i) = 1.;
- kf.transition_matrix(i + 6, i + 6) = 1.;
- // "extract" positions, i.e. the first 6 state dof.
- kf.measurement_matrix(i, i) = 1.;
- }
-
- double noise_variance_position = settings::map_slider_value(s.noise_pos_slider_value);
- double noise_variance_angle = settings::map_slider_value(s.noise_rot_slider_value);
- for (int i = 0; i < 3; ++i)
- {
- kf.measurement_noise_cov(i , i ) = noise_variance_position;
- kf.measurement_noise_cov(i + 3, i + 3) = noise_variance_angle;
- }
-
- fill_transition_matrix(0.03);
- fill_process_noise_cov_matrix(kf_adaptive_process_noise_cov.base_cov, 0.03);
-
- kf.process_noise_cov = kf_adaptive_process_noise_cov.base_cov;
- kf.state_cov = kf.process_noise_cov;
-
- for (int i = 0; i < 6; i++) {
- last_input[i] = 0;
- }
- dt_since_last_input = 0;
-
- prev_slider_pos[0] = s.noise_pos_slider_value;
- prev_slider_pos[1] = s.noise_rot_slider_value;
-
- dz_filter.reset();
-}
-
-
-void kalman::filter(const double* input_, double *output_)
-{
- // almost non-existent cost, so might as well ...
- Eigen::Map<const PoseVector> input(input_, PoseVector::RowsAtCompileTime, 1);
- Eigen::Map<PoseVector> output(output_, PoseVector::RowsAtCompileTime, 1);
-
- if (!(prev_slider_pos[0] == s.noise_pos_slider_value &&
- prev_slider_pos[1] == s.noise_rot_slider_value))
- {
- reset();
- }
-
- // Start the timer on first filter evaluation.
- if (first_run)
- {
- timer.start();
- first_run = false;
- return;
- }
-
- // Note this is a terrible way to detect when there is a new
- // frame of tracker input, but it is the best we have.
- bool new_input = input.cwiseNotEqual(last_input).any();
-
- // Get the time in seconds since last run and restart the timer.
- const double dt = timer.elapsed_seconds();
- dt_since_last_input += dt;
- timer.start();
-
- output = do_kalman_filter(input, dt, new_input);
-
- {
- // Compute deadzone size base on estimated state variance.
- // Given a constant input plus measurement noise, KF should converge to the true input.
- // This works well. That is the output pose becomes very still afte some time.
- // The QScaling adaptive filter makes the state cov vary depending on the estimated noise
- // and the measured noise of the innovation sequence. After a sudden movement it peaks
- // and then decays asymptotically to some constant value taken in stationary state.
- // We can use this to calculate the size of the deadzone, so that in the stationary state the
- // deadzone size is small. Thus the tracking error due to the dz-filter becomes also small.
- PoseVector variance = kf.state_cov.diagonal().head(6);
- dz_filter.dz_size = variance.cwiseSqrt() * settings::deadzone_scale;
- }
- output = dz_filter.filter(output);
-
- if (new_input)
- {
- dt_since_last_input = 0;
- last_input = input;
- }
-}
-
-
-
-dialog_kalman::dialog_kalman()
- : filter(nullptr)
-{
- ui.setupUi(this);
- connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK()));
- connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel()));
-
- tie_setting(s.noise_rot_slider_value, ui.noiseRotSlider);
- tie_setting(s.noise_pos_slider_value, ui.noisePosSlider);
-
- connect(&s.noise_rot_slider_value, SIGNAL(valueChanged(const slider_value&)), this, SLOT(updateLabels(const slider_value&)));
- connect(&s.noise_pos_slider_value, SIGNAL(valueChanged(const slider_value&)), this, SLOT(updateLabels(const slider_value&)));
-
- updateLabels(slider_value());
-}
-
-
-void dialog_kalman::updateLabels(const slider_value&)
-{
- this->ui.noiseRotLabel->setText(
- QString::number(settings::map_slider_value(s.noise_rot_slider_value), 'f', 3) + "°");
-
- this->ui.noisePosLabel->setText(
- QString::number(settings::map_slider_value(s.noise_pos_slider_value), 'f', 3) + " cm");
-}
-
-
-void dialog_kalman::doOK() {
- s.b->save();
- close();
-}
-
-
-void dialog_kalman::doCancel()
-{
- close();
-}
-
-double settings::map_slider_value(const slider_value& v_)
-{
- const double v = v_;
-#if 0
- //return std::pow(10., v * 4. - 3.);
-#else
- constexpr int min_log10 = -3;
- constexpr int max_log10 = 1;
- constexpr int num_divisions = max_log10 - min_log10;
- /* ascii art representation of slider
- // ----- // ------// ------// ------- // 4 divisions
- -3 - 2 -1 0 1 power of 10
- | |
- | f + left_side_log10
- |
- left_side_log10
- */
- const int k = int(v * num_divisions); // in which division are we?!
- const double f = v * num_divisions - k; // where in the division are we?!
- const double ff = f * 9. + 1.;
- const double multiplier = int(ff * 10.) / 10.;
- const int left_side_log10 = min_log10 + k;
- const double val = std::pow(10., left_side_log10) * multiplier;
- return val;
-#endif
-}
-
-OPENTRACK_DECLARE_FILTER(kalman, dialog_kalman, kalmanDll)
diff --git a/filter-kalman/kalman.h b/filter-kalman/kalman.h
deleted file mode 100644
index 1e6e2ac4..00000000
--- a/filter-kalman/kalman.h
+++ /dev/null
@@ -1,159 +0,0 @@
-#pragma once
-/* Copyright (c) 2016 Michael Welter <mw.pub@welter-4d.de>
- *
- * 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 "compat/timer.hpp"
-#include "api/plugin-api.hpp"
-#include "options/options.hpp"
-using namespace options;
-
-// Eigen can't check for SSE3 on MSVC
-#if defined _MSC_VER && defined __SSE2__
-# define EIGEN_VECTORIZE_SSE3
-// this hardware is 10 years old
-# define EIGEN_VECTORIZE_SSE4_1
-# define EIGEN_VECTORIZE_SSE4_2
-#endif
-
-// nodiscard for placement new
-#ifdef _MSC_VER
-# pragma warning(push)
-# pragma warning(disable : 4834)
-#endif
-
-#include <Eigen/Core>
-#include <Eigen/LU>
-
-#ifdef _MSC_VER
-# pragma warning(pop)
-#endif
-
-#include "ui_ftnoir_kalman_filtercontrols.h"
-#include <QString>
-#include <QWidget>
-
-static constexpr int NUM_STATE_DOF = 12;
-static constexpr int NUM_MEASUREMENT_DOF = 6;
-// These vectors are compile time fixed size, stack allocated
-using StateToMeasureMatrix = Eigen::Matrix<double, NUM_MEASUREMENT_DOF, NUM_STATE_DOF>;
-using StateMatrix = Eigen::Matrix<double, NUM_STATE_DOF, NUM_STATE_DOF>;
-using MeasureToStateMatrix = Eigen::Matrix<double, NUM_STATE_DOF, NUM_MEASUREMENT_DOF>;
-using MeasureMatrix = Eigen::Matrix<double, NUM_MEASUREMENT_DOF, NUM_MEASUREMENT_DOF>;
-using StateVector = Eigen::Matrix<double, NUM_STATE_DOF, 1>;
-using PoseVector = Eigen::Matrix<double, NUM_MEASUREMENT_DOF, 1>;
-
-struct KalmanFilter
-{
- MeasureMatrix
- measurement_noise_cov;
- StateMatrix
- process_noise_cov,
- state_cov,
- state_cov_prior,
- transition_matrix;
- MeasureToStateMatrix
- kalman_gain;
- StateToMeasureMatrix
- measurement_matrix;
- StateVector
- state,
- state_prior;
- PoseVector
- innovation;
-
- void init();
- void time_update();
- void measurement_update(const PoseVector &measurement);
-};
-
-struct KalmanProcessNoiseScaler
-{
- MeasureMatrix
- innovation_cov_estimate;
- StateMatrix
- base_cov; // baseline (unscaled) process noise covariance matrix
- void init();
- void update(KalmanFilter &kf, double dt);
-};
-
-
-struct DeadzoneFilter
-{
- PoseVector last_output { PoseVector::Zero() },
- dz_size { PoseVector::Zero() };
-
- DeadzoneFilter() = default;
- void reset();
- PoseVector filter(const PoseVector &input);
-};
-
-
-struct settings : opts {
- value<slider_value> noise_rot_slider_value { b, "noise-rotation-slider", { .5, 0, 1 } };
- value<slider_value> noise_pos_slider_value { b, "noise-position-slider", { .5, 0, 1 } };
-
- static constexpr double adaptivity_window_length = 0.25; // seconds
- static constexpr double deadzone_scale = 8;
- static constexpr double deadzone_exponent = 2.0;
- static constexpr double process_sigma_pos = 0.5;
- static constexpr double process_sigma_rot = 0.5;
-
- static double map_slider_value(const slider_value &v);
-
- settings() : opts("kalman-filter") {}
-};
-
-class kalman : public IFilter
-{
- PoseVector do_kalman_filter(const PoseVector &input, double dt, bool new_input);
- void fill_transition_matrix(double dt);
- void fill_process_noise_cov_matrix(StateMatrix &target, double dt) const;
-public:
- kalman();
- void reset();
- void filter(const double *input, double *output) override;
- void center() override { reset(); }
- module_status initialize() override { return status_ok(); }
-
- double dt_since_last_input;
- PoseVector last_input;
- KalmanFilter kf;
- KalmanProcessNoiseScaler kf_adaptive_process_noise_cov;
- DeadzoneFilter dz_filter;
- settings s;
- slider_value prev_slider_pos[2] {
- *s.noise_pos_slider_value,
- *s.noise_rot_slider_value,
- };
- Timer timer;
-
- bool first_run = true;
-};
-
-class kalmanDll : public Metadata
-{
- Q_OBJECT
-
- QString name() override { return tr("Kalman"); }
- QIcon icon() override { return QIcon(":/images/filter-16.png"); }
-};
-
-class dialog_kalman: public IFilterDialog
-{
- Q_OBJECT
-public:
- dialog_kalman();
- Ui::KalmanUICdialog_kalman ui;
- void register_filter(IFilter*) override {}
- void unregister_filter() override {}
- settings s;
- kalman *filter;
-public slots:
- void doOK();
- void doCancel();
- void updateLabels(const slider_value&);
-};
diff --git a/filter-kalman/kalman_simulation.py b/filter-kalman/kalman_simulation.py
deleted file mode 100644
index 3e792212..00000000
--- a/filter-kalman/kalman_simulation.py
+++ /dev/null
@@ -1,76 +0,0 @@
-# -*- coding: utf-8 -*-
-import numpy as np
-import numpy.matlib as mt
-import matplotlib.pyplot as plt
-
-M = 2
-N = 500
-dt = 1.0 / 30.
-sigma_measure = 0.1
-# x = 1/2 a t**2
-# assume x = 0.5 in 0.5, i.e. head turns half maximal movement range in 0.5 sec.
-# so a = 2 x / t**2 = 4.0
-sigma_accel = 4.0
-
-x = np.zeros(N, dtype = np.float)
-x[N/3:] = 1.0 # step input
-measurement = x.copy()
-measurement += np.random.normal(0., sigma_measure, N)
-
-A = np.matrix([
- [ 1. , dt ],
- [ 0. , 1. ]
-])
-
-R = np.matrix([[ sigma_measure**2 ]])
-
-dv = sigma_accel * dt
-dp = sigma_accel * 0.5 * dt * dt
-Q = np.matrix([
- [ dp*dp, dp*dv ],
- [ dv*dp, dv*dv ]
-])
-
-H = np.matrix([
- [ 1., 0. ],
-])
-
-I = mt.identity(M, dtype = np.float)
-
-def arrayOfMatrices(n, shape):
- return np.asarray([mt.zeros(shape, dtype = np.float) for i in xrange(n)])
-
-# Base on the scipy-cookbook http://scipy-cookbook.readthedocs.io/items/KalmanFiltering.html
-sz_state = (M, 1)
-sz_cov = (M, M)
-sz_K = (M, 1)
-xhat= arrayOfMatrices(N, sz_state) # a posteri estimate of x
-P=arrayOfMatrices(N, sz_cov) # a posteri error estimate
-xhatminus=arrayOfMatrices(N, sz_state) # a priori estimate of x
-Pminus=arrayOfMatrices(N, sz_cov) # a priori error estimate
-K=arrayOfMatrices(N, sz_K) # gain or blending factor
-
-P[0] = mt.ones((M,M)) * 100
-xhat[0] = measurement[0]
-
-for k in range(1,N):
- # time update
- xhatminus[k] = A * xhat[k-1]
- Pminus[k] = A * P[k-1] * A.T + Q
-
- # measurement update
- K[k] = Pminus[k] * H.T * np.linalg.inv( H * Pminus[k] * H.T + R )
- xhat[k] = xhatminus[k] + K[k] * (measurement[k] - H * xhatminus[k])
- P[k] = ( I - K[k]*H ) * Pminus[k]
-
-t = np.arange(N) * dt
-plt.figure()
-plt.subplot(2,1,1)
-plt.plot(t, measurement,'k+',label='noisy measurements')
-plt.plot(t, xhat[:,0,0],'b-',label='position estimate')
-plt.plot(t, x, 'r-', label='ground truth')
-
-plt.subplot(2,1,2)
-plt.plot(t, xhat[:,1,0],'g-',label='velocity estimate')
-
-plt.show()
diff --git a/filter-nm/CMakeLists.txt b/filter-nm/CMakeLists.txt
new file mode 100644
index 00000000..49daaee2
--- /dev/null
+++ b/filter-nm/CMakeLists.txt
@@ -0,0 +1 @@
+otr_module(filter-nm)
diff --git a/filter-nm/ftnoir_filter_nm.cpp b/filter-nm/ftnoir_filter_nm.cpp
new file mode 100644
index 00000000..b222184b
--- /dev/null
+++ b/filter-nm/ftnoir_filter_nm.cpp
@@ -0,0 +1,67 @@
+/* Copyright (c) 2023 Tom Brazier <tom_github@firstsolo.net>
+ *
+ * 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 "ftnoir_filter_nm.h"
+#include "compat/math-imports.hpp"
+#include "compat/macros.h"
+
+#include "api/plugin-api.hpp"
+#include "opentrack/defs.hpp"
+
+#include <algorithm>
+
+filter_nm::filter_nm()
+{
+}
+
+void filter_nm::filter(const double* input, double* output)
+{
+ tVector position = { input[TX], input[TY], input[TZ] };
+ tQuat rotation = QuatFromYPR(input + Yaw);
+
+ // order of axes: x, y, z, yaw, pitch, roll
+ if (unlikely(first_run))
+ {
+ first_run = false;
+ t.start();
+
+ last_pos_speed = tVector();
+ last_rot_speed = tQuat();
+ last_pos_out = position;
+ last_rot_out = rotation;
+ }
+ else
+ {
+ const double dt = t.elapsed_seconds();
+ t.start();
+
+ const tVector pos_speed = (position - last_pos_in) / dt;
+ const double pos_tau = 1. / *s.pos_responsiveness;
+ double alpha = dt / (dt + pos_tau);
+ last_pos_speed += (pos_speed - last_pos_speed) * alpha;
+ const double factor_pos = min(1.0, VectorLength(last_pos_speed) / (*s.pos_drift_speed * 3.0));
+ alpha *= factor_pos * factor_pos;
+ last_pos_out += (position - last_pos_out) * alpha;
+
+ const tQuat rot_delta = QuatDivide(rotation, last_rot_in);
+ constexpr double ms_per_s = 1000.0; // angular speed quaternions need to be small to work so use °/ms
+ const tQuat rot_speed = Slerp(tQuat(), rot_delta, 1.0 / ms_per_s / dt );
+ const double rot_tau = 1. / *s.rot_responsiveness;
+ alpha = dt / (dt + rot_tau);
+ last_rot_speed = Slerp(last_rot_speed, rot_speed, alpha);
+ const double angular_speed = AngleBetween(tQuat(), last_rot_speed) * ms_per_s;
+ const double factor_rot = min(1.0, angular_speed / (*s.rot_drift_speed * 3.0));
+ alpha *= factor_rot * factor_rot;
+ last_rot_out = Slerp(last_rot_out, rotation, alpha);
+ }
+
+ last_pos_in = position;
+ last_rot_in = rotation;
+ std::copy(last_pos_out.v, last_pos_out.v + 3, output + TX);
+ QuatToYPR(last_rot_out, &output[Yaw]);
+}
+
+OPENTRACK_DECLARE_FILTER(filter_nm, dialog_nm, nmDll)
diff --git a/filter-nm/ftnoir_filter_nm.h b/filter-nm/ftnoir_filter_nm.h
new file mode 100644
index 00000000..84775207
--- /dev/null
+++ b/filter-nm/ftnoir_filter_nm.h
@@ -0,0 +1,81 @@
+/* Copyright (c) 2023 Tom Brazier <tom_github@firstsolo.net>
+ *
+ * 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 "ui_ftnoir_nm_filtercontrols.h"
+
+#include "api/plugin-api.hpp"
+#include "compat/timer.hpp"
+#include "compat/hamilton-tools.h"
+#include "options/options.hpp"
+
+using namespace options;
+
+struct settings_nm : opts
+{
+ value<slider_value> pos_responsiveness;
+ value<slider_value> rot_responsiveness;
+ value<slider_value> pos_drift_speed;
+ value<slider_value> rot_drift_speed;
+
+ settings_nm() :
+ opts("nm-filter"),
+ pos_responsiveness(value<slider_value>(b, "pos-responsiveness", { 13.0, .0, 20.0 })),
+ rot_responsiveness(value<slider_value>(b, "rot-responsiveness", { 16.0, .0, 20.0 })),
+ pos_drift_speed(value<slider_value>(b, "pos-drift-speed", { 5.0, 0.1, 50.0 })),
+ rot_drift_speed(value<slider_value>(b, "rot-drift-speed", { 7.0, 0.1, 50.0 }))
+ {
+ }
+};
+
+struct filter_nm : IFilter
+{
+ filter_nm();
+ void filter(const double* input, double* output) override;
+ void center() override { first_run = true; }
+ module_status initialize() override { return status_ok(); }
+
+private:
+ tVector last_pos_in;
+ tQuat last_rot_in;
+ tVector last_pos_out;
+ tQuat last_rot_out;
+ tVector last_pos_speed;
+ tQuat last_rot_speed;
+ Timer t;
+ settings_nm s;
+ bool first_run = true;
+};
+
+class dialog_nm : public IFilterDialog
+{
+ Q_OBJECT
+public:
+ dialog_nm();
+ void register_filter(IFilter*) override {}
+ void unregister_filter() override {}
+ void save() override;
+ void reload() override;
+ bool embeddable() noexcept override { return true; }
+ void set_buttons_visible(bool x) override;
+
+private:
+ Ui::UICdialog_nm ui;
+ settings_nm s;
+
+private slots:
+ void doOK();
+ void doCancel();
+};
+
+class nmDll : public Metadata
+{
+ Q_OBJECT
+
+ QString name() override { return tr("NaturalMovement"); }
+ QIcon icon() override { return QIcon(":/images/filter-16.png"); }
+};
diff --git a/filter-nm/ftnoir_filter_nm_dialog.cpp b/filter-nm/ftnoir_filter_nm_dialog.cpp
new file mode 100644
index 00000000..f3626cae
--- /dev/null
+++ b/filter-nm/ftnoir_filter_nm_dialog.cpp
@@ -0,0 +1,56 @@
+/* Copyright (c) 2023 Tom Brazier <tom_github@firstsolo.net>
+ *
+ * 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 "ftnoir_filter_nm.h"
+
+using namespace options;
+
+dialog_nm::dialog_nm()
+{
+ ui.setupUi(this);
+
+ tie_setting(s.pos_responsiveness, ui.pos_responsiveness_slider);
+ tie_setting(s.pos_responsiveness, ui.pos_responsiveness, [](double x)
+ { return QStringLiteral("%1").arg(x, 0, 'f', 2); });
+
+ tie_setting(s.rot_responsiveness, ui.rot_responsiveness_slider);
+ tie_setting(s.rot_responsiveness, ui.rot_responsiveness, [](double x)
+ { return QStringLiteral("%1").arg(x, 0, 'f', 2); });
+
+ tie_setting(s.pos_drift_speed, ui.pos_drift_speed_slider);
+ tie_setting(s.pos_drift_speed, ui.pos_drift_speed, [](double x)
+ { return QStringLiteral("%1").arg(x, 0, 'f', 2); });
+
+ tie_setting(s.rot_drift_speed, ui.rot_drift_speed_slider);
+ tie_setting(s.rot_drift_speed, ui.rot_drift_speed, [](double x)
+ { return QStringLiteral("%1").arg(x, 0, 'f', 2); });
+}
+
+void dialog_nm::doOK()
+{
+ save();
+ close();
+}
+
+void dialog_nm::doCancel()
+{
+ close();
+}
+
+void dialog_nm::save()
+{
+ s.b->save();
+}
+
+void dialog_nm::reload()
+{
+ s.b->reload();
+}
+
+void dialog_nm::set_buttons_visible(bool x)
+{
+ ui.buttonBox->setVisible(x);
+}
diff --git a/filter-nm/ftnoir_nm_filtercontrols.ui b/filter-nm/ftnoir_nm_filtercontrols.ui
new file mode 100644
index 00000000..7e317e74
--- /dev/null
+++ b/filter-nm/ftnoir_nm_filtercontrols.ui
@@ -0,0 +1,312 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>UICdialog_nm</class>
+ <widget class="QDialog" name="UICdialog_nm">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>438</width>
+ <height>559</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Dialog</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="0" column="0">
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Responsiveness</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_7">
+ <property name="text">
+ <string>Rotation</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="pos_responsiveness">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>30</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>10.0</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLabel" name="rot_responsiveness">
+ <property name="minimumSize">
+ <size>
+ <width>30</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>10.0</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QSlider" name="rot_responsiveness_slider">
+ <property name="maximum">
+ <number>40</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QSlider" name="pos_responsiveness_slider">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximum">
+ <number>40</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Position</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Natural movement filter by Tom Brazier: Cancels higher frequency noise and the natural tendency for our heads to drift even when we think we are sitting still.</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QGroupBox" name="groupBox_2">
+ <property name="title">
+ <string>Drift speeds</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_3">
+ <item row="0" column="2">
+ <widget class="QLabel" name="label_14">
+ <property name="text">
+ <string>mm/s</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Position</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="pos_drift_speed">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>40</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>50</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="3">
+ <widget class="QSlider" name="rot_drift_speed_slider">
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>500</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="label_8">
+ <property name="text">
+ <string>Rotation</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QLabel" name="rot_drift_speed">
+ <property name="minimumSize">
+ <size>
+ <width>40</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>100</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="2">
+ <widget class="QLabel" name="label_24">
+ <property name="text">
+ <string>°/s</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <widget class="QSlider" name="pos_drift_speed_slider">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>500</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="6" column="0">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="label_15">
+ <property name="text">
+ <string>Instructions: Set all sliders to minimum. Then for each of rotation and position: First, increase responsiveness until the filter only just cancels jerkiness for faster head movements. Second, increase drift speed until the filter only just cancels drift movement when your head is still.</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0">
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <tabstops>
+ <tabstop>pos_responsiveness_slider</tabstop>
+ <tabstop>rot_responsiveness_slider</tabstop>
+ <tabstop>pos_drift_speed_slider</tabstop>
+ <tabstop>rot_drift_speed_slider</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>UICdialog_nm</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>UICdialog_nm</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/filter-nm/lang/de_DE.ts b/filter-nm/lang/de_DE.ts
new file mode 100644
index 00000000..9cfb1a22
--- /dev/null
+++ b/filter-nm/lang/de_DE.ts
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>UICdialog_nm</name>
+ <message>
+ <source>Dialog</source>
+ <translation>Dialog</translation>
+ </message>
+ <message>
+ <source>Responsiveness</source>
+ <translation>Ansprechempfindlichkeit</translation>
+ </message>
+ <message>
+ <source>Rotation</source>
+ <translation>Rotation</translation>
+ </message>
+ <message>
+ <source>10.0</source>
+ <translation>10,0</translation>
+ </message>
+ <message>
+ <source>Position</source>
+ <translation>Position</translation>
+ </message>
+ <message>
+ <source>Natural movement filter by Tom Brazier: Cancels higher frequency noise and the natural tendency for our heads to drift even when we think we are sitting still.</source>
+ <translation>Natural Movement Filter von Tom Brazier: Hebt hohes Frequenzrauschen auf und unterdrückt die natürlich Tendenz unserer Köpfe, zu driften, selbst wenn wir denken, stillzusitzen.</translation>
+ </message>
+ <message>
+ <source>Drift speeds</source>
+ <translation>Drift-Geschwindigkeiten</translation>
+ </message>
+ <message>
+ <source>mm/s</source>
+ <translation>mm/s</translation>
+ </message>
+ <message>
+ <source>50</source>
+ <translation>50</translation>
+ </message>
+ <message>
+ <source>100</source>
+ <translation>100</translation>
+ </message>
+ <message>
+ <source>°/s</source>
+ <translation>°/s</translation>
+ </message>
+ <message>
+ <source>Instructions: Set all sliders to minimum. Then for each of rotation and position: First, increase responsiveness until the filter only just cancels jerkiness for faster head movements. Second, increase drift speed until the filter only just cancels drift movement when your head is still.</source>
+ <translation>Anleitung: Setze alle Regler auf Minimum. Dann führe folgendes für jede Rotation und Position durch: Erhöhe zunächst die Ansprechempfindlichkeit bis der Filter geradeeben das Ruckeln bei schnellen Kopfbewegungen aufhebt. Als nächstes erhöhe die Driftgeschwindigkeit, bis der Filter knapp das Driften des Kopfes aufhebt, während du ihn nicht bewegst.</translation>
+ </message>
+</context>
+<context>
+ <name>nmDll</name>
+ <message>
+ <source>NaturalMovement</source>
+ <translation>NaturalMovement</translation>
+ </message>
+</context>
+</TS>
diff --git a/trackmouse/lang/zh_CN.ts b/filter-nm/lang/nl_NL.ts
index 968e31c5..540e2d51 100644
--- a/trackmouse/lang/zh_CN.ts
+++ b/filter-nm/lang/nl_NL.ts
@@ -1,73 +1,61 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="nl_NL">
<context>
- <name>main_window</name>
+ <name>UICdialog_nm</name>
<message>
- <source>The Octopus is sad</source>
+ <source>Dialog</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source> :: </source>
+ <source>°/s</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Check permissions for your .ini directory:
-
-%1&quot;%2
-
-Exiting now.</source>
- <translation type="unfinished"></translation>
- </message>
-</context>
-<context>
- <name>window</name>
- <message>
- <source>trackmouse prototype</source>
+ <source>mm/s</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Keyboard shortcuts</source>
+ <source>Responsiveness</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>start/stop tracking</source>
+ <source>10.0</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Alt+F10</source>
+ <source>Drift speeds</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>center</source>
+ <source>50</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Alt+F11</source>
+ <source>100</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Alt+F12</source>
+ <source>Natural movement filter by Tom Brazier: Cancels higher frequency noise and the natural tendency for our heads to drift even when we think we are sitting still.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Sensitivity</source>
+ <source>Rotation</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>100%</source>
+ <source>Position</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Start</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>Stop</source>
+ <source>Instructions: Set all sliders to minimum. Then for each of rotation and position: First, increase responsiveness until the filter only just cancels jerkiness for faster head movements. Second, increase drift speed until the filter only just cancels drift movement when your head is still.</source>
<translation type="unfinished"></translation>
</message>
+</context>
+<context>
+ <name>nmDll</name>
<message>
- <source>freeze toggle</source>
+ <source>NaturalMovement</source>
<translation type="unfinished"></translation>
</message>
</context>
diff --git a/filter-nm/lang/ru_RU.ts b/filter-nm/lang/ru_RU.ts
new file mode 100644
index 00000000..31e5d5cc
--- /dev/null
+++ b/filter-nm/lang/ru_RU.ts
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="ru_RU">
+<context>
+ <name>UICdialog_nm</name>
+ <message>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>°/s</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>mm/s</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Responsiveness</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>10.0</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Drift speeds</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>50</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>100</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Natural movement filter by Tom Brazier: Cancels higher frequency noise and the natural tendency for our heads to drift even when we think we are sitting still.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Rotation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Position</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Instructions: Set all sliders to minimum. Then for each of rotation and position: First, increase responsiveness until the filter only just cancels jerkiness for faster head movements. Second, increase drift speed until the filter only just cancels drift movement when your head is still.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>nmDll</name>
+ <message>
+ <source>NaturalMovement</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/trackmouse/lang/stub.ts b/filter-nm/lang/stub.ts
index 968e31c5..3b960804 100644
--- a/trackmouse/lang/stub.ts
+++ b/filter-nm/lang/stub.ts
@@ -1,73 +1,61 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="stub">
<context>
- <name>main_window</name>
+ <name>UICdialog_nm</name>
<message>
- <source>The Octopus is sad</source>
+ <source>Dialog</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source> :: </source>
+ <source>°/s</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Check permissions for your .ini directory:
-
-%1&quot;%2
-
-Exiting now.</source>
- <translation type="unfinished"></translation>
- </message>
-</context>
-<context>
- <name>window</name>
- <message>
- <source>trackmouse prototype</source>
+ <source>mm/s</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Keyboard shortcuts</source>
+ <source>Responsiveness</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>start/stop tracking</source>
+ <source>10.0</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Alt+F10</source>
+ <source>Drift speeds</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>center</source>
+ <source>50</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Alt+F11</source>
+ <source>100</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Alt+F12</source>
+ <source>Natural movement filter by Tom Brazier: Cancels higher frequency noise and the natural tendency for our heads to drift even when we think we are sitting still.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Sensitivity</source>
+ <source>Rotation</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>100%</source>
+ <source>Position</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Start</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>Stop</source>
+ <source>Instructions: Set all sliders to minimum. Then for each of rotation and position: First, increase responsiveness until the filter only just cancels jerkiness for faster head movements. Second, increase drift speed until the filter only just cancels drift movement when your head is still.</source>
<translation type="unfinished"></translation>
</message>
+</context>
+<context>
+ <name>nmDll</name>
<message>
- <source>freeze toggle</source>
+ <source>NaturalMovement</source>
<translation type="unfinished"></translation>
</message>
</context>
diff --git a/filter-nm/lang/zh_CN.ts b/filter-nm/lang/zh_CN.ts
new file mode 100644
index 00000000..341f4eee
--- /dev/null
+++ b/filter-nm/lang/zh_CN.ts
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="zh_CN">
+<context>
+ <name>UICdialog_nm</name>
+ <message>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>°/s</source>
+ <translation></translation>
+ </message>
+ <message>
+ <source>mm/s</source>
+ <translation></translation>
+ </message>
+ <message>
+ <source>Responsiveness</source>
+ <translation>反应</translation>
+ </message>
+ <message>
+ <source>10.0</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Drift speeds</source>
+ <translation>漂移速度</translation>
+ </message>
+ <message>
+ <source>50</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>100</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Natural movement filter by Tom Brazier: Cancels higher frequency noise and the natural tendency for our heads to drift even when we think we are sitting still.</source>
+ <translation>Natural movement filter by Tom Brazier: 过滤高频噪声与我们头部的自然飘移, 即便我们认为我们保持不动.</translation>
+ </message>
+ <message>
+ <source>Rotation</source>
+ <translation>旋转</translation>
+ </message>
+ <message>
+ <source>Position</source>
+ <translation>位置</translation>
+ </message>
+ <message>
+ <source>Instructions: Set all sliders to minimum. Then for each of rotation and position: First, increase responsiveness until the filter only just cancels jerkiness for faster head movements. Second, increase drift speed until the filter only just cancels drift movement when your head is still.</source>
+ <translation>先将所有滑块最小化. 然后, 对于每个旋转和位置: 首先提高响应能力,直到过滤器只是刚好消除抖动(以获得更快的头部运动); 其次,增加漂移速度,直到过滤器只是在头部静止时刚好消除漂移. </translation>
+ </message>
+</context>
+<context>
+ <name>nmDll</name>
+ <message>
+ <source>NaturalMovement</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt
index db2f0b9e..89e07371 100644
--- a/gui/CMakeLists.txt
+++ b/gui/CMakeLists.txt
@@ -7,13 +7,6 @@ target_link_libraries(${self}
opentrack-pose-widget
)
-# for process detector
-if(APPLE)
- target_link_libraries(${self} proc)
-elseif(LINUX)
- otr_pkgconfig(${self} libprocps)
-endif()
-
if(NOT APPLE AND NOT WIN32)
target_compile_definitions(${self} PRIVATE -DOTR_X11_THREADS)
otr_pkgconfig(${self} x11)
diff --git a/gui/images/english.png b/gui/images/english.png
index 187aabf9..87d9f7d7 100644
--- a/gui/images/english.png
+++ b/gui/images/english.png
Binary files differ
diff --git a/gui/images/settings16.png b/gui/images/settings16.png
index 3b31623b..fa2e4a11 100644
--- a/gui/images/settings16.png
+++ b/gui/images/settings16.png
Binary files differ
diff --git a/gui/init.cpp b/gui/init.cpp
index 2b7b01ad..b666b4a9 100644
--- a/gui/init.cpp
+++ b/gui/init.cpp
@@ -21,7 +21,6 @@ using namespace options;
#include <QStyleFactory>
#include <QLocale>
#include <QTranslator>
-#include <QApplication>
#include <QDir>
#include <QFile>
#include <QFileDialog>
@@ -51,8 +50,10 @@ static void set_fp_mask()
#endif
#ifdef __APPLE__
+#if defined __i386__ || defined __x86_64__
fesetenv(FE_DFL_DISABLE_SSE_DENORMS_ENV);
#endif
+#endif
#ifdef _WIN32
# ifdef __clang__
@@ -194,51 +195,56 @@ static void apply_dark_windows_theme_if_needed()
static void add_win32_path()
{
- // see https://software.intel.com/en-us/articles/limitation-to-the-length-of-the-system-path-variable
- static char env_path[4096] { '\0', };
+ // see https://web.archive.org/web/20180924055536/https://software.intel.com/en-us/articles/limitation-to-the-length-of-the-system-path-variable
{
QString lib_path = OPENTRACK_BASE_PATH;
lib_path.replace("/", "\\");
- const QByteArray lib_path_ = QFile::encodeName(lib_path);
-
QString mod_path = OPENTRACK_BASE_PATH + OPENTRACK_LIBRARY_PATH;
mod_path.replace("/", "\\");
- const QByteArray mod_path_ = QFile::encodeName(mod_path);
-
- const char* contents[] {
- "PATH=",
- lib_path_.constData(),
- ";",
- mod_path_.constData(),
- ";",
- getenv("PATH"),
- };
- bool ok = true;
+ const QString orig_path = qgetenv("PATH");
- for (const char* ptr : contents)
- {
- if (ptr)
- strcat_s(env_path, sizeof(env_path), ptr);
+ QString env_path; env_path.reserve(4096);
- if (!ptr || ptr[0] == '\0' || env_path[0] == '\0')
- {
- qDebug() << "bad path element"
- << (ptr == nullptr ? "<null>" : ptr);
- ok = false;
- break;
- }
- }
+#if 0
+ qDebug() << "orig" << orig_path;
+ qDebug() << "libpath" << lib_path;
+ qDebug() << "modpath" << mod_path;
+#endif
- if (ok)
+ if (lib_path.isEmpty())
+ qDebug() << "env: empty lib_path!";
+ else
{
- const int error = _putenv(env_path);
-
- if (error)
- qDebug() << "can't _putenv win32 path";
+ if (!QFile(lib_path).exists())
+ qDebug() << "env: lib_path doesn't exist, this shouldn't happen!";
+ env_path += lib_path;
+ env_path += ';';
+ }
+ if (mod_path.isEmpty())
+ qDebug() << "env: can't add mod_path to env PATH";
+ else
+ {
+ if (!QFile(mod_path).exists())
+ qDebug() << "env: mod_path doesn't exist, did you install it correctly?";
+ env_path += mod_path;
+ env_path += ';';
}
+
+ if (orig_path.isEmpty())
+ qDebug() << "env: empty PATH";
else
- qDebug() << "can't set win32 path";
+ env_path += orig_path;
+
+#if 0
+ qDebug() << "data" << env_path.constData();
+#endif
+
+ // better length limit than putenv() and SetEnvironmentVariableA
+ bool ret = SetEnvironmentVariableW(L"PATH", (const wchar_t*)env_path.constData());
+
+ if (!ret)
+ qDebug() << "_putenv() failed with" << (void*)GetLastError();
}
}
@@ -296,18 +302,18 @@ int otr_main(int argc, char** argv, std::function<std::unique_ptr<QWidget>()> co
#endif
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
-#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
-#endif
QApplication app(argc, argv);
#ifdef _WIN32
- apply_dark_windows_theme_if_needed();
- add_win32_path();
attach_parent_console();
#endif
(void)qInstallMessageHandler(qdebug_to_console);
+#ifdef _WIN32
+ apply_dark_windows_theme_if_needed();
+ add_win32_path();
+#endif
QDir::setCurrent(OPENTRACK_BASE_PATH);
@@ -348,4 +354,3 @@ int otr_main(int argc, char** argv, std::function<std::unique_ptr<QWidget>()> co
return ret;
}
-
diff --git a/gui/init.hpp b/gui/init.hpp
index 4274f437..28ff55c7 100644
--- a/gui/init.hpp
+++ b/gui/init.hpp
@@ -8,8 +8,6 @@
OTR_GUI_EXPORT int otr_main(int argc, char** argv, std::function<std::unique_ptr<QWidget>()> const& make_main_window);
-// XXX TODO need split MainWindow into mixins each implementing part of the functionality
-
template<typename F>
auto run_application(int argc, char** argv, F&& fun)
{
diff --git a/gui/lang/de_DE.ts b/gui/lang/de_DE.ts
new file mode 100644
index 00000000..8d2dbcae
--- /dev/null
+++ b/gui/lang/de_DE.ts
@@ -0,0 +1,428 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>BrowseButton</name>
+ <message>
+ <source>Set executable name</source>
+ <translation>Namen der ausführbaren Datei setzen</translation>
+ </message>
+ <message>
+ <source>Executable (*.exe);;All Files (*)</source>
+ <translation>Ausführbare Dateien (*.exe);;Alle Dateien (*)</translation>
+ </message>
+</context>
+<context>
+ <name>Form</name>
+ <message>
+ <source>Press &quot;calibrate&quot; in given row to calibrate that axis. Follow instructions in the next window.
+Press &quot;clear calibration&quot; to remove any calibration data pertaining to that axis of position change or rotation.</source>
+ <translation>Klicke „Kalibrieren“ in den angegebenen Zeilen, um die jeweilige Achse zu kalibieren. Folge den Anweisungen im nächsten Fenster.
+Klicke „Kalibrierung löschen“, um jegliche Kalibrierungsdaten der zugehörigen Positions- oder Rotationsachse zu löschen.</translation>
+ </message>
+ <message>
+ <source>X</source>
+ <translation>X</translation>
+ </message>
+ <message>
+ <source>Y</source>
+ <translation>Y</translation>
+ </message>
+ <message>
+ <source>Z</source>
+ <translation>Z</translation>
+ </message>
+ <message>
+ <source>Yaw</source>
+ <translation>Gieren</translation>
+ </message>
+ <message>
+ <source>Pitch</source>
+ <translation>Nicken</translation>
+ </message>
+ <message>
+ <source>Roll</source>
+ <translation>Rollen</translation>
+ </message>
+ <message>
+ <source>Calibrate</source>
+ <translation>Kalibrieren</translation>
+ </message>
+ <message>
+ <source>Clear calibration</source>
+ <translation>Kalibrierung löschen</translation>
+ </message>
+ <message>
+ <source>1</source>
+ <translation>1</translation>
+ </message>
+</context>
+<context>
+ <name>keyboard_listener</name>
+ <message>
+ <source>Dialog</source>
+ <translation>Dialog</translation>
+ </message>
+ <message>
+ <source>Press a key or close this window to remove the keybinding.</source>
+ <translation>Drücke eine Taste oder schließe das Fenster, um die Kurztaste zu löschen.</translation>
+ </message>
+</context>
+<context>
+ <name>mapping_dialog</name>
+ <message>
+ <source>Mapping properties</source>
+ <translation>Abbildungsparameter</translation>
+ </message>
+ <message>
+ <source>Yaw</source>
+ <translation>Gieren</translation>
+ </message>
+ <message>
+ <source>Max input</source>
+ <translation>Maximale Eingabe</translation>
+ </message>
+ <message>
+ <source>Asymmetric mapping below</source>
+ <translation>Assymmetrische Abbildung unterhalb</translation>
+ </message>
+ <message>
+ <source>Pitch</source>
+ <translation>Nicken</translation>
+ </message>
+ <message>
+ <source>Max output</source>
+ <translation>Maximale Ausgabe</translation>
+ </message>
+ <message>
+ <source>180°</source>
+ <translation>180°</translation>
+ </message>
+ <message>
+ <source>90°</source>
+ <translation>90°</translation>
+ </message>
+ <message>
+ <source>Roll</source>
+ <translation>Rollen</translation>
+ </message>
+ <message>
+ <source>X</source>
+ <translation>X</translation>
+ </message>
+ <message>
+ <source>Y</source>
+ <translation>Y</translation>
+ </message>
+ <message>
+ <source>Z</source>
+ <translation>Z</translation>
+ </message>
+ <message>
+ <source>%1°</source>
+ <translation>%1°</translation>
+ </message>
+ <message>
+ <source>%1 cm</source>
+ <translation>%1 cm</translation>
+ </message>
+</context>
+<context>
+ <name>options_dialog</name>
+ <message>
+ <source>Options</source>
+ <translation>Optionen</translation>
+ </message>
+ <message>
+ <source>Shortcuts</source>
+ <translation>Kurztasten</translation>
+ </message>
+ <message>
+ <source>Global shortcuts</source>
+ <translation>Globale Kurztasten</translation>
+ </message>
+ <message>
+ <source>Use current tracker pose as looking perfectly forward.</source>
+ <translation>Benutze aktuelle Tracker-Pose als perfekt nach vorn gerichtet.</translation>
+ </message>
+ <message>
+ <source>Center</source>
+ <translation>Zentrieren</translation>
+ </message>
+ <message>
+ <source>Bind</source>
+ <translation>Belegen</translation>
+ </message>
+ <message>
+ <source>Start tracking</source>
+ <translation>Tracking starten</translation>
+ </message>
+ <message>
+ <source>Stop tracking</source>
+ <translation>Tracking stoppen</translation>
+ </message>
+ <message>
+ <source>Keep looking forward until next zero keypress.</source>
+ <translation>Weiter vorwärts schauen bis zum nächsten Drücken der Nullen-Taste.</translation>
+ </message>
+ <message>
+ <source>Zero</source>
+ <translation>Nullen</translation>
+ </message>
+ <message>
+ <source>Freeze the position returned by the tracker while this mode is active.</source>
+ <translation>Position des Trackers einfrieren, solange dieser Modus aktiv ist.</translation>
+ </message>
+ <message>
+ <source>Toggle</source>
+ <translation>Umschalten</translation>
+ </message>
+ <message>
+ <source>Zero while held</source>
+ <translation>Nullen während gedrückt</translation>
+ </message>
+ <message>
+ <source>Restart tracking</source>
+ <translation>Tracking neu starten</translation>
+ </message>
+ <message>
+ <source>Toggle while held</source>
+ <translation>Umschalten während gedrückt</translation>
+ </message>
+ <message>
+ <source>Toggle tracking</source>
+ <translation>Tracking umschalten</translation>
+ </message>
+ <message>
+ <source>Disable user interface localization</source>
+ <translation>Übersetzungen der Benutzeroberfläche abschalten</translation>
+ </message>
+ <message>
+ <source>Centering method</source>
+ <translation>Zentriermethode</translation>
+ </message>
+ <message>
+ <source>Center at startup</source>
+ <translation>Zentrieren beim Start</translation>
+ </message>
+ <message>
+ <source>Disabled</source>
+ <translation>Abgeschaltet</translation>
+ </message>
+ <message>
+ <source>Point</source>
+ <translation>Punkt</translation>
+ </message>
+ <message>
+ <source>Wireless VR 360</source>
+ <translation>Wireless VR 360</translation>
+ </message>
+ <message>
+ <source>Roll compensated</source>
+ <translation>rollkompensiert</translation>
+ </message>
+ <message>
+ <source>Minimize to tray</source>
+ <translation>In den Systemabschnitt minimieren</translation>
+ </message>
+ <message>
+ <source>Enable tray</source>
+ <translation>Benachrichtigungssymbol aktivieren</translation>
+ </message>
+ <message>
+ <source>Minimize to tray on startup when enabled</source>
+ <translation>Wenn aktiviert, beim Start in den Systemabschnitt minimieren</translation>
+ </message>
+ <message>
+ <source>Output</source>
+ <translation>Ausgabe</translation>
+ </message>
+ <message>
+ <source>Axis assignment</source>
+ <translation>Achsen-Zuweisung</translation>
+ </message>
+ <message>
+ <source>Pitch</source>
+ <translation>Nicken</translation>
+ </message>
+ <message>
+ <source>X</source>
+ <translation>X</translation>
+ </message>
+ <message>
+ <source>Y</source>
+ <translation>Y</translation>
+ </message>
+ <message>
+ <source>Z</source>
+ <translation>Z</translation>
+ </message>
+ <message>
+ <source>Yaw</source>
+ <translation>Gieren</translation>
+ </message>
+ <message>
+ <source>Roll</source>
+ <translation>Rollen</translation>
+ </message>
+ <message>
+ <source>Relative translation only</source>
+ <translation>Nur relative Übersetzung</translation>
+ </message>
+ <message>
+ <source>Source</source>
+ <translation>Quelle</translation>
+ </message>
+ <message>
+ <source>Pre-invert</source>
+ <translation>Vor-Invertieren</translation>
+ </message>
+ <message>
+ <source>Destination</source>
+ <translation>Ziel</translation>
+ </message>
+ <message>
+ <source>Post-invert</source>
+ <translation>Nach-Invertieren</translation>
+ </message>
+ <message>
+ <source>Custom center pose</source>
+ <translation>Benutzerdefinierte Mittelpunkt-Pose</translation>
+ </message>
+ <message>
+ <source>Alter the centered position sent to games. Useful if the default position is too much downward or upward.</source>
+ <translation>Verändere die zentrierte Position, die an Spiele gesendet wird. Nützlich, falls die Standard-Position zu weit unten oder oben ist.</translation>
+ </message>
+ <message>
+ <source>°</source>
+ <translation>°</translation>
+ </message>
+ <message>
+ <source> cm</source>
+ <translation> cm</translation>
+ </message>
+ <message>
+ <source>CSV Data Logging</source>
+ <translation>CSV-Datenausgabe</translation>
+ </message>
+ <message>
+ <source>Enable - You will be asked for a filename whenever tracking starts</source>
+ <translation>Einschalten - Der Dateiname wird jedesmal abgefragt, wenn das Tracking startet</translation>
+ </message>
+ <message>
+ <source>Relative translation</source>
+ <translation>Relative Übersetzung</translation>
+ </message>
+ <message>
+ <source>With relative mode on, translation is applied after rotation. For example, rotating +180 degrees yaw and moving backwards results in moving forward as a result of that rotation.</source>
+ <translation>Wenn die relative Modus aktiv ist, wird die Übersetzung nach der Rotation angewendet. Zum Beispiel wird dadurch beim Gieren um 180° mit Rückwärtsbewegung in eine Vorwärtsbewegung umgewandelt.</translation>
+ </message>
+ <message>
+ <source>Mode</source>
+ <translation>Modus</translation>
+ </message>
+ <message>
+ <source>Enabled</source>
+ <translation>Eingeschaltet</translation>
+ </message>
+ <message>
+ <source>Enabled when not aiming</source>
+ <translation>Eingeschaltet, während nicht gezielt wird</translation>
+ </message>
+ <message>
+ <source>Disable for Y</source>
+ <translation>Für Y abschalten</translation>
+ </message>
+ <message>
+ <source>Disable for X</source>
+ <translation>Für X abschalten</translation>
+ </message>
+ <message>
+ <source>Disable effect by roll</source>
+ <translation>Effekt durch Rollen abschalten</translation>
+ </message>
+ <message>
+ <source>Disable for Z (for zoom on Z axis)</source>
+ <translation>Für Z abschalten (für Zoom auf der Z-Achse)</translation>
+ </message>
+ <message>
+ <source>Disable effect by pitch</source>
+ <translation>Effekt durch Nicken abschalten</translation>
+ </message>
+ <message>
+ <source>Disable effect by yaw</source>
+ <translation>Effekt durch Gieren abschalten</translation>
+ </message>
+ <message>
+ <source>Neck displacement</source>
+ <translation>Nacken-Versatz</translation>
+ </message>
+ <message>
+ <source>Eyes will be offset from the pivot of rotation, assumed to be the neck. It also works with relative translation disabled.</source>
+ <translation>Die Augen werden gegenüber des Drehpunktes versetzt sein, unter Annahme, sie seien der Nacken. Dies funktioniert auch dann, wenn die relative Übersetzung abgeschaltet ist.</translation>
+ </message>
+ <message>
+ <source>Enable</source>
+ <translation>Einschalten</translation>
+ </message>
+ <message>
+ <source>Forward from center of rotation</source>
+ <translation>Vom Drehpunkt nach vorn verschieben</translation>
+ </message>
+ <message>
+ <source>Game detection</source>
+ <translation>Spiel-Erkennung</translation>
+ </message>
+ <message>
+ <source>Start tracking automatically when a game starts with selected profile, and stop when the game exits.</source>
+ <translation>Tracking beim Spielstart automatisch mit dem ausgewählten Profil starten, und stoppen, wenn das Spiel beendet wird.</translation>
+ </message>
+ <message>
+ <source>Mouse %1</source>
+ <translation>Maus %1</translation>
+ </message>
+ <message>
+ <source>Joy button %1</source>
+ <translation>Joystick-Knopf %1</translation>
+ </message>
+ <message>
+ <source>None</source>
+ <translation>Nichts</translation>
+ </message>
+ <message>
+ <source>Tracker</source>
+ <translation>Tracker</translation>
+ </message>
+ <message>
+ <source>Filter</source>
+ <translation>Filter</translation>
+ </message>
+</context>
+<context>
+ <name>process_detector</name>
+ <message>
+ <source>Game detector</source>
+ <translation>Spiel-Erkennung</translation>
+ </message>
+ <message>
+ <source>Start profiles from game executable names in this list</source>
+ <translation>Profile anhand der ausführbaren Dateien dieser Liste starten</translation>
+ </message>
+ <message>
+ <source>Executable</source>
+ <translation>Ausführbare Datei</translation>
+ </message>
+ <message>
+ <source>Profile</source>
+ <translation>Profil</translation>
+ </message>
+ <message>
+ <source>+</source>
+ <translation>Hinzufügen</translation>
+ </message>
+ <message>
+ <source>-</source>
+ <translation>Entfernen</translation>
+ </message>
+</context>
+</TS>
diff --git a/gui/lang/nl_NL.ts b/gui/lang/nl_NL.ts
index 60b84189..1b9d8fe9 100644
--- a/gui/lang/nl_NL.ts
+++ b/gui/lang/nl_NL.ts
@@ -141,10 +141,6 @@ Press &quot;clear calibration&quot; to remove any calibration data pertaining to
<translation>Algehele sneltoetsen</translation>
</message>
<message>
- <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Center&lt;/span&gt; - use current pose as looking perfectly forward.&lt;br/&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Toggle&lt;/span&gt; - keep looking at same spot until next toggle keypress.&lt;br/&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Zero&lt;/span&gt; - keep looking forward until next zero keypress.&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
- <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Centreren&lt;/span&gt; - gebruik de huidige kijkrichting als ijkpunt voor het centreren.&lt;br/&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Wisselen&lt;/span&gt; - kijkrichting blijft op dezelfde plek staan, totdat de ingestelde knop opnieuw wordt ingedrukt.&lt;br/&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Zero&lt;/span&gt; - Kijkrichting blijft recht vooruit, totdat er op de ingestelde toets gedrukt wordt.&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
- </message>
- <message>
<source>Bind</source>
<translation>Koppelen</translation>
</message>
@@ -285,27 +281,15 @@ Press &quot;clear calibration&quot; to remove any calibration data pertaining to
<translation>Effect uitschakelen bij rol</translation>
</message>
<message>
- <source>Output remap</source>
- <translation>Uitvoer aanpassen</translation>
- </message>
- <message>
- <source>Assign input axis to output axis.</source>
- <translation>Ingangs-as toewijzen aan uitgangs-as.</translation>
- </message>
- <message>
<source>X</source>
<translation>X</translation>
</message>
<message>
- <source>Invert</source>
- <translation>Omkeren</translation>
- </message>
- <message>
<source>Y</source>
<translation>Y</translation>
</message>
<message>
- <source>Never translate the application interface</source>
+ <source>Disable user interface localization</source>
<translation>De applicatie nooit vertaald weergeven</translation>
</message>
<message>
@@ -360,6 +344,58 @@ Press &quot;clear calibration&quot; to remove any calibration data pertaining to
<source>None</source>
<translation type="unfinished">Geen</translation>
</message>
+ <message>
+ <source>Centering method</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Point</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Wireless VR 360</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Roll compensated</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Freeze the position returned by the tracker while this mode is active.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Tracker</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Filter</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Mouse %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Use current tracker pose as looking perfectly forward.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Keep looking forward until next zero keypress.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Axis assignment</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Pre-invert</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Post-invert</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>process_detector</name>
diff --git a/gui/lang/ru_RU.ts b/gui/lang/ru_RU.ts
index d40b4775..5b3fcc03 100644
--- a/gui/lang/ru_RU.ts
+++ b/gui/lang/ru_RU.ts
@@ -141,11 +141,6 @@ Press &quot;clear calibration&quot; to remove any calibration data pertaining to
<translation>Глобальные горячие клавиши</translation>
</message>
<message>
- <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Center&lt;/span&gt; - use current pose as looking perfectly forward.&lt;br/&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Toggle&lt;/span&gt; - keep looking at same spot until next toggle keypress.&lt;br/&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Zero&lt;/span&gt; - keep looking forward until next zero keypress.&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
- <translatorcomment>использовать текущую позу как позу вперед</translatorcomment>
- <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Центрирование&lt;/span&gt; - использовать текущую позу в качестве центра.&lt;br/&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Фиксация обзора&lt;/span&gt; - фиксация обзора до следующего нажатия клавиши.&lt;br/&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Центр&lt;/span&gt; - фиксирует взгляд в нулевой точке до следующего нажатия клавиши.&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
- </message>
- <message>
<source>Bind</source>
<translation>Назначить</translation>
</message>
@@ -190,10 +185,6 @@ Press &quot;clear calibration&quot; to remove any calibration data pertaining to
<translation>Центрирование при запуске</translation>
</message>
<message>
- <source>Never translate the application interface</source>
- <translation></translation>
- </message>
- <message>
<source>Minimize to tray</source>
<translation>Настройка трея</translation>
</message>
@@ -276,22 +267,10 @@ Press &quot;clear calibration&quot; to remove any calibration data pertaining to
<translation>Смещение точки взгляда относительно оси вращения. Работает вместе с относительным сдвигом</translation>
</message>
<message>
- <source>Output remap</source>
- <translation>Переназначение осей</translation>
- </message>
- <message>
- <source>Assign input axis to output axis.</source>
- <translation>Сопоставление исходных и игровых осей.</translation>
- </message>
- <message>
<source>X</source>
<translation>X</translation>
</message>
<message>
- <source>Invert</source>
- <translation>Инвертировать</translation>
- </message>
- <message>
<source>Y</source>
<translation>Y</translation>
</message>
@@ -363,6 +342,62 @@ Press &quot;clear calibration&quot; to remove any calibration data pertaining to
<source>None</source>
<translation>Не назначена</translation>
</message>
+ <message>
+ <source>Centering method</source>
+ <translation>Метод центровки</translation>
+ </message>
+ <message>
+ <source>Point</source>
+ <translation>Точечная</translation>
+ </message>
+ <message>
+ <source>Wireless VR 360</source>
+ <translation>Беспроводная VR 360</translation>
+ </message>
+ <message>
+ <source>Roll compensated</source>
+ <translation>Roll компенсированная</translation>
+ </message>
+ <message>
+ <source>Freeze the position returned by the tracker while this mode is active.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Disable user interface localization</source>
+ <translation>Использовать английский интерфейс</translation>
+ </message>
+ <message>
+ <source>Tracker</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Filter</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Mouse %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Use current tracker pose as looking perfectly forward.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Keep looking forward until next zero keypress.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Axis assignment</source>
+ <translation>Переназначение осей</translation>
+ </message>
+ <message>
+ <source>Pre-invert</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Post-invert</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>process_detector</name>
diff --git a/gui/lang/stub.ts b/gui/lang/stub.ts
index b383a0d8..ee96d3be 100644
--- a/gui/lang/stub.ts
+++ b/gui/lang/stub.ts
@@ -141,10 +141,6 @@ Press &quot;clear calibration&quot; to remove any calibration data pertaining to
<translation type="unfinished"></translation>
</message>
<message>
- <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Center&lt;/span&gt; - use current pose as looking perfectly forward.&lt;br/&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Toggle&lt;/span&gt; - keep looking at same spot until next toggle keypress.&lt;br/&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Zero&lt;/span&gt; - keep looking forward until next zero keypress.&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
<source>Bind</source>
<translation type="unfinished"></translation>
</message>
@@ -189,10 +185,6 @@ Press &quot;clear calibration&quot; to remove any calibration data pertaining to
<translation type="unfinished"></translation>
</message>
<message>
- <source>Never translate the application interface</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
<source>Minimize to tray</source>
<translation type="unfinished"></translation>
</message>
@@ -293,22 +285,10 @@ Press &quot;clear calibration&quot; to remove any calibration data pertaining to
<translation type="unfinished"></translation>
</message>
<message>
- <source>Output remap</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>Assign input axis to output axis.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
<source>X</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Invert</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
<source>Y</source>
<translation type="unfinished"></translation>
</message>
@@ -360,6 +340,62 @@ Press &quot;clear calibration&quot; to remove any calibration data pertaining to
<source>None</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Centering method</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Point</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Wireless VR 360</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Roll compensated</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Freeze the position returned by the tracker while this mode is active.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Disable user interface localization</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Tracker</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Filter</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Mouse %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Use current tracker pose as looking perfectly forward.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Keep looking forward until next zero keypress.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Axis assignment</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Pre-invert</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Post-invert</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>process_detector</name>
diff --git a/gui/lang/zh_CN.ts b/gui/lang/zh_CN.ts
index fa2d512c..4914f7e1 100644
--- a/gui/lang/zh_CN.ts
+++ b/gui/lang/zh_CN.ts
@@ -142,10 +142,6 @@ Press &quot;clear calibration&quot; to remove any calibration data pertaining to
<translation>全局快捷键</translation>
</message>
<message>
- <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Center&lt;/span&gt; - use current pose as looking perfectly forward.&lt;br/&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Toggle&lt;/span&gt; - keep looking at same spot until next toggle keypress.&lt;br/&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Zero&lt;/span&gt; - keep looking forward until next zero keypress.&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
- <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;回中位置&lt;/span&gt; - 以现在的姿势为最完美向前看.&lt;br/&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;盯着看&lt;/span&gt; - 盯着固定位置直到再次按键.&lt;br/&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;归零位置&lt;/span&gt; - 保持向前看直到再次按键.&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
- </message>
- <message>
<source>Center</source>
<translation>回中位置</translation>
</message>
@@ -187,10 +183,10 @@ Press &quot;clear calibration&quot; to remove any calibration data pertaining to
</message>
<message>
<source>Center at startup</source>
- <translation>启动时自动回中</translation>
+ <translation>启动时回中</translation>
</message>
<message>
- <source>Never translate the application interface</source>
+ <source>Disable user interface localization</source>
<translation>关闭翻译界面</translation>
</message>
<message>
@@ -210,10 +206,6 @@ Press &quot;clear calibration&quot; to remove any calibration data pertaining to
<translation>输出</translation>
</message>
<message>
- <source>Output remap</source>
- <translation>输出重新映射</translation>
- </message>
- <message>
<source>X</source>
<translation></translation>
</message>
@@ -250,24 +242,16 @@ Press &quot;clear calibration&quot; to remove any calibration data pertaining to
<translation>源头</translation>
</message>
<message>
- <source>Invert</source>
- <translation>反向</translation>
- </message>
- <message>
<source>Destination</source>
<translation>目标</translation>
</message>
<message>
- <source>Assign input axis to output axis.</source>
- <translation>指定输入坐标到输出坐标.</translation>
- </message>
- <message>
<source>Custom center pose</source>
<translation>定制回中姿态</translation>
</message>
<message>
<source>Alter the centered position sent to games. Useful if the default position is too much downward or upward.</source>
- <translation>改变游戏中的得到的回中位置. 对于默认位置有明显偏移的很有用.</translation>
+ <translation>改变发送到游戏的回中位置. 对于默认位置有明显偏移的很有用.</translation>
</message>
<message>
<source>°</source>
@@ -275,7 +259,7 @@ Press &quot;clear calibration&quot; to remove any calibration data pertaining to
</message>
<message>
<source> cm</source>
- <translation> 厘米</translation>
+ <translation></translation>
</message>
<message>
<source>CSV Data Logging</source>
@@ -361,6 +345,58 @@ Press &quot;clear calibration&quot; to remove any calibration data pertaining to
<source>None</source>
<translation>空</translation>
</message>
+ <message>
+ <source>Centering method</source>
+ <translation>回中方法</translation>
+ </message>
+ <message>
+ <source>Point</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Wireless VR 360</source>
+ <translation type="unfinished">无线 VR 360</translation>
+ </message>
+ <message>
+ <source>Roll compensated</source>
+ <translation>滚转补偿</translation>
+ </message>
+ <message>
+ <source>Freeze the position returned by the tracker while this mode is active.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Tracker</source>
+ <translation>输入</translation>
+ </message>
+ <message>
+ <source>Filter</source>
+ <translation>过滤器</translation>
+ </message>
+ <message>
+ <source>Mouse %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Use current tracker pose as looking perfectly forward.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Keep looking forward until next zero keypress.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Axis assignment</source>
+ <translation>输出重新映射</translation>
+ </message>
+ <message>
+ <source>Pre-invert</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Post-invert</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>process_detector</name>
diff --git a/gui/mapping-dialog.cpp b/gui/mapping-dialog.cpp
index af35c999..cdbf532e 100644
--- a/gui/mapping-dialog.cpp
+++ b/gui/mapping-dialog.cpp
@@ -59,6 +59,10 @@ mapping_dialog::mapping_dialog(Mappings& m) : m(m), widgets{}
tie_setting(s.a_y.clamp_x_, ui.max_y_translation);
tie_setting(s.a_z.clamp_x_, ui.max_z_translation);
+ tie_setting(s.a_x.clamp_y_, ui.max_x_out);
+ tie_setting(s.a_y.clamp_y_, ui.max_y_out);
+ tie_setting(s.a_z.clamp_y_, ui.max_z_out);
+
tie_setting(s.a_pitch.clamp_y_, ui.max_pitch_output);
}
@@ -96,9 +100,13 @@ void mapping_dialog::load()
x->addItem(tr("%1°").arg(y), y);
for (QComboBox* x : { ui.max_x_translation, ui.max_y_translation, ui.max_z_translation })
- for (a y : { a::t30, a::t20, a::t15, a::t10, a::t100 })
+ for (a y : { a::t30, a::t20, a::t15, a::t10, a::t75, a::t100, a::t150, a::t300, a::t600 })
x->addItem(tr("%1 cm").arg(int(y)), y);
+ for (QComboBox* x : { ui.max_x_out, ui.max_y_out, ui.max_z_out })
+ for (a y : { a::o_t75, a::o_t100, a::o_t150, a::o_t300, a::o_t600 })
+ x->addItem(tr("%1 cm").arg(abs(int(y))), y);
+
for (int i = 0; qfcs[i].qfc; i++)
{
const bool altp = qfcs[i].altp;
@@ -122,8 +130,12 @@ void mapping_dialog::load()
value = 1;
else if (clamp_x <= a::r45)
value = 5;
- else
+ else if (clamp_x <= a::t150)
value = 10;
+ else if (clamp_x <= a::t300)
+ value = 25;
+ else
+ value = 50;
qfc.set_x_step(value);
};
@@ -139,6 +151,13 @@ void mapping_dialog::load()
value = 10; break;
case a::o_t75:
value = 5; break;
+ case a::o_t100:
+ case a::o_t150:
+ value = 10; break;
+ case a::o_t300:
+ value = 50; break;
+ case a::o_t600:
+ value = 100; break;
}
qfc.set_y_step(value);
};
diff --git a/gui/mapping-dialog.hpp b/gui/mapping-dialog.hpp
index 09170c62..3758e7c7 100644
--- a/gui/mapping-dialog.hpp
+++ b/gui/mapping-dialog.hpp
@@ -16,6 +16,7 @@ class OTR_GUI_EXPORT mapping_dialog final : public QDialog
public:
mapping_dialog(Mappings& m);
void refresh_tab();
+ inline bool embeddable() noexcept { return false; }
private:
Ui::mapping_dialog ui;
Mappings& m;
diff --git a/gui/mapping-dialog.ui b/gui/mapping-dialog.ui
index 8d72533d..545b5ae5 100644
--- a/gui/mapping-dialog.ui
+++ b/gui/mapping-dialog.ui
@@ -352,6 +352,29 @@
</property>
</widget>
</item>
+ <item>
+ <widget class="QLabel" name="label4out">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Max output</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="max_x_out">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
</item>
@@ -432,6 +455,29 @@
</property>
</widget>
</item>
+ <item>
+ <widget class="QLabel" name="label5out">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Max output</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="max_y_out">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
</item>
@@ -512,6 +558,29 @@
</property>
</widget>
</item>
+ <item>
+ <widget class="QLabel" name="label6out">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Max output</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="max_z_out">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
</item>
diff --git a/gui/settings.cpp b/gui/options-dialog.cpp
index 3851f0c2..76c3313a 100644
--- a/gui/settings.cpp
+++ b/gui/options-dialog.cpp
@@ -6,7 +6,7 @@
* notice appear in all copies.
*/
-#include "settings.hpp"
+#include "options-dialog.hpp"
#include "keyboard.h"
#include "compat/library-path.hpp"
@@ -30,7 +30,10 @@ QString options_dialog::kopts_to_string(const key_opts& kopts)
if (mods & Qt::ControlModifier) mm += "Control+";
if (mods & Qt::AltModifier) mm += "Alt+";
if (mods & Qt::ShiftModifier) mm += "Shift+";
- return mm + tr("Joy button %1").arg(QString::number(btn));
+ const auto& str = kopts.guid == QStringLiteral("mouse")
+ ? tr("Mouse %1")
+ : tr("Joy button %1");
+ return mm + str.arg(QString::number(btn));
}
if (kopts.keycode->isEmpty())
return tr("None");
@@ -46,27 +49,40 @@ void options_dialog::set_disable_translation_state(bool value)
});
}
-options_dialog::options_dialog(std::function<void(bool)> pause_keybindings) :
+options_dialog::options_dialog(std::unique_ptr<ITrackerDialog>& tracker_dialog_,
+ std::unique_ptr<IProtocolDialog>& proto_dialog_,
+ std::unique_ptr<IFilterDialog>& filter_dialog_,
+ std::function<void(bool)> pause_keybindings) :
pause_keybindings(std::move(pause_keybindings))
{
ui.setupUi(this);
- connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK()));
- connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel()));
+ connect(ui.buttonBox, &QDialogButtonBox::accepted, this, &options_dialog::doOK);
+ connect(ui.buttonBox, &QDialogButtonBox::rejected, this, &options_dialog::close);
tie_setting(main.tray_enabled, ui.trayp);
tie_setting(main.tray_start, ui.tray_start);
tie_setting(main.center_at_startup, ui.center_at_startup);
+ const centering_state centering_modes[] = {
+ center_disabled,
+ center_point,
+ center_vr360,
+ center_roll_compensated,
+ };
+ for (int k = 0; k < 4; k++)
+ ui.cbox_centering->setItemData(k, centering_modes[k]);
+ tie_setting(main.centering_mode, ui.cbox_centering);
+
const reltrans_state reltrans_modes[] = {
reltrans_disabled,
reltrans_enabled,
reltrans_non_center,
};
- for (unsigned k = 0; k < 3; k++)
- ui.reltrans_mode->setItemData(k, int(reltrans_modes[k]));
+ for (int k = 0; k < 3; k++)
+ ui.reltrans_mode->setItemData(k, reltrans_modes[k]);
tie_setting(main.reltrans_mode, ui.reltrans_mode);
@@ -87,12 +103,19 @@ options_dialog::options_dialog(std::function<void(bool)> pause_keybindings) :
tie_setting(main.a_pitch.zero, ui.pos_ry);
tie_setting(main.a_roll.zero, ui.pos_rz);
- tie_setting(main.a_yaw.invert, ui.invert_yaw);
- tie_setting(main.a_pitch.invert, ui.invert_pitch);
- tie_setting(main.a_roll.invert, ui.invert_roll);
- tie_setting(main.a_x.invert, ui.invert_x);
- tie_setting(main.a_y.invert, ui.invert_y);
- tie_setting(main.a_z.invert, ui.invert_z);
+ tie_setting(main.a_yaw.invert_pre, ui.invert_yaw_pre);
+ tie_setting(main.a_pitch.invert_pre, ui.invert_pitch_pre);
+ tie_setting(main.a_roll.invert_pre, ui.invert_roll_pre);
+ tie_setting(main.a_x.invert_pre, ui.invert_x_pre);
+ tie_setting(main.a_y.invert_pre, ui.invert_y_pre);
+ tie_setting(main.a_z.invert_pre, ui.invert_z_pre);
+
+ tie_setting(main.a_yaw.invert_post, ui.invert_yaw_post);
+ tie_setting(main.a_pitch.invert_post, ui.invert_pitch_post);
+ tie_setting(main.a_roll.invert_post, ui.invert_roll_post);
+ tie_setting(main.a_x.invert_post, ui.invert_x_post);
+ tie_setting(main.a_y.invert_post, ui.invert_y_post);
+ tie_setting(main.a_z.invert_post, ui.invert_z_post);
tie_setting(main.a_yaw.src, ui.src_yaw);
tie_setting(main.a_pitch.src, ui.src_pitch);
@@ -156,14 +179,25 @@ options_dialog::options_dialog(std::function<void(bool)> pause_keybindings) :
val.label,
[=](const QString&) { val.label->setText(kopts_to_string(val.opt)); });
{
- connect(val.button, &QPushButton::clicked, this, [=] { bind_key(val.opt, val.label); });
+ connect(val.button, &QPushButton::clicked, this, [=, this] { bind_key(val.opt, val.label); });
}
}
-}
-void options_dialog::closeEvent(QCloseEvent *)
-{
- done(result());
+ auto add_module_tab = [this] (auto& place, auto&& dlg, const QString& label) {
+ if (dlg && dlg->embeddable())
+ {
+ using BaseDialog = plugin_api::detail::BaseDialog;
+
+ dlg->set_buttons_visible(false);
+ place = dlg.release();
+ ui.tabWidget->addTab(place, label);
+ QObject::connect(place, &BaseDialog::closing, this, &QDialog::close);
+ }
+ };
+
+ add_module_tab(tracker_dialog, tracker_dialog_, tr("Tracker"));
+ add_module_tab(proto_dialog, proto_dialog_, tr("Output"));
+ add_module_tab(filter_dialog, filter_dialog_, tr("Filter"));
}
void options_dialog::bind_key(key_opts& kopts, QLabel* label)
@@ -197,7 +231,7 @@ void options_dialog::bind_key(key_opts& kopts, QLabel* label)
k->close();
}
});
- connect(main.b.get(), &options::detail::bundle::reloading, k, &QDialog::close);
+ connect(&*main.b, &options::detail::bundle::reloading, k, &QDialog::close);
pause_keybindings(true);
k->exec();
pause_keybindings(false);
@@ -218,41 +252,152 @@ void options_dialog::bind_key(key_opts& kopts, QLabel* label)
label->setText(kopts_to_string(kopts));
}
-void options_dialog::doOK()
+void options_dialog::switch_to_tracker_tab()
+{
+ if (tracker_dialog)
+ ui.tabWidget->setCurrentWidget(tracker_dialog);
+ else
+ eval_once(qDebug() << "options: asked for tracker tab widget with old-style widget dialog!");
+}
+
+void options_dialog::switch_to_proto_tab()
+{
+ if (proto_dialog)
+ ui.tabWidget->setCurrentWidget(proto_dialog);
+ else
+ eval_once(qDebug() << "options: asked for proto tab widget with old-style widget dialog!");
+}
+
+void options_dialog::switch_to_filter_tab()
+{
+ if (filter_dialog)
+ ui.tabWidget->setCurrentWidget(filter_dialog);
+ else
+ eval_once(qDebug() << "options: asked for filter tab widget with old-style widget dialog!");
+}
+
+void options_dialog::tracker_module_changed()
+{
+ if (tracker_dialog)
+ {
+ unregister_tracker();
+ reload();
+ delete tracker_dialog;
+ tracker_dialog = nullptr;
+ }
+}
+
+void options_dialog::proto_module_changed()
+{
+ if (proto_dialog)
+ {
+ unregister_protocol();
+ reload();
+ delete proto_dialog;
+ proto_dialog = nullptr;
+ }
+}
+
+void options_dialog::filter_module_changed()
+{
+ if (filter_dialog)
+ {
+ unregister_filter();
+ reload();
+ delete filter_dialog;
+ filter_dialog = nullptr;
+ }
+}
+
+void options_dialog::register_tracker(ITracker* t)
{
- if (isHidden()) // close() can return true twice in a row it seems
- return;
- hide();
- if (!close()) // dialog was closed already
- return;
+ if (tracker_dialog)
+ tracker_dialog->register_tracker(t);
+}
+
+void options_dialog::unregister_tracker()
+{
+ if (tracker_dialog)
+ tracker_dialog->unregister_tracker();
+}
+
+void options_dialog::register_protocol(IProtocol* p)
+{
+ if (proto_dialog)
+ proto_dialog->register_protocol(p);
+}
+
+void options_dialog::unregister_protocol()
+{
+ if (proto_dialog)
+ proto_dialog->unregister_protocol();
+}
+void options_dialog::register_filter(IFilter* f)
+{
+ if (filter_dialog)
+ filter_dialog->register_filter(f);
+}
+
+void options_dialog::unregister_filter()
+{
+ if (filter_dialog)
+ filter_dialog->unregister_filter();
+}
+
+using module_list = std::initializer_list<plugin_api::detail::BaseDialog*>;
+
+void options_dialog::save()
+{
main.b->save();
ui.game_detector->save();
set_disable_translation_state(ui.disable_translation->isChecked());
- emit closing();
+
+ for (auto* dlg : module_list{ tracker_dialog, proto_dialog, filter_dialog })
+ if (dlg)
+ dlg->save();
}
-void options_dialog::doCancel()
+void options_dialog::reload()
{
ui.game_detector->revert();
- if (isHidden()) // close() can return true twice in a row it seems
- return;
- hide();
- if (!close()) // dialog was closed already
- return;
-
main.b->reload();
- emit closing();
+ for (auto* dlg : module_list{ tracker_dialog, proto_dialog, filter_dialog })
+ if (dlg)
+ dlg->reload();
}
-void options_dialog::done(int res)
+void options_dialog::doOK()
{
if (isVisible())
{
- if (res == QDialog::Accepted)
- doOK();
- else
- doCancel();
+ save();
+ close();
}
}
+
+void options_dialog::doCancel()
+{
+ if (isVisible())
+ {
+ reload();
+ close();
+ }
+}
+
+void options_dialog::closeEvent(QCloseEvent *)
+{
+ reload();
+ emit closing();
+}
+
+options_dialog::~options_dialog()
+{
+ if (tracker_dialog)
+ tracker_dialog->unregister_tracker();
+ if (proto_dialog)
+ proto_dialog->unregister_protocol();
+ if (filter_dialog)
+ filter_dialog->unregister_filter();
+}
diff --git a/gui/options-dialog.hpp b/gui/options-dialog.hpp
new file mode 100644
index 00000000..2e5586a5
--- /dev/null
+++ b/gui/options-dialog.hpp
@@ -0,0 +1,57 @@
+#pragma once
+
+#include "export.hpp"
+
+#include "gui/ui_options-dialog.h"
+#include "logic/shortcuts.h"
+
+#include <functional>
+
+#include <QObject>
+#include <QDialog>
+#include <QWidget>
+
+class OTR_GUI_EXPORT options_dialog final : public QDialog
+{
+ Q_OBJECT
+signals:
+ void closing();
+public:
+ options_dialog(std::unique_ptr<ITrackerDialog>& tracker_dialog_,
+ std::unique_ptr<IProtocolDialog>& proto_dialog_,
+ std::unique_ptr<IFilterDialog>& filter_dialog_,
+ std::function<void(bool)> pause_keybindings);
+ ~options_dialog() override;
+ inline bool embeddable() noexcept { return false; }
+ void switch_to_tracker_tab();
+ void switch_to_proto_tab();
+ void switch_to_filter_tab();
+ void tracker_module_changed();
+ void proto_module_changed();
+ void filter_module_changed();
+ void register_tracker(ITracker* t);
+ void unregister_tracker();
+ void register_protocol(IProtocol* p);
+ void unregister_protocol();
+ void register_filter(IFilter* f);
+ void unregister_filter();
+ void save();
+ void reload();
+private:
+ void closeEvent(QCloseEvent*) override;
+ static QString kopts_to_string(const key_opts& opts);
+
+ main_settings main;
+ std::function<void(bool)> pause_keybindings;
+ Ui::options_dialog ui;
+
+ ITrackerDialog* tracker_dialog = nullptr;
+ IProtocolDialog* proto_dialog = nullptr;
+ IFilterDialog* filter_dialog = nullptr;
+
+private slots:
+ void doOK();
+ void doCancel();
+ void bind_key(key_opts &kopts, QLabel* label);
+ void set_disable_translation_state(bool value);
+};
diff --git a/gui/options-dialog.ui b/gui/options-dialog.ui
new file mode 100644
index 00000000..9e2c0042
--- /dev/null
+++ b/gui/options-dialog.ui
@@ -0,0 +1,2364 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>options_dialog</class>
+ <widget class="QWidget" name="options_dialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>529</width>
+ <height>533</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Options</string>
+ </property>
+ <property name="windowIcon">
+ <iconset>
+ <normaloff>images/opentrack.png</normaloff>images/opentrack.png</iconset>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_5">
+ <property name="topMargin">
+ <number>5</number>
+ </property>
+ <item>
+ <widget class="QTabWidget" name="tabWidget">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="currentIndex">
+ <number>0</number>
+ </property>
+ <property name="usesScrollButtons">
+ <bool>false</bool>
+ </property>
+ <widget class="QWidget" name="tab">
+ <attribute name="title">
+ <string>Shortcuts</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item alignment="Qt::AlignTop">
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Global shortcuts</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_8">
+ <property name="leftMargin">
+ <number>16</number>
+ </property>
+ <item row="1" column="1">
+ <widget class="QLabel" name="toggle_text">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>255</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="_textLabel2_3">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="cursor">
+ <cursorShape>WhatsThisCursor</cursorShape>
+ </property>
+ <property name="toolTip">
+ <string>Use current tracker pose as looking perfectly forward.</string>
+ </property>
+ <property name="text">
+ <string>Center</string>
+ </property>
+ <property name="wordWrap">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="4">
+ <widget class="QPushButton" name="bind_toggle_tracking_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Bind</string>
+ </property>
+ </widget>
+ </item>
+ <item row="8" column="3">
+ <widget class="QLabel" name="restart_tracking_text_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>255</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="8" column="4">
+ <widget class="QPushButton" name="bind_restart_tracking_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Bind</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0">
+ <widget class="QLabel" name="_textLabel2_7">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="cursor">
+ <cursorShape>WhatsThisCursor</cursorShape>
+ </property>
+ <property name="text">
+ <string>Start tracking</string>
+ </property>
+ <property name="wordWrap">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="2">
+ <widget class="QPushButton" name="bind_stop">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Bind</string>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="1">
+ <widget class="QLabel" name="toggle_tracking_text">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>255</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="center_text">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>255</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="0">
+ <widget class="QLabel" name="_textLabel2_8">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="cursor">
+ <cursorShape>WhatsThisCursor</cursorShape>
+ </property>
+ <property name="text">
+ <string>Stop tracking</string>
+ </property>
+ <property name="wordWrap">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QLabel" name="zero_text">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>255</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="_textLabel2_6">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="cursor">
+ <cursorShape>WhatsThisCursor</cursorShape>
+ </property>
+ <property name="toolTip">
+ <string>Keep looking forward until next zero keypress.</string>
+ </property>
+ <property name="text">
+ <string>Zero</string>
+ </property>
+ <property name="wordWrap">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="_textLabel2_5">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="cursor">
+ <cursorShape>WhatsThisCursor</cursorShape>
+ </property>
+ <property name="toolTip">
+ <string>Freeze the position returned by the tracker while this mode is active.</string>
+ </property>
+ <property name="text">
+ <string>Toggle</string>
+ </property>
+ <property name="wordWrap">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="_label_28">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="cursor">
+ <cursorShape>WhatsThisCursor</cursorShape>
+ </property>
+ <property name="text">
+ <string>Zero while held</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QPushButton" name="bind_center">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Bind</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLabel" name="toggle_held_text">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>255</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QLabel" name="zero_held_text">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>255</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QPushButton" name="bind_toggle_held">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Bind</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="2">
+ <widget class="QPushButton" name="bind_zero_held">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Bind</string>
+ </property>
+ </widget>
+ </item>
+ <item row="8" column="0">
+ <widget class="QLabel" name="_textLabel2_10">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="cursor">
+ <cursorShape>WhatsThisCursor</cursorShape>
+ </property>
+ <property name="text">
+ <string>Restart tracking</string>
+ </property>
+ <property name="wordWrap">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="8" column="1">
+ <widget class="QLabel" name="restart_tracking_text">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>255</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="_label_27">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="cursor">
+ <cursorShape>WhatsThisCursor</cursorShape>
+ </property>
+ <property name="text">
+ <string>Toggle while held</string>
+ </property>
+ </widget>
+ </item>
+ <item row="8" column="2">
+ <widget class="QPushButton" name="bind_restart_tracking">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Bind</string>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="2">
+ <widget class="QPushButton" name="bind_toggle_tracking">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Bind</string>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="0">
+ <widget class="QLabel" name="_textLabel2_9">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="cursor">
+ <cursorShape>WhatsThisCursor</cursorShape>
+ </property>
+ <property name="text">
+ <string>Toggle tracking</string>
+ </property>
+ <property name="wordWrap">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="2">
+ <widget class="QPushButton" name="bind_start">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Bind</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QPushButton" name="bind_toggle">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Bind</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1">
+ <widget class="QLabel" name="start_tracking_text">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>255</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="1">
+ <widget class="QLabel" name="stop_tracking_text">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>255</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <widget class="QLabel" name="toggle_text_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>255</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="4">
+ <widget class="QPushButton" name="bind_toggle_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Bind</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <widget class="QLabel" name="center_text_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>255</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="4">
+ <widget class="QPushButton" name="bind_center_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Bind</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="4">
+ <widget class="QPushButton" name="bind_toggle_held_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Bind</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="2">
+ <widget class="QPushButton" name="bind_zero">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Bind</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="3">
+ <widget class="QLabel" name="toggle_held_text_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>255</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="4">
+ <widget class="QPushButton" name="bind_zero_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Bind</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="3">
+ <widget class="QLabel" name="zero_held_text_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>255</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="3">
+ <widget class="QLabel" name="start_tracking_text_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>255</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="3">
+ <widget class="QLabel" name="zero_text_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>255</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="4">
+ <widget class="QPushButton" name="bind_zero_held_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Bind</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="3">
+ <widget class="QLabel" name="stop_tracking_text_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>255</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="3">
+ <widget class="QLabel" name="toggle_tracking_text_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>255</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="4">
+ <widget class="QPushButton" name="bind_stop_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Bind</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="4">
+ <widget class="QPushButton" name="bind_start_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Bind</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QFrame" name="frame_5">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Sunken</enum>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="1" column="0">
+ <widget class="QCheckBox" name="disable_translation">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Disable user interface localization</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="label_18">
+ <property name="text">
+ <string>Centering method</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QCheckBox" name="center_at_startup">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Center at startup</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLabel" name="label_29">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="pixmap">
+ <pixmap resource="opentrack-res.qrc">:/images/english.png</pixmap>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QComboBox" name="cbox_centering">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>4</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="currentIndex">
+ <number>3</number>
+ </property>
+ <item>
+ <property name="text">
+ <string>Disabled</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Point</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Wireless VR 360</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Roll compensated</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_11">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Minimize to tray</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <property name="bottomMargin">
+ <number>9</number>
+ </property>
+ <item>
+ <widget class="QCheckBox" name="trayp">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Enable tray</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="tray_start">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Minimize to tray on startup when enabled</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>1</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_4">
+ <attribute name="title">
+ <string>Output</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <item>
+ <widget class="QGroupBox" name="groupBox_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Axis assignment</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_5">
+ <property name="horizontalSpacing">
+ <number>12</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>9</number>
+ </property>
+ <item row="4" column="0">
+ <widget class="QLabel" name="label_8">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>254</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Pitch</string>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="1">
+ <widget class="QComboBox" name="src_y">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <item>
+ <property name="text">
+ <string>X</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Y</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Z</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Yaw</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Pitch</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Roll</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Disabled</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Relative translation only</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="label_13">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>254</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Source</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QLabel" name="label_14">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>254</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Pre-invert</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="0">
+ <widget class="QLabel" name="label_10">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>254</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>X</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="2">
+ <widget class="QCheckBox" name="invert_yaw_pre">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>255</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="2">
+ <widget class="QCheckBox" name="invert_roll_pre">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>255</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QComboBox" name="src_yaw">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <item>
+ <property name="text">
+ <string>X</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Y</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Z</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Yaw</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Pitch</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Roll</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Disabled</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="6" column="2">
+ <widget class="QCheckBox" name="invert_x_pre">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>255</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="3">
+ <widget class="QCheckBox" name="invert_yaw_post">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>255</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QComboBox" name="src_pitch">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <item>
+ <property name="text">
+ <string>X</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Y</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Z</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Yaw</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Pitch</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Roll</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Disabled</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="5" column="0">
+ <widget class="QLabel" name="label_9">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>254</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Roll</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_15">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>254</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Destination</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="2">
+ <widget class="QCheckBox" name="invert_pitch_pre">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>255</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="1">
+ <widget class="QComboBox" name="src_x">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <item>
+ <property name="text">
+ <string>X</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Y</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Z</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Yaw</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Pitch</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Roll</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Disabled</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Relative translation only</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="7" column="0">
+ <widget class="QLabel" name="label_11">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>254</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Y</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1">
+ <widget class="QComboBox" name="src_roll">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <item>
+ <property name="text">
+ <string>X</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Y</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Z</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Yaw</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Pitch</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Roll</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Disabled</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_7">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>254</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Yaw</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <widget class="QLabel" name="label_19">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>254</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Post-invert</string>
+ </property>
+ </widget>
+ </item>
+ <item row="8" column="0">
+ <widget class="QLabel" name="label_12">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>254</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Z</string>
+ </property>
+ </widget>
+ </item>
+ <item row="8" column="2">
+ <widget class="QCheckBox" name="invert_z_pre">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>255</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="8" column="1">
+ <widget class="QComboBox" name="src_z">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <item>
+ <property name="text">
+ <string>X</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Y</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Z</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Yaw</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Pitch</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Roll</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Disabled</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Relative translation only</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="7" column="2">
+ <widget class="QCheckBox" name="invert_y_pre">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>255</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="3">
+ <widget class="QCheckBox" name="invert_pitch_post">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>255</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="3">
+ <widget class="QCheckBox" name="invert_roll_post">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>255</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="3">
+ <widget class="QCheckBox" name="invert_x_post">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>255</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="3">
+ <widget class="QCheckBox" name="invert_y_post">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>255</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="8" column="3">
+ <widget class="QCheckBox" name="invert_z_post">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>255</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ <zorder>label_15</zorder>
+ <zorder>label_13</zorder>
+ <zorder>label_14</zorder>
+ <zorder>src_yaw</zorder>
+ <zorder>invert_yaw_pre</zorder>
+ <zorder>label_7</zorder>
+ <zorder>src_pitch</zorder>
+ <zorder>label_8</zorder>
+ <zorder>invert_pitch_pre</zorder>
+ <zorder>label_9</zorder>
+ <zorder>src_roll</zorder>
+ <zorder>invert_roll_pre</zorder>
+ <zorder>label_10</zorder>
+ <zorder>src_x</zorder>
+ <zorder>invert_x_pre</zorder>
+ <zorder>label_11</zorder>
+ <zorder>src_y</zorder>
+ <zorder>invert_y_pre</zorder>
+ <zorder>label_12</zorder>
+ <zorder>src_z</zorder>
+ <zorder>invert_z_pre</zorder>
+ <zorder>label_19</zorder>
+ <zorder>invert_yaw_post</zorder>
+ <zorder>invert_pitch_post</zorder>
+ <zorder>invert_roll_post</zorder>
+ <zorder>invert_x_post</zorder>
+ <zorder>invert_y_post</zorder>
+ <zorder>invert_z_post</zorder>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_6">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Custom center pose</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_6">
+ <item>
+ <widget class="QLabel" name="label_22">
+ <property name="text">
+ <string>Alter the centered position sent to games. Useful if the default position is too much downward or upward.</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="margin">
+ <number>2</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_7">
+ <property name="styleSheet">
+ <string notr="true">QGroupBox {
+ border: 0;
+}</string>
+ </property>
+ <property name="title">
+ <string/>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <property name="checkable">
+ <bool>false</bool>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_3">
+ <item row="2" column="1">
+ <widget class="QDoubleSpinBox" name="pos_rz">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="suffix">
+ <string>°</string>
+ </property>
+ <property name="decimals">
+ <number>2</number>
+ </property>
+ <property name="minimum">
+ <double>-180.000000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>180.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="3">
+ <widget class="QDoubleSpinBox" name="pos_tz">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="suffix">
+ <string> cm</string>
+ </property>
+ <property name="decimals">
+ <number>3</number>
+ </property>
+ <property name="minimum">
+ <double>-500.000000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>500.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QLabel" name="label_4">
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="text">
+ <string>X</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <widget class="QDoubleSpinBox" name="pos_tx">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="suffix">
+ <string> cm</string>
+ </property>
+ <property name="decimals">
+ <number>3</number>
+ </property>
+ <property name="minimum">
+ <double>-500.000000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>500.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Pitch</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>Y</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QDoubleSpinBox" name="pos_ry">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="suffix">
+ <string>°</string>
+ </property>
+ <property name="decimals">
+ <number>2</number>
+ </property>
+ <property name="minimum">
+ <double>-180.000000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>180.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QLabel" name="label_6">
+ <property name="text">
+ <string>Z</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Roll</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <widget class="QDoubleSpinBox" name="pos_ty">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="suffix">
+ <string> cm</string>
+ </property>
+ <property name="decimals">
+ <number>3</number>
+ </property>
+ <property name="minimum">
+ <double>-500.000000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>500.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Yaw</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QDoubleSpinBox" name="pos_rx">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="suffix">
+ <string>°</string>
+ </property>
+ <property name="decimals">
+ <number>2</number>
+ </property>
+ <property name="minimum">
+ <double>-180.000000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>180.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_10">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>50</height>
+ </size>
+ </property>
+ <property name="title">
+ <string>CSV Data Logging</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_11">
+ <item>
+ <widget class="QCheckBox" name="tracklogging_enabled">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>1</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Enable - You will be asked for a filename whenever tracking starts</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>1</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_2">
+ <attribute name="title">
+ <string>Relative translation</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QGroupBox" name="groupBox_">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="title">
+ <string>Relative translation</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_7">
+ <item>
+ <widget class="QLabel" name="label_16">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>With relative mode on, translation is applied after rotation. For example, rotating +180 degrees yaw and moving backwards results in moving forward as a result of that rotation.</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignJustify|Qt::AlignVCenter</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="margin">
+ <number>2</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="widget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLabel" name="label_17">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>10</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Mode</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="reltrans_mode">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>4</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="currentIndex">
+ <number>0</number>
+ </property>
+ <item>
+ <property name="text">
+ <string>Disabled</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Enabled</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Enabled when not aiming</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QFrame" name="frame_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <item row="2" column="0">
+ <widget class="QCheckBox" name="tcomp_ty_disable">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>3</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string>Disable for Y</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QCheckBox" name="tcomp_tx_disable">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>3</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string>Disable for X</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QCheckBox" name="tcomp_src_roll_disable">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>2</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Disable effect by roll</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QCheckBox" name="tcomp_tz_disable">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>3</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string>Disable for Z (for zoom on Z axis)</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QCheckBox" name="tcomp_src_pitch_disable">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>2</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Disable effect by pitch</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QCheckBox" name="tcomp_src_yaw_disable">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>2</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Disable effect by yaw</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_12">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Neck displacement</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_13">
+ <property name="spacing">
+ <number>7</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="label_33">
+ <property name="text">
+ <string>Eyes will be offset from the pivot of rotation, assumed to be the neck. It also works with relative translation disabled.</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QFrame" name="frame_4">
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_9">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>9</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QCheckBox" name="neck_enable">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string>Enable</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QSpinBox" name="neck_z">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>4</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="suffix">
+ <string> cm</string>
+ </property>
+ <property name="minimum">
+ <number>-100</number>
+ </property>
+ <property name="maximum">
+ <number>100</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_32">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>15</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Forward from center of rotation</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_5">
+ <attribute name="title">
+ <string>Game detection</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_9">
+ <item>
+ <widget class="QGroupBox" name="groupBox_1">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Game detection</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_12">
+ <item>
+ <widget class="QLabel" name="label_24">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Start tracking automatically when a game starts with selected profile, and stop when the game exits.</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="process_detector" name="game_detector" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>process_detector</class>
+ <extends>QWidget</extends>
+ <header>gui/process_detector.h</header>
+ </customwidget>
+ </customwidgets>
+ <tabstops>
+ <tabstop>tabWidget</tabstop>
+ <tabstop>bind_center</tabstop>
+ <tabstop>bind_toggle</tabstop>
+ <tabstop>bind_toggle_held</tabstop>
+ <tabstop>bind_zero</tabstop>
+ <tabstop>bind_zero_held</tabstop>
+ <tabstop>bind_start</tabstop>
+ <tabstop>bind_stop</tabstop>
+ <tabstop>bind_toggle_tracking</tabstop>
+ <tabstop>bind_restart_tracking</tabstop>
+ <tabstop>bind_center_2</tabstop>
+ <tabstop>bind_toggle_2</tabstop>
+ <tabstop>bind_toggle_held_2</tabstop>
+ <tabstop>bind_zero_2</tabstop>
+ <tabstop>bind_zero_held_2</tabstop>
+ <tabstop>bind_start_2</tabstop>
+ <tabstop>bind_stop_2</tabstop>
+ <tabstop>bind_toggle_tracking_2</tabstop>
+ <tabstop>bind_restart_tracking_2</tabstop>
+ <tabstop>center_at_startup</tabstop>
+ <tabstop>cbox_centering</tabstop>
+ <tabstop>disable_translation</tabstop>
+ <tabstop>trayp</tabstop>
+ <tabstop>tray_start</tabstop>
+ <tabstop>src_yaw</tabstop>
+ <tabstop>invert_yaw_pre</tabstop>
+ <tabstop>invert_yaw_post</tabstop>
+ <tabstop>src_pitch</tabstop>
+ <tabstop>invert_pitch_pre</tabstop>
+ <tabstop>invert_pitch_post</tabstop>
+ <tabstop>src_roll</tabstop>
+ <tabstop>invert_roll_pre</tabstop>
+ <tabstop>invert_roll_post</tabstop>
+ <tabstop>src_x</tabstop>
+ <tabstop>invert_x_pre</tabstop>
+ <tabstop>invert_x_post</tabstop>
+ <tabstop>src_y</tabstop>
+ <tabstop>invert_y_pre</tabstop>
+ <tabstop>invert_y_post</tabstop>
+ <tabstop>src_z</tabstop>
+ <tabstop>invert_z_pre</tabstop>
+ <tabstop>invert_z_post</tabstop>
+ <tabstop>pos_rx</tabstop>
+ <tabstop>pos_ry</tabstop>
+ <tabstop>pos_rz</tabstop>
+ <tabstop>pos_tx</tabstop>
+ <tabstop>pos_ty</tabstop>
+ <tabstop>pos_tz</tabstop>
+ <tabstop>tracklogging_enabled</tabstop>
+ <tabstop>reltrans_mode</tabstop>
+ <tabstop>tcomp_tx_disable</tabstop>
+ <tabstop>tcomp_ty_disable</tabstop>
+ <tabstop>tcomp_tz_disable</tabstop>
+ <tabstop>tcomp_src_yaw_disable</tabstop>
+ <tabstop>tcomp_src_pitch_disable</tabstop>
+ <tabstop>tcomp_src_roll_disable</tabstop>
+ <tabstop>neck_enable</tabstop>
+ <tabstop>neck_z</tabstop>
+ </tabstops>
+ <resources>
+ <include location="opentrack-res.qrc"/>
+ </resources>
+ <connections/>
+ <slots>
+ <slot>startEngineClicked()</slot>
+ <slot>stopEngineClicked()</slot>
+ <slot>cameraSettingsClicked()</slot>
+ </slots>
+</ui>
diff --git a/gui/process_detector.cpp b/gui/process_detector.cpp
index 58ea4ca9..74e8caac 100644
--- a/gui/process_detector.cpp
+++ b/gui/process_detector.cpp
@@ -63,7 +63,13 @@ void proc_detector_settings::set_is_enabled(bool enabled)
QHash<QString, QString> proc_detector_settings::split_process_names()
{
QString str = get_game_list();
- QStringList pairs = str.split(RECORD_SEPARATOR, QString::SkipEmptyParts);
+ constexpr auto split_flag =
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+ Qt::SkipEmptyParts;
+#else
+ QString::SkipEmptyParts;
+#endif
+ QStringList pairs = str.split(RECORD_SEPARATOR, split_flag);
QHash<QString, QString> ret;
ret.reserve(pairs.size() * 2);
for (auto const& pair : pairs)
diff --git a/gui/process_widget.ui b/gui/process_widget.ui
index 9e7b4973..005b0d9b 100644
--- a/gui/process_widget.ui
+++ b/gui/process_widget.ui
@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>366</width>
- <height>325</height>
+ <height>380</height>
</rect>
</property>
<property name="windowTitle">
@@ -24,7 +24,7 @@
<item>
<widget class="FancyTable" name="tableWidget">
<property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
@@ -83,12 +83,15 @@
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="topMargin">
- <number>5</number>
+ <number>4</number>
+ </property>
+ <property name="bottomMargin">
+ <number>4</number>
</property>
<item>
<widget class="QPushButton" name="add">
<property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
@@ -108,7 +111,7 @@
<item>
<widget class="QPushButton" name="remove">
<property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
diff --git a/gui/settings-dialog.ui b/gui/settings-dialog.ui
deleted file mode 100644
index 6333692a..00000000
--- a/gui/settings-dialog.ui
+++ /dev/null
@@ -1,2202 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>options_dialog</class>
- <widget class="QWidget" name="options_dialog">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>548</width>
- <height>599</height>
- </rect>
- </property>
- <property name="windowTitle">
- <string>Options</string>
- </property>
- <property name="windowIcon">
- <iconset>
- <normaloff>images/opentrack.png</normaloff>images/opentrack.png</iconset>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout_5">
- <property name="topMargin">
- <number>5</number>
- </property>
- <item>
- <widget class="QTabWidget" name="tabWidget">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="currentIndex">
- <number>0</number>
- </property>
- <property name="usesScrollButtons">
- <bool>false</bool>
- </property>
- <widget class="QWidget" name="tab">
- <attribute name="title">
- <string>Shortcuts</string>
- </attribute>
- <layout class="QVBoxLayout" name="verticalLayout">
- <item>
- <widget class="QGroupBox" name="groupBox_8">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="title">
- <string>Global shortcuts</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout_8">
- <item>
- <widget class="QLabel" name="label_23">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Center&lt;/span&gt; - use current pose as looking perfectly forward.&lt;br/&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Toggle&lt;/span&gt; - keep looking at same spot until next toggle keypress.&lt;br/&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Zero&lt;/span&gt; - keep looking forward until next zero keypress.&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
- </property>
- <property name="wordWrap">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QGroupBox" name="groupBox">
- <property name="styleSheet">
- <string notr="true">QGroupBox { border: 0; }</string>
- </property>
- <layout class="QGridLayout" name="gridLayout_8">
- <item row="1" column="1">
- <widget class="QLabel" name="toggle_text">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>255</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>100</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="0" column="0">
- <widget class="QLabel" name="_textLabel2_3">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Center</string>
- </property>
- <property name="wordWrap">
- <bool>false</bool>
- </property>
- </widget>
- </item>
- <item row="7" column="4">
- <widget class="QPushButton" name="bind_toggle_tracking_2">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Bind</string>
- </property>
- </widget>
- </item>
- <item row="8" column="3">
- <widget class="QLabel" name="restart_tracking_text_2">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>255</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>100</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="8" column="4">
- <widget class="QPushButton" name="bind_restart_tracking_2">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Bind</string>
- </property>
- </widget>
- </item>
- <item row="5" column="0">
- <widget class="QLabel" name="_textLabel2_7">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Start tracking</string>
- </property>
- <property name="wordWrap">
- <bool>false</bool>
- </property>
- </widget>
- </item>
- <item row="6" column="2">
- <widget class="QPushButton" name="bind_stop">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Bind</string>
- </property>
- </widget>
- </item>
- <item row="7" column="1">
- <widget class="QLabel" name="toggle_tracking_text">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>255</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>100</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QLabel" name="center_text">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>255</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>100</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="6" column="0">
- <widget class="QLabel" name="_textLabel2_8">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Stop tracking</string>
- </property>
- <property name="wordWrap">
- <bool>false</bool>
- </property>
- </widget>
- </item>
- <item row="3" column="1">
- <widget class="QLabel" name="zero_text">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>255</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>100</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="3" column="0">
- <widget class="QLabel" name="_textLabel2_6">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Zero</string>
- </property>
- <property name="wordWrap">
- <bool>false</bool>
- </property>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QLabel" name="_textLabel2_5">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Toggle</string>
- </property>
- <property name="wordWrap">
- <bool>false</bool>
- </property>
- </widget>
- </item>
- <item row="4" column="0">
- <widget class="QLabel" name="_label_28">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Zero while held</string>
- </property>
- </widget>
- </item>
- <item row="0" column="2">
- <widget class="QPushButton" name="bind_center">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Bind</string>
- </property>
- </widget>
- </item>
- <item row="2" column="1">
- <widget class="QLabel" name="toggle_held_text">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>255</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>100</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="4" column="1">
- <widget class="QLabel" name="zero_held_text">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>255</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>100</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="2" column="2">
- <widget class="QPushButton" name="bind_toggle_held">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Bind</string>
- </property>
- </widget>
- </item>
- <item row="4" column="2">
- <widget class="QPushButton" name="bind_zero_held">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Bind</string>
- </property>
- </widget>
- </item>
- <item row="8" column="0">
- <widget class="QLabel" name="_textLabel2_10">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Restart tracking</string>
- </property>
- <property name="wordWrap">
- <bool>false</bool>
- </property>
- </widget>
- </item>
- <item row="8" column="1">
- <widget class="QLabel" name="restart_tracking_text">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>255</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>100</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="2" column="0">
- <widget class="QLabel" name="_label_27">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Toggle while held</string>
- </property>
- </widget>
- </item>
- <item row="8" column="2">
- <widget class="QPushButton" name="bind_restart_tracking">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Bind</string>
- </property>
- </widget>
- </item>
- <item row="7" column="2">
- <widget class="QPushButton" name="bind_toggle_tracking">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Bind</string>
- </property>
- </widget>
- </item>
- <item row="7" column="0">
- <widget class="QLabel" name="_textLabel2_9">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Toggle tracking</string>
- </property>
- <property name="wordWrap">
- <bool>false</bool>
- </property>
- </widget>
- </item>
- <item row="5" column="2">
- <widget class="QPushButton" name="bind_start">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Bind</string>
- </property>
- </widget>
- </item>
- <item row="1" column="2">
- <widget class="QPushButton" name="bind_toggle">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Bind</string>
- </property>
- </widget>
- </item>
- <item row="5" column="1">
- <widget class="QLabel" name="start_tracking_text">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>255</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>100</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="6" column="1">
- <widget class="QLabel" name="stop_tracking_text">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>255</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>100</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="1" column="3">
- <widget class="QLabel" name="toggle_text_2">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>255</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>100</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="1" column="4">
- <widget class="QPushButton" name="bind_toggle_2">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Bind</string>
- </property>
- </widget>
- </item>
- <item row="0" column="3">
- <widget class="QLabel" name="center_text_2">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>255</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>100</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="0" column="4">
- <widget class="QPushButton" name="bind_center_2">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Bind</string>
- </property>
- </widget>
- </item>
- <item row="2" column="4">
- <widget class="QPushButton" name="bind_toggle_held_2">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Bind</string>
- </property>
- </widget>
- </item>
- <item row="3" column="2">
- <widget class="QPushButton" name="bind_zero">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Bind</string>
- </property>
- </widget>
- </item>
- <item row="2" column="3">
- <widget class="QLabel" name="toggle_held_text_2">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>255</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>100</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="3" column="4">
- <widget class="QPushButton" name="bind_zero_2">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Bind</string>
- </property>
- </widget>
- </item>
- <item row="4" column="3">
- <widget class="QLabel" name="zero_held_text_2">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>255</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>100</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="5" column="3">
- <widget class="QLabel" name="start_tracking_text_2">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>255</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>100</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="3" column="3">
- <widget class="QLabel" name="zero_text_2">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>255</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>100</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="4" column="4">
- <widget class="QPushButton" name="bind_zero_held_2">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Bind</string>
- </property>
- </widget>
- </item>
- <item row="6" column="3">
- <widget class="QLabel" name="stop_tracking_text_2">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>255</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>100</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="7" column="3">
- <widget class="QLabel" name="toggle_tracking_text_2">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>255</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>100</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="6" column="4">
- <widget class="QPushButton" name="bind_stop_2">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Bind</string>
- </property>
- </widget>
- </item>
- <item row="5" column="4">
- <widget class="QPushButton" name="bind_start_2">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Bind</string>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <widget class="QFrame" name="frame_5">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="frameShape">
- <enum>QFrame::StyledPanel</enum>
- </property>
- <property name="frameShadow">
- <enum>QFrame::Sunken</enum>
- </property>
- <layout class="QGridLayout" name="gridLayout_2">
- <item row="0" column="0">
- <widget class="QCheckBox" name="center_at_startup">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Center at startup</string>
- </property>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QCheckBox" name="disable_translation">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Never translate the application interface</string>
- </property>
- </widget>
- </item>
- <item row="1" column="1">
- <widget class="QLabel" name="label_29">
- <property name="text">
- <string/>
- </property>
- <property name="pixmap">
- <pixmap resource="opentrack-res.qrc">:/images/english.png</pixmap>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <widget class="QGroupBox" name="groupBox_11">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="title">
- <string>Minimize to tray</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout_3">
- <property name="bottomMargin">
- <number>9</number>
- </property>
- <item>
- <widget class="QCheckBox" name="trayp">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Enable tray</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QCheckBox" name="tray_start">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Minimize to tray on startup when enabled</string>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- </layout>
- </widget>
- <widget class="QWidget" name="tab_4">
- <attribute name="title">
- <string>Output</string>
- </attribute>
- <layout class="QVBoxLayout" name="verticalLayout_4">
- <property name="topMargin">
- <number>0</number>
- </property>
- <item>
- <widget class="QGroupBox" name="groupBox_4">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="font">
- <font>
- <kerning>true</kerning>
- </font>
- </property>
- <property name="title">
- <string>Output remap</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignHCenter|Qt::AlignTop</set>
- </property>
- <property name="flat">
- <bool>false</bool>
- </property>
- <property name="checkable">
- <bool>false</bool>
- </property>
- <layout class="QGridLayout" name="gridLayout_4">
- <item row="1" column="0">
- <widget class="QGroupBox" name="groupBox_2">
- <property name="sizePolicy">
- <sizepolicy hsizetype="MinimumExpanding" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="styleSheet">
- <string notr="true">QGroupBox
-{
- border: 0;
-}</string>
- </property>
- <layout class="QGridLayout" name="gridLayout_5">
- <property name="horizontalSpacing">
- <number>12</number>
- </property>
- <property name="verticalSpacing">
- <number>9</number>
- </property>
- <item row="7" column="1">
- <widget class="QComboBox" name="src_y">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <item>
- <property name="text">
- <string>X</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Y</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Z</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Yaw</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Pitch</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Roll</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Disabled</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Relative translation only</string>
- </property>
- </item>
- </widget>
- </item>
- <item row="6" column="0">
- <widget class="QLabel" name="label_10">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>254</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>X</string>
- </property>
- </widget>
- </item>
- <item row="5" column="0">
- <widget class="QLabel" name="label_9">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>254</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Roll</string>
- </property>
- </widget>
- </item>
- <item row="7" column="2">
- <widget class="QCheckBox" name="invert_y">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>255</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="4" column="2">
- <widget class="QCheckBox" name="invert_pitch">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>255</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="3" column="0">
- <widget class="QLabel" name="label_7">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>254</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Yaw</string>
- </property>
- </widget>
- </item>
- <item row="6" column="2">
- <widget class="QCheckBox" name="invert_x">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>255</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QLabel" name="label_13">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>254</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Source</string>
- </property>
- </widget>
- </item>
- <item row="5" column="1">
- <widget class="QComboBox" name="src_roll">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <item>
- <property name="text">
- <string>X</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Y</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Z</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Yaw</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Pitch</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Roll</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Disabled</string>
- </property>
- </item>
- </widget>
- </item>
- <item row="3" column="2">
- <widget class="QCheckBox" name="invert_yaw">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>255</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="4" column="1">
- <widget class="QComboBox" name="src_pitch">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <item>
- <property name="text">
- <string>X</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Y</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Z</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Yaw</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Pitch</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Roll</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Disabled</string>
- </property>
- </item>
- </widget>
- </item>
- <item row="8" column="0">
- <widget class="QLabel" name="label_12">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>254</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Z</string>
- </property>
- </widget>
- </item>
- <item row="8" column="2">
- <widget class="QCheckBox" name="invert_z">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>255</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="8" column="1">
- <widget class="QComboBox" name="src_z">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <item>
- <property name="text">
- <string>X</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Y</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Z</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Yaw</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Pitch</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Roll</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Disabled</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Relative translation only</string>
- </property>
- </item>
- </widget>
- </item>
- <item row="3" column="1">
- <widget class="QComboBox" name="src_yaw">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <item>
- <property name="text">
- <string>X</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Y</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Z</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Yaw</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Pitch</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Roll</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Disabled</string>
- </property>
- </item>
- </widget>
- </item>
- <item row="5" column="2">
- <widget class="QCheckBox" name="invert_roll">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>255</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="0" column="2">
- <widget class="QLabel" name="label_14">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>254</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Invert</string>
- </property>
- </widget>
- </item>
- <item row="6" column="1">
- <widget class="QComboBox" name="src_x">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <item>
- <property name="text">
- <string>X</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Y</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Z</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Yaw</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Pitch</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Roll</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Disabled</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Relative translation only</string>
- </property>
- </item>
- </widget>
- </item>
- <item row="0" column="0">
- <widget class="QLabel" name="label_15">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>254</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Destination</string>
- </property>
- </widget>
- </item>
- <item row="7" column="0">
- <widget class="QLabel" name="label_11">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>254</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Y</string>
- </property>
- </widget>
- </item>
- <item row="4" column="0">
- <widget class="QLabel" name="label_8">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>254</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Pitch</string>
- </property>
- </widget>
- </item>
- </layout>
- <zorder>label_15</zorder>
- <zorder>label_13</zorder>
- <zorder>label_14</zorder>
- <zorder>src_yaw</zorder>
- <zorder>invert_yaw</zorder>
- <zorder>label_7</zorder>
- <zorder>src_pitch</zorder>
- <zorder>label_8</zorder>
- <zorder>invert_pitch</zorder>
- <zorder>label_9</zorder>
- <zorder>src_roll</zorder>
- <zorder>invert_roll</zorder>
- <zorder>label_10</zorder>
- <zorder>src_x</zorder>
- <zorder>invert_x</zorder>
- <zorder>label_11</zorder>
- <zorder>src_y</zorder>
- <zorder>invert_y</zorder>
- <zorder>label_12</zorder>
- <zorder>src_z</zorder>
- <zorder>invert_z</zorder>
- </widget>
- </item>
- <item row="0" column="0">
- <widget class="QLabel" name="label_20">
- <property name="text">
- <string>Assign input axis to output axis.</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignJustify|Qt::AlignVCenter</set>
- </property>
- <property name="wordWrap">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <widget class="QGroupBox" name="groupBox_6">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="title">
- <string>Custom center pose</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout_6">
- <item>
- <widget class="QLabel" name="label_22">
- <property name="text">
- <string>Alter the centered position sent to games. Useful if the default position is too much downward or upward.</string>
- </property>
- <property name="wordWrap">
- <bool>true</bool>
- </property>
- <property name="margin">
- <number>2</number>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QGroupBox" name="groupBox_7">
- <property name="styleSheet">
- <string notr="true">QGroupBox {
- border: 0;
-}</string>
- </property>
- <property name="title">
- <string/>
- </property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
- </property>
- <property name="flat">
- <bool>false</bool>
- </property>
- <property name="checkable">
- <bool>false</bool>
- </property>
- <layout class="QGridLayout" name="gridLayout_3">
- <item row="2" column="1">
- <widget class="QDoubleSpinBox" name="pos_rz">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="suffix">
- <string>°</string>
- </property>
- <property name="decimals">
- <number>2</number>
- </property>
- <property name="minimum">
- <double>-180.000000000000000</double>
- </property>
- <property name="maximum">
- <double>180.000000000000000</double>
- </property>
- </widget>
- </item>
- <item row="2" column="3">
- <widget class="QDoubleSpinBox" name="pos_tz">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="suffix">
- <string> cm</string>
- </property>
- <property name="decimals">
- <number>3</number>
- </property>
- <property name="minimum">
- <double>-500.000000000000000</double>
- </property>
- <property name="maximum">
- <double>500.000000000000000</double>
- </property>
- </widget>
- </item>
- <item row="0" column="2">
- <widget class="QLabel" name="label_4">
- <property name="text">
- <string>X</string>
- </property>
- </widget>
- </item>
- <item row="0" column="3">
- <widget class="QDoubleSpinBox" name="pos_tx">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="suffix">
- <string> cm</string>
- </property>
- <property name="decimals">
- <number>3</number>
- </property>
- <property name="minimum">
- <double>-500.000000000000000</double>
- </property>
- <property name="maximum">
- <double>500.000000000000000</double>
- </property>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QLabel" name="label">
- <property name="text">
- <string>Pitch</string>
- </property>
- </widget>
- </item>
- <item row="1" column="2">
- <widget class="QLabel" name="label_5">
- <property name="text">
- <string>Y</string>
- </property>
- </widget>
- </item>
- <item row="1" column="1">
- <widget class="QDoubleSpinBox" name="pos_ry">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="suffix">
- <string>°</string>
- </property>
- <property name="decimals">
- <number>2</number>
- </property>
- <property name="minimum">
- <double>-180.000000000000000</double>
- </property>
- <property name="maximum">
- <double>180.000000000000000</double>
- </property>
- </widget>
- </item>
- <item row="2" column="2">
- <widget class="QLabel" name="label_6">
- <property name="text">
- <string>Z</string>
- </property>
- </widget>
- </item>
- <item row="2" column="0">
- <widget class="QLabel" name="label_3">
- <property name="text">
- <string>Roll</string>
- </property>
- </widget>
- </item>
- <item row="1" column="3">
- <widget class="QDoubleSpinBox" name="pos_ty">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="suffix">
- <string> cm</string>
- </property>
- <property name="decimals">
- <number>3</number>
- </property>
- <property name="minimum">
- <double>-500.000000000000000</double>
- </property>
- <property name="maximum">
- <double>500.000000000000000</double>
- </property>
- </widget>
- </item>
- <item row="0" column="0">
- <widget class="QLabel" name="label_2">
- <property name="text">
- <string>Yaw</string>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QDoubleSpinBox" name="pos_rx">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="suffix">
- <string>°</string>
- </property>
- <property name="decimals">
- <number>2</number>
- </property>
- <property name="minimum">
- <double>-180.000000000000000</double>
- </property>
- <property name="maximum">
- <double>180.000000000000000</double>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <widget class="QGroupBox" name="groupBox_10">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>50</height>
- </size>
- </property>
- <property name="title">
- <string>CSV Data Logging</string>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout_11">
- <item>
- <widget class="QCheckBox" name="tracklogging_enabled">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>1</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Enable - You will be asked for a filename whenever tracking starts</string>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- </layout>
- </widget>
- <widget class="QWidget" name="tab_2">
- <attribute name="title">
- <string>Relative translation</string>
- </attribute>
- <layout class="QVBoxLayout" name="verticalLayout_2">
- <item>
- <widget class="QGroupBox" name="groupBox_">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="styleSheet">
- <string notr="true"/>
- </property>
- <property name="title">
- <string>Relative translation</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout_7">
- <item>
- <widget class="QLabel" name="label_16">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>With relative mode on, translation is applied after rotation. For example, rotating +180 degrees yaw and moving backwards results in moving forward as a result of that rotation.</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignJustify|Qt::AlignVCenter</set>
- </property>
- <property name="wordWrap">
- <bool>true</bool>
- </property>
- <property name="margin">
- <number>2</number>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QWidget" name="widget" native="true">
- <layout class="QHBoxLayout" name="horizontalLayout">
- <item>
- <widget class="QLabel" name="label_17">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
- <horstretch>10</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Mode</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QComboBox" name="reltrans_mode">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
- <horstretch>4</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <item>
- <property name="text">
- <string>Disabled</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Enabled</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Enabled when not aiming</string>
- </property>
- </item>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <widget class="QFrame" name="frame_2">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="frameShape">
- <enum>QFrame::NoFrame</enum>
- </property>
- <property name="frameShadow">
- <enum>QFrame::Raised</enum>
- </property>
- <layout class="QGridLayout" name="gridLayout">
- <property name="leftMargin">
- <number>0</number>
- </property>
- <property name="topMargin">
- <number>0</number>
- </property>
- <property name="rightMargin">
- <number>0</number>
- </property>
- <property name="bottomMargin">
- <number>0</number>
- </property>
- <property name="spacing">
- <number>0</number>
- </property>
- <item row="2" column="0">
- <widget class="QCheckBox" name="tcomp_ty_disable">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>3</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="styleSheet">
- <string notr="true"/>
- </property>
- <property name="text">
- <string>Disable for Y</string>
- </property>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QCheckBox" name="tcomp_tx_disable">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>3</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="styleSheet">
- <string notr="true"/>
- </property>
- <property name="text">
- <string>Disable for X</string>
- </property>
- </widget>
- </item>
- <item row="3" column="1">
- <widget class="QCheckBox" name="tcomp_src_roll_disable">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>2</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Disable effect by roll</string>
- </property>
- </widget>
- </item>
- <item row="3" column="0">
- <widget class="QCheckBox" name="tcomp_tz_disable">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>3</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="styleSheet">
- <string notr="true"/>
- </property>
- <property name="text">
- <string>Disable for Z (for zoom on Z axis)</string>
- </property>
- </widget>
- </item>
- <item row="2" column="1">
- <widget class="QCheckBox" name="tcomp_src_pitch_disable">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>2</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Disable effect by pitch</string>
- </property>
- </widget>
- </item>
- <item row="1" column="1">
- <widget class="QCheckBox" name="tcomp_src_yaw_disable">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>2</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Disable effect by yaw</string>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <widget class="QGroupBox" name="groupBox_12">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="title">
- <string>Neck displacement</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout_13">
- <property name="spacing">
- <number>7</number>
- </property>
- <item>
- <widget class="QLabel" name="label_33">
- <property name="text">
- <string>Eyes will be offset from the pivot of rotation, assumed to be the neck. It also works with relative translation disabled.</string>
- </property>
- <property name="wordWrap">
- <bool>true</bool>
- </property>
- <property name="margin">
- <number>0</number>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QFrame" name="frame_4">
- <property name="frameShape">
- <enum>QFrame::NoFrame</enum>
- </property>
- <layout class="QGridLayout" name="gridLayout_9">
- <property name="leftMargin">
- <number>0</number>
- </property>
- <property name="topMargin">
- <number>9</number>
- </property>
- <property name="rightMargin">
- <number>0</number>
- </property>
- <property name="bottomMargin">
- <number>0</number>
- </property>
- <property name="spacing">
- <number>0</number>
- </property>
- <item row="0" column="0">
- <widget class="QCheckBox" name="neck_enable">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="styleSheet">
- <string notr="true"/>
- </property>
- <property name="text">
- <string>Enable</string>
- </property>
- </widget>
- </item>
- <item row="1" column="1">
- <widget class="QSpinBox" name="neck_z">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
- <horstretch>4</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- <property name="suffix">
- <string> cm</string>
- </property>
- <property name="minimum">
- <number>-100</number>
- </property>
- <property name="maximum">
- <number>100</number>
- </property>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QLabel" name="label_32">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
- <horstretch>15</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Forward from center of rotation</string>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <spacer name="verticalSpacer">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </widget>
- <widget class="QWidget" name="tab_5">
- <attribute name="title">
- <string>Game detection</string>
- </attribute>
- <layout class="QVBoxLayout" name="verticalLayout_9">
- <item>
- <widget class="QGroupBox" name="groupBox_1">
- <property name="sizePolicy">
- <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="title">
- <string>Game detection</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout_12">
- <item>
- <widget class="QLabel" name="label_24">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Start tracking automatically when a game starts with selected profile, and stop when the game exits.</string>
- </property>
- <property name="wordWrap">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item>
- <widget class="process_detector" name="game_detector" native="true">
- <property name="sizePolicy">
- <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- </layout>
- </widget>
- </widget>
- </item>
- <item>
- <widget class="QDialogButtonBox" name="buttonBox">
- <property name="standardButtons">
- <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- <customwidgets>
- <customwidget>
- <class>process_detector</class>
- <extends>QWidget</extends>
- <header>gui/process_detector.h</header>
- </customwidget>
- </customwidgets>
- <tabstops>
- <tabstop>tabWidget</tabstop>
- <tabstop>bind_center</tabstop>
- <tabstop>bind_toggle</tabstop>
- <tabstop>bind_toggle_held</tabstop>
- <tabstop>bind_zero</tabstop>
- <tabstop>bind_zero_held</tabstop>
- <tabstop>bind_start</tabstop>
- <tabstop>bind_stop</tabstop>
- <tabstop>bind_toggle_tracking</tabstop>
- <tabstop>bind_restart_tracking</tabstop>
- <tabstop>bind_center_2</tabstop>
- <tabstop>bind_toggle_2</tabstop>
- <tabstop>bind_toggle_held_2</tabstop>
- <tabstop>bind_zero_2</tabstop>
- <tabstop>bind_zero_held_2</tabstop>
- <tabstop>bind_start_2</tabstop>
- <tabstop>bind_stop_2</tabstop>
- <tabstop>bind_toggle_tracking_2</tabstop>
- <tabstop>bind_restart_tracking_2</tabstop>
- <tabstop>center_at_startup</tabstop>
- <tabstop>disable_translation</tabstop>
- <tabstop>trayp</tabstop>
- <tabstop>tray_start</tabstop>
- <tabstop>src_yaw</tabstop>
- <tabstop>invert_yaw</tabstop>
- <tabstop>src_pitch</tabstop>
- <tabstop>invert_pitch</tabstop>
- <tabstop>src_roll</tabstop>
- <tabstop>invert_roll</tabstop>
- <tabstop>src_x</tabstop>
- <tabstop>invert_x</tabstop>
- <tabstop>src_y</tabstop>
- <tabstop>invert_y</tabstop>
- <tabstop>src_z</tabstop>
- <tabstop>invert_z</tabstop>
- <tabstop>tracklogging_enabled</tabstop>
- <tabstop>tcomp_tx_disable</tabstop>
- <tabstop>tcomp_ty_disable</tabstop>
- <tabstop>tcomp_tz_disable</tabstop>
- <tabstop>tcomp_src_yaw_disable</tabstop>
- <tabstop>tcomp_src_pitch_disable</tabstop>
- <tabstop>tcomp_src_roll_disable</tabstop>
- <tabstop>neck_enable</tabstop>
- <tabstop>neck_z</tabstop>
- </tabstops>
- <resources>
- <include location="opentrack-res.qrc"/>
- </resources>
- <connections/>
- <slots>
- <slot>startEngineClicked()</slot>
- <slot>stopEngineClicked()</slot>
- <slot>cameraSettingsClicked()</slot>
- </slots>
-</ui>
diff --git a/gui/settings.hpp b/gui/settings.hpp
deleted file mode 100644
index 8ef5aa2d..00000000
--- a/gui/settings.hpp
+++ /dev/null
@@ -1,33 +0,0 @@
-#pragma once
-
-#include "export.hpp"
-
-#include "gui/ui_settings-dialog.h"
-#include "logic/shortcuts.h"
-
-#include <functional>
-
-#include <QObject>
-#include <QDialog>
-#include <QWidget>
-
-class OTR_GUI_EXPORT options_dialog : public QDialog
-{
- Q_OBJECT
-signals:
- void closing();
-public:
- explicit options_dialog(std::function<void(bool)> pause_keybindings);
-private:
- main_settings main;
- std::function<void(bool)> pause_keybindings;
- Ui::options_dialog ui;
- void closeEvent(QCloseEvent*) override;
- static QString kopts_to_string(const key_opts& opts);
-private slots:
- void doOK();
- void doCancel();
- void done(int res) override;
- void bind_key(key_opts &kopts, QLabel* label);
- void set_disable_translation_state(bool value);
-};
diff --git a/installer/.gitignore b/installer/.gitignore
new file mode 100644
index 00000000..f569d96c
--- /dev/null
+++ b/installer/.gitignore
@@ -0,0 +1 @@
+/Output
diff --git a/installer/Output/.gitignore b/installer/Output/.gitignore
deleted file mode 100644
index 72e8ffc0..00000000
--- a/installer/Output/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-*
diff --git a/installer/Output/.gitkeep b/installer/Output/.gitkeep
deleted file mode 100644
index e69de29b..00000000
--- a/installer/Output/.gitkeep
+++ /dev/null
diff --git a/installer/opentrack-installer.iss b/installer/opentrack-installer.iss
index 32b9159c..960ddf7f 100644
--- a/installer/opentrack-installer.iss
+++ b/installer/opentrack-installer.iss
@@ -20,7 +20,7 @@ AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
-DefaultDirName={pf}\{#MyAppName}
+DefaultDirName={commonpf}\{#MyAppName}
DefaultGroupName={#MyAppName}
AllowNoIcons=yes
OutputBaseFilename={#MyAppVersion}-win32-setup
@@ -34,7 +34,6 @@ RestartIfNeededByRun=False
InternalCompressLevel=ultra
CompressionThreads=4
LZMANumFastBytes=273
-MinVersion=0,6
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
@@ -42,6 +41,9 @@ Name: "english"; MessagesFile: "compiler:Default.isl"
[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
+[InstallDelete]
+Type: filesandordirs; Name: "{app}"
+
[Files]
Source: "..\build\install\*"; DestDir: "{app}"; Flags: ignoreversion createallsubdirs recursesubdirs
diff --git a/logic/extensions.cpp b/logic/extensions.cpp
deleted file mode 100644
index 03d03a83..00000000
--- a/logic/extensions.cpp
+++ /dev/null
@@ -1,74 +0,0 @@
-#include "extensions.hpp"
-
-#include <functional>
-
-using namespace options;
-
-using ext_fun_type = void(IExtension::*)(Pose&);
-using ext_mask = IExtension::event_mask;
-using ext_ord = IExtension::event_ordinal;
-
-static constexpr struct event_type_mapping
-{
- ext_fun_type ptr;
- ext_mask mask;
- ext_ord idx;
-} ordinal_to_function[] = {
- { &IExtension::process_raw, ext_mask::on_raw, ext_ord::ev_raw, },
- { &IExtension::process_before_filter, ext_mask::on_before_filter, ext_ord::ev_before_filter, },
- { &IExtension::process_before_mapping, ext_mask::on_before_mapping, ext_ord::ev_before_mapping, },
- { &IExtension::process_finished, ext_mask::on_finished, ext_ord::ev_finished, },
-};
-
-bool event_handler::is_enabled(const QString& name)
-{
- (void)name;
-#if 1
- return true;
-#else
- if (!ext_bundle->contains(name))
- return false;
-
- return ext_bundle->get<bool>(name);
-#endif
-}
-
-event_handler::event_handler(Modules::dylib_list const& extensions) : ext_bundle(make_bundle("extensions"))
-{
- for (std::shared_ptr<dylib> const& lib : extensions)
- {
- std::shared_ptr<IExtension> ext(reinterpret_cast<IExtension*>(lib->Constructor()));
- std::shared_ptr<IExtensionDialog> dlg(reinterpret_cast<IExtensionDialog*>(lib->Dialog()));
- std::shared_ptr<Metadata_> m(lib->Meta());
-
- const ext_mask mask = ext->hook_types();
-
- if (!is_enabled(lib->module_name))
- continue;
-
-#if 0
- qDebug() << "extension" << lib->module_name << "mask" << (void*)mask;
-#endif
-
- for (event_type_mapping const& mapping : ordinal_to_function)
- {
- const unsigned i = mapping.idx;
- const ext_mask mask_ = mapping.mask;
-
- if (mask & mask_)
- extensions_for_event[i].push_back({ ext, dlg, m });
- }
- }
-}
-
-void event_handler::run_events(event_ordinal k, Pose& pose)
-{
-#if 0
- auto fun = std::mem_fn(ordinal_to_function[k].ptr);
-
- for (extension& x : extensions_for_event[k])
- fun(*x.logic, pose);
-#else
- (void)k; (void)pose;
-#endif
-}
diff --git a/logic/extensions.hpp b/logic/extensions.hpp
deleted file mode 100644
index 3368b118..00000000
--- a/logic/extensions.hpp
+++ /dev/null
@@ -1,44 +0,0 @@
-#pragma once
-
-/* Copyright (c) 2017 Stanislaw Halik
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- */
-
-#include "api/plugin-support.hpp"
-#include "options/options.hpp"
-
-#include <vector>
-#include <array>
-
-#include "export.hpp"
-
-struct OTR_LOGIC_EXPORT event_handler final
-{
- using event_ordinal = IExtension::event_ordinal;
-
- struct extension
- {
- using ext = std::shared_ptr<IExtension>;
- using dlg = std::shared_ptr<IExtensionDialog>;
- using m = std::shared_ptr<Metadata_>;
-
- ext logic;
- dlg dialog;
- m metadata;
- };
-
- void run_events(event_ordinal k, Pose& pose);
- event_handler(Modules::dylib_list const& extensions);
-
-private:
- using ext_list = std::vector<extension>;
- std::array<ext_list, IExtension::event_count> extensions_for_event;
-
- options::bundle ext_bundle;
-
- bool is_enabled(const QString& name);
-};
-
diff --git a/logic/lang/de_DE.ts b/logic/lang/de_DE.ts
new file mode 100644
index 00000000..eca10616
--- /dev/null
+++ b/logic/lang/de_DE.ts
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>Work</name>
+ <message>
+ <source>Select filename</source>
+ <translation>Dateiname wählen</translation>
+ </message>
+ <message>
+ <source>CSV File (*.csv)</source>
+ <translation>CSV-Datei (*.csv)</translation>
+ </message>
+ <message>
+ <source>Logging error</source>
+ <translation>Protokollierungsfehler</translation>
+ </message>
+ <message>
+ <source>Unable to open file &apos;%1&apos;. Proceeding without logging.</source>
+ <translation>Datei &apos;%1&apos; kann nicht geöffnet werden. Es wird ohne Protokollierung fortgefahren.</translation>
+ </message>
+</context>
+<context>
+ <name>runtime_libraries</name>
+ <message>
+ <source>Library load failure</source>
+ <translation>Fehler beim Laden der Bibliothek</translation>
+ </message>
+ <message>
+ <source>Error occurred while loading protocol %1
+
+%2
+</source>
+ <translation>Ein Fehler trat auf beim Laden des Protokolls %1
+
+%2
+</translation>
+ </message>
+ <message>
+ <source>Error occurred while loading filter %1
+
+%2
+</source>
+ <translation>Fehler beim Laden des Filters %1
+
+%2
+</translation>
+ </message>
+ <message>
+ <source>Error occurred while loading tracker %1
+
+%2
+</source>
+ <translation>Fehler beim Laden des Trackers %1
+
+%2
+</translation>
+ </message>
+ <message>
+ <source>Startup failure</source>
+ <translation>Ausführungsfehler</translation>
+ </message>
+</context>
+</TS>
diff --git a/logic/lang/zh_CN.ts b/logic/lang/zh_CN.ts
index cc15f98a..000aff7c 100644
--- a/logic/lang/zh_CN.ts
+++ b/logic/lang/zh_CN.ts
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
<context>
<name>Work</name>
<message>
<source>Select filename</source>
- <translation type="unfinished"></translation>
+ <translation>选择文件名</translation>
</message>
<message>
<source>CSV File (*.csv)</source>
@@ -17,35 +17,44 @@
</message>
<message>
<source>Unable to open file &apos;%1&apos;. Proceeding without logging.</source>
- <translation type="unfinished"></translation>
+ <translation type="unfinished">未能打开文件 &apos;%1&apos;. Proceeding without logging.</translation>
</message>
</context>
<context>
<name>runtime_libraries</name>
<message>
<source>Library load failure</source>
- <translation type="unfinished"></translation>
+ <translation>库加载失败</translation>
</message>
<message>
<source>Error occurred while loading protocol %1
%2
</source>
- <translation type="unfinished"></translation>
+ <translation type="unfinished">加载协议 %1 时发生错误
+
+%2
+</translation>
</message>
<message>
<source>Error occurred while loading filter %1
%2
</source>
- <translation type="unfinished"></translation>
+ <translation type="unfinished">加载滤波器 %1 时发生错误
+
+%2
+</translation>
</message>
<message>
<source>Error occurred while loading tracker %1
%2
</source>
- <translation type="unfinished"></translation>
+ <translation type="unfinished">加载跟踪器 %1 时发生错误
+
+%2
+</translation>
</message>
<message>
<source>Startup failure</source>
diff --git a/logic/main-settings.hpp b/logic/main-settings.hpp
index 8fef7ea7..fcd5e745 100644
--- a/logic/main-settings.hpp
+++ b/logic/main-settings.hpp
@@ -15,13 +15,21 @@
#include "export.hpp"
-enum reltrans_state
+enum reltrans_state : int
{
reltrans_disabled = 0,
reltrans_enabled = 1,
reltrans_non_center = 2,
};
+enum centering_state : int
+{
+ center_disabled = 0,
+ center_point = 1,
+ center_vr360 = 2,
+ center_roll_compensated = 3,
+};
+
namespace main_settings_impl {
using namespace options;
@@ -65,7 +73,7 @@ struct OTR_LOGIC_EXPORT main_settings final
value<bool> tray_start { b, "start-in-tray", false };
value<bool> center_at_startup { b, "center-at-startup", true };
- //value<int> center_method;
+ value<centering_state> centering_mode { b, "centering-mode", center_roll_compensated };
value<int> neck_z { b, "neck-depth", 0 };
value<bool> neck_enable { b, "neck-enable", false };
diff --git a/logic/pipeline.cpp b/logic/pipeline.cpp
index 658c62bb..2e8efe55 100644
--- a/logic/pipeline.cpp
+++ b/logic/pipeline.cpp
@@ -15,7 +15,7 @@
#include "compat/sleep.hpp"
#include "compat/math.hpp"
#include "compat/meta.hpp"
-#include "compat/macros.hpp"
+#include "compat/macros.h"
#include "compat/thread-name.hpp"
#include "pipeline.hpp"
@@ -184,8 +184,8 @@ Pose_ reltrans::apply_neck(const rmat& R, int nz, bool disable_tz) const
return neck;
}
-pipeline::pipeline(const Mappings& m, const runtime_libraries& libs, event_handler& ev, TrackLogger& logger) :
- m(m), ev(ev), libs(libs), logger(logger)
+pipeline::pipeline(const Mappings& m, const runtime_libraries& libs, TrackLogger& logger) :
+ m(m), libs(libs), logger(logger)
{
}
@@ -272,7 +272,7 @@ bool pipeline::maybe_enable_center_on_tracking_started()
return false;
}
-void pipeline::maybe_set_center_pose(const Pose& value, bool own_center_logic)
+void pipeline::maybe_set_center_pose(const centering_state mode, const Pose& value, bool own_center_logic)
{
if (b.get(f_center | f_held_center))
{
@@ -283,34 +283,73 @@ void pipeline::maybe_set_center_pose(const Pose& value, bool own_center_logic)
if (own_center_logic)
{
- center.inv_R = rmat::eye();
- center.T = {};
+ center.P = {};
+ center.QC = QQuaternion(1,0,0,0);
+ center.QR = QQuaternion(1,0,0,0);
}
else
{
- center.inv_R = euler_to_rmat(Pose_(&value[Yaw]) * (M_PI / 180)).t();
- center.T = Pose_(&value[TX]);
+ if (mode)
+ {
+ center.P = value;
+ center.QC = QQuaternion::fromEulerAngles(value[Pitch], value[Yaw], -value[Roll]).conjugated();
+ center.QR = QQuaternion::fromEulerAngles(value[Pitch], value[Yaw], 0).conjugated();
+ }
+ else
+ {
+ // To reset the centering coordinates
+ // just select "Centering method [Disabled]" and then press [Center] button
+ center.P = {};
+ center.QC = QQuaternion(1,0,0,0);
+ center.QR = QQuaternion(1,0,0,0);
+ }
}
}
}
-Pose pipeline::apply_center(Pose value) const
+Pose pipeline::apply_center(const centering_state mode, Pose value) const
{
- {
- for (unsigned k = 0; k < 3; k++)
- value(k) -= center.T(k);
-
- Pose_ rot = rmat_to_euler(
- euler_to_rmat(Pose_(&value[Yaw]) * (M_PI / 180)) * center.inv_R
- );
- for (unsigned k = 0; k < 3; k++)
- value(k+3) = rot(k) * 180 / M_PI;
+ if (mode != center_disabled)
+ {
+ for (unsigned k = TX; k <= TZ; k++)
+ value(k) -= center.P(k);
+
+ QQuaternion q;
+ QVector3D v;
+
+ switch (mode)
+ {
+ case center_point:
+ for (unsigned k = Yaw; k <= Roll; k++)
+ {
+ value(k) -= center.P(k);
+ if (fabs(value[k]) > 180) value[k] -= copysign(360, value[k]);
+ }
+ break;
+ case center_vr360:
+ q = QQuaternion::fromEulerAngles(value[Pitch], value[Yaw], -value[Roll]);
+ q = center.QC * q;
+ v = q.toEulerAngles();
+ value[Pitch] = v.x();
+ value[Yaw] = v.y();
+ value[Roll] = -v.z();
+ break;
+ case center_roll_compensated:
+ q = QQuaternion::fromEulerAngles(value[Pitch], value[Yaw], center.P[Roll] - value[Roll]);
+ q = center.QR * q;
+ v = q.toEulerAngles();
+ value[Pitch] = v.x();
+ value[Yaw] = v.y();
+ value[Roll] = -v.z();
+ break;
+ case center_disabled: break;
+ }
}
for (int i = 0; i < 6; i++)
// don't invert after reltrans
// inverting here doesn't break centering
- if (m(i).opts.invert)
+ if (m(i).opts.invert_pre)
value(i) = -value(i);
return value;
@@ -352,7 +391,7 @@ Pose pipeline::maybe_apply_filter(const Pose& value) const
Pose pipeline::apply_zero_pos(Pose value) const
{
for (int i = 0; i < 6; i++)
- value(i) += m(i).opts.zero * (m(i).opts.invert ? -1 : 1);
+ value(i) += m(i).opts.zero * (m(i).opts.invert_pre ? -1 : 1);
return value;
}
@@ -383,7 +422,6 @@ Pose pipeline::apply_reltrans(Pose value, vec6_bool disabled, bool centerp)
void pipeline::logic()
{
using namespace euler;
- using EV = event_handler::event_ordinal;
logger.write_dt();
logger.reset_dt();
@@ -396,7 +434,6 @@ void pipeline::logic()
{
Pose tmp;
libs.pTracker->data(tmp);
- ev.run_events(EV::ev_raw, tmp);
newpose = tmp;
}
@@ -407,15 +444,14 @@ void pipeline::logic()
{
maybe_enable_center_on_tracking_started();
- maybe_set_center_pose(value, own_center_logic);
- value = apply_center(value);
+ maybe_set_center_pose(s.centering_mode, value, own_center_logic);
+ value = apply_center(s.centering_mode, value);
// "corrected" - after various transformations to account for camera position
logger.write_pose(value);
}
{
- ev.run_events(EV::ev_before_filter, value);
// we must proceed with all the filtering since the filter
// needs fresh values to prevent deconvergence
if (center_ordered)
@@ -427,7 +463,6 @@ void pipeline::logic()
}
{
- ev.run_events(EV::ev_before_mapping, value);
// CAVEAT rotation only, due to reltrans
for (int i = 3; i < 6; i++)
value(i) = map(value(i), m(i));
@@ -442,14 +477,13 @@ void pipeline::logic()
nan_check(value);
}
- if (!hold_ordered)
- goto ok;
+ goto ok;
error:
{
QMutexLocker foo(&mtx);
- value = output_pose;
+ value = last_value;
raw = raw_6dof;
// for widget last value display
@@ -465,10 +499,15 @@ ok:
for (int i = 0; i < 6; i++)
value(i) = 0;
- if (hold_ordered) value = output_pose;
- else value = apply_zero_pos(value);
+ if (hold_ordered)
+ value = last_value;
+ last_value = value;
+ value = apply_zero_pos(value);
+
+ for (int i = 0; i < 6; i++)
+ if (m(i).opts.invert_post)
+ value(i) = -value(i);
- ev.run_events(EV::ev_finished, value);
libs.pProtocol->pose(value, raw);
QMutexLocker foo(&mtx);
@@ -565,8 +604,7 @@ void pipeline::run()
backlog_time = {};
}
- const int sleep_ms = (int)clamp(interval - backlog_time,
- ms{0}, ms{10}).count();
+ const int sleep_ms = (int)std::clamp(interval - backlog_time, ms{0}, ms{10}).count();
#ifdef DEBUG_TIMINGS
debug_timings(backlog_time.count(), sleep_ms);
diff --git a/logic/pipeline.hpp b/logic/pipeline.hpp
index 7775054e..a539525d 100644
--- a/logic/pipeline.hpp
+++ b/logic/pipeline.hpp
@@ -8,7 +8,6 @@
#include "compat/euler.hpp"
#include "compat/enum-operators.hpp"
#include "runtime-libraries.hpp"
-#include "extensions.hpp"
#include "spline/spline.hpp"
#include "main-settings.hpp"
@@ -20,6 +19,7 @@
#include <atomic>
#include <cmath>
+#include <QQuaternion>
#include "export.hpp"
@@ -84,10 +84,9 @@ class OTR_LOGIC_EXPORT pipeline : private QThread
mutable QMutex mtx;
main_settings s;
const Mappings& m;
- event_handler& ev;
Timer t;
- Pose output_pose, raw_6dof;
+ Pose output_pose, raw_6dof, last_value;
Pose newpose;
runtime_libraries const& libs;
@@ -99,8 +98,9 @@ class OTR_LOGIC_EXPORT pipeline : private QThread
reltrans rel;
struct {
- rmat inv_R = rmat::eye();
- Pose_ T;
+ Pose P;
+ QQuaternion QC = QQuaternion(1,0,0,0);
+ QQuaternion QR = QQuaternion(1,0,0,0);
} center;
time_units::ms backlog_time {};
@@ -111,8 +111,8 @@ class OTR_LOGIC_EXPORT pipeline : private QThread
void logic();
void run() override;
bool maybe_enable_center_on_tracking_started();
- void maybe_set_center_pose(const Pose& value, bool own_center_logic);
- Pose apply_center(Pose value) const;
+ void maybe_set_center_pose(const centering_state mode, const Pose& value, bool own_center_logic);
+ Pose apply_center(const centering_state mode, Pose value) const;
std::tuple<Pose, Pose, vec6_bool> get_selected_axis_values(const Pose& newpose) const;
Pose maybe_apply_filter(const Pose& value) const;
Pose apply_reltrans(Pose value, vec6_bool disabled, bool centerp);
@@ -123,7 +123,7 @@ class OTR_LOGIC_EXPORT pipeline : private QThread
bits b;
public:
- pipeline(const Mappings& m, const runtime_libraries& libs, event_handler& ev, TrackLogger& logger);
+ pipeline(const Mappings& m, const runtime_libraries& libs, TrackLogger& logger);
~pipeline() override;
void raw_and_mapped_pose(double* mapped, double* raw) const;
diff --git a/logic/shortcuts.cpp b/logic/shortcuts.cpp
index 26465101..df21f7d2 100644
--- a/logic/shortcuts.cpp
+++ b/logic/shortcuts.cpp
@@ -51,6 +51,12 @@ void Shortcuts::bind_shortcut(K& key, const key_opts& k, bool held)
int idx = 0;
QKeySequence code(QKeySequence::UnknownKey);
+ if (k.guid == QStringLiteral("mouse"))
+ {
+ key.guid = k.guid;
+ key.keycode = k.button;
+ key.held = held;
+ }
if (!k.guid->isEmpty())
{
key.guid = k.guid;
@@ -70,7 +76,7 @@ void Shortcuts::bind_shortcut(K& key, const key_opts& k, bool held)
code != QKeySequence{ QKeySequence::UnknownKey } &&
win_key::from_qt(code, idx, mods))
{
- key.guid = "";
+ key.guid = QString{};
key.keycode = idx;
key.held = held;
key.ctrl = !!(mods & Qt::ControlModifier);
@@ -88,7 +94,7 @@ void Shortcuts::receiver(const Key& key)
const unsigned sz = keys.size();
for (unsigned i = 0; i < sz; i++)
{
- K& k = std::get<0>(keys[i]);
+ auto& [k, f, held] = keys[i];
if (key.guid != k.guid)
continue;
if (key.keycode != k.keycode)
@@ -103,7 +109,6 @@ void Shortcuts::receiver(const Key& key)
if (!k.should_process())
continue;
}
- fun& f = std::get<1>(keys[i]);
f(key.held);
}
}
diff --git a/logic/state.cpp b/logic/state.cpp
index afdf5b12..dc5e5a36 100644
--- a/logic/state.cpp
+++ b/logic/state.cpp
@@ -1,5 +1,5 @@
#include "state.hpp"
-
+#include "opentrack/defs.hpp"
#include <iterator>
using dylib_ptr = Modules::dylib_ptr;
@@ -22,13 +22,19 @@ std::tuple<dylib_ptr, int> State::module_by_name(const QString& name, dylib_list
State::State(const QString& library_path) :
modules(library_path),
- ev(modules.extensions()),
- pose(s.all_axis_opts)
+ pose(s.all_axis_opts),
+ library_path{library_path}
{}
dylib_ptr State::current_tracker()
{
- auto [ptr, idx] = module_by_name(m.tracker_dll, modules.trackers());
+ const QString& module =
+#ifdef UI_FORCED_TRACKER
+ UI_FORCED_TRACKER;
+#else
+ m.tracker_dll;
+#endif
+ auto [ptr, idx] = module_by_name(module, modules.trackers());
return ptr;
}
@@ -40,6 +46,12 @@ dylib_ptr State::current_protocol()
dylib_ptr State::current_filter()
{
- auto [ptr, idx] = module_by_name(m.filter_dll, modules.filters());
+ const QString& module =
+#ifdef UI_FORCED_FILTER
+ UI_FORCED_FILTER;
+#else
+ m.filter_dll;
+#endif
+ auto [ptr, idx] = module_by_name(module, modules.filters());
return ptr;
}
diff --git a/logic/state.hpp b/logic/state.hpp
index abce1daf..7fc06a5c 100644
--- a/logic/state.hpp
+++ b/logic/state.hpp
@@ -12,7 +12,6 @@
#include "api/plugin-support.hpp"
#include "main-settings.hpp"
#include "mappings.hpp"
-#include "extensions.hpp"
#include "work.hpp"
#include "export.hpp"
@@ -32,9 +31,9 @@ struct OTR_LOGIC_EXPORT State
dylib_ptr current_filter();
Modules modules;
- event_handler ev;
main_settings s;
module_settings m;
Mappings pose;
std::shared_ptr<Work> work;
+ QString library_path;
};
diff --git a/logic/win32-shortcuts.cpp b/logic/win32-shortcuts.cpp
index 086c8d92..cb4f99b3 100644
--- a/logic/win32-shortcuts.cpp
+++ b/logic/win32-shortcuts.cpp
@@ -41,6 +41,9 @@ static const win_key windows_key_sequences[] {
{ DIK_F10, Qt::Key_F10 },
{ DIK_F11, Qt::Key_F11 },
{ DIK_F12, Qt::Key_F12 },
+ { DIK_F13, Qt::Key_F13 },
+ { DIK_F14, Qt::Key_F14 },
+ { DIK_F15, Qt::Key_F15 },
{ DIK_LEFT, Qt::Key_Left },
{ DIK_RIGHT, Qt::Key_Right },
{ DIK_UP, Qt::Key_Up },
@@ -63,32 +66,6 @@ static const win_key windows_key_sequences[] {
{ DIK_MINUS, Qt::Key_Minus },
{ DIK_EQUALS, Qt::Key_Equal },
{ DIK_PERIOD, Qt::Key_Period },
- { DIK_F1, Qt::Key_F1 },
- { DIK_F2, Qt::Key_F2 },
- { DIK_F3, Qt::Key_F3 },
- { DIK_F4, Qt::Key_F4 },
- { DIK_F5, Qt::Key_F5 },
- { DIK_F6, Qt::Key_F6 },
- { DIK_F7, Qt::Key_F7 },
- { DIK_F8, Qt::Key_F8 },
- { DIK_F9, Qt::Key_F9 },
- { DIK_F10, Qt::Key_F10 },
- { DIK_F11, Qt::Key_F11 },
- { DIK_F13, Qt::Key_F12 },
- { DIK_F14, Qt::Key_F12 },
- { DIK_F15, Qt::Key_F12 },
- { 0x67, Qt::Key_F12 },
- { 0x68, Qt::Key_F13 },
- { 0x69, Qt::Key_F14 },
- { 0x6a, Qt::Key_F15 },
- { 0x6b, Qt::Key_F16 },
- { 0x6c, Qt::Key_F17 },
- { 0x6d, Qt::Key_F18 },
- { 0x6e, Qt::Key_F19 },
- { 0x70, Qt::Key_F20 },
- { 0x71, Qt::Key_F21 },
- { 0x72, Qt::Key_F23 },
- { 0x73, Qt::Key_F24 },
{ DIK_0, Qt::Key_0 },
{ DIK_1, Qt::Key_1 },
{ DIK_2, Qt::Key_2 },
diff --git a/logic/work.cpp b/logic/work.cpp
index 7689f916..8c6a3a62 100644
--- a/logic/work.cpp
+++ b/logic/work.cpp
@@ -57,10 +57,10 @@ std::unique_ptr<TrackLogger> Work::make_logger(main_settings &s)
}
-Work::Work(const Mappings& m, event_handler& ev, QFrame* frame,
+Work::Work(const Mappings& m, QFrame* frame,
const dylibptr& tracker, const dylibptr& filter, const dylibptr& proto) :
libs(frame, tracker, filter, proto),
- pipeline_{ m, libs, ev, *logger }
+ pipeline_{ m, libs, *logger }
{
if (!is_ok())
return;
diff --git a/logic/work.hpp b/logic/work.hpp
index 82449e47..ef839257 100644
--- a/logic/work.hpp
+++ b/logic/work.hpp
@@ -45,21 +45,21 @@ public:
std::vector<key_tuple> keys {
// third argument means "keydown only"
- key_tuple(s.key_center1, [&](bool x) { pipeline_.set_held_center(x); }, false),
- key_tuple(s.key_center2, [&](bool x) { pipeline_.set_held_center(x); }, false),
+ key_tuple(s.key_center1, [this](bool x) { pipeline_.set_held_center(x); }, false),
+ key_tuple(s.key_center2, [this](bool x) { pipeline_.set_held_center(x); }, false),
- key_tuple(s.key_toggle1, [&](bool) { pipeline_.toggle_enabled(); }, true),
- key_tuple(s.key_toggle2, [&](bool) { pipeline_.toggle_enabled(); }, true),
- key_tuple(s.key_toggle_press1, [&](bool x) { pipeline_.set_enabled(!x); }, false),
- key_tuple(s.key_toggle_press2, [&](bool x) { pipeline_.set_enabled(!x); }, false),
+ key_tuple(s.key_toggle1, [this](bool) { pipeline_.toggle_enabled(); }, true),
+ key_tuple(s.key_toggle2, [this](bool) { pipeline_.toggle_enabled(); }, true),
+ key_tuple(s.key_toggle_press1, [this](bool x) { pipeline_.set_enabled(!x); }, false),
+ key_tuple(s.key_toggle_press2, [this](bool x) { pipeline_.set_enabled(!x); }, false),
- key_tuple(s.key_zero1, [&](bool) { pipeline_.toggle_zero(); }, true),
- key_tuple(s.key_zero2, [&](bool) { pipeline_.toggle_zero(); }, true),
- key_tuple(s.key_zero_press1, [&](bool x) { pipeline_.set_zero(x); }, false),
- key_tuple(s.key_zero_press2, [&](bool x) { pipeline_.set_zero(x); }, false),
+ key_tuple(s.key_zero1, [this](bool) { pipeline_.toggle_zero(); }, true),
+ key_tuple(s.key_zero2, [this](bool) { pipeline_.toggle_zero(); }, true),
+ key_tuple(s.key_zero_press1, [this](bool x) { pipeline_.set_zero(x); }, false),
+ key_tuple(s.key_zero_press2, [this](bool x) { pipeline_.set_zero(x); }, false),
};
- Work(const Mappings& m, event_handler& ev, QFrame* frame,
+ Work(const Mappings& m, QFrame* frame,
const dylibptr& tracker, const dylibptr& filter, const dylibptr& proto);
void reload_shortcuts();
bool is_ok() const;
diff --git a/macosx/make-app-bundle.sh b/macosx/make-app-bundle.sh
index 9265eeb9..d04dc1bd 100755
--- a/macosx/make-app-bundle.sh
+++ b/macosx/make-app-bundle.sh
@@ -38,9 +38,9 @@ cp "$dir/PkgInfo" "$install/$APPNAME.app/Contents/"
mkdir -p "$install/$APPNAME.app/Contents/MacOS/Plugins"
cp -r "$install/Plugins" "$install/$APPNAME.app/Contents/MacOS/"
-# Add framework and other libraries and fixup other libraries
+# Use either of these, two of them at the same time will break things!
macdeployqt "$install/$APPNAME.app" -libpath="$install/Library"
-sh "$dir/install-fail-tool" "$install/$APPNAME.app/Contents/Frameworks"
+#sh "$dir/install-fail-tool" "$install/$APPNAME.app/Contents/Frameworks"
# Create an 512 resolution size for the icon (for retina displays mostly)
#gm convert -size 512x512 "$dir/../gui/images/opentrack.png" "$tmp/opentrack.png"
@@ -74,7 +74,6 @@ create-dmg \
--hide-extension "$APPNAME.app" \
--no-internet-enable \
--add-folder "Document" "$install/doc" 20 40 \
- --add-folder "source-code" "$install/source-code" 220 40 \
--add-folder "Xplane-Plugin" "$install/xplane" 420 40 \
--add-folder "thirdparty" "$install/thirdparty" 620 40 \
"$version.dmg" \
diff --git a/main-window/CMakeLists.txt b/main-window/CMakeLists.txt
deleted file mode 100644
index c9228536..00000000
--- a/main-window/CMakeLists.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-if(opentrack_maintainer-mode)
- otr_module(main-window BIN NO-INSTALL)
- foreach(k user-interface logic pose-widget migration spline)
- target_link_libraries(${self} opentrack-${k})
- endforeach()
-endif()
diff --git a/main-window/export.hpp b/main-window/export.hpp
deleted file mode 100644
index 184cf035..00000000
--- a/main-window/export.hpp
+++ /dev/null
@@ -1,11 +0,0 @@
-// generates export.hpp for each module from compat/linkage.hpp
-
-#pragma once
-
-#include "compat/linkage-macros.hpp"
-
-#ifdef BUILD_MAIN_WINDOW
-# define OTR_MAIN_EXPORT OTR_GENERIC_EXPORT
-#else
-# define OTR_MAIN_EXPORT OTR_GENERIC_IMPORT
-#endif
diff --git a/main-window/lang/nl_NL.ts b/main-window/lang/nl_NL.ts
deleted file mode 100644
index 6401616d..00000000
--- a/main-window/lang/nl_NL.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!DOCTYPE TS>
-<TS version="2.1">
-</TS>
diff --git a/main-window/lang/ru_RU.ts b/main-window/lang/ru_RU.ts
deleted file mode 100644
index 6401616d..00000000
--- a/main-window/lang/ru_RU.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!DOCTYPE TS>
-<TS version="2.1">
-</TS>
diff --git a/main-window/lang/stub.ts b/main-window/lang/stub.ts
deleted file mode 100644
index 6401616d..00000000
--- a/main-window/lang/stub.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!DOCTYPE TS>
-<TS version="2.1">
-</TS>
diff --git a/main-window/lang/zh_CN.ts b/main-window/lang/zh_CN.ts
deleted file mode 100644
index 6401616d..00000000
--- a/main-window/lang/zh_CN.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!DOCTYPE TS>
-<TS version="2.1">
-</TS>
diff --git a/main-window/mixin-traits.cpp b/main-window/mixin-traits.cpp
deleted file mode 100644
index b74a6f7a..00000000
--- a/main-window/mixin-traits.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-#define MIXIN_TRAIT_TESTS
-
-#ifdef MIXIN_TRAIT_TESTS
-# include "mixin-traits.hpp"
-
-// the `impl' class provides a cast template through the CRTP pattern.
-// mixins don't do direct inheritance on themselves,
-// that's what mixin_traits::depends is for.
-
-namespace mixins::traits_detail {
-
-struct A {};
-struct B {};
-struct C {};
-struct D {};
-
-template<> struct mixin_traits<A>
-{
- using depends = tuple<>;
-};
-
-template<> struct mixin_traits<B>
-{
- using depends = tuple<A>;
-};
-
-template<> struct mixin_traits<C>
-{
- using depends = tuple<A>;
-};
-
-template<> struct mixin_traits<D>
-{
- using depends = tuple<C>;
-};
-
-extern void test1();
-
-void test1()
-{
- struct U : B, A {};
- struct V : D {};
- struct W : C, A {};
- struct Q : virtual W, virtual D {};
-
-//#define SHOULD_NOT_COMPILE
-#ifdef SHOULD_NOT_COMPILE
- (void)impl<Q, W>{}; // W not a mixin
- (void)impl<V, A>{}; // A
- (void)impl<V, D>{}; // D => C => A
- (void)impl<V, D>{}; // D => C => A
- (void)impl<W, C, B>{}; // B
-#else
- (void)impl<U, B>{};
- (void)impl<W, C>{};
- (void)impl<Q, D, A>{};
-#endif
-}
-
-} // ns mixins::traits_detail
-
-#endif
diff --git a/main-window/mixin-traits.hpp b/main-window/mixin-traits.hpp
deleted file mode 100644
index 45df7fdb..00000000
--- a/main-window/mixin-traits.hpp
+++ /dev/null
@@ -1,42 +0,0 @@
-#pragma once
-
-#include "compat/meta.hpp"
-
-#include <type_traits>
-
-namespace mixins::traits_detail {
-
- using namespace meta;
-
- template<typename... xs> using tuple = tuple_<xs...>;
-
- template<typename t>
- struct mixin_traits {
- // implement this!
- //using depends = tuple<>;
- };
-
- template<typename klass, typename...> struct check_depends_;
-
- template<typename klass>
- struct check_depends_<klass> : std::true_type
- {
- };
-
- template<typename klass, typename x, typename... xs>
- struct check_depends_<klass, x, xs...> :
- std::bool_constant<
- std::is_base_of_v<x, klass> &&
- lift_v<check_depends_, cons<klass, typename mixin_traits<x>::depends>> &&
- check_depends_<klass, xs...>::value
- >
- {
- };
-
- template<typename klass, typename... xs>
- struct impl
- {
- static_assert(lift<check_depends_, tuple<klass, xs...>>::value,
- "class must inherit dependent mixins");
- };
-} // ns mixins::traits_detail
diff --git a/main-window/mixins.hpp b/main-window/mixins.hpp
deleted file mode 100644
index b85e6498..00000000
--- a/main-window/mixins.hpp
+++ /dev/null
@@ -1,13 +0,0 @@
-#pragma once
-
-#include "export.hpp"
-
-// XXX TODO add is_base_of and void_t stuff
-
-#define OTR_MIXIN_NS(name) \
- mixins :: detail :: name
-
-#define OTR_DECLARE_MIXIN(name) \
- namespace mixins { \
- using name = :: OTR_MIXIN_NS(name) :: name; \
- }
diff --git a/main-window/module-mixin.cpp b/main-window/module-mixin.cpp
deleted file mode 100644
index 18b2867d..00000000
--- a/main-window/module-mixin.cpp
+++ /dev/null
@@ -1,126 +0,0 @@
-#include "module-mixin.hpp"
-
-#include <algorithm>
-
-namespace OTR_MIXIN_NS(module_mixin) {
-
-std::tuple<dylib_ptr, int>
-module_mixin::module_by_name(const QString& name, const dylib_list& list) const
-{
- auto it = std::find_if(list.cbegin(), list.cend(), [&name](const dylib_ptr& lib) {
- if (!lib)
- return name.isEmpty();
- else
- return name == lib->module_name;
- });
-
- if (it == list.cend())
- return { nullptr, -1 };
- else
- return { *it, int(std::distance(list.cbegin(), it)) };
-}
-
-//static
-void show_window(QWidget& d, bool fresh)
-{
- if (fresh)
- {
- d.setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | d.windowFlags());
- d.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
-
- d.show();
- d.adjustSize();
- d.raise();
- }
- else
- {
- d.show();
- d.raise();
- }
-}
-
-template<typename t, typename F>
-static bool mk_window_common(std::unique_ptr<t>& d, F&& fun)
-{
- bool fresh = false;
-
- if (!d)
- d = fun(), fresh = !!d;
-
- if (d)
- show_window(*d, fresh);
-
- return fresh;
-}
-
-template<typename t>
-static bool mk_dialog(std::unique_ptr<t>& place, const std::shared_ptr<dylib>& lib)
-{
- using u = std::unique_ptr<t>;
-
- return mk_window_common(place, [&] {
- if (lib && lib->Dialog)
- return u{ (t*)lib->Dialog() };
- else
- return u{};
- });
-}
-
-dylib_ptr module_mixin::current_tracker()
-{
- auto [ptr, idx] = module_by_name(s.tracker_dll, modules.trackers());
- return ptr;
-}
-
-dylib_ptr module_mixin::current_protocol()
-{
- auto [ptr, idx] = module_by_name(s.protocol_dll, modules.protocols());
- return ptr;
-}
-
-dylib_ptr module_mixin::current_filter()
-{
- auto [ptr, idx] = module_by_name(s.filter_dll, modules.filters());
- return ptr;
-}
-
-void module_mixin::show_tracker_settings_()
-{
- if (mk_dialog(tracker_dialog, current_tracker()) && state.work && state.work->libs.pTracker)
- tracker_dialog->register_tracker(state.work->libs.pTracker.get());
- if (tracker_dialog)
- QObject::connect(tracker_dialog.get(), &ITrackerDialog::closing,
- &fuzz, [this] { tracker_dialog = nullptr; });
-}
-
-void module_mixin::show_proto_settings_()
-{
- if (mk_dialog(proto_dialog, current_protocol()) && state.work && state.work->libs.pProtocol)
- proto_dialog->register_protocol(state.work->libs.pProtocol.get());
- if (proto_dialog)
- QObject::connect(proto_dialog.get(), &IProtocolDialog::closing,
- &fuzz, [this] { proto_dialog = nullptr; });
-}
-
-void module_mixin::show_filter_settings_()
-{
- if (mk_dialog(filter_dialog, current_filter()) && state.work && state.work->libs.pFilter)
- filter_dialog->register_filter(state.work->libs.pFilter.get());
- if (filter_dialog)
- QObject::connect(filter_dialog.get(), &IFilterDialog::closing,
- &fuzz, [this] { filter_dialog = nullptr; });
-}
-
-// this template function must go to a separate function like "options_mixin".
-template<typename t, typename... Args>
-static bool mk_window(std::unique_ptr<t>& place, Args&&... params)
-{
- return mk_window_common(place, [&] {
- return std::make_unique<t>(params...);
- });
-}
-
-module_mixin::module_mixin() = default;
-module_mixin::~module_mixin() = default;
-
-} // ns
diff --git a/main-window/module-mixin.hpp b/main-window/module-mixin.hpp
deleted file mode 100644
index cde0484c..00000000
--- a/main-window/module-mixin.hpp
+++ /dev/null
@@ -1,52 +0,0 @@
-#pragma once
-
-#include "mixins.hpp"
-#include "compat/library-path.hpp"
-#include "api/plugin-api.hpp"
-#include "logic/extensions.hpp"
-#include "logic/state.hpp"
-#include "logic/main-settings.hpp"
-
-#include <memory>
-#include <utility>
-
-#include <QObject>
-
-namespace OTR_MIXIN_NS(module_mixin) {
-
-using namespace options;
-
-using dylib_ptr = Modules::dylib_ptr;
-using dylib_list = Modules::dylib_list;
-
-struct OTR_MAIN_EXPORT module_mixin
-{
- module_mixin();
- virtual ~module_mixin();
-
- std::unique_ptr<ITrackerDialog> tracker_dialog;
- std::unique_ptr<IProtocolDialog> proto_dialog;
- std::unique_ptr<IFilterDialog> filter_dialog;
-
- std::tuple<dylib_ptr, int> module_by_name(const QString& name, const dylib_list& list) const;
-
- dylib_ptr current_tracker();
- dylib_ptr current_protocol();
- dylib_ptr current_filter();
-
- void show_tracker_settings_();
- void show_proto_settings_();
- void show_filter_settings_();
-
-private:
- Modules modules { OPENTRACK_BASE_PATH + OPENTRACK_LIBRARY_PATH };
- event_handler ev { modules.extensions() };
- module_settings s;
- State state { OPENTRACK_BASE_PATH + OPENTRACK_LIBRARY_PATH };
-
- QObject fuzz;
-};
-
-}
-
-OTR_DECLARE_MIXIN(module_mixin)
diff --git a/migration/20220105_00-pt-grayscale.cpp b/migration/20220105_00-pt-grayscale.cpp
new file mode 100644
index 00000000..44bdc470
--- /dev/null
+++ b/migration/20220105_00-pt-grayscale.cpp
@@ -0,0 +1,38 @@
+#include "migration.hpp"
+#include "options/options.hpp"
+
+using namespace migrations;
+using namespace options;
+
+#include "api/plugin-support.hpp"
+#include "compat/library-path.hpp"
+
+struct pt_color_grayscale : migration
+{
+ bundle b { make_bundle("tracker-pt") };
+ enum : int { pt_color_average = 5, pt_color_bt709 = 2, };
+
+ QString unique_date() const override
+ {
+ return "20220105_00";
+ }
+
+ QString name() const override
+ {
+ return "pt color enum";
+ }
+
+ bool should_run() const override
+ {
+ auto x = b->get_variant("blob-color").toInt();
+ return x == pt_color_average;
+ }
+
+ void run() override
+ {
+ b->store_kv("blob-color", QVariant::fromValue((int)pt_color_bt709));
+ b->save();
+ }
+};
+
+OPENTRACK_MIGRATION(pt_color_grayscale)
diff --git a/migration/20220126_00-camera-name.cpp b/migration/20220126_00-camera-name.cpp
new file mode 100644
index 00000000..adb6d718
--- /dev/null
+++ b/migration/20220126_00-camera-name.cpp
@@ -0,0 +1,79 @@
+#ifdef _WIN32
+#include "migration.hpp"
+#include "options/options.hpp"
+#include "compat/camera-names.hpp"
+
+using namespace migrations;
+using namespace options;
+
+#include "api/plugin-support.hpp"
+#include "compat/library-path.hpp"
+#include <tuple>
+#include <QString>
+
+static const std::tuple<const char*, const char*> modules[] = {
+ { "tracker-aruco", "camera-name" },
+ { "tracker-easy", "camera-name" },
+ { "neuralnet-tracker", "camera-name" },
+ { "tracker-pt", "camera-name" },
+};
+
+struct win32_camera_name : migration
+{
+ QString unique_date() const override
+ {
+ return "20220126_00";
+ }
+
+ QString name() const override
+ {
+ return "camera name";
+ }
+
+ bool should_run() const override
+ {
+ for (const auto& [name, prop] : modules)
+ {
+ bundle b { make_bundle(name) };
+ QString str = b->get_variant(prop).toString();
+ if (!str.isEmpty() && !str.contains(" ["))
+ return true;
+ }
+ return false;
+ }
+
+ void run() override
+ {
+ auto cameras = get_camera_names();
+
+ for (const auto& [bundle_name, prop] : modules)
+ {
+ bundle b { make_bundle(bundle_name) };
+ QString name = b->get_variant(prop).toString();
+ if (name.isEmpty() || name.contains(" ["))
+ continue;
+ auto full_name_of = [&](const QString& str) {
+ QString ret = str;
+ auto prefix = str + " [";
+ for (const auto& [x, _] : cameras)
+ {
+ if (x == str)
+ return str;
+ if (x.startsWith(prefix))
+ ret = x;
+ }
+ return ret;
+ };
+ auto full_name = full_name_of(name);
+ if (name != full_name)
+ {
+ b->store_kv(prop, full_name);
+ b->save();
+ }
+ }
+ }
+};
+
+OPENTRACK_MIGRATION(win32_camera_name)
+
+#endif
diff --git a/migration/lang/de_DE.ts b/migration/lang/de_DE.ts
new file mode 100644
index 00000000..1552582e
--- /dev/null
+++ b/migration/lang/de_DE.ts
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+</TS>
diff --git a/migration/lang/zh_CN.ts b/migration/lang/zh_CN.ts
index 6401616d..e5ca8aa9 100644
--- a/migration/lang/zh_CN.ts
+++ b/migration/lang/zh_CN.ts
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
</TS>
diff --git a/migration/migration.cpp b/migration/migration.cpp
index 4ddb2c8c..f4b1739b 100644
--- a/migration/migration.cpp
+++ b/migration/migration.cpp
@@ -26,14 +26,25 @@ using namespace options::globals;
namespace migrations::detail {
-static std::vector<mptr> migration_list;
-static std::vector<mfun> migration_thunks;
+
+std::vector<mptr>& migrator::migration_list()
+{
+ static std::vector<mptr> v;
+ return v;
+}
+
+std::vector<mfun>& migrator::migration_thunks()
+{
+ static std::vector<mfun> v;
+ return v;
+}
+
void migrator::register_migration(mptr const& m)
{
const QString date = m->unique_date();
- for (mptr const& m2 : migration_list)
+ for (mptr const& m2 : migration_list())
if (m2->unique_date() == date)
std::abort();
@@ -65,35 +76,35 @@ void migrator::register_migration(mptr const& m)
if (day_ < 1 || day_ > 31)
abort();
- migration_list.push_back(m);
+ migration_list().push_back(m);
}
void migrator::eval_thunks()
{
- for (auto& fun : migration_thunks)
+ for (auto& fun : migration_thunks())
{
mptr m = fun();
register_migration(m);
}
- if (!migration_thunks.empty())
+ if (!migration_thunks().empty())
sort_migrations();
- migration_thunks.clear();
+ migration_thunks().clear();
}
void migrator::add_migration_thunk(mfun& thunk)
{
- migration_thunks.push_back(thunk);
+ migration_thunks().push_back(thunk);
}
std::vector<mptr>& migrator::migrations()
{
eval_thunks();
- return migration_list;
+ return migration_list();
}
void migrator::sort_migrations()
{
- std::sort(migration_list.begin(), migration_list.end(),
+ std::sort(migration_list().begin(), migration_list().end(),
[](const mptr x, const mptr y) {
return x->unique_date() < y->unique_date();
});
diff --git a/migration/migration.hpp b/migration/migration.hpp
index a3035247..7fc18c97 100644
--- a/migration/migration.hpp
+++ b/migration/migration.hpp
@@ -43,6 +43,9 @@ namespace detail {
static void set_last_migration_time(const QString& val);
static int to_int(const QString& str, bool& ok);
+
+ static std::vector<mptr>& migration_list();
+ static std::vector<mfun>& migration_thunks();
};
template<typename t>
diff --git a/opentrack/CMakeLists.txt b/opentrack/CMakeLists.txt
index a8829aa5..0fd72475 100644
--- a/opentrack/CMakeLists.txt
+++ b/opentrack/CMakeLists.txt
@@ -1,3 +1,8 @@
+if(MSVC)
+ add_compile_options(-EHsc)
+ add_definitions(-D_HAS_EXCEPTIONS=1)
+endif()
+
otr_module(executable EXECUTABLE BIN)
set_target_properties(opentrack-executable PROPERTIES
@@ -6,4 +11,15 @@ set_target_properties(opentrack-executable PROPERTIES
PREFIX ""
)
+set_source_files_properties(resources.rc OBJECT_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/opentrack.ico")
+
+if(APPLE)
+ set_source_files_properties(appnap_mac.mm PROPERTIES COMPILE_FLAGS "-fno-objc-arc")
+ target_sources(${self} PRIVATE appnap_mac.mm)
+endif()
+
target_link_libraries(${self} opentrack-user-interface opentrack-version)
+
+if(APPLE)
+ target_link_libraries(${self} "-framework Foundation")
+endif()
diff --git a/opentrack/appnap_mac.mm b/opentrack/appnap_mac.mm
new file mode 100644
index 00000000..3e0bea73
--- /dev/null
+++ b/opentrack/appnap_mac.mm
@@ -0,0 +1,47 @@
+#ifdef __APPLE__
+
+#import <Foundation/Foundation.h>
+
+/**
+ * Used to prevent macOS from throttling the opentrack process.
+ */
+
+id token = nil;
+
+void disable_appnap_start();
+void disable_appnap_stop();
+
+void disable_appnap_start() {
+
+ if(token){
+ NSLog(@"disable_appnap_start: already started");
+ return;
+ }
+
+
+ NSLog(@"disable_appnap_start");
+ token = [[NSProcessInfo processInfo]
+ beginActivityWithOptions: NSActivityUserInitiatedAllowingIdleSystemSleep
+ reason: @"Disable AppNap"];
+ [token retain];
+}
+
+void disable_appnap_stop() {
+ if(!token){
+ NSLog(@"disable_appnap_start: not started");
+ return;
+ }
+
+ NSLog(@"disable_appnap_stop");
+ [[NSProcessInfo processInfo] endActivity:token];
+ [token release];
+ token = nil;
+}
+
+
+
+#endif
+
+
+
+
diff --git a/opentrack/defs.hpp b/opentrack/defs.hpp
new file mode 100644
index 00000000..b2f0c467
--- /dev/null
+++ b/opentrack/defs.hpp
@@ -0,0 +1,16 @@
+#pragma once
+
+//#define UI_FORCED_TRACKER "pt"
+//#define UI_FORCED_FILTER "accela"
+
+//#define UI_NO_TRACKER_COMBOBOX
+//#define UI_NO_FILTER_COMBOBOX
+
+//#define UI_NO_TRACKER_SETTINGS_BUTTON
+//#define UI_NO_FILTER_SETTINGS_BUTTON
+
+//#define UI_NO_RAW_DATA
+//#define UI_NO_GAME_DATA
+//#define UI_NO_VIDEO_FEED
+
+//define UI_ACCELA_OLD_STAIRCASE
diff --git a/opentrack/lang/de_DE.ts b/opentrack/lang/de_DE.ts
new file mode 100644
index 00000000..6eaf28d4
--- /dev/null
+++ b/opentrack/lang/de_DE.ts
@@ -0,0 +1,193 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>UI_new_config</name>
+ <message>
+ <source>Config filename</source>
+ <translation>Konfigurationsdatei</translation>
+ </message>
+ <message>
+ <source>New file name:</source>
+ <translation>Neuer Dateiname:</translation>
+ </message>
+</context>
+<context>
+ <name>main_window</name>
+ <message>
+ <source>Raw tracker data</source>
+ <translation>Rohe Tracker-Daten</translation>
+ </message>
+ <message>
+ <source>Z</source>
+ <translation>Z</translation>
+ </message>
+ <message>
+ <source>Pitch</source>
+ <translation>Nicken</translation>
+ </message>
+ <message>
+ <source>Y</source>
+ <translation>Y</translation>
+ </message>
+ <message>
+ <source>X</source>
+ <translation>X</translation>
+ </message>
+ <message>
+ <source>Roll</source>
+ <translation>Rollen</translation>
+ </message>
+ <message>
+ <source>Yaw</source>
+ <translation>Gieren</translation>
+ </message>
+ <message>
+ <source>Game data</source>
+ <translation>Spieldaten</translation>
+ </message>
+ <message>
+ <source>Profile</source>
+ <translation>Profil</translation>
+ </message>
+ <message>
+ <source>Options</source>
+ <translation>Optionen</translation>
+ </message>
+ <message>
+ <source>Mapping</source>
+ <translation>Abbildung</translation>
+ </message>
+ <message>
+ <source>Tracking</source>
+ <translation>Tracking</translation>
+ </message>
+ <message>
+ <source>Start</source>
+ <translation>Starten</translation>
+ </message>
+ <message>
+ <source>Stop</source>
+ <translation>Stoppen</translation>
+ </message>
+ <message>
+ <source>Input</source>
+ <translation>Eingabe</translation>
+ </message>
+ <message>
+ <source>🔨</source>
+ <translation>🔨</translation>
+ </message>
+ <message>
+ <source>Output</source>
+ <translation>Ausgabe</translation>
+ </message>
+ <message>
+ <source>Filter</source>
+ <translation>Filter</translation>
+ </message>
+ <message>
+ <source>Create new empty config</source>
+ <translation>Neue leere Konfiguration erstellen</translation>
+ </message>
+ <message>
+ <source>Create new copied config</source>
+ <translation>Neue kopierte Konfiguration erstellen</translation>
+ </message>
+ <message>
+ <source>Open configuration directory</source>
+ <translation>Konfigurationsordner öffnen</translation>
+ </message>
+ <message>
+ <source>opentrack</source>
+ <translation>opentrack</translation>
+ </message>
+ <message>
+ <source> (debug)</source>
+ <translation> (Fehlersuche)</translation>
+ </message>
+ <message>
+ <source>Show the Octopus</source>
+ <translation>Den Oktopus anzeigen</translation>
+ </message>
+ <message>
+ <source>Hide the Octopus</source>
+ <translation>Den Oktopus verstecken</translation>
+ </message>
+ <message>
+ <source>Tracker settings</source>
+ <translation>Tracker-Einstellungen</translation>
+ </message>
+ <message>
+ <source>Filter settings</source>
+ <translation>Filter-Einstellungen</translation>
+ </message>
+ <message>
+ <source>Protocol settings</source>
+ <translation>Protokoll-Einstellungen</translation>
+ </message>
+ <message>
+ <source>Mappings</source>
+ <translation>Abbildungen</translation>
+ </message>
+ <message>
+ <source>Exit</source>
+ <translation>Beenden</translation>
+ </message>
+ <message>
+ <source>The Octopus is sad</source>
+ <translation>Der Oktopus ist traurig</translation>
+ </message>
+ <message>
+ <source>Check permissions for your .ini directory:
+
+&quot;%1&quot;%2
+
+Exiting now.</source>
+ <translation>Überprüfe die Berechtigungen des .ini-Ordners:
+
+&quot;%1&quot;%2
+
+Das Programm beendet sich nun.</translation>
+ </message>
+ <message>
+ <source> :: </source>
+ <translation> :: </translation>
+ </message>
+ <message>
+ <source>Running as root is bad</source>
+ <translation>Das Starten als root ist schlecht</translation>
+ </message>
+ <message>
+ <source>Do not run as root. Set correct device node permissions.</source>
+ <translation>Führe dieses Programm nicht als root aus. Setze die korrekten Gerätedatei-Berechtigungen.</translation>
+ </message>
+ <message>
+ <source>Running as root is bad, seriously</source>
+ <translation>Das Starten als root ist schlecht, ernsthaft</translation>
+ </message>
+ <message>
+ <source>Do not run as root. I&apos;ll keep whining at every startup.</source>
+ <translation>Führe dieses Programm nicht als root aus. Ich werde bei jedem Start rumheulen.</translation>
+ </message>
+ <message>
+ <source>Be annoyed, comprehensively.</source>
+ <translation>Sei verärgert, vollumfänglich.</translation>
+ </message>
+ <message>
+ <source>Don&apos;t run as root to remove these annoying messages.</source>
+ <translation>Führe das Programm nicht mehr als root aus, um diese ärgerliche Nachricht loszuwerden.</translation>
+ </message>
+</context>
+<context>
+ <name>new_file_dialog</name>
+ <message>
+ <source>File exists</source>
+ <translation>Die Datei existiert</translation>
+ </message>
+ <message>
+ <source>This file already exists. Pick another name.</source>
+ <translation>Diese Datei existiert bereits. Wähle einen anderen Namen.</translation>
+ </message>
+</context>
+</TS>
diff --git a/opentrack/main-window.cpp b/opentrack/main-window.cpp
index 4f6972f4..67ffe1e1 100644
--- a/opentrack/main-window.cpp
+++ b/opentrack/main-window.cpp
@@ -13,17 +13,29 @@
#include "migration/migration.hpp"
#include "compat/check-visible.hpp"
#include "compat/sleep.hpp"
-#include "compat/macros.hpp"
+#include "compat/macros.h"
#include "compat/library-path.hpp"
#include "compat/math.hpp"
#include "compat/sysexits.hpp"
+#include "opentrack/defs.hpp"
-#include <algorithm>
+#include <cstring>
#include <utility>
#include <QMessageBox>
#include <QDesktopServices>
+#include <QApplication>
+
+#include <QFile>
+#include <QFileInfo>
#include <QDir>
+#include <QDateTime>
+
+
+#ifdef __APPLE__
+void disable_appnap_start();
+void disable_appnap_stop();
+#endif
extern "C" const char* const opentrack_version;
@@ -38,7 +50,8 @@ main_window::main_window() : State(OPENTRACK_BASE_PATH + OPENTRACK_LIBRARY_PATH)
annoy_if_root();
#endif
- setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | windowFlags());
+ adjustSize();
+ setWindowFlag(Qt::MSWindowsFixedSizeDialogHint);
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
init_profiles();
@@ -55,6 +68,14 @@ main_window::main_window() : State(OPENTRACK_BASE_PATH + OPENTRACK_LIBRARY_PATH)
connect(&det_timer, &QTimer::timeout,
this, &main_window::maybe_start_profile_from_executable);
det_timer.start(1000);
+ connect(&*s.b, &options::bundle_::reloading, this, &main_window::register_shortcuts);
+ connect(&*s.b, &options::bundle_::saving, this, &main_window::register_shortcuts);
+
+ ui.btnStartTracker->setFocus();
+#ifdef UI_NO_VIDEO_FEED
+ fake_video_frame.resize(640, 480);
+ fake_video_frame_parent.setVisible(false);
+#endif
}
void main_window::init_shortcuts()
@@ -74,23 +95,31 @@ void main_window::init_dylibs()
modules.filters().insert(modules.filters().begin(),
std::make_shared<dylib>("", dylib_type::Filter));
+#ifndef UI_NO_TRACKER_COMBOBOX
for (dylib_ptr& x : modules.trackers())
ui.iconcomboTrackerSource->addItem(x->icon, x->name, x->module_name);
+#endif
for (dylib_ptr& x : modules.protocols())
ui.iconcomboProtocol->addItem(x->icon, x->name, x->module_name);
+#ifndef UI_NO_FILTER_COMBOBOX
for (dylib_ptr& x : modules.filters())
ui.iconcomboFilter->addItem(x->icon, x->name, x->module_name);
+#endif
+#ifndef UI_NO_TRACKER_COMBOBOX
connect(ui.iconcomboTrackerSource, &QComboBox::currentTextChanged,
- this, [&](const QString&) { pTrackerDialog = nullptr; });
+ this, [this](const QString&) { pTrackerDialog = nullptr; if (options_widget) options_widget->tracker_module_changed(); });
+#endif
connect(ui.iconcomboProtocol, &QComboBox::currentTextChanged,
- this, [&](const QString&) { pProtocolDialog = nullptr; });
+ this, [this](const QString&) { pProtocolDialog = nullptr; if (options_widget) options_widget->proto_module_changed(); });
+#ifndef UI_NO_FILTER_COMBOBOX
connect(ui.iconcomboFilter, &QComboBox::currentTextChanged,
- this, [&](const QString&) { pFilterDialog = nullptr; });
+ this, [this](const QString&) { pFilterDialog = nullptr; if (options_widget) options_widget->filter_module_changed(); });
+#endif
connect(&m.tracker_dll, value_::value_changed<QString>(),
this, &main_window::save_modules,
@@ -112,9 +141,13 @@ void main_window::init_dylibs()
};
list types[] {
+#ifndef UI_NO_TRACKER_COMBOBOX
{ modules.trackers(), ui.iconcomboTrackerSource, m.tracker_dll },
+#endif
{ modules.protocols(), ui.iconcomboProtocol, m.protocol_dll },
+#ifndef UI_NO_FILTER_COMBOBOX
{ modules.filters(), ui.iconcomboFilter, m.filter_dll },
+#endif
};
for (list& type : types)
@@ -138,6 +171,7 @@ void main_window::init_dylibs()
void main_window::init_profiles()
{
+ copy_presets();
refresh_profile_list();
// implicitly created by `ini_directory()'
if (ini_directory().isEmpty() || !QDir(ini_directory()).isReadable())
@@ -176,7 +210,7 @@ void main_window::init_tray_menu()
menu_action_show.setIconVisibleInMenu(true);
menu_action_show.setText(isHidden() ? tr("Show the Octopus") : tr("Hide the Octopus"));
menu_action_show.setIcon(QIcon(":/images/opentrack.png"));
- QObject::connect(&menu_action_show, &QAction::triggered, this, [&] { toggle_restore_from_tray(QSystemTrayIcon::Trigger); });
+ QObject::connect(&menu_action_show, &QAction::triggered, this, [this] { toggle_restore_from_tray(QSystemTrayIcon::Trigger); });
tray_menu.addAction(&menu_action_show);
tray_menu.addSeparator();
@@ -205,7 +239,7 @@ void main_window::init_tray_menu()
menu_action_options.setIcon(QIcon(":/images/tools.png"));
menu_action_options.setText(tr("Options"));
- QObject::connect(&menu_action_options, &QAction::triggered, this, &main_window::show_options_dialog);
+ QObject::connect(&menu_action_options, &QAction::triggered, this, [this] { show_options_dialog(true); });
tray_menu.addAction(&menu_action_options);
tray_menu.addSeparator();
@@ -222,10 +256,14 @@ void main_window::init_buttons()
{
update_button_state(false, false);
connect(ui.btnEditCurves, &QPushButton::clicked, this, &main_window::show_mapping_window);
- connect(ui.btnShortcuts, &QPushButton::clicked, this, &main_window::show_options_dialog);
+ connect(ui.btnShortcuts, &QPushButton::clicked, this, [this] { show_options_dialog(true); });
+#ifndef UI_NO_TRACKER_SETTINGS_BUTTON
connect(ui.btnShowEngineControls, &QPushButton::clicked, this, &main_window::show_tracker_settings);
+#endif
connect(ui.btnShowServerControls, &QPushButton::clicked, this, &main_window::show_proto_settings);
+#ifndef UI_NO_FILTER_SETTINGS_BUTTON
connect(ui.btnShowFilterControls, &QPushButton::clicked, this, &main_window::show_filter_settings);
+#endif
connect(ui.btnStartTracker, &QPushButton::clicked, this, &main_window::start_tracker_);
connect(ui.btnStopTracker, &QPushButton::clicked, this, &main_window::stop_tracker_);
}
@@ -274,7 +312,11 @@ bool main_window::profile_name_from_dialog(QString& ret)
main_window::~main_window()
{
// stupid ps3 eye has LED issues
+#ifndef UI_NO_VIDEO_FEED
if (work && ui.video_frame->layout())
+#else
+ if (work)
+#endif
{
hide();
stop_tracker_();
@@ -296,7 +338,7 @@ void main_window::create_empty_profile()
QString name;
if (profile_name_from_dialog(name))
{
- QFile(ini_combine(name)).open(QFile::ReadWrite);
+ (void)create_profile_from_preset(name);
refresh_profile_list();
if (profile_list.contains(name))
@@ -375,9 +417,14 @@ void main_window::update_button_state(bool running, bool inertialp)
ui.btnStartTracker->setEnabled(not_running);
ui.btnStopTracker->setEnabled(running);
ui.iconcomboProtocol->setEnabled(not_running);
+#ifndef UI_NO_FILTER_COMBOBOX
ui.iconcomboFilter->setEnabled(not_running);
+#endif
+#ifndef UI_NO_TRACKER_COMBOBOX
ui.iconcomboTrackerSource->setEnabled(not_running);
+#endif
ui.profile_button->setEnabled(not_running);
+#ifndef UI_NO_VIDEO_FEED
ui.video_frame_label->setVisible(not_running || inertialp);
if(not_running)
{
@@ -386,6 +433,7 @@ void main_window::update_button_state(bool running, bool inertialp)
else {
ui.video_frame_label->setPixmap(QPixmap(":/images/no-feed.png"));
}
+#endif
}
void main_window::start_tracker_()
@@ -393,7 +441,17 @@ void main_window::start_tracker_()
if (work)
return;
- work = std::make_shared<Work>(pose, ev, ui.video_frame, current_tracker(), current_protocol(), current_filter());
+#ifdef __APPLE__
+ disable_appnap_start();
+#endif
+
+
+#ifndef UI_NO_VIDEO_FEED
+ auto* frame = ui.video_frame;
+#else
+ auto* frame = &fake_video_frame;
+#endif
+ work = std::make_shared<Work>(pose, frame, current_tracker(), current_protocol(), current_filter());
if (!work->is_ok())
{
@@ -407,19 +465,27 @@ void main_window::start_tracker_()
}
if (pTrackerDialog)
- pTrackerDialog->register_tracker(work->libs.pTracker.get());
+ pTrackerDialog->register_tracker(&*work->libs.pTracker);
- if (pFilterDialog)
- pFilterDialog->register_filter(work->libs.pFilter.get());
+ if (pFilterDialog && work->libs.pFilter)
+ pFilterDialog->register_filter(&*work->libs.pFilter);
if (pProtocolDialog)
- pProtocolDialog->register_protocol(work->libs.pProtocol.get());
+ pProtocolDialog->register_protocol(&*work->libs.pProtocol);
+
+ if (options_widget)
+ {
+ options_widget->register_tracker(&*work->libs.pTracker);
+ options_widget->register_protocol(&*work->libs.pProtocol);
+ if (work->libs.pFilter)
+ options_widget->register_filter(&*work->libs.pFilter);
+ }
- pose_update_timer.start(50);
+ pose_update_timer.start(1000/30);
// NB check valid since SelectedLibraries ctor called
// trackers take care of layout state updates
- const bool is_inertial = ui.video_frame->layout() == nullptr;
+ const bool is_inertial = frame->layout() == nullptr;
update_button_state(true, is_inertial);
ui.btnStopTracker->setFocus();
@@ -430,12 +496,22 @@ void main_window::stop_tracker_()
if (!work)
return;
+#ifdef __APPLE__
+ disable_appnap_stop();
+#endif
+
force_is_visible(true);
with_tracker_teardown sentinel;
pose_update_timer.stop();
ui.pose_display->present(0,0,0, 0,0,0);
+ if (options_widget)
+ {
+ // XXX TODO other module types
+ options_widget->unregister_tracker();
+ }
+
if (pTrackerDialog)
pTrackerDialog->unregister_tracker();
@@ -462,20 +538,29 @@ void main_window::show_pose_(const double* mapped, const double* raw)
ui.pose_display->present(mapped[Yaw], mapped[Pitch], -mapped[Roll],
mapped[TX], mapped[TY], mapped[TZ]);
+#ifndef UI_NO_RAW_DATA
QLCDNumber* raw_[] = {
ui.raw_x, ui.raw_y, ui.raw_z,
ui.raw_yaw, ui.raw_pitch, ui.raw_roll,
};
-
+#endif
+#ifndef UI_NO_GAME_DATA
QLCDNumber* mapped_[] = {
ui.pose_x, ui.pose_y, ui.pose_z,
ui.pose_yaw, ui.pose_pitch, ui.pose_roll,
};
+#endif
+#if !defined UI_NO_RAW_DATA || !defined UI_NO_GAME_DATA
for (int k = 0; k < 6; k++)
+#endif
{
+#ifndef UI_NO_RAW_DATA
raw_[k]->display(iround(raw[k]));
+#endif
+#ifndef UI_NO_GAME_DATA
mapped_[k]->display(iround(mapped[k]));
+#endif
}
QString game_title;
@@ -516,15 +601,57 @@ void main_window::show_pose()
show_pose_(mapped, raw);
}
+bool main_window::module_tabs_enabled() const
+{
+ return true;
+#if 0
+ enum module_tab_state { tabs_maybe = -1, tabs_disable, tabs_enable };
+
+ static const auto force = progn(
+ auto str = getenv("OPENTRACK_MODULE_TABS");
+ if (!str || !*str)
+ return tabs_maybe;
+ constexpr const char* strings_for_false[] = {
+ "0", "n", "f", "disable",
+ };
+ constexpr const char* strings_for_true[] = {
+ "1", "y", "t", "enable",
+ };
+ for (const auto* x : strings_for_false)
+ if (!strncasecmp(str, x, strlen(x)))
+ return tabs_disable;
+ for (const auto* x : strings_for_true)
+ if (!strncasecmp(str, x, strlen(x)))
+ return tabs_enable;
+ qDebug() << "main-window: invalid boolean for OPENTRACK_MODULE_TABS:"
+ << QLatin1String{str};
+ return tabs_maybe;
+ );
+ switch (force)
+ {
+ case tabs_disable: return false;
+ case tabs_enable: return true;
+ case tabs_maybe: (void)0;
+ }
+ auto* d = QApplication::desktop();
+ if (!d)
+ return false;
+ // Windows 10: 40px, Windows 11: 48px, KDE: 51px
+ constexpr int taskbar_size = 51;
+ constexpr int min_avail_height = 768 - taskbar_size;
+ QRect rect = d->availableGeometry(this);
+ return rect.height() >= min_avail_height;
+#endif
+}
+
static void show_window(QWidget& d, bool fresh)
{
if (fresh)
{
- d.setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | d.windowFlags());
- d.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
-
- d.show();
+ d.setWindowFlag(Qt::MSWindowsFixedSizeDialogHint);
d.adjustSize();
+ d.setFixedSize(d.size());
+ d.show();
#ifdef __APPLE__
d.raise();
#endif
@@ -540,83 +667,130 @@ static void show_window(QWidget& d, bool fresh)
}
}
-template<typename t, typename F>
-static bool mk_window_common(std::unique_ptr<t>& d, F&& fun)
+template<typename t, typename... Args>
+static bool mk_window(std::unique_ptr<t>& d, bool show, Args&&... params)
{
bool fresh = false;
- if (!d)
- d = fun(), fresh = !!d;
+ if (!(d && d->isVisible()))
+ {
+ d = std::make_unique<t>(std::forward<Args>(params)...);
+ fresh = !!d;
+ }
if (d)
- show_window(*d, fresh);
+ {
+ if (show && !d->embeddable())
+ show_window(*d, fresh);
+ }
return fresh;
}
-template<typename t, typename... Args>
-static bool mk_window(std::unique_ptr<t>& place, Args&&... params)
+template<typename Instance, typename Dialog>
+static void show_module_settings(std::shared_ptr<Instance> instance,
+ std::unique_ptr<Dialog>& dialog,
+ const Modules::dylib_ptr& lib,
+ std::unique_ptr<options_dialog>& options_widget,
+ main_window* win,
+ bool show,
+ void(Dialog::*register_fun)(Instance*),
+ void(options_dialog::*switch_tab_fun)())
{
- return mk_window_common(place, [&] {
- return std::make_unique<t>(params...);
- });
-}
+ if (!lib || !lib->Dialog)
+ return;
-template<typename t>
-static bool mk_dialog(std::unique_ptr<t>& place, const std::shared_ptr<dylib>& lib)
-{
- using u = std::unique_ptr<t>;
+ bool fresh = !(dialog && dialog->isVisible());
+ if (fresh)
+ dialog = std::unique_ptr<Dialog>{(Dialog*)lib->Dialog()};
+ bool embed = dialog->embeddable() && win->module_tabs_enabled();
- return mk_window_common(place, [&] {
- if (lib && lib->Dialog)
- return u{ (t*)lib->Dialog() };
- else
- return u{};
- });
+ if (!embed)
+ {
+ if (fresh)
+ {
+ if (instance)
+ ((*dialog).*register_fun)(&*instance);
+ }
+ if (show)
+ show_window(*dialog, fresh);
+ }
+ else if (show)
+ {
+ bool fresh = !options_widget;
+ win->show_options_dialog(false);
+ ((*options_widget).*switch_tab_fun)();
+ show_window(*options_widget, fresh);
+ }
}
-void main_window::show_tracker_settings()
+void main_window::show_tracker_settings_(bool show)
{
- if (mk_dialog(pTrackerDialog, current_tracker()) && work && work->libs.pTracker)
- pTrackerDialog->register_tracker(work->libs.pTracker.get());
- if (pTrackerDialog)
- QObject::connect(pTrackerDialog.get(), &ITrackerDialog::closing,
- this, [this] { pTrackerDialog = nullptr; });
+ show_module_settings(work ? work->libs.pTracker : nullptr, pTrackerDialog, current_tracker(),
+ options_widget, this, show,
+ &ITrackerDialog::register_tracker, &options_dialog::switch_to_tracker_tab);
}
-void main_window::show_proto_settings()
+void main_window::show_proto_settings_(bool show)
{
- if (mk_dialog(pProtocolDialog, current_protocol()) && work && work->libs.pProtocol)
- pProtocolDialog->register_protocol(work->libs.pProtocol.get());
- if (pProtocolDialog)
- QObject::connect(pProtocolDialog.get(), &IProtocolDialog::closing,
- this, [this] { pProtocolDialog = nullptr; });
+ show_module_settings(work ? work->libs.pProtocol : nullptr, pProtocolDialog, current_protocol(),
+ options_widget, this, show,
+ &IProtocolDialog::register_protocol, &options_dialog::switch_to_proto_tab);
}
-void main_window::show_filter_settings()
+void main_window::show_filter_settings_(bool show)
{
- if (mk_dialog(pFilterDialog, current_filter()) && work && work->libs.pFilter)
- pFilterDialog->register_filter(work->libs.pFilter.get());
- if (pFilterDialog)
- QObject::connect(pFilterDialog.get(), &IFilterDialog::closing,
- this, [this] { pFilterDialog = nullptr; });
+ show_module_settings(work ? work->libs.pFilter : nullptr, pFilterDialog, current_filter(),
+ options_widget, this, show,
+ &IFilterDialog::register_filter, &options_dialog::switch_to_filter_tab);
}
-void main_window::show_options_dialog()
+void main_window::show_options_dialog(bool show)
{
- if (mk_window(options_widget, [&](bool flag) { set_keys_enabled(!flag); }))
+ if (options_widget && options_widget->isVisible())
+ {
+ if (show)
+ show_window(*options_widget, false);
+ return;
+ }
+
+ bool embed = module_tabs_enabled();
+
+ if (embed)
{
- // XXX this should logically connect to a bundle
- // also doesn't work when switching profiles with options dialog open
- // move shortcuts to a separate bundle and add a migration -sh 20180218
- connect(options_widget.get(), &options_dialog::closing,
- this, &main_window::register_shortcuts);
+ show_tracker_settings_(false);
+ show_proto_settings_(false);
+ show_filter_settings_(false);
}
+
+ // make them into unique_ptr<BaseDialog>
+ std::unique_ptr<ITrackerDialog> empty_ITD;
+ std::unique_ptr<IProtocolDialog> empty_IPD;
+ std::unique_ptr<IFilterDialog> empty_IFD;
+
+ mk_window(options_widget, false,
+ embed ? pTrackerDialog : empty_ITD,
+ embed ? pProtocolDialog : empty_IPD,
+ embed ? pFilterDialog : empty_IFD,
+ [this](bool flag) { set_keys_enabled(!flag); });
+
+ if (work)
+ {
+ if (work->libs.pTracker)
+ options_widget->register_tracker(&*work->libs.pTracker);
+ if (work->libs.pProtocol)
+ options_widget->register_protocol(&*work->libs.pProtocol);
+ if (work->libs.pFilter)
+ options_widget->register_filter(&*work->libs.pFilter);
+ }
+
+ if (show)
+ show_window(*options_widget, true);
}
void main_window::show_mapping_window()
{
- mk_window(mapping_widget, pose);
+ mk_window(mapping_widget, true, pose);
}
void main_window::exit(int status)
@@ -645,14 +819,25 @@ void main_window::set_profile(const QString& new_name_, bool migrate)
QSignalBlocker b(ui.iconcomboProfile);
QString new_name = new_name_;
+ bool do_refresh = false;
- if (!profile_list.contains(new_name))
+ if (!profile_list.contains(new_name_))
{
- new_name = OPENTRACK_DEFAULT_PROFILE;
+ new_name = QStringLiteral(OPENTRACK_DEFAULT_PROFILE);
if (!profile_list.contains(new_name))
+ {
migrate = false;
+ do_refresh = true;
+ }
}
+ if (create_profile_from_preset(new_name))
+ migrate = true;
+
+ if (do_refresh)
+ refresh_profile_list();
+ assert(profile_list.contains(new_name));
+
const bool status = new_name != ini_filename();
if (status)
@@ -706,10 +891,8 @@ void main_window::ensure_tray()
tray->setContextMenu(&tray_menu);
tray->show();
- connect(tray.get(),
- &QSystemTrayIcon::activated,
- this,
- &main_window::toggle_restore_from_tray);
+ connect(&*tray, &QSystemTrayIcon::activated,
+ this, &main_window::toggle_restore_from_tray);
}
QApplication::setQuitOnLastWindowClosed(false);
@@ -866,6 +1049,56 @@ void main_window::toggle_tracker_()
start_tracker_();
}
+void main_window::copy_presets()
+{
+ const QString preset_dir = library_path + "/presets/";
+ const QDir dir{preset_dir};
+ if (!dir.exists())
+ {
+ qDebug() << "no preset dir";
+ return;
+ }
+ with_global_settings_object([&](QSettings& s) {
+ const QString& key = QStringLiteral("last-preset-copy-time");
+ const auto last_time = s.value(key, -1LL).toLongLong();
+ for (const auto& file : dir.entryInfoList({ "*.ini" }, QDir::Files, QDir::Name))
+ {
+ if (file.fileName() == QStringLiteral("default.ini"))
+ continue;
+ if (last_time < file.lastModified().toSecsSinceEpoch())
+ {
+ qDebug() << "copy preset" << file.fileName();
+ (void)QFile::copy(file.filePath(), ini_combine(file.fileName()));
+ }
+ }
+ s.setValue(key, QDateTime::currentSecsSinceEpoch());
+ });
+}
+
+bool main_window::create_profile_from_preset(const QString& name)
+{
+ const QString dest = ini_combine(name);
+
+ if (QFile::exists(dest))
+ return false;
+
+ const QString& default_name = QStringLiteral(OPENTRACK_DEFAULT_PROFILE);
+ const QString default_preset = (library_path + "/presets/%1").arg(default_name);
+
+ if (QFile::exists(default_preset))
+ {
+ bool ret = QFile::copy(default_preset, dest);
+ if (ret)
+ qDebug() << "create profile" << name << "from default preset" << (ret ? "" : "FAILED!");
+ return ret;
+ }
+ else
+ {
+ (void)QFile(ini_combine(name)).open(QFile::ReadWrite);
+ return false;
+ }
+}
+
#if !defined _WIN32
# include <unistd.h>
void main_window::annoy_if_root()
diff --git a/opentrack/main-window.hpp b/opentrack/main-window.hpp
index 9ffb7019..1dcbd0eb 100644
--- a/opentrack/main-window.hpp
+++ b/opentrack/main-window.hpp
@@ -8,9 +8,10 @@
#pragma once
+#include "opentrack/defs.hpp"
#include "api/plugin-support.hpp"
#include "gui/mapping-dialog.hpp"
-#include "gui/settings.hpp"
+#include "gui/options-dialog.hpp"
#include "gui/process_detector.h"
#include "logic/main-settings.hpp"
#include "logic/pipeline.hpp"
@@ -20,21 +21,16 @@
#include "options/options.hpp"
#include "compat/qt-signal.hpp"
-#include <QApplication>
#include <QMainWindow>
#include <QKeySequence>
#include <QShortcut>
-#include <QPixmap>
#include <QTimer>
#include <QSystemTrayIcon>
#include <QString>
#include <QMenu>
#include <QAction>
-#include <QEvent>
-#include <QCloseEvent>
#include <QList>
-#include <tuple>
#include <memory>
#include "ui_main-window.h"
@@ -55,6 +51,11 @@ class main_window final : public QMainWindow, private State
Shortcuts global_shortcuts;
QShortcut kbd_quit { QKeySequence("Ctrl+Q"), this };
+#ifdef UI_NO_VIDEO_FEED
+ QWidget fake_video_frame_parent;
+ QFrame fake_video_frame{&fake_video_frame_parent};
+#endif
+
std::unique_ptr<options_dialog> options_widget;
std::unique_ptr<mapping_dialog> mapping_widget;
@@ -78,11 +79,12 @@ class main_window final : public QMainWindow, private State
bool exiting_already { false };
- qt_sig::nullary start_tracker { this, &main_window::start_tracker_, Qt::QueuedConnection };
- qt_sig::nullary stop_tracker { this, &main_window::stop_tracker_, Qt::QueuedConnection };
- qt_sig::nullary toggle_tracker { this, &main_window::toggle_tracker_, Qt::QueuedConnection };
- qt_sig::nullary restart_tracker { this, &main_window::restart_tracker_, Qt::QueuedConnection };
+ qt_signal<void> start_tracker { this, &main_window::start_tracker_, Qt::QueuedConnection };
+ qt_signal<void> stop_tracker { this, &main_window::stop_tracker_, Qt::QueuedConnection };
+ qt_signal<void> toggle_tracker { this, &main_window::toggle_tracker_, Qt::QueuedConnection };
+ qt_signal<void> restart_tracker { this, &main_window::restart_tracker_, Qt::QueuedConnection };
+public:
void init_dylibs();
void init_tray_menu();
void init_profiles();
@@ -103,11 +105,14 @@ class main_window final : public QMainWindow, private State
void closeEvent(QCloseEvent *event) override;
bool event(QEvent *event) override;
- void show_tracker_settings();
- void show_proto_settings();
- void show_filter_settings();
+ void show_tracker_settings_(bool show);
+ void show_proto_settings_(bool show);
+ void show_filter_settings_(bool show);
+ void show_tracker_settings() { show_tracker_settings_(true); }
+ void show_proto_settings() { show_proto_settings_(true); }
+ void show_filter_settings() { show_filter_settings_(true); }
- void show_options_dialog();
+ void show_options_dialog(bool show);
void show_mapping_window();
void show_pose();
@@ -119,12 +124,14 @@ class main_window final : public QMainWindow, private State
void restart_tracker_();
void toggle_tracker_();
+ [[nodiscard]] bool create_profile_from_preset(const QString& name);
void set_profile(const QString& new_name, bool migrate = true);
void set_profile_in_registry(const QString& profile);
void refresh_profile_list();
void die_on_profile_not_writable();
void maybe_start_profile_from_executable();
[[nodiscard]] static bool profile_name_from_dialog(QString& ret);
+ void copy_presets();
void create_empty_profile();
void create_copied_profile();
@@ -136,6 +143,7 @@ class main_window final : public QMainWindow, private State
bool start_in_tray();
void save_modules();
+ bool module_tabs_enabled() const;
void exit(int status = EXIT_SUCCESS);
diff --git a/opentrack/main-window.ui b/opentrack/main-window.ui
index af3234c5..32c9f57a 100644
--- a/opentrack/main-window.ui
+++ b/opentrack/main-window.ui
@@ -7,12 +7,12 @@
<rect>
<x>0</x>
<y>0</y>
- <width>649</width>
- <height>511</height>
+ <width>655</width>
+ <height>502</height>
</rect>
</property>
<property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
@@ -33,15 +33,12 @@
</property>
<widget class="QWidget" name="content">
<property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <layout class="QHBoxLayout" name="horizontalLayout_5">
- <property name="spacing">
- <number>0</number>
- </property>
+ <layout class="QGridLayout" name="gridLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
@@ -49,32 +46,468 @@
<number>0</number>
</property>
<property name="rightMargin">
- <number>6</number>
+ <number>9</number>
</property>
<property name="bottomMargin">
- <number>6</number>
+ <number>9</number>
</property>
- <item>
- <widget class="QFrame" name="frame">
+ <item row="0" column="0">
+ <widget class="QFrame" name="video_feed">
<property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="frameShape">
- <enum>QFrame::NoFrame</enum>
+ <property name="minimumSize">
+ <size>
+ <width>320</width>
+ <height>240</height>
+ </size>
</property>
- <property name="frameShadow">
- <enum>QFrame::Raised</enum>
+ <property name="maximumSize">
+ <size>
+ <width>320</width>
+ <height>240</height>
+ </size>
</property>
<property name="lineWidth">
<number>0</number>
</property>
- <layout class="QVBoxLayout" name="verticalLayout_5">
+ <widget class="QFrame" name="video_frame">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>320</width>
+ <height>240</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>320</width>
+ <height>240</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>320</width>
+ <height>240</height>
+ </size>
+ </property>
+ <widget class="QLabel" name="video_frame_label">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>320</width>
+ <height>240</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>320</width>
+ <height>240</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>320</width>
+ <height>240</height>
+ </size>
+ </property>
+ <property name="font">
+ <font>
+ <family>Candara</family>
+ <pointsize>37</pointsize>
+ <weight>50</weight>
+ <bold>false</bold>
+ <kerning>true</kerning>
+ </font>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="pixmap">
+ <pixmap resource="../gui/opentrack-res.qrc">:/images/tracking-not-started.png</pixmap>
+ </property>
+ <property name="scaledContents">
+ <bool>false</bool>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </widget>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="pose_widget" name="pose_display" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>320</width>
+ <height>240</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QGroupBox" name="box_raw_headpose">
+ <property name="minimumSize">
+ <size>
+ <width>316</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="title">
+ <string>Raw tracker data</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_12">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
<property name="spacing">
<number>0</number>
</property>
+ <item row="0" column="3">
+ <widget class="QLCDNumber" name="raw_yaw">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <stylestrategy>NoAntialias</stylestrategy>
+ <kerning>false</kerning>
+ </font>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="smallDecimalPoint">
+ <bool>true</bool>
+ </property>
+ <property name="digitCount">
+ <number>4</number>
+ </property>
+ <property name="segmentStyle">
+ <enum>QLCDNumber::Flat</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="lblZ_4">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <property name="text">
+ <string>Z</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QLabel" name="lblRotY_4">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <property name="text">
+ <string>Pitch</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <widget class="QLCDNumber" name="raw_pitch">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <stylestrategy>NoAntialias</stylestrategy>
+ <kerning>false</kerning>
+ </font>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="smallDecimalPoint">
+ <bool>true</bool>
+ </property>
+ <property name="digitCount">
+ <number>4</number>
+ </property>
+ <property name="segmentStyle">
+ <enum>QLCDNumber::Flat</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="lblY_4">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <property name="text">
+ <string>Y</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="lblX_4">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <property name="text">
+ <string>X</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLCDNumber" name="raw_x">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <stylestrategy>NoAntialias</stylestrategy>
+ <kerning>false</kerning>
+ </font>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="smallDecimalPoint">
+ <bool>true</bool>
+ </property>
+ <property name="digitCount">
+ <number>4</number>
+ </property>
+ <property name="segmentStyle">
+ <enum>QLCDNumber::Flat</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QLabel" name="lblRotZ_4">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <property name="text">
+ <string>Roll</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QLabel" name="lblRotX_4">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <property name="text">
+ <string>Yaw</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLCDNumber" name="raw_y">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <stylestrategy>NoAntialias</stylestrategy>
+ <kerning>false</kerning>
+ </font>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="smallDecimalPoint">
+ <bool>true</bool>
+ </property>
+ <property name="digitCount">
+ <number>4</number>
+ </property>
+ <property name="segmentStyle">
+ <enum>QLCDNumber::Flat</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="3">
+ <widget class="QLCDNumber" name="raw_roll">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <stylestrategy>NoAntialias</stylestrategy>
+ <kerning>false</kerning>
+ </font>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="smallDecimalPoint">
+ <bool>true</bool>
+ </property>
+ <property name="digitCount">
+ <number>4</number>
+ </property>
+ <property name="segmentStyle">
+ <enum>QLCDNumber::Flat</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLCDNumber" name="raw_z">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <stylestrategy>NoAntialias</stylestrategy>
+ <kerning>false</kerning>
+ </font>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="smallDecimalPoint">
+ <bool>true</bool>
+ </property>
+ <property name="digitCount">
+ <number>4</number>
+ </property>
+ <property name="segmentStyle">
+ <enum>QLCDNumber::Flat</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QGroupBox" name="box_mapped_headpose">
+ <property name="minimumSize">
+ <size>
+ <width>316</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="title">
+ <string>Game data</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_14">
<property name="leftMargin">
<number>0</number>
</property>
@@ -87,159 +520,473 @@
<property name="bottomMargin">
<number>0</number>
</property>
- <item alignment="Qt::AlignLeft|Qt::AlignTop">
- <widget class="QWidget" name="top" native="true">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <item row="1" column="3">
+ <widget class="QLCDNumber" name="pose_pitch">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
<property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Expanding">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>240</height>
- </size>
+ <property name="font">
+ <font>
+ <stylestrategy>NoAntialias</stylestrategy>
+ <kerning>false</kerning>
+ </font>
</property>
- <property name="maximumSize">
- <size>
- <width>640</width>
- <height>16777215</height>
- </size>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="smallDecimalPoint">
+ <bool>true</bool>
+ </property>
+ <property name="digitCount">
+ <number>4</number>
+ </property>
+ <property name="segmentStyle">
+ <enum>QLCDNumber::Flat</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <widget class="QLCDNumber" name="pose_yaw">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <stylestrategy>NoAntialias</stylestrategy>
+ <kerning>false</kerning>
+ </font>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="smallDecimalPoint">
+ <bool>true</bool>
+ </property>
+ <property name="digitCount">
+ <number>4</number>
+ </property>
+ <property name="segmentStyle">
+ <enum>QLCDNumber::Flat</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLCDNumber" name="pose_z">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <stylestrategy>NoAntialias</stylestrategy>
+ <kerning>false</kerning>
+ </font>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="smallDecimalPoint">
+ <bool>true</bool>
+ </property>
+ <property name="digitCount">
+ <number>4</number>
+ </property>
+ <property name="segmentStyle">
+ <enum>QLCDNumber::Flat</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="lblx">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <property name="text">
+ <string>X</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="lblY_2">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <property name="text">
+ <string>Y</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLCDNumber" name="pose_x">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <stylestrategy>NoAntialias</stylestrategy>
+ <kerning>false</kerning>
+ </font>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="smallDecimalPoint">
+ <bool>true</bool>
+ </property>
+ <property name="digitCount">
+ <number>4</number>
+ </property>
+ <property name="segmentStyle">
+ <enum>QLCDNumber::Flat</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QLabel" name="lblRotY_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <property name="text">
+ <string>Pitch</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QLabel" name="lblRotZ_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <property name="text">
+ <string>Roll</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QLabel" name="lblRotX_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <property name="text">
+ <string>Yaw</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="lblZ_2">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <property name="text">
+ <string>Z</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLCDNumber" name="pose_y">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <stylestrategy>NoAntialias</stylestrategy>
+ <kerning>false</kerning>
+ </font>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="smallDecimalPoint">
+ <bool>true</bool>
+ </property>
+ <property name="digitCount">
+ <number>4</number>
+ </property>
+ <property name="segmentStyle">
+ <enum>QLCDNumber::Flat</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="3">
+ <widget class="QLCDNumber" name="pose_roll">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <stylestrategy>NoAntialias</stylestrategy>
+ <kerning>false</kerning>
+ </font>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="smallDecimalPoint">
+ <bool>true</bool>
+ </property>
+ <property name="digitCount">
+ <number>4</number>
+ </property>
+ <property name="segmentStyle">
+ <enum>QLCDNumber::Flat</enum>
</property>
- <layout class="QHBoxLayout" name="horizontalLayout_2">
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QWidget" name="groupControls" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
+ <horstretch>4</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>315</width>
+ <height>0</height>
+ </size>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <property name="spacing">
+ <number>4</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="groupProfile" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
- <number>0</number>
+ <number>4</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
- <number>0</number>
+ <number>4</number>
</property>
<property name="bottomMargin">
- <number>4</number>
+ <number>0</number>
</property>
- <item alignment="Qt::AlignLeft|Qt::AlignTop">
- <widget class="QFrame" name="video_feed">
+ <item>
+ <widget class="QToolButton" name="profile_button">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="focusPolicy">
+ <enum>Qt::StrongFocus</enum>
+ </property>
+ <property name="text">
+ <string>Profile</string>
+ </property>
+ <property name="popupMode">
+ <enum>QToolButton::InstantPopup</enum>
+ </property>
+ <property name="toolButtonStyle">
+ <enum>Qt::ToolButtonTextBesideIcon</enum>
+ </property>
+ <property name="autoRaise">
+ <bool>true</bool>
+ </property>
+ <property name="arrowType">
+ <enum>Qt::DownArrow</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="iconcomboProfile">
<property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
- <width>320</width>
- <height>240</height>
+ <width>245</width>
+ <height>0</height>
</size>
</property>
- <property name="maximumSize">
+ <property name="focusPolicy">
+ <enum>Qt::StrongFocus</enum>
+ </property>
+ <property name="maxVisibleItems">
+ <number>20</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="groupOptions" native="true">
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <property name="leftMargin">
+ <number>4</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>4</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="btnShortcuts">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Options</string>
+ </property>
+ <property name="icon">
+ <iconset resource="../gui/opentrack-res.qrc">
+ <normaloff>:/images/tools.png</normaloff>:/images/tools.png</iconset>
+ </property>
+ <property name="iconSize">
<size>
- <width>320</width>
- <height>240</height>
+ <width>80</width>
+ <height>24</height>
</size>
</property>
- <property name="lineWidth">
- <number>0</number>
- </property>
- <widget class="QFrame" name="video_frame">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>320</width>
- <height>240</height>
- </rect>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>320</width>
- <height>240</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>320</width>
- <height>240</height>
- </size>
- </property>
- <widget class="QLabel" name="video_frame_label">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>320</width>
- <height>240</height>
- </rect>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>320</width>
- <height>240</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>320</width>
- <height>240</height>
- </size>
- </property>
- <property name="font">
- <font>
- <family>Candara</family>
- <pointsize>37</pointsize>
- <weight>50</weight>
- <bold>false</bold>
- <kerning>true</kerning>
- </font>
- </property>
- <property name="text">
- <string/>
- </property>
- <property name="pixmap">
- <pixmap resource="../gui/opentrack-res.qrc">:/images/tracking-not-started.png</pixmap>
- </property>
- <property name="scaledContents">
- <bool>false</bool>
- </property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
- </property>
- <property name="wordWrap">
- <bool>true</bool>
- </property>
- </widget>
- </widget>
</widget>
</item>
<item>
- <widget class="pose_widget" name="pose_display" native="true">
+ <widget class="QPushButton" name="btnEditCurves">
<property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <sizepolicy hsizetype="Expanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="minimumSize">
+ <property name="text">
+ <string>Mapping</string>
+ </property>
+ <property name="icon">
+ <iconset resource="../gui/opentrack-res.qrc">
+ <normaloff>:/images/curves.png</normaloff>:/images/curves.png</iconset>
+ </property>
+ <property name="iconSize">
<size>
- <width>320</width>
- <height>240</height>
+ <width>80</width>
+ <height>24</height>
</size>
</property>
</widget>
@@ -248,1244 +995,329 @@
</widget>
</item>
<item>
- <widget class="QFrame" name="top_display">
+ <widget class="QGroupBox" name="groupStartStop">
<property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
- <horstretch>0</horstretch>
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Maximum">
+ <horstretch>4</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="frameShape">
- <enum>QFrame::NoFrame</enum>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
</property>
- <property name="lineWidth">
- <number>0</number>
+ <property name="title">
+ <string>Tracking</string>
</property>
- <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="spacing">
- <number>6</number>
+ <number>7</number>
</property>
<property name="leftMargin">
- <number>5</number>
+ <number>4</number>
</property>
<property name="topMargin">
- <number>0</number>
+ <number>4</number>
</property>
<property name="rightMargin">
- <number>0</number>
+ <number>4</number>
</property>
<property name="bottomMargin">
- <number>6</number>
+ <number>4</number>
</property>
<item>
- <widget class="QGroupBox" name="box_raw_headpose">
+ <widget class="QToolButton" name="btnStartTracker">
<property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Maximum">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
- <width>316</width>
- <height>0</height>
+ <width>0</width>
+ <height>34</height>
</size>
</property>
- <property name="title">
- <string>Raw tracker data</string>
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Start</string>
</property>
- <layout class="QGridLayout" name="gridLayout_12">
- <property name="leftMargin">
- <number>6</number>
- </property>
- <property name="topMargin">
- <number>0</number>
- </property>
- <property name="rightMargin">
- <number>0</number>
- </property>
- <property name="bottomMargin">
- <number>6</number>
- </property>
- <property name="spacing">
- <number>0</number>
- </property>
- <item row="0" column="3">
- <widget class="QLCDNumber" name="raw_yaw">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="font">
- <font>
- <stylestrategy>NoAntialias</stylestrategy>
- <kerning>false</kerning>
- </font>
- </property>
- <property name="frameShape">
- <enum>QFrame::NoFrame</enum>
- </property>
- <property name="smallDecimalPoint">
- <bool>true</bool>
- </property>
- <property name="digitCount">
- <number>4</number>
- </property>
- <property name="segmentStyle">
- <enum>QLCDNumber::Flat</enum>
- </property>
- </widget>
- </item>
- <item row="2" column="0">
- <widget class="QLabel" name="lblZ_4">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="autoFillBackground">
- <bool>false</bool>
- </property>
- <property name="frameShadow">
- <enum>QFrame::Raised</enum>
- </property>
- <property name="text">
- <string>Z</string>
- </property>
- </widget>
- </item>
- <item row="1" column="2">
- <widget class="QLabel" name="lblRotY_4">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="frameShadow">
- <enum>QFrame::Raised</enum>
- </property>
- <property name="text">
- <string>Pitch</string>
- </property>
- </widget>
- </item>
- <item row="1" column="3">
- <widget class="QLCDNumber" name="raw_pitch">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="font">
- <font>
- <stylestrategy>NoAntialias</stylestrategy>
- <kerning>false</kerning>
- </font>
- </property>
- <property name="frameShape">
- <enum>QFrame::NoFrame</enum>
- </property>
- <property name="smallDecimalPoint">
- <bool>true</bool>
- </property>
- <property name="digitCount">
- <number>4</number>
- </property>
- <property name="segmentStyle">
- <enum>QLCDNumber::Flat</enum>
- </property>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QLabel" name="lblY_4">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="autoFillBackground">
- <bool>false</bool>
- </property>
- <property name="frameShadow">
- <enum>QFrame::Raised</enum>
- </property>
- <property name="text">
- <string>Y</string>
- </property>
- </widget>
- </item>
- <item row="0" column="0">
- <widget class="QLabel" name="lblX_4">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="frameShadow">
- <enum>QFrame::Raised</enum>
- </property>
- <property name="text">
- <string>X</string>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QLCDNumber" name="raw_x">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="font">
- <font>
- <stylestrategy>NoAntialias</stylestrategy>
- <kerning>false</kerning>
- </font>
- </property>
- <property name="frameShape">
- <enum>QFrame::NoFrame</enum>
- </property>
- <property name="smallDecimalPoint">
- <bool>true</bool>
- </property>
- <property name="digitCount">
- <number>4</number>
- </property>
- <property name="segmentStyle">
- <enum>QLCDNumber::Flat</enum>
- </property>
- </widget>
- </item>
- <item row="2" column="2">
- <widget class="QLabel" name="lblRotZ_4">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="frameShadow">
- <enum>QFrame::Raised</enum>
- </property>
- <property name="text">
- <string>Roll</string>
- </property>
- </widget>
- </item>
- <item row="0" column="2">
- <widget class="QLabel" name="lblRotX_4">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="frameShadow">
- <enum>QFrame::Raised</enum>
- </property>
- <property name="text">
- <string>Yaw</string>
- </property>
- </widget>
- </item>
- <item row="1" column="1">
- <widget class="QLCDNumber" name="raw_y">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="font">
- <font>
- <stylestrategy>NoAntialias</stylestrategy>
- <kerning>false</kerning>
- </font>
- </property>
- <property name="frameShape">
- <enum>QFrame::NoFrame</enum>
- </property>
- <property name="smallDecimalPoint">
- <bool>true</bool>
- </property>
- <property name="digitCount">
- <number>4</number>
- </property>
- <property name="segmentStyle">
- <enum>QLCDNumber::Flat</enum>
- </property>
- </widget>
- </item>
- <item row="2" column="3">
- <widget class="QLCDNumber" name="raw_roll">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="font">
- <font>
- <stylestrategy>NoAntialias</stylestrategy>
- <kerning>false</kerning>
- </font>
- </property>
- <property name="frameShape">
- <enum>QFrame::NoFrame</enum>
- </property>
- <property name="smallDecimalPoint">
- <bool>true</bool>
- </property>
- <property name="digitCount">
- <number>4</number>
- </property>
- <property name="segmentStyle">
- <enum>QLCDNumber::Flat</enum>
- </property>
- </widget>
- </item>
- <item row="2" column="1">
- <widget class="QLCDNumber" name="raw_z">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="font">
- <font>
- <stylestrategy>NoAntialias</stylestrategy>
- <kerning>false</kerning>
- </font>
- </property>
- <property name="frameShape">
- <enum>QFrame::NoFrame</enum>
- </property>
- <property name="smallDecimalPoint">
- <bool>true</bool>
- </property>
- <property name="digitCount">
- <number>4</number>
- </property>
- <property name="segmentStyle">
- <enum>QLCDNumber::Flat</enum>
- </property>
- </widget>
- </item>
- </layout>
</widget>
</item>
<item>
- <widget class="QGroupBox" name="box_mapped_headpose">
+ <widget class="QToolButton" name="btnStopTracker">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
<property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Maximum">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
- <width>316</width>
- <height>0</height>
+ <width>0</width>
+ <height>34</height>
</size>
</property>
- <property name="title">
- <string>Game data</string>
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Stop</string>
</property>
- <layout class="QGridLayout" name="gridLayout_14">
- <property name="leftMargin">
- <number>6</number>
- </property>
- <property name="topMargin">
- <number>0</number>
- </property>
- <property name="rightMargin">
- <number>0</number>
- </property>
- <property name="bottomMargin">
- <number>6</number>
- </property>
- <property name="spacing">
- <number>0</number>
- </property>
- <item row="1" column="3">
- <widget class="QLCDNumber" name="pose_pitch">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="font">
- <font>
- <stylestrategy>NoAntialias</stylestrategy>
- <kerning>false</kerning>
- </font>
- </property>
- <property name="frameShape">
- <enum>QFrame::NoFrame</enum>
- </property>
- <property name="smallDecimalPoint">
- <bool>true</bool>
- </property>
- <property name="digitCount">
- <number>4</number>
- </property>
- <property name="segmentStyle">
- <enum>QLCDNumber::Flat</enum>
- </property>
- </widget>
- </item>
- <item row="0" column="3">
- <widget class="QLCDNumber" name="pose_yaw">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="font">
- <font>
- <stylestrategy>NoAntialias</stylestrategy>
- <kerning>false</kerning>
- </font>
- </property>
- <property name="frameShape">
- <enum>QFrame::NoFrame</enum>
- </property>
- <property name="smallDecimalPoint">
- <bool>true</bool>
- </property>
- <property name="digitCount">
- <number>4</number>
- </property>
- <property name="segmentStyle">
- <enum>QLCDNumber::Flat</enum>
- </property>
- </widget>
- </item>
- <item row="2" column="1">
- <widget class="QLCDNumber" name="pose_z">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="font">
- <font>
- <stylestrategy>NoAntialias</stylestrategy>
- <kerning>false</kerning>
- </font>
- </property>
- <property name="frameShape">
- <enum>QFrame::NoFrame</enum>
- </property>
- <property name="smallDecimalPoint">
- <bool>true</bool>
- </property>
- <property name="digitCount">
- <number>4</number>
- </property>
- <property name="segmentStyle">
- <enum>QLCDNumber::Flat</enum>
- </property>
- </widget>
- </item>
- <item row="0" column="0">
- <widget class="QLabel" name="lblx">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="frameShadow">
- <enum>QFrame::Raised</enum>
- </property>
- <property name="text">
- <string>X</string>
- </property>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QLabel" name="lblY_2">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="autoFillBackground">
- <bool>false</bool>
- </property>
- <property name="frameShadow">
- <enum>QFrame::Raised</enum>
- </property>
- <property name="text">
- <string>Y</string>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QLCDNumber" name="pose_x">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="font">
- <font>
- <stylestrategy>NoAntialias</stylestrategy>
- <kerning>false</kerning>
- </font>
- </property>
- <property name="frameShape">
- <enum>QFrame::NoFrame</enum>
- </property>
- <property name="smallDecimalPoint">
- <bool>true</bool>
- </property>
- <property name="digitCount">
- <number>4</number>
- </property>
- <property name="segmentStyle">
- <enum>QLCDNumber::Flat</enum>
- </property>
- </widget>
- </item>
- <item row="1" column="2">
- <widget class="QLabel" name="lblRotY_2">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="frameShadow">
- <enum>QFrame::Raised</enum>
- </property>
- <property name="text">
- <string>Pitch</string>
- </property>
- </widget>
- </item>
- <item row="2" column="2">
- <widget class="QLabel" name="lblRotZ_2">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="frameShadow">
- <enum>QFrame::Raised</enum>
- </property>
- <property name="text">
- <string>Roll</string>
- </property>
- </widget>
- </item>
- <item row="0" column="2">
- <widget class="QLabel" name="lblRotX_2">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="frameShadow">
- <enum>QFrame::Raised</enum>
- </property>
- <property name="text">
- <string>Yaw</string>
- </property>
- </widget>
- </item>
- <item row="2" column="0">
- <widget class="QLabel" name="lblZ_2">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="autoFillBackground">
- <bool>false</bool>
- </property>
- <property name="frameShadow">
- <enum>QFrame::Raised</enum>
- </property>
- <property name="text">
- <string>Z</string>
- </property>
- </widget>
- </item>
- <item row="1" column="1">
- <widget class="QLCDNumber" name="pose_y">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="font">
- <font>
- <stylestrategy>NoAntialias</stylestrategy>
- <kerning>false</kerning>
- </font>
- </property>
- <property name="frameShape">
- <enum>QFrame::NoFrame</enum>
- </property>
- <property name="smallDecimalPoint">
- <bool>true</bool>
- </property>
- <property name="digitCount">
- <number>4</number>
- </property>
- <property name="segmentStyle">
- <enum>QLCDNumber::Flat</enum>
- </property>
- </widget>
- </item>
- <item row="2" column="3">
- <widget class="QLCDNumber" name="pose_roll">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="font">
- <font>
- <stylestrategy>NoAntialias</stylestrategy>
- <kerning>false</kerning>
- </font>
- </property>
- <property name="frameShape">
- <enum>QFrame::NoFrame</enum>
- </property>
- <property name="smallDecimalPoint">
- <bool>true</bool>
- </property>
- <property name="digitCount">
- <number>4</number>
- </property>
- <property name="segmentStyle">
- <enum>QLCDNumber::Flat</enum>
- </property>
- </widget>
- </item>
- </layout>
</widget>
</item>
</layout>
</widget>
</item>
+ </layout>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QWidget" name="modules" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>310</width>
+ <height>0</height>
+ </size>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="spacing">
+ <number>4</number>
+ </property>
+ <property name="leftMargin">
+ <number>4</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
<item>
- <widget class="QFrame" name="bottom_controls">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="maximumSize">
- <size>
- <width>16777215</width>
- <height>180</height>
- </size>
+ <widget class="QGroupBox" name="groupTracker">
+ <property name="title">
+ <string>Input</string>
</property>
- <layout class="QGridLayout" name="gridLayout_2">
+ <layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
- <number>6</number>
+ <number>4</number>
</property>
<property name="topMargin">
- <number>0</number>
+ <number>4</number>
</property>
<property name="rightMargin">
- <number>0</number>
+ <number>4</number>
</property>
<property name="bottomMargin">
+ <number>4</number>
+ </property>
+ <property name="horizontalSpacing">
+ <number>6</number>
+ </property>
+ <property name="verticalSpacing">
<number>0</number>
</property>
- <property name="spacing">
+ <item row="0" column="0">
+ <widget class="QComboBox" name="iconcomboTrackerSource">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="focusPolicy">
+ <enum>Qt::TabFocus</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QToolButton" name="btnShowEngineControls">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <family>DejaVu Sans</family>
+ <stylestrategy>PreferAntialias</stylestrategy>
+ <kerning>false</kerning>
+ </font>
+ </property>
+ <property name="focusPolicy">
+ <enum>Qt::ClickFocus</enum>
+ </property>
+ <property name="text">
+ <string>🔨</string>
+ </property>
+ <property name="flat" stdset="0">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupProto">
+ <property name="title">
+ <string>Output</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_4">
+ <property name="leftMargin">
+ <number>4</number>
+ </property>
+ <property name="topMargin">
+ <number>4</number>
+ </property>
+ <property name="rightMargin">
+ <number>4</number>
+ </property>
+ <property name="bottomMargin">
+ <number>4</number>
+ </property>
+ <property name="horizontalSpacing">
<number>6</number>
</property>
+ <property name="verticalSpacing">
+ <number>0</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QComboBox" name="iconcomboProtocol">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="focusPolicy">
+ <enum>Qt::TabFocus</enum>
+ </property>
+ </widget>
+ </item>
<item row="0" column="1">
- <widget class="QFrame" name="groupWindows">
+ <widget class="QToolButton" name="btnShowServerControls">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
<property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
- <horstretch>4</horstretch>
+ <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="minimumSize">
- <size>
- <width>315</width>
- <height>0</height>
- </size>
+ <property name="font">
+ <font>
+ <family>DejaVu Sans</family>
+ <stylestrategy>PreferAntialias</stylestrategy>
+ <kerning>false</kerning>
+ </font>
+ </property>
+ <property name="focusPolicy">
+ <enum>Qt::ClickFocus</enum>
</property>
- <property name="lineWidth">
- <number>0</number>
+ <property name="text">
+ <string>🔨</string>
+ </property>
+ <property name="flat" stdset="0">
+ <bool>false</bool>
</property>
- <layout class="QVBoxLayout" name="verticalLayout_3">
- <property name="spacing">
- <number>3</number>
- </property>
- <property name="leftMargin">
- <number>0</number>
- </property>
- <property name="topMargin">
- <number>0</number>
- </property>
- <property name="rightMargin">
- <number>0</number>
- </property>
- <property name="bottomMargin">
- <number>0</number>
- </property>
- <item>
- <widget class="QFrame" name="groupProfile">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <layout class="QHBoxLayout" name="horizontalLayout">
- <property name="spacing">
- <number>0</number>
- </property>
- <property name="leftMargin">
- <number>0</number>
- </property>
- <property name="topMargin">
- <number>0</number>
- </property>
- <property name="rightMargin">
- <number>0</number>
- </property>
- <property name="bottomMargin">
- <number>0</number>
- </property>
- <item>
- <widget class="QToolButton" name="profile_button">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="focusPolicy">
- <enum>Qt::StrongFocus</enum>
- </property>
- <property name="text">
- <string>Profile</string>
- </property>
- <property name="popupMode">
- <enum>QToolButton::InstantPopup</enum>
- </property>
- <property name="toolButtonStyle">
- <enum>Qt::ToolButtonTextBesideIcon</enum>
- </property>
- <property name="autoRaise">
- <bool>true</bool>
- </property>
- <property name="arrowType">
- <enum>Qt::DownArrow</enum>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QComboBox" name="iconcomboProfile">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>245</width>
- <height>0</height>
- </size>
- </property>
- <property name="focusPolicy">
- <enum>Qt::StrongFocus</enum>
- </property>
- <property name="maxVisibleItems">
- <number>20</number>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="btnShortcuts">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Options</string>
- </property>
- <property name="icon">
- <iconset resource="../gui/opentrack-res.qrc">
- <normaloff>:/images/tools.png</normaloff>:/images/tools.png</iconset>
- </property>
- <property name="iconSize">
- <size>
- <width>80</width>
- <height>24</height>
- </size>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="btnEditCurves">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Mapping</string>
- </property>
- <property name="icon">
- <iconset resource="../gui/opentrack-res.qrc">
- <normaloff>:/images/curves.png</normaloff>:/images/curves.png</iconset>
- </property>
- <property name="iconSize">
- <size>
- <width>80</width>
- <height>24</height>
- </size>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QGroupBox" name="groupStartStop">
- <property name="sizePolicy">
- <sizepolicy hsizetype="MinimumExpanding" vsizetype="Maximum">
- <horstretch>4</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- <property name="title">
- <string>Tracking</string>
- </property>
- <property name="flat">
- <bool>true</bool>
- </property>
- <layout class="QHBoxLayout" name="horizontalLayout_4">
- <property name="spacing">
- <number>8</number>
- </property>
- <property name="leftMargin">
- <number>0</number>
- </property>
- <property name="topMargin">
- <number>6</number>
- </property>
- <property name="rightMargin">
- <number>0</number>
- </property>
- <property name="bottomMargin">
- <number>6</number>
- </property>
- <item>
- <widget class="QToolButton" name="btnStartTracker">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>34</height>
- </size>
- </property>
- <property name="font">
- <font>
- <weight>75</weight>
- <bold>true</bold>
- </font>
- </property>
- <property name="text">
- <string>Start</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QToolButton" name="btnStopTracker">
- <property name="enabled">
- <bool>false</bool>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>34</height>
- </size>
- </property>
- <property name="font">
- <font>
- <weight>75</weight>
- <bold>true</bold>
- </font>
- </property>
- <property name="text">
- <string>Stop</string>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- </layout>
</widget>
</item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupFilter">
+ <property name="title">
+ <string>Filter</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_3">
+ <property name="leftMargin">
+ <number>4</number>
+ </property>
+ <property name="topMargin">
+ <number>4</number>
+ </property>
+ <property name="rightMargin">
+ <number>4</number>
+ </property>
+ <property name="bottomMargin">
+ <number>4</number>
+ </property>
+ <property name="horizontalSpacing">
+ <number>6</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>0</number>
+ </property>
<item row="0" column="0">
- <widget class="QFrame" name="frame_2">
+ <widget class="QComboBox" name="iconcomboFilter">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="focusPolicy">
+ <enum>Qt::TabFocus</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QToolButton" name="btnShowFilterControls">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="minimumSize">
- <size>
- <width>310</width>
- <height>0</height>
- </size>
+ <property name="font">
+ <font>
+ <family>DejaVu Sans</family>
+ <stylestrategy>PreferAntialias</stylestrategy>
+ <kerning>false</kerning>
+ </font>
+ </property>
+ <property name="focusPolicy">
+ <enum>Qt::ClickFocus</enum>
</property>
- <property name="frameShape">
- <enum>QFrame::NoFrame</enum>
+ <property name="text">
+ <string>🔨</string>
</property>
- <property name="frameShadow">
- <enum>QFrame::Raised</enum>
+ <property name="flat" stdset="0">
+ <bool>false</bool>
</property>
- <layout class="QVBoxLayout" name="verticalLayout">
- <property name="spacing">
- <number>2</number>
- </property>
- <property name="leftMargin">
- <number>3</number>
- </property>
- <property name="topMargin">
- <number>2</number>
- </property>
- <property name="rightMargin">
- <number>3</number>
- </property>
- <property name="bottomMargin">
- <number>8</number>
- </property>
- <item>
- <widget class="QGroupBox" name="groupTrackerSource">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="title">
- <string>Input</string>
- </property>
- <layout class="QGridLayout" name="gridLayout">
- <property name="leftMargin">
- <number>4</number>
- </property>
- <property name="topMargin">
- <number>0</number>
- </property>
- <property name="rightMargin">
- <number>0</number>
- </property>
- <property name="bottomMargin">
- <number>0</number>
- </property>
- <property name="horizontalSpacing">
- <number>3</number>
- </property>
- <property name="verticalSpacing">
- <number>0</number>
- </property>
- <item row="0" column="0">
- <widget class="QComboBox" name="iconcomboTrackerSource">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="focusPolicy">
- <enum>Qt::TabFocus</enum>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QToolButton" name="btnShowEngineControls">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="font">
- <font>
- <family>DejaVu Sans</family>
- <stylestrategy>PreferAntialias</stylestrategy>
- <kerning>false</kerning>
- </font>
- </property>
- <property name="focusPolicy">
- <enum>Qt::ClickFocus</enum>
- </property>
- <property name="text">
- <string>🔨</string>
- </property>
- <property name="flat" stdset="0">
- <bool>false</bool>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <widget class="QGroupBox" name="groupGameProtocol">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="title">
- <string>Output</string>
- </property>
- <layout class="QGridLayout" name="gridLayout_4">
- <property name="leftMargin">
- <number>4</number>
- </property>
- <property name="topMargin">
- <number>0</number>
- </property>
- <property name="rightMargin">
- <number>0</number>
- </property>
- <property name="bottomMargin">
- <number>0</number>
- </property>
- <property name="horizontalSpacing">
- <number>3</number>
- </property>
- <property name="verticalSpacing">
- <number>0</number>
- </property>
- <item row="0" column="0">
- <widget class="QComboBox" name="iconcomboProtocol">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="focusPolicy">
- <enum>Qt::TabFocus</enum>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QToolButton" name="btnShowServerControls">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="font">
- <font>
- <family>DejaVu Sans</family>
- <stylestrategy>PreferAntialias</stylestrategy>
- <kerning>false</kerning>
- </font>
- </property>
- <property name="focusPolicy">
- <enum>Qt::ClickFocus</enum>
- </property>
- <property name="text">
- <string>🔨</string>
- </property>
- <property name="flat" stdset="0">
- <bool>false</bool>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <widget class="QGroupBox" name="groupFilter">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="title">
- <string>Filter</string>
- </property>
- <layout class="QGridLayout" name="gridLayout_3">
- <property name="leftMargin">
- <number>4</number>
- </property>
- <property name="topMargin">
- <number>0</number>
- </property>
- <property name="rightMargin">
- <number>0</number>
- </property>
- <property name="bottomMargin">
- <number>0</number>
- </property>
- <property name="horizontalSpacing">
- <number>3</number>
- </property>
- <property name="verticalSpacing">
- <number>0</number>
- </property>
- <item row="0" column="0">
- <widget class="QComboBox" name="iconcomboFilter">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="focusPolicy">
- <enum>Qt::TabFocus</enum>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QToolButton" name="btnShowFilterControls">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="font">
- <font>
- <family>DejaVu Sans</family>
- <stylestrategy>PreferAntialias</stylestrategy>
- <kerning>false</kerning>
- </font>
- </property>
- <property name="focusPolicy">
- <enum>Qt::ClickFocus</enum>
- </property>
- <property name="text">
- <string>🔨</string>
- </property>
- <property name="flat" stdset="0">
- <bool>false</bool>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- </layout>
</widget>
</item>
</layout>
@@ -1504,14 +1336,6 @@
<header>pose-widget/pose-widget.hpp</header>
</customwidget>
</customwidgets>
- <tabstops>
- <tabstop>btnStartTracker</tabstop>
- <tabstop>btnStopTracker</tabstop>
- <tabstop>profile_button</tabstop>
- <tabstop>iconcomboProfile</tabstop>
- <tabstop>btnShortcuts</tabstop>
- <tabstop>btnEditCurves</tabstop>
- </tabstops>
<resources>
<include location="../gui/opentrack-res.qrc"/>
</resources>
diff --git a/opentrack/new_file_dialog.h b/opentrack/new_file_dialog.h
index 7244e524..9e3c86a6 100644
--- a/opentrack/new_file_dialog.h
+++ b/opentrack/new_file_dialog.h
@@ -3,7 +3,6 @@
#include "ui_new_config.h"
#include "options/options.hpp"
#include <QFile>
-#include <QRegExp>
#include <QString>
#include <QMessageBox>
diff --git a/options/base-value.cpp b/options/base-value.cpp
index d4ec4b6c..950629d0 100644
--- a/options/base-value.cpp
+++ b/options/base-value.cpp
@@ -1,7 +1,20 @@
#include "base-value.hpp"
+#include <QThread>
using namespace options;
+//#define OTR_TRACE_NOTIFY
+
+const bool value_::TRACE_NOTIFY =
+#ifdef OTR_TRACE_NOTIFY
+ true;
+#else
+ [] {
+ auto b = qgetenv("OTR_TRACE_NOTIFY");
+ return !b.isEmpty() && b != "0";
+ }();
+#endif
+
value_::value_(bundle const& b, const QString& name) noexcept :
b(b), self_name(name)
{
@@ -12,3 +25,9 @@ value_::~value_()
{
b->on_value_destructed(this);
}
+
+void value_::maybe_trace(const char* str) const
+{
+ if (TRACE_NOTIFY)
+ qDebug().noquote() << str << QThread::currentThreadId() << b->name() << self_name << get_variant();
+}
diff --git a/options/base-value.hpp b/options/base-value.hpp
index 722107a4..5317191b 100644
--- a/options/base-value.hpp
+++ b/options/base-value.hpp
@@ -6,7 +6,6 @@
#include "metatype.hpp"
#include "export.hpp"
-#include "compat/macros.hpp"
#include "value-traits.hpp"
#include <utility>
@@ -26,6 +25,7 @@ class OTR_OPTIONS_EXPORT value_ : public QObject
{
Q_OBJECT
+ template<typename t> using cv_qualified = detail::cv_qualified<t>;
template<typename t>
using signal_sig = void(value_::*)(cv_qualified<t>) const;
@@ -40,6 +40,8 @@ public:
return static_cast<signal_sig<t>>(&value_::valueChanged);
}
+ static const bool TRACE_NOTIFY;
+
signals:
OTR_OPTIONS_SIGNAL(double);
OTR_OPTIONS_SIGNAL(float);
@@ -65,6 +67,8 @@ protected:
virtual void store_variant(QVariant&&) noexcept = 0;
virtual void store_variant(const QVariant&) noexcept = 0;
+ void maybe_trace(const char* str) const;
+
template<typename t>
void store_(const t& datum)
{
@@ -91,6 +95,7 @@ public slots:
virtual void set_to_default() noexcept = 0;
virtual void notify() const = 0;
+ virtual void notify_() const = 0;
virtual QVariant get_variant() const noexcept = 0;
};
diff --git a/options/bundle.hpp b/options/bundle.hpp
index 158fcef9..c97eeff2 100644
--- a/options/bundle.hpp
+++ b/options/bundle.hpp
@@ -21,7 +21,7 @@
#include <QObject>
#include <QString>
#include <QVariant>
-#include <QMutex>
+#include <QRecursiveMutex>
#include <QDebug>
@@ -46,7 +46,7 @@ class OTR_OPTIONS_EXPORT bundle final : public QObject, public connector
friend struct bundler;
- mutable QMutex mtx { QMutex::Recursive };
+ mutable QRecursiveMutex mtx;
const QString group_name;
group saved;
group transient;
@@ -62,7 +62,7 @@ public:
bundle(const bundle&) = delete;
bundle& operator=(const bundle&) = delete;
- QMutex* get_mtx() const override { return &mtx; }
+ QRecursiveMutex* get_mtx() const override { return &mtx; }
QString name() const { return group_name; }
explicit bundle(const QString& group_name);
@@ -93,7 +93,7 @@ struct OTR_OPTIONS_EXPORT bundler final
static void reload();
private:
- QMutex implsgl_mtx { QMutex::Recursive };
+ QRecursiveMutex implsgl_mtx;
std::unordered_map<k, weak> implsgl_data {};
void notify_();
@@ -114,4 +114,3 @@ private:
void set_value_to_default(value_* val);
} // ns options::detail
-
diff --git a/options/connector.cpp b/options/connector.cpp
index 40c99a82..e86958f7 100644
--- a/options/connector.cpp
+++ b/options/connector.cpp
@@ -64,6 +64,8 @@ void connector::on_value_created(value_type val)
void connector::notify_values(const QString& name) const
{
+ QMutexLocker l(get_mtx());
+
auto it = connected_values.find(name);
if (it != connected_values.cend())
for (value_type val : it->second)
@@ -72,6 +74,8 @@ void connector::notify_values(const QString& name) const
void connector::notify_all_values() const
{
+ QMutexLocker l(get_mtx());
+
for (const auto& [k, v] : connected_values)
for (value_type val : v)
val->notify();
@@ -79,6 +83,8 @@ void connector::notify_all_values() const
void connector::set_all_to_default_()
{
+ QMutexLocker l(get_mtx());
+
for (auto& pair : connected_values)
for (auto& val : pair.second)
val->set_to_default();
diff --git a/options/connector.hpp b/options/connector.hpp
index 025efda2..bcac5676 100644
--- a/options/connector.hpp
+++ b/options/connector.hpp
@@ -14,7 +14,7 @@
#include <vector>
#include <QString>
-#include <QMutex>
+#include <QRecursiveMutex>
#include "export.hpp"
@@ -38,7 +38,7 @@ class OTR_OPTIONS_EXPORT connector
protected:
void notify_values(const QString& name) const;
void notify_all_values() const;
- virtual QMutex* get_mtx() const = 0;
+ virtual QRecursiveMutex* get_mtx() const = 0;
void set_all_to_default_();
public:
diff --git a/options/defs.hpp b/options/defs.hpp
index 9ea4f3b3..797a8fda 100644
--- a/options/defs.hpp
+++ b/options/defs.hpp
@@ -2,5 +2,3 @@
#define OPENTRACK_PROFILE_FILENAME_KEY "settings-filename"
#define OPENTRACK_DEFAULT_PROFILE "default.ini"
-
-
diff --git a/options/globals.cpp b/options/globals.cpp
index 386ef56d..39eb6014 100644
--- a/options/globals.cpp
+++ b/options/globals.cpp
@@ -1,10 +1,13 @@
#include "globals.hpp"
#include "compat/base-path.hpp"
#include "defs.hpp"
+#include "opentrack-org.hxx"
#include <QFile>
+#include <QFileInfo>
#include <QDir>
#include <QStandardPaths>
+#include <QDateTime>
#include <QDebug>
namespace options::globals::detail {
@@ -124,9 +127,24 @@ QString ini_combine(const QString& filename)
QStringList ini_list()
{
- QDir settings_dir(ini_directory());
+ static QMutex mtx;
+ static QStringList list;
+ QMutexLocker l{&mtx};
+
+ const QString dirname = ini_directory();
+
+ {
+ static QDateTime last_time = {};
+ auto time = QFileInfo{dirname}.lastModified();
+ if (time == last_time)
+ return list;
+ last_time = time;
+ }
+
+ QDir settings_dir(dirname);
+
using f = QDir::Filter;
- auto list = settings_dir.entryList({ QStringLiteral("*.ini") }, f::Files | f::Readable, QDir::Name);
+ list = settings_dir.entryList({ QStringLiteral("*.ini") }, f::Files | f::Readable, QDir::Name);
std::sort(list.begin(), list.end());
return list;
}
diff --git a/options/globals.hpp b/options/globals.hpp
index 7af6533d..1a92cc47 100644
--- a/options/globals.hpp
+++ b/options/globals.hpp
@@ -1,13 +1,13 @@
#pragma once
#include "export.hpp"
-#include "compat/macros.hpp"
+#include "compat/macros.h"
#include <optional>
#include <QString>
#include <QSettings>
-#include <QMutex>
+#include <QRecursiveMutex>
namespace options::globals::detail {
@@ -17,7 +17,7 @@ struct OTR_OPTIONS_EXPORT ini_ctx
{
std::optional<QSettings> qsettings { std::in_place };
QString pathname;
- QMutex mtx { QMutex::Recursive };
+ QRecursiveMutex mtx;
unsigned refcount = 0;
bool modifiedp = false;
diff --git a/options/group.hpp b/options/group.hpp
index 93299b6e..11bab965 100644
--- a/options/group.hpp
+++ b/options/group.hpp
@@ -4,7 +4,7 @@
#include "compat/base-path.hpp"
#include "compat/library-path.hpp"
-#include "compat/macros.hpp"
+#include "compat/macros.h"
#include "compat/qhash.hpp"
#include "export.hpp"
diff --git a/options/lang/de_DE.ts b/options/lang/de_DE.ts
new file mode 100644
index 00000000..1552582e
--- /dev/null
+++ b/options/lang/de_DE.ts
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+</TS>
diff --git a/options/lang/zh_CN.ts b/options/lang/zh_CN.ts
index 6401616d..e5ca8aa9 100644
--- a/options/lang/zh_CN.ts
+++ b/options/lang/zh_CN.ts
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
</TS>
diff --git a/options/metatype.cpp b/options/metatype.cpp
index 7962b81b..f85429e0 100644
--- a/options/metatype.cpp
+++ b/options/metatype.cpp
@@ -1,5 +1,4 @@
#include <QMetaType>
-#include "compat/macros.hpp"
namespace options::detail {
@@ -7,7 +6,6 @@ template<typename t>
void declare_metatype_for_type(const char* str)
{
qRegisterMetaType<t>(str);
- qRegisterMetaTypeStreamOperators<t>();
}
} // ns options::detail
diff --git a/options/scoped.cpp b/options/scoped.cpp
index f5e219e2..33db7907 100644
--- a/options/scoped.cpp
+++ b/options/scoped.cpp
@@ -1,4 +1,5 @@
#include "scoped.hpp"
+#include "compat/run-in-thread.hpp"
#include <QApplication>
#include <QThread>
@@ -47,8 +48,14 @@ static bool is_tracker_teardown()
opts::~opts()
{
- if (!is_tracker_teardown())
+ // XXX TODO debug crash with ps3eye dialog in pt tracker -sh 20211019
+ if (!is_tracker_teardown() && raii)
+#if 1
+ run_in_thread_sync(qApp->thread(), [this]{ b->reload(); });
+#else
+ assert(b);
b->reload();
+#endif
#if 0
else
qDebug() << "in teardown, not reloading" << b->name();
diff --git a/options/scoped.hpp b/options/scoped.hpp
index dd7dbacf..81e6bd19 100644
--- a/options/scoped.hpp
+++ b/options/scoped.hpp
@@ -27,9 +27,13 @@ struct OTR_OPTIONS_EXPORT opts
opts& operator=(const opts&) = delete;
opts(const opts&) = delete;
+ void set_raii_dtor_state(bool x) { raii = x; }
+
protected:
explicit opts(const QString& name);
~opts();
+private:
+ bool raii = true;
};
}
diff --git a/options/slider.hpp b/options/slider.hpp
index 5d21bf0f..1e721ae0 100644
--- a/options/slider.hpp
+++ b/options/slider.hpp
@@ -8,7 +8,6 @@
#pragma once
#include "export.hpp"
-#include "compat/macros.hpp"
#include <type_traits>
diff --git a/options/tie.cpp b/options/tie.cpp
index 43e6c596..adf26b53 100644
--- a/options/tie.cpp
+++ b/options/tie.cpp
@@ -8,7 +8,7 @@
#include "tie.hpp"
#include "compat/run-in-thread.hpp"
-#include "compat/macros.hpp"
+#include "compat/macros.h"
#include "value-traits.hpp"
@@ -28,8 +28,12 @@ void tie_setting(value<QString>& v, QComboBox* cb)
{
cb->setCurrentText(v);
v = cb->currentText();
- value_::connect(cb, SIGNAL(currentTextChanged(QString)), &v, SLOT(setValue(const QString&)), v.DIRECT_CONNTYPE);
- value_::connect(&v, SIGNAL(valueChanged(const QString&)), cb, SLOT(setCurrentText(const QString&)), v.SAFE_CONNTYPE);
+ auto set_current_text = [cb, &v](const QString& str) {
+ cb->setCurrentText(str);
+ v = cb->currentText();
+ };
+ value_::connect(cb, &QComboBox::currentTextChanged, &v, v.value_changed<QString>(), v.DIRECT_CONNTYPE);
+ value_::connect(&v, v.value_changed<QString>(), cb, set_current_text, v.SAFE_CONNTYPE);
}
void tie_setting(value<QVariant>& v, QComboBox* cb)
@@ -39,13 +43,10 @@ void tie_setting(value<QVariant>& v, QComboBox* cb)
int idx = -1;
for (int k = 0; k < sz; k++)
- {
- if (cb->itemData(k) == var)
- {
+ if (cb->itemData(k) == var) {
idx = k;
break;
}
- }
cb->setCurrentIndex(idx);
return idx;
};
@@ -58,16 +59,11 @@ void tie_setting(value<QVariant>& v, QComboBox* cb)
v = {};
value_::connect(cb, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
- &v, [cb, &v](int idx) {
- v = cb->itemData(idx);
- }, v.DIRECT_CONNTYPE);
+ &v, [cb, &v](int idx) { v = cb->itemData(idx); },
+ v.DIRECT_CONNTYPE);
value_::connect(&v, value_::value_changed<QVariant>(),
- cb,
- [cb, set_idx](const QVariant& var) {
- run_in_thread_sync(cb, [&] {
- set_idx(var);
- });
- }, v.DIRECT_CONNTYPE);
+ cb, [set_idx](const QVariant& var) { set_idx(var); },
+ v.SAFE_CONNTYPE);
}
void tie_setting(value<bool>& v, QRadioButton* cb)
@@ -128,10 +124,7 @@ void tie_setting(value<slider_value>& v, QSlider* w)
v = v().update_from_slider(w->value(), q_min, q_max);
}
- value_::connect(w,
- &QSlider::valueChanged,
- &v,
- [=, &v](int pos)
+ value_::connect(w, &QSlider::valueChanged, &v, [=, &v](int pos)
{
run_in_thread_sync(w, [&]()
{
diff --git a/options/tie.hpp b/options/tie.hpp
index 2ac27d64..194a3a5d 100644
--- a/options/tie.hpp
+++ b/options/tie.hpp
@@ -38,16 +38,12 @@ std::enable_if_t<std::is_enum_v<t>> tie_setting(value<t>& v, QComboBox* cb)
v = static_cast<t>(cb->currentData().toInt());
value_::connect(cb, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
- &v, [&v, cb](int idx) {
- run_in_thread_sync(cb, [&] {
- v = static_cast<t>(cb->itemData(idx).toInt());
- });
- }, v.DIRECT_CONNTYPE);
+ &v, [&v, cb](int idx) { v = static_cast<t>(cb->itemData(idx).toInt()); },
+ v.DIRECT_CONNTYPE);
value_::connect(&v, value_::value_changed<int>(),
- cb, [cb](int x) {
- run_in_thread_sync(cb, [=] { cb->setCurrentIndex(cb->findData(x)); });
- }, v.DIRECT_CONNTYPE);
+ cb, [cb](int x) { cb->setCurrentIndex(cb->findData(x)); },
+ v.SAFE_CONNTYPE);
}
template<typename t, typename From, typename To>
@@ -57,23 +53,17 @@ void tie_setting(value<t>& v, QComboBox* cb, From&& fn_to_index, To&& fn_to_valu
v = fn_to_value(cb->currentIndex(), cb->currentData());
value_::connect(cb, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
- &v, [&v, cb, fn_to_value](int idx) {
- run_in_thread_sync(cb, [&] {
- v = fn_to_value(idx, cb->currentData());
- });
- }, v.DIRECT_CONNTYPE);
+ &v, [&v, cb, fn_to_value](int idx) { v = fn_to_value(idx, cb->currentData()); },
+ v.DIRECT_CONNTYPE);
value_::connect(&v, value_::value_changed<t>(),
- cb, [cb, fn_to_index](cv_qualified<t>& v) {
- run_in_thread_sync(cb, [&] {
- cb->setCurrentIndex(fn_to_index(v));
- });
- }, v.DIRECT_CONNTYPE);
+ cb, [cb, fn_to_index](detail::cv_qualified<t>& v) { cb->setCurrentIndex(fn_to_index(v)); },
+ v.SAFE_CONNTYPE);
}
template<typename t, typename F>
void tie_setting(value<t>& v, QLabel* lb, F&& fun)
{
- auto closure = [lb, fun](cv_qualified<t> v) { lb->setText(fun(v)); };
+ auto closure = [lb, fun](detail::cv_qualified<t> v) { lb->setText(fun(v)); };
closure(v());
value_::connect(&v, value_::value_changed<t>(),
diff --git a/options/value-traits.hpp b/options/value-traits.hpp
index aeb34cfa..145cd924 100644
--- a/options/value-traits.hpp
+++ b/options/value-traits.hpp
@@ -9,97 +9,110 @@
#include <QString>
namespace options::detail {
-
-template<typename t, typename Enable = void>
-struct value_traits;
+template<typename t>
+using cv_qualified =
+ std::conditional_t<std::is_fundamental_v<std::remove_cvref_t<t>>,
+ std::remove_cvref_t<t>,
+ std::add_lvalue_reference_t<std::add_const_t<std::remove_cvref_t<t>>>>;
template<typename t, typename u = t, typename Enable = void>
struct default_value_traits
{
using value_type = t;
using stored_type = u;
- using self = value_traits<value_type>;
- static value_type value_with_default(const value_type& val, const value_type&)
+ static inline
+ value_type value_with_default(cv_qualified<value_type> val, cv_qualified<value_type>)
{
return val;
}
- static value_type value_from_storage(const stored_type& x)
+ static inline
+ value_type value_from_storage(cv_qualified<stored_type> x)
{
return static_cast<value_type>(x);
}
- static stored_type storage_from_value(const value_type& val)
+ static inline
+ stored_type storage_from_value(cv_qualified<value_type> val)
{
return static_cast<stored_type>(val);
}
- static value_type value_from_qvariant(const QVariant& x)
+ static inline
+ value_type value_from_qvariant(const QVariant& x)
{
- return self::value_from_storage(self::storage_from_qvariant(x));
+ return value_from_storage(storage_from_qvariant(x));
}
- static QVariant qvariant_from_value(const value_type& val)
+ static inline
+ QVariant qvariant_from_value(cv_qualified<value_type> val)
{
- return self::qvariant_from_storage(self::storage_from_value(val));
+ return qvariant_from_storage(storage_from_value(val));
}
- static constexpr value_type pass_value(const value_type& x)
+ static constexpr inline
+ value_type pass_value(cv_qualified<value_type> x)
{
if constexpr(std::is_same_v<value_type, stored_type>)
return x;
else
- return self::value_from_storage(self::storage_from_value(x));
+ return value_from_storage(storage_from_value(x));
}
- static stored_type storage_from_qvariant(const QVariant& x)
+ static inline
+ stored_type storage_from_qvariant(const QVariant& x)
{
// XXX TODO
return x.value<stored_type>();
}
- static QVariant qvariant_from_storage(const stored_type& val)
+ static inline
+ QVariant qvariant_from_storage(cv_qualified<stored_type> val)
{
// XXX TODO
return QVariant::fromValue<stored_type>(val);
}
- static bool is_equal(const value_type& x, const value_type& y)
+ static inline
+ bool is_equal(cv_qualified<value_type> x, cv_qualified<value_type> y)
{
return x == y;
}
};
-template<typename t, typename Enable>
+template<typename t, typename Enable = void>
struct value_traits : default_value_traits<t> {};
template<>
-struct value_traits<double> : default_value_traits<double>
+inline
+bool default_value_traits<double>::is_equal(double x, double y)
{
- static bool is_equal(value_type x, value_type y) { return std::fabs(x - y) < 1e-6; }
-};
+ return std::fabs(x - y) < 1e-6;
+}
+
+template<> struct value_traits<float, double> : default_value_traits<float> {};
template<>
-struct value_traits<float> : default_value_traits<float>
+inline
+bool default_value_traits<float, double>::is_equal(float x, float y)
{
- static bool is_equal(value_type x, value_type y) { return std::fabs(x - y) < 1e-6f; }
-};
+ return std::fabs(x - y) < 1e-6f;
+}
template<>
-struct value_traits<slider_value> : default_value_traits<slider_value>
+inline
+slider_value default_value_traits<slider_value>::value_with_default(cv_qualified<slider_value> val, cv_qualified<slider_value> def)
{
- static slider_value value_with_default(const slider_value& val, const slider_value& def)
- {
- return { val.cur(), def.min(), def.max() };
- }
+ return { val.cur(), def.min(), def.max() };
+}
- static bool is_equal(const slider_value& x, const slider_value& y)
- {
- using tr = value_traits<double>;
- return tr::is_equal(x.cur(), y.cur());
- }
-};
+template<>
+inline
+bool default_value_traits<slider_value>::is_equal(cv_qualified<slider_value> x, cv_qualified<slider_value> y)
+{
+ return value_traits<double>::is_equal(x.cur(), y.cur());
+}
// Qt uses int a lot in slots so use it for all enums
template<typename t>
diff --git a/options/value.hpp b/options/value.hpp
index 92e2878f..9a7487b8 100644
--- a/options/value.hpp
+++ b/options/value.hpp
@@ -14,7 +14,6 @@
#include "slider.hpp"
#include "base-value.hpp"
#include "value-traits.hpp"
-#include "compat/macros.hpp"
#include <type_traits>
#include <utility>
@@ -38,8 +37,10 @@ namespace options {
template<typename t>
class value final : public value_
{
- static_assert(std::is_same_v<t, remove_cvref_t<t>>);
+ static_assert(std::is_same_v<t, std::remove_cvref_t<t>>);
+ mutable QMutex mtx;
const t def;
+ mutable t cached_value;
using traits = detail::value_traits<t>;
never_inline
@@ -92,10 +93,42 @@ public:
return traits::qvariant_from_value(def);
}
+ never_inline
+ void notify_() const override
+ {
+ auto x = get();
+ {
+ QMutexLocker l(&mtx);
+ cached_value = x;
+ }
+ maybe_trace("notify +");
+ emit valueChanged(traits::storage_from_value(x));
+ maybe_trace("notify -");
+ }
+
+ never_inline
void notify() const override
{
- if (!is_null())
- emit valueChanged(traits::storage_from_value(get()));
+ if (is_null())
+ return;
+
+ auto x = get();
+ {
+ QMutexLocker l(&mtx);
+ if (traits::is_equal(x, cached_value))
+ {
+ //maybe_trace("notify ~");
+ return;
+ }
+ else
+ {
+ cached_value = x;
+ l.unlock();
+ maybe_trace("notify +");
+ emit valueChanged(traits::storage_from_value(x));
+ maybe_trace("notify -");
+ }
+ }
}
auto& operator=(t&& datum) noexcept
@@ -104,6 +137,7 @@ public:
return *this;
store_variant(traits::qvariant_from_value(traits::pass_value(datum)));
+ maybe_trace("set-value");
return *this;
}
@@ -126,7 +160,7 @@ public:
static constexpr Qt::ConnectionType DIRECT_CONNTYPE = Qt::DirectConnection;
static constexpr Qt::ConnectionType SAFE_CONNTYPE = Qt::QueuedConnection;
- value(bundle b, const QString& name, t def) noexcept : value_(b, name), def(std::move(def))
+ value(bundle b, const QString& name, t def) noexcept : value_(b, name), def(std::move(def)), cached_value{get()}
{
}
diff --git a/pose-widget/lang/de_DE.ts b/pose-widget/lang/de_DE.ts
new file mode 100644
index 00000000..1552582e
--- /dev/null
+++ b/pose-widget/lang/de_DE.ts
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+</TS>
diff --git a/pose-widget/lang/zh_CN.ts b/pose-widget/lang/zh_CN.ts
index 6401616d..e5ca8aa9 100644
--- a/pose-widget/lang/zh_CN.ts
+++ b/pose-widget/lang/zh_CN.ts
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
</TS>
diff --git a/pose-widget/pose-widget.cpp b/pose-widget/pose-widget.cpp
index e662424c..ac3aa74a 100644
--- a/pose-widget/pose-widget.cpp
+++ b/pose-widget/pose-widget.cpp
@@ -47,6 +47,8 @@ pose_widget::pose_widget(QWidget* parent) : QWidget(parent)
p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
p.drawImage(QPointF(0,0), front);
p.end();
+
+ mirror.setFocusPolicy(Qt::NoFocus);
}
void pose_widget::present(double yaw, double pitch, double roll, double x, double y, double z)
@@ -63,19 +65,6 @@ void pose_widget::resizeEvent(QResizeEvent *event)
float w = event->size().width();
float h = event->size().height();
- // draw axes
- QImage background(QImage(w, h, QImage::Format_ARGB32));
- QPainter p(&background);
- p.setPen(QPen(Qt::gray, 1, Qt::SolidLine));
- p.drawLine(0.5*w, 0 , 0.5*w, h );
- p.drawLine( 0 , 0.5*h, w , 0.5*h);
-
- // set AutoFillBackground
- QPalette palette;
- palette.setBrush(this->backgroundRole(), QBrush(background));
- setPalette(palette);
- setAutoFillBackground(true);
-
// move the mirror checkbox in the lower right corner of the widget
mirror.setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
mirror.move(w - mirror.width(), h - mirror.height());
@@ -98,6 +87,17 @@ void pose_widget::paintEvent(QPaintEvent*)
p.setRenderHint(QPainter::Antialiasing, true);
#endif
+ {
+ p.fillRect(rect(), palette().brush(backgroundRole()));
+ // draw axes
+ p.save();
+ p.setPen(QPen(Qt::gray, 1, Qt::SolidLine));
+ int w = width(), h = height();
+ p.drawLine(w/2, 0, w/2, h);
+ p.drawLine( 0, h/2, w, h/2);
+ p.restore();
+ }
+
// check mirror state
if (mirror.checkState() == Qt::Checked) x = -x;
else { yaw = -yaw; roll = -roll; }
@@ -136,9 +136,4 @@ void pose_widget::paintEvent(QPaintEvent*)
p.drawImage(QPointF(0,0), forward == (alpha >= 0.0) ? shine : shadow);
}
-QSize pose_widget::sizeHint() const
-{
- return { 1 << 16, 1 << 16 };
-}
-
} // ns pose_widget_impl
diff --git a/pose-widget/pose-widget.hpp b/pose-widget/pose-widget.hpp
index 49044d93..9152e960 100644
--- a/pose-widget/pose-widget.hpp
+++ b/pose-widget/pose-widget.hpp
@@ -24,10 +24,9 @@ using namespace euler;
struct OTR_POSE_WIDGET_EXPORT pose_widget final : QWidget
{
public:
- pose_widget(QWidget *parent = nullptr);
+ explicit pose_widget(QWidget *parent = nullptr);
void present(double xAngle, double yAngle, double zAngle, double x, double y, double z);
- QSize sizeHint() const override;
- QCheckBox mirror{QCheckBox{"Mirror", this}};
+ QCheckBox mirror{"Mirror", this};
private:
void resizeEvent(QResizeEvent *event) override;
void paintEvent(QPaintEvent*) override;
diff --git a/presets/README.txt b/presets/README.txt
new file mode 100644
index 00000000..a89793f8
--- /dev/null
+++ b/presets/README.txt
@@ -0,0 +1,12 @@
+Here you can add presets as .ini files in order to get them copied for
+the end-user as profiles.
+
+Additionally, a preset called 'default.ini' will be sourced into each
+profile the user creates.
+
+This functionality exists for hardware vendors who create their custom
+distributions of opentrack.
+
+The user can delete the profiles in his 'Documents/opentrack-2.3'
+directory. The presets won't be copied again unless their modification
+time changes inside the opentrack's distribution.
diff --git a/proto-flightgear/ftnoir_protocol_fg.cpp b/proto-flightgear/ftnoir_protocol_fg.cpp
index d777d801..32fe3b5b 100644
--- a/proto-flightgear/ftnoir_protocol_fg.cpp
+++ b/proto-flightgear/ftnoir_protocol_fg.cpp
@@ -26,13 +26,13 @@ void flightgear::pose(const double* headpose, const double*) {
}
module_status flightgear::initialize()
-{
+{
if (outSocket.bind(QHostAddress::Any, 0, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint))
return status_ok();
else
return error(tr("Can't bind to [%1.%2.%3.%4]:%5")
- .arg(s.ip1).arg(s.ip2).arg(s.ip3).arg(s.ip4)
- .arg(s.port));
+ .arg(*s.ip1).arg(*s.ip2).arg(*s.ip3).arg(*s.ip4)
+ .arg(*s.port));
}
OPENTRACK_DECLARE_PROTOCOL(flightgear, FGControls, flightgearDll)
diff --git a/proto-flightgear/lang/de_DE.ts b/proto-flightgear/lang/de_DE.ts
new file mode 100644
index 00000000..436087bc
--- /dev/null
+++ b/proto-flightgear/lang/de_DE.ts
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>UICFGControls</name>
+ <message>
+ <source>FlightGear protocol settings</source>
+ <translation>FlightGear-Protokolleinstellungen</translation>
+ </message>
+ <message>
+ <source>IP address</source>
+ <translation>IP-Adresse</translation>
+ </message>
+ <message>
+ <source>Port</source>
+ <translation>Port</translation>
+ </message>
+</context>
+<context>
+ <name>flightgear</name>
+ <message>
+ <source>Can&apos;t bind to [%1.%2.%3.%4]:%5</source>
+ <translation>Konnte nicht an [%1.%2.%3.%4]:%5 binden</translation>
+ </message>
+ <message>
+ <source>FlightGear</source>
+ <translation>FlightGear</translation>
+ </message>
+</context>
+<context>
+ <name>flightgearDll</name>
+ <message>
+ <source>FlightGear</source>
+ <translation>FlightGear</translation>
+ </message>
+</context>
+</TS>
diff --git a/proto-flightgear/lang/zh_CN.ts b/proto-flightgear/lang/zh_CN.ts
index dc093c57..ba70f8a0 100644
--- a/proto-flightgear/lang/zh_CN.ts
+++ b/proto-flightgear/lang/zh_CN.ts
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
<context>
<name>UICFGControls</name>
<message>
diff --git a/proto-fsuipc/lang/zh_CN.ts b/proto-fsuipc/lang/zh_CN.ts
index 031e1916..8df82996 100644
--- a/proto-fsuipc/lang/zh_CN.ts
+++ b/proto-fsuipc/lang/zh_CN.ts
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
<context>
<name>FSUIPCControls</name>
<message>
diff --git a/proto-ft/ftnoir_ftcontrols.ui b/proto-ft/ftnoir_ftcontrols.ui
index 0d142d09..8edf5a9d 100644
--- a/proto-ft/ftnoir_ftcontrols.ui
+++ b/proto-ft/ftnoir_ftcontrols.ui
@@ -12,22 +12,10 @@
<rect>
<x>0</x>
<y>0</y>
- <width>508</width>
- <height>232</height>
+ <width>533</width>
+ <height>326</height>
</rect>
</property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
<property name="windowTitle">
<string>freetrack protocol settings</string>
</property>
@@ -42,6 +30,13 @@
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout_2">
+ <item row="5" column="0">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox_3">
<property name="sizePolicy">
@@ -95,64 +90,138 @@
</layout>
</widget>
</item>
- <item row="1" column="0">
+ <item row="1" column="0" rowspan="2">
<widget class="QGroupBox" name="groupBox_4">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
<property name="title">
- <string>Repair NPClient location</string>
+ <string>Library location</string>
</property>
<property name="alignment">
<set>Qt::AlignJustify|Qt::AlignTop</set>
</property>
- <property name="flat">
- <bool>false</bool>
- </property>
- <layout class="QHBoxLayout" name="horizontalLayout_2">
- <property name="spacing">
- <number>9</number>
- </property>
- <item>
- <widget class="QPushButton" name="bntLocateNPClient">
- <property name="text">
- <string>Locate DLL</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QLabel" name="label_10">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Replace the registry entry if you want to use other software with the NPClient protocol and it doesn't work automatically.
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QWidget" name="widget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="bntLocateNPClient">
+ <property name="text">
+ <string>Locate DLL</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_10">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Replace the registry entry if you want to use other software with the NPClient protocol and it doesn't work automatically.
Starting tracking will again overwrite the DLL locations.</string>
- </property>
- <property name="wordWrap">
- <bool>true</bool>
- </property>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QWidget" name="widget2" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QCheckBox" name="enable_custom_location">
+ <property name="toolTip">
+ <string>Useful for titles like Elite: Dangerous that require the library to reside in a specified location. Use this to avoid relocating your opentrack installation.</string>
+ </property>
+ <property name="text">
+ <string>Custom location</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="custom_location">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="set_custom_location">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Browse...</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QWidget" name="widget_2" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_4">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QCheckBox" name="ephemeral_registry_entry">
+ <property name="toolTip">
+ <string>This is useful when you're only evaluating opentrack, and haven't yet decided to use it all the time.</string>
+ </property>
+ <property name="text">
+ <string>Clear location when tracking is stopped</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
</widget>
</item>
</layout>
</widget>
</item>
- <item row="2" column="0">
- <widget class="QDialogButtonBox" name="buttonBox">
- <property name="standardButtons">
- <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
- </property>
- </widget>
- </item>
</layout>
</widget>
+ <tabstops>
+ <tabstop>cbxSelectInterface</tabstop>
+ <tabstop>bntLocateNPClient</tabstop>
+ <tabstop>enable_custom_location</tabstop>
+ <tabstop>custom_location</tabstop>
+ <tabstop>set_custom_location</tabstop>
+ <tabstop>ephemeral_registry_entry</tabstop>
+ </tabstops>
<resources>
<include location="ft-protocol.qrc"/>
<include location="ft-protocol.qrc"/>
diff --git a/proto-ft/ftnoir_protocol_ft.cpp b/proto-ft/ftnoir_protocol_ft.cpp
index c6259593..013dc363 100644
--- a/proto-ft/ftnoir_protocol_ft.cpp
+++ b/proto-ft/ftnoir_protocol_ft.cpp
@@ -6,10 +6,15 @@
* copyright notice and this permission notice appear in all copies.
*/
+#ifdef __CLION_IDE__
+#define _CRT_USE_BUILTIN_OFFSETOF
+#endif
+
#include "compat/library-path.hpp"
#include "ftnoir_protocol_ft.h"
#include "csv/csv.h"
+#include <QDir>
#include <cstddef>
#include <cmath>
@@ -17,7 +22,23 @@
freetrack::~freetrack()
{
- dummyTrackIR.close();
+ //dummyTrackIR.kill(); dummyTrackIR.waitForFinished();
+ if (s.ephemeral_library_location)
+ {
+ QSettings settings_ft("Freetrack", "FreetrackClient");
+ QSettings settings_npclient("NaturalPoint", "NATURALPOINT\\NPClient Location");
+
+ settings_ft.setValue("Path", "");
+ settings_npclient.setValue("Path", "");
+
+ if (dummyTrackIR.state() == dummyTrackIR.Running)
+ {
+ dummyTrackIR.kill();
+ dummyTrackIR.waitForFinished(100);
+ }
+ }
+ else
+ dummyTrackIR.close();
}
static_assert(sizeof(LONG) == sizeof(std::int32_t));
@@ -95,7 +116,7 @@ void freetrack::pose(const double* headpose, const double* raw)
t.ints[0] = 0; t.ints[1] = 0;
- (void)CSV::getGameData(id, t.table, gamename);
+ (void)getGameData(id, t.table, gamename);
{
// FTHeap pMemData happens to be aligned on a page boundary by virtue of
@@ -133,7 +154,7 @@ void freetrack::start_dummy() {
dummyTrackIR.start();
}
-void freetrack::set_protocols(bool ft, bool npclient)
+module_status freetrack::set_protocols()
{
static const QString program_dir = OPENTRACK_BASE_PATH + OPENTRACK_LIBRARY_PATH;
@@ -141,15 +162,46 @@ void freetrack::set_protocols(bool ft, bool npclient)
QSettings settings_ft("Freetrack", "FreetrackClient");
QSettings settings_npclient("NaturalPoint", "NATURALPOINT\\NPClient Location");
- if (ft)
- settings_ft.setValue("Path", program_dir);
- else
- settings_ft.setValue("Path", "");
+ QString location = *s.custom_location_pathname;
- if (npclient)
- settings_npclient.setValue("Path", program_dir);
+ bool use_freetrack = true, use_npclient = true;
+ switch (*s.used_interface)
+ {
+ case settings::enable_npclient: use_freetrack = false; break;
+ case settings::enable_freetrack: use_npclient = false; break;
+ case settings::enable_both: use_freetrack = true; use_npclient = true; break;
+ default:
+ return error(tr("proto/freetrack: wrong interface selection '%1'")
+ .arg(*s.used_interface));
+ }
+
+ if (!s.use_custom_location || s.custom_location_pathname->isEmpty() || !QDir{s.custom_location_pathname}.exists())
+ location = program_dir;
else
- settings_npclient.setValue("Path", "");
+ {
+ bool copy = true;
+
+ if (use_npclient && !QFile{location + "/NPClient.dll"}.exists())
+ copy &= QFile::copy(program_dir + "/NPClient.dll", location + "/NPClient.dll");
+ if (use_npclient && !QFile{location + "/NPClient64.dll"}.exists())
+ copy &= QFile::copy(program_dir + "/NPClient64.dll", location + "/NPClient64.dll");
+ if (use_freetrack && !QFile{location + "/freetrackclient.dll"}.exists())
+ copy &= QFile::copy(program_dir + "/freetrackclient.dll", location + "/freetrackclient.dll");
+ if (use_freetrack && !QFile{location + "/freetrackclient64.dll"}.exists())
+ copy &= QFile::copy(program_dir + "/freetrackclient64.dll", location + "/freetrackclient64.dll");
+
+ if (!copy)
+ return {tr("Can't copy library to selected custom location '%1'").arg(s.custom_location_pathname)};
+ }
+
+ location.replace('\\', '/');
+ if (!location.endsWith('/'))
+ location += '/';
+
+ settings_ft.setValue("Path", use_freetrack ? location : "");
+ settings_npclient.setValue("Path", use_npclient ? location : "");
+
+ return {};
}
module_status freetrack::initialize()
@@ -157,24 +209,8 @@ module_status freetrack::initialize()
if (!shm.success())
return error(tr("Can't load freetrack memory mapping"));
- bool use_ft = false, use_npclient = false;
-
- switch (s.intUsedInterface) {
- case 0:
- use_ft = true;
- use_npclient = true;
- break;
- case 1:
- use_ft = true;
- break;
- case 2:
- use_npclient = true;
- break;
- default:
- break;
- }
-
- set_protocols(use_ft, use_npclient);
+ if (auto ret = set_protocols(); !ret.is_ok())
+ return ret;
pMemData->data.DataID = 1;
pMemData->data.CamWidth = 100;
@@ -195,7 +231,7 @@ module_status freetrack::initialize()
store(pMemData->table_ints[k], 0);
// more games need the dummy executable than previously thought
- if (use_npclient)
+ if (s.used_interface != settings::enable_freetrack)
start_dummy();
return status_ok();
diff --git a/proto-ft/ftnoir_protocol_ft.h b/proto-ft/ftnoir_protocol_ft.h
index a59796dd..a07747d9 100644
--- a/proto-ft/ftnoir_protocol_ft.h
+++ b/proto-ft/ftnoir_protocol_ft.h
@@ -27,11 +27,12 @@
using namespace options;
struct settings : opts {
- value<int> intUsedInterface;
- settings() :
- opts("proto-freetrack"),
- intUsedInterface(b, "used-interfaces", 0)
- {}
+ enum enable_status : int { enable_both, enable_freetrack, enable_npclient, };
+ value<int> used_interface{b, "used-interfaces", (int)enable_both};
+ value<bool> ephemeral_library_location{b, "ephemeral-library-location", false};
+ value<bool> use_custom_location{b, "use-custom-location", false};
+ value<QString> custom_location_pathname{b, "custom-library-location", {}};
+ settings() : opts("proto-freetrack") {}
};
class freetrack : TR, public IProtocol
@@ -58,7 +59,9 @@ private:
void start_dummy();
public:
- static void set_protocols(bool ft, bool npclient);
+ enum class status { starting, stopping };
+ [[nodiscard]] module_status set_protocols();
+ void clear_protocols();
};
class FTControls: public IProtocolDialog
@@ -75,6 +78,7 @@ private slots:
void selectDLL();
void doOK();
void doCancel();
+ void set_custom_location();
};
class freetrackDll : public Metadata
diff --git a/proto-ft/ftnoir_protocol_ft_dialog.cpp b/proto-ft/ftnoir_protocol_ft_dialog.cpp
index 58077b87..90ff059a 100644
--- a/proto-ft/ftnoir_protocol_ft_dialog.cpp
+++ b/proto-ft/ftnoir_protocol_ft_dialog.cpp
@@ -22,7 +22,12 @@ FTControls::FTControls()
connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel()));
connect(ui.bntLocateNPClient, SIGNAL(clicked()), this, SLOT(selectDLL()));
- tie_setting(s.intUsedInterface, ui.cbxSelectInterface);
+ tie_setting(s.used_interface, ui.cbxSelectInterface);
+ tie_setting(s.ephemeral_library_location, ui.ephemeral_registry_entry);
+ tie_setting(s.custom_location_pathname, ui.custom_location);
+ tie_setting(s.use_custom_location, ui.enable_custom_location);
+
+ connect(ui.set_custom_location, &QAbstractButton::clicked, this, &FTControls::set_custom_location);
}
void FTControls::doOK()
@@ -50,4 +55,16 @@ void FTControls::selectDLL()
node.setValue("Path", dllname.dir().path());
}
}
-
+void FTControls::set_custom_location()
+{
+ static const auto program_directory = OPENTRACK_BASE_PATH + OPENTRACK_LIBRARY_PATH;
+ auto previous_location = *s.custom_location_pathname;
+ if (!s.use_custom_location || previous_location.isEmpty() || !QDir{previous_location}.exists())
+ previous_location = program_directory;
+ auto dir = QFileDialog::getExistingDirectory(this, tr("Select library location"), previous_location);
+ if (dir.isEmpty() || !QDir{dir}.exists())
+ dir = QString{};
+ else
+ ui.enable_custom_location->setEnabled(true);
+ ui.custom_location->setText(dir);
+}
diff --git a/proto-ft/lang/de_DE.ts b/proto-ft/lang/de_DE.ts
new file mode 100644
index 00000000..a4291d41
--- /dev/null
+++ b/proto-ft/lang/de_DE.ts
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>FTControls</name>
+ <message>
+ <source>Select the desired NPClient DLL</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Dll file (*.dll);;All Files (*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Select library location</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>UICFTControls</name>
+ <message>
+ <source>freetrack protocol settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Select interface</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Enable both</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Use freetrack, disable TrackIR</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Use TrackIR, disable freetrack</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Disable one of the protocols if game is confused by presence of both at the same time.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Library location</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Locate DLL</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Replace the registry entry if you want to use other software with the NPClient protocol and it doesn&apos;t work automatically.
+
+Starting tracking will again overwrite the DLL locations.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Useful for titles like Elite: Dangerous that require the library to reside in a specified location. Use this to avoid relocating your opentrack installation.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Custom location</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>This is useful when you&apos;re only evaluating opentrack, and haven&apos;t yet decided to use it all the time.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Clear location when tracking is stopped</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>freetrack</name>
+ <message>
+ <source>Unknown game</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>proto/freetrack: wrong interface selection &apos;%1&apos;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Can&apos;t copy library to selected custom location &apos;%1&apos;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Can&apos;t load freetrack memory mapping</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>freetrackDll</name>
+ <message>
+ <source>freetrack 2.0 Enhanced</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/proto-ft/lang/nl_NL.ts b/proto-ft/lang/nl_NL.ts
index ec4f58da..c8603973 100644
--- a/proto-ft/lang/nl_NL.ts
+++ b/proto-ft/lang/nl_NL.ts
@@ -11,6 +11,10 @@
<source>Dll file (*.dll);;All Files (*)</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Select library location</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>UICFTControls</name>
@@ -27,10 +31,6 @@
<translation type="unfinished"></translation>
</message>
<message>
- <source>Repair NPClient location</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
<source>Locate DLL</source>
<translation type="unfinished"></translation>
</message>
@@ -52,6 +52,30 @@ Starting tracking will again overwrite the DLL locations.</source>
<source>Use TrackIR, disable freetrack</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Library location</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Custom location</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>This is useful when you&apos;re only evaluating opentrack, and haven&apos;t yet decided to use it all the time.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Clear location when tracking is stopped</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Useful for titles like Elite: Dangerous that require the library to reside in a specified location. Use this to avoid relocating your opentrack installation.</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>freetrack</name>
@@ -63,6 +87,14 @@ Starting tracking will again overwrite the DLL locations.</source>
<source>Can&apos;t load freetrack memory mapping</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Can&apos;t copy library to selected custom location &apos;%1&apos;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>proto/freetrack: wrong interface selection &apos;%1&apos;</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>freetrackDll</name>
diff --git a/proto-ft/lang/ru_RU.ts b/proto-ft/lang/ru_RU.ts
index c793ae6b..a6433fd5 100644
--- a/proto-ft/lang/ru_RU.ts
+++ b/proto-ft/lang/ru_RU.ts
@@ -11,6 +11,10 @@
<source>Dll file (*.dll);;All Files (*)</source>
<translation></translation>
</message>
+ <message>
+ <source>Select library location</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>UICFTControls</name>
@@ -27,10 +31,6 @@
<translation>Отключите один из протоколов в случае, если при включении обоих интерфейсов игра не корректно определяет их.</translation>
</message>
<message>
- <source>Repair NPClient location</source>
- <translation>Решение проблем с расположением NPClient&apos;а </translation>
- </message>
- <message>
<source>Locate DLL</source>
<translation>Укажите DLL</translation>
</message>
@@ -54,6 +54,30 @@ Starting tracking will again overwrite the DLL locations.</source>
<source>Use TrackIR, disable freetrack</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Library location</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Custom location</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>This is useful when you&apos;re only evaluating opentrack, and haven&apos;t yet decided to use it all the time.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Clear location when tracking is stopped</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Useful for titles like Elite: Dangerous that require the library to reside in a specified location. Use this to avoid relocating your opentrack installation.</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>freetrack</name>
@@ -65,6 +89,14 @@ Starting tracking will again overwrite the DLL locations.</source>
<source>Can&apos;t load freetrack memory mapping</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Can&apos;t copy library to selected custom location &apos;%1&apos;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>proto/freetrack: wrong interface selection &apos;%1&apos;</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>freetrackDll</name>
diff --git a/proto-ft/lang/stub.ts b/proto-ft/lang/stub.ts
index 635844a5..ac88d895 100644
--- a/proto-ft/lang/stub.ts
+++ b/proto-ft/lang/stub.ts
@@ -11,6 +11,10 @@
<source>Dll file (*.dll);;All Files (*)</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Select library location</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>UICFTControls</name>
@@ -27,10 +31,6 @@
<translation type="unfinished"></translation>
</message>
<message>
- <source>Repair NPClient location</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
<source>Locate DLL</source>
<translation type="unfinished"></translation>
</message>
@@ -52,6 +52,30 @@ Starting tracking will again overwrite the DLL locations.</source>
<source>Use TrackIR, disable freetrack</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Library location</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Custom location</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>This is useful when you&apos;re only evaluating opentrack, and haven&apos;t yet decided to use it all the time.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Clear location when tracking is stopped</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Useful for titles like Elite: Dangerous that require the library to reside in a specified location. Use this to avoid relocating your opentrack installation.</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>freetrack</name>
@@ -63,6 +87,14 @@ Starting tracking will again overwrite the DLL locations.</source>
<source>Can&apos;t load freetrack memory mapping</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Can&apos;t copy library to selected custom location &apos;%1&apos;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>proto/freetrack: wrong interface selection &apos;%1&apos;</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>freetrackDll</name>
diff --git a/proto-ft/lang/zh_CN.ts b/proto-ft/lang/zh_CN.ts
index 442a1aa1..7706d27c 100644
--- a/proto-ft/lang/zh_CN.ts
+++ b/proto-ft/lang/zh_CN.ts
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
<context>
<name>FTControls</name>
<message>
@@ -11,6 +11,10 @@
<source>Dll file (*.dll);;All Files (*)</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Select library location</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>UICFTControls</name>
@@ -19,10 +23,6 @@
<translation type="unfinished"></translation>
</message>
<message>
- <source>Repair NPClient location</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
<source>Locate DLL</source>
<translation type="unfinished"></translation>
</message>
@@ -52,6 +52,30 @@ Starting tracking will again overwrite the DLL locations.</source>
<source>Use TrackIR, disable freetrack</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Library location</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Custom location</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>This is useful when you&apos;re only evaluating opentrack, and haven&apos;t yet decided to use it all the time.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Clear location when tracking is stopped</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Useful for titles like Elite: Dangerous that require the library to reside in a specified location. Use this to avoid relocating your opentrack installation.</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>freetrack</name>
@@ -63,6 +87,14 @@ Starting tracking will again overwrite the DLL locations.</source>
<source>Can&apos;t load freetrack memory mapping</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Can&apos;t copy library to selected custom location &apos;%1&apos;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>proto/freetrack: wrong interface selection &apos;%1&apos;</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>freetrackDll</name>
diff --git a/proto-iokit-foohid/foohidjoystick.cpp b/proto-iokit-foohid/foohidjoystick.cpp
index 898db2f4..b4964bb7 100644
--- a/proto-iokit-foohid/foohidjoystick.cpp
+++ b/proto-iokit-foohid/foohidjoystick.cpp
@@ -7,8 +7,6 @@
#include "foohidjoystick.h"
-#include "compat/macros.hpp"
-
const char FOOHID_SERVICE_NAME[] = "it_unbit_foohid";
enum class FooHIDMethod {
diff --git a/proto-iokit-foohid/iokitprotocol.cpp b/proto-iokit-foohid/iokitprotocol.cpp
index abc43a60..a364fd8a 100644
--- a/proto-iokit-foohid/iokitprotocol.cpp
+++ b/proto-iokit-foohid/iokitprotocol.cpp
@@ -10,8 +10,6 @@
#include "foohidjoystick.h"
#include "iokitprotocoldialog.h"
-#include "compat/macros.hpp"
-
#include <QDebug>
IOKitProtocol::IOKitProtocol()
@@ -49,12 +47,12 @@ static uint8_t valueToStick(FooHIDJoystick *stick, double min, double value, dou
void IOKitProtocol::pose(const double *headpose, const double*)
{
- const uint8_t x = valueToStick(joystick.get(), -75., headpose[0], +75.);
- const uint8_t y = valueToStick(joystick.get(), -75., headpose[1], +75.);
- const uint8_t z = valueToStick(joystick.get(), -75., headpose[2], +75.);
- const uint8_t rx = valueToStick(joystick.get(), -180., headpose[3], +180.);
- const uint8_t ry = valueToStick(joystick.get(), -180., headpose[4], +180.);
- const uint8_t rz = valueToStick(joystick.get(), -180., headpose[5], +180.);
+ const uint8_t x = valueToStick(&*joystick, -75., headpose[0], +75.);
+ const uint8_t y = valueToStick(&*joystick, -75., headpose[1], +75.);
+ const uint8_t z = valueToStick(&*joystick, -75., headpose[2], +75.);
+ const uint8_t rx = valueToStick(&*joystick, -180., headpose[3], +180.);
+ const uint8_t ry = valueToStick(&*joystick, -180., headpose[4], +180.);
+ const uint8_t rz = valueToStick(&*joystick, -180., headpose[5], +180.);
joystick->setValue({x, y, z, rx, ry, rz});
}
diff --git a/proto-iokit-foohid/lang/zh_CN.ts b/proto-iokit-foohid/lang/zh_CN.ts
index 6401616d..e5ca8aa9 100644
--- a/proto-iokit-foohid/lang/zh_CN.ts
+++ b/proto-iokit-foohid/lang/zh_CN.ts
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
</TS>
diff --git a/proto-libevdev/ftnoir_protocol_libevdev.cpp b/proto-libevdev/ftnoir_protocol_libevdev.cpp
index 5b07beff..fefcd9bb 100644
--- a/proto-libevdev/ftnoir_protocol_libevdev.cpp
+++ b/proto-libevdev/ftnoir_protocol_libevdev.cpp
@@ -55,6 +55,11 @@ evdev::evdev()
CHECK_LIBEVDEV(libevdev_enable_property(dev, INPUT_PROP_BUTTONPAD));
libevdev_set_name(dev, "opentrack headpose");
+
+ libevdev_set_id_bustype(dev, 3);
+ libevdev_set_id_vendor(dev, 4324);
+ libevdev_set_id_product(dev, 3798);
+ libevdev_set_id_version(dev, 123);
struct input_absinfo absinfo;
@@ -120,7 +125,7 @@ void evdev::pose(const double* headpose, const double*) {
for (int i = 0; i < 6; i++)
{
int value = (int)(headpose[i] * mid_input / max_value[i] + mid_input);
- int normalized = clamp(value, min_input, max_input);
+ int normalized = std::clamp(value, min_input, max_input);
(void) libevdev_uinput_write_event(uidev, EV_ABS, axes[i], normalized);
}
diff --git a/proto-libevdev/ftnoir_protocol_libevdev.h b/proto-libevdev/ftnoir_protocol_libevdev.h
index 0b0f2612..b81c3baf 100644
--- a/proto-libevdev/ftnoir_protocol_libevdev.h
+++ b/proto-libevdev/ftnoir_protocol_libevdev.h
@@ -7,7 +7,6 @@
#pragma once
#include "ui_ftnoir_libevdev_controls.h"
-#include "compat/macros.hpp"
#include "api/plugin-api.hpp"
#include <libevdev/libevdev.h>
#include <libevdev/libevdev-uinput.h>
diff --git a/proto-libevdev/lang/de_DE.ts b/proto-libevdev/lang/de_DE.ts
new file mode 100644
index 00000000..f66a1b39
--- /dev/null
+++ b/proto-libevdev/lang/de_DE.ts
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>UICLibevdevControls</name>
+ <message>
+ <source>libevdev options</source>
+ <translation>libevdev-Optionen</translation>
+ </message>
+ <message>
+ <source>Make sure rw for /dev/input/uinput!</source>
+ <translation>Stelle sicher, dass /dev/input/uinput rw-Berechtigungen hat!</translation>
+ </message>
+ <message>
+ <source>OK</source>
+ <translation>Okay</translation>
+ </message>
+ <message>
+ <source>Cancel</source>
+ <translation>Abbruch</translation>
+ </message>
+</context>
+<context>
+ <name>evdev</name>
+ <message>
+ <source>Virtual joystick for Linux</source>
+ <translation>Virtueller Joystick für Linux</translation>
+ </message>
+</context>
+<context>
+ <name>evdevDll</name>
+ <message>
+ <source>libevdev joystick receiver</source>
+ <translation>libevdev-Joystick-Empfänger</translation>
+ </message>
+</context>
+</TS>
diff --git a/proto-libevdev/lang/zh_CN.ts b/proto-libevdev/lang/zh_CN.ts
index ffafa2ec..f5e8694c 100644
--- a/proto-libevdev/lang/zh_CN.ts
+++ b/proto-libevdev/lang/zh_CN.ts
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
<context>
<name>UICLibevdevControls</name>
<message>
diff --git a/proto-mouse/ftnoir_protocol_mouse.cpp b/proto-mouse/ftnoir_protocol_mouse.cpp
index aed49f2b..e5b2192b 100644
--- a/proto-mouse/ftnoir_protocol_mouse.cpp
+++ b/proto-mouse/ftnoir_protocol_mouse.cpp
@@ -31,12 +31,12 @@ void mouse::pose(const double* headpose, const double*)
int mouse_x = 0, mouse_y = 0;
- if (axis_x == clamp(axis_x, Axis_MIN, Axis_MAX))
+ if (axis_x == std::clamp(axis_x, (int)Axis_MIN, (int)Axis_MAX))
mouse_x = get_value(headpose[axis_x] * invert[axis_x],
*s.sensitivity_x,
axis_x >= 3);
- if (axis_y == clamp(axis_y, Axis_MIN, Axis_MAX))
+ if (axis_y == std::clamp(axis_y, (int)Axis_MIN, (int)Axis_MAX))
mouse_y = get_value(headpose[axis_y] * invert[axis_y],
*s.sensitivity_y,
axis_y >= 3);
diff --git a/trackmouse/lang/nl_NL.ts b/proto-mouse/lang/de_DE.ts
index 92f790af..12d34698 100644
--- a/trackmouse/lang/nl_NL.ts
+++ b/proto-mouse/lang/de_DE.ts
@@ -1,73 +1,76 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1" language="nl_NL">
+<TS version="2.1" language="de_DE">
<context>
- <name>main_window</name>
+ <name>UI_mouse</name>
<message>
- <source>The Octopus is sad</source>
+ <source>Mouse protocol</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source> :: </source>
+ <source>X axis</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Check permissions for your .ini directory:
-
-%1&quot;%2
-
-Exiting now.</source>
+ <source>None</source>
<translation type="unfinished"></translation>
</message>
-</context>
-<context>
- <name>window</name>
<message>
- <source>trackmouse prototype</source>
+ <source>X</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Keyboard shortcuts</source>
+ <source>Y</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>start/stop tracking</source>
+ <source>Z</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Alt+F10</source>
+ <source>Yaw</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>center</source>
+ <source>Pitch</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Alt+F11</source>
+ <source>Roll</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Alt+F12</source>
+ <source>Sensitivity</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Sensitivity</source>
+ <source>Y axis</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>100%</source>
+ <source>Method</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Start</source>
+ <source>Direct input</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Stop</source>
+ <source>Legacy</source>
<translation type="unfinished"></translation>
</message>
+</context>
+<context>
+ <name>mouse</name>
+ <message>
+ <source>Mouse tracker</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>mouseDll</name>
<message>
- <source>freeze toggle</source>
+ <source>mouse emulation</source>
<translation type="unfinished"></translation>
</message>
</context>
diff --git a/proto-mouse/lang/zh_CN.ts b/proto-mouse/lang/zh_CN.ts
index 2a811df1..e69c4bfe 100644
--- a/proto-mouse/lang/zh_CN.ts
+++ b/proto-mouse/lang/zh_CN.ts
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
<context>
<name>UI_mouse</name>
<message>
@@ -9,47 +9,47 @@
</message>
<message>
<source>X axis</source>
- <translation type="unfinished"></translation>
+ <translation>X 轴</translation>
</message>
<message>
<source>None</source>
- <translation type="unfinished"></translation>
+ <translation>无</translation>
</message>
<message>
<source>X</source>
- <translation type="unfinished"></translation>
+ <translation></translation>
</message>
<message>
<source>Y</source>
- <translation type="unfinished"></translation>
+ <translation></translation>
</message>
<message>
<source>Z</source>
- <translation type="unfinished"></translation>
+ <translation></translation>
</message>
<message>
<source>Yaw</source>
- <translation type="unfinished"></translation>
+ <translation>航向</translation>
</message>
<message>
<source>Pitch</source>
- <translation type="unfinished"></translation>
+ <translation>俯仰</translation>
</message>
<message>
<source>Roll</source>
- <translation type="unfinished"></translation>
+ <translation>滚转</translation>
</message>
<message>
<source>Y axis</source>
- <translation type="unfinished"></translation>
+ <translation>Y 轴</translation>
</message>
<message>
<source>Sensitivity</source>
- <translation type="unfinished"></translation>
+ <translation>灵敏度</translation>
</message>
<message>
<source>Method</source>
- <translation type="unfinished"></translation>
+ <translation>方式</translation>
</message>
<message>
<source>Direct input</source>
diff --git a/proto-osc/CMakeLists.txt b/proto-osc/CMakeLists.txt
new file mode 100644
index 00000000..eeaf206c
--- /dev/null
+++ b/proto-osc/CMakeLists.txt
@@ -0,0 +1,15 @@
+set(SDK_OSCPACK "" CACHE PATH "oscpack build directory")
+if(SDK_OSCPACK)
+ if(WIN32)
+ if(NOT EXISTS "${SDK_OSCPACK}/include/.")
+ message(FATAL_ERROR "SDK_OSCPACK should have 'include' subdirectory (or symlink) to src dir")
+ endif()
+ link_directories("${SDK_OSCPACK}")
+ include_directories("${SDK_OSCPACK}/include" "${SDK_OSCPACK}/include/oscpack")
+ else()
+ link_directories("${SDK_OSCPACK}/lib" "${SDK_OSCPACK}/lib32")
+ include_directories("${SDK_OSCPACK}/include/oscpack")
+ endif()
+ link_libraries(oscpack)
+ otr_module(proto-osc)
+endif()
diff --git a/proto-osc/dialog.cpp b/proto-osc/dialog.cpp
new file mode 100644
index 00000000..54bf6885
--- /dev/null
+++ b/proto-osc/dialog.cpp
@@ -0,0 +1,38 @@
+#include "dialog.hpp"
+#include <QHostAddress>
+#include <QPalette>
+
+void osc_dialog::host_address_edited(const QString& str)
+{
+ bool bad = QHostAddress{str}.isNull();
+ auto pal = pal_;
+ for (auto role : { QPalette::Highlight, QPalette::Window })
+ if (bad)
+ pal.setColor(role, Qt::red);
+ ui.address->setPalette(pal);
+}
+
+osc_dialog::osc_dialog() :
+ pal_{palette()}
+{
+ ui.setupUi( this );
+
+ tie_setting(s.address, ui.address);
+ tie_setting(s.port, ui.port);
+ connect(ui.buttonBox, &QDialogButtonBox::accepted, this, &osc_dialog::doOK);
+ connect(ui.buttonBox, &QDialogButtonBox::rejected, this, &osc_dialog::doCancel);
+ connect(ui.address, &QLineEdit::textChanged, this, &osc_dialog::host_address_edited);
+ host_address_edited(ui.address->text());
+}
+
+void osc_dialog::save() { s.b->save(); }
+void osc_dialog::reload() { s.b->reload(); }
+
+void osc_dialog::doOK() { s.b->save(); close(); }
+void osc_dialog::doCancel() { close(); }
+
+void osc_dialog::register_protocol(IProtocol*) {}
+void osc_dialog::unregister_protocol() {}
+
+bool osc_dialog::embeddable() noexcept { return true; }
+void osc_dialog::set_buttons_visible(bool x) noexcept { ui.buttonBox->setVisible(x); }
diff --git a/proto-osc/dialog.hpp b/proto-osc/dialog.hpp
new file mode 100644
index 00000000..29682843
--- /dev/null
+++ b/proto-osc/dialog.hpp
@@ -0,0 +1,29 @@
+#pragma once
+
+#include "settings.hpp"
+#include "ui_dialog.h"
+#include "api/plugin-api.hpp"
+
+class osc_dialog: public IProtocolDialog
+{
+ Q_OBJECT
+
+public:
+ osc_dialog();
+ void register_protocol(IProtocol*) override;
+ void unregister_protocol() override;
+private:
+ void set_buttons_visible(bool x) noexcept override;
+ bool embeddable() noexcept override;
+ void save() override;
+ void reload() override;
+
+ Ui_OSC_Dialog ui;
+ osc_settings s;
+ const QPalette pal_;
+
+private slots:
+ void doOK();
+ void doCancel();
+ void host_address_edited(const QString& str);
+};
diff --git a/proto-osc/dialog.ui b/proto-osc/dialog.ui
new file mode 100644
index 00000000..5a078bbd
--- /dev/null
+++ b/proto-osc/dialog.ui
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>OSC_Dialog</class>
+ <widget class="QWidget" name="OSC_Dialog">
+ <property name="windowModality">
+ <enum>Qt::NonModal</enum>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>284</width>
+ <height>102</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>284</width>
+ <height>102</height>
+ </size>
+ </property>
+ <property name="windowTitle">
+ <string>OSC protocol settings</string>
+ </property>
+ <property name="windowIcon">
+ <iconset resource="osc-res.qrc">
+ <normaloff>:/images/osc-icon.png</normaloff>:/images/osc-icon.png</iconset>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QFormLayout" name="formLayout_2">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Destination address</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="address">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="maxLength">
+ <number>256</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Port</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QSpinBox" name="port">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximum">
+ <number>65535</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </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>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources>
+ <include location="../gui/opentrack-res.qrc"/>
+ <include location="osc-res.qrc"/>
+ </resources>
+ <connections/>
+ <designerdata>
+ <property name="gridDeltaX">
+ <number>5</number>
+ </property>
+ <property name="gridDeltaY">
+ <number>5</number>
+ </property>
+ <property name="gridSnapX">
+ <bool>true</bool>
+ </property>
+ <property name="gridSnapY">
+ <bool>true</bool>
+ </property>
+ <property name="gridVisible">
+ <bool>false</bool>
+ </property>
+ </designerdata>
+ <slots>
+ <slot>startEngineClicked()</slot>
+ <slot>stopEngineClicked()</slot>
+ <slot>cameraSettingsClicked()</slot>
+ </slots>
+</ui>
diff --git a/proto-osc/images/osc-icon.png b/proto-osc/images/osc-icon.png
new file mode 100644
index 00000000..3ef546e9
--- /dev/null
+++ b/proto-osc/images/osc-icon.png
Binary files differ
diff --git a/proto-osc/lang/de_DE.ts b/proto-osc/lang/de_DE.ts
new file mode 100644
index 00000000..d58e56a7
--- /dev/null
+++ b/proto-osc/lang/de_DE.ts
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>OSC_Dialog</name>
+ <message>
+ <source>OSC protocol settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Destination address</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Port</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>osc_metadata</name>
+ <message>
+ <source>Open Sound Control</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>osc_proto</name>
+ <message>
+ <source>Invalid destination address &apos;%1&apos;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error binding socket to INADDR_ANY</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Open Sound Control</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/proto-osc/lang/nl_NL.ts b/proto-osc/lang/nl_NL.ts
new file mode 100644
index 00000000..260b7adc
--- /dev/null
+++ b/proto-osc/lang/nl_NL.ts
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="nl_NL">
+<context>
+ <name>OSC_Dialog</name>
+ <message>
+ <source>OSC protocol settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Port</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Destination address</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>osc_metadata</name>
+ <message>
+ <source>Open Sound Control</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>osc_proto</name>
+ <message>
+ <source>Open Sound Control</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error binding socket to INADDR_ANY</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Invalid destination address &apos;%1&apos;</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/filter-kalman/lang/ru_RU.ts b/proto-osc/lang/ru_RU.ts
index 6fda97be..498d68d6 100644
--- a/filter-kalman/lang/ru_RU.ts
+++ b/proto-osc/lang/ru_RU.ts
@@ -2,36 +2,43 @@
<!DOCTYPE TS>
<TS version="2.1" language="ru_RU">
<context>
- <name>KalmanUICdialog_kalman</name>
+ <name>OSC_Dialog</name>
<message>
- <source>Kalman settings</source>
+ <source>OSC protocol settings</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Measurement noise</source>
+ <source>Port</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Rotation</source>
+ <source>Destination address</source>
<translation type="unfinished"></translation>
</message>
+</context>
+<context>
+ <name>osc_metadata</name>
<message>
- <source>Position</source>
+ <source>Open Sound Control</source>
<translation type="unfinished"></translation>
</message>
+</context>
+<context>
+ <name>osc_proto</name>
<message>
- <source>°</source>
+ <source>Open Sound Control</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>-</source>
+ <source>Error binding socket to INADDR_ANY</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1: %2</source>
<translation type="unfinished"></translation>
</message>
-</context>
-<context>
- <name>kalmanDll</name>
<message>
- <source>Kalman</source>
+ <source>Invalid destination address &apos;%1&apos;</source>
<translation type="unfinished"></translation>
</message>
</context>
diff --git a/filter-kalman/lang/stub.ts b/proto-osc/lang/stub.ts
index 29fae5c0..20828cbd 100644
--- a/filter-kalman/lang/stub.ts
+++ b/proto-osc/lang/stub.ts
@@ -2,36 +2,43 @@
<!DOCTYPE TS>
<TS version="2.1">
<context>
- <name>KalmanUICdialog_kalman</name>
+ <name>OSC_Dialog</name>
<message>
- <source>Kalman settings</source>
+ <source>OSC protocol settings</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Measurement noise</source>
+ <source>Port</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Rotation</source>
+ <source>Destination address</source>
<translation type="unfinished"></translation>
</message>
+</context>
+<context>
+ <name>osc_metadata</name>
<message>
- <source>Position</source>
+ <source>Open Sound Control</source>
<translation type="unfinished"></translation>
</message>
+</context>
+<context>
+ <name>osc_proto</name>
<message>
- <source>°</source>
+ <source>Open Sound Control</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>-</source>
+ <source>Error binding socket to INADDR_ANY</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1: %2</source>
<translation type="unfinished"></translation>
</message>
-</context>
-<context>
- <name>kalmanDll</name>
<message>
- <source>Kalman</source>
+ <source>Invalid destination address &apos;%1&apos;</source>
<translation type="unfinished"></translation>
</message>
</context>
diff --git a/filter-kalman/lang/zh_CN.ts b/proto-osc/lang/zh_CN.ts
index 29fae5c0..e0d49844 100644
--- a/filter-kalman/lang/zh_CN.ts
+++ b/proto-osc/lang/zh_CN.ts
@@ -1,37 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
<context>
- <name>KalmanUICdialog_kalman</name>
+ <name>OSC_Dialog</name>
<message>
- <source>Kalman settings</source>
+ <source>OSC protocol settings</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Measurement noise</source>
+ <source>Port</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Rotation</source>
+ <source>Destination address</source>
<translation type="unfinished"></translation>
</message>
+</context>
+<context>
+ <name>osc_metadata</name>
<message>
- <source>Position</source>
+ <source>Open Sound Control</source>
<translation type="unfinished"></translation>
</message>
+</context>
+<context>
+ <name>osc_proto</name>
<message>
- <source>°</source>
+ <source>Open Sound Control</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>-</source>
+ <source>Error binding socket to INADDR_ANY</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1: %2</source>
<translation type="unfinished"></translation>
</message>
-</context>
-<context>
- <name>kalmanDll</name>
<message>
- <source>Kalman</source>
+ <source>Invalid destination address &apos;%1&apos;</source>
<translation type="unfinished"></translation>
</message>
</context>
diff --git a/proto-osc/metadata.cpp b/proto-osc/metadata.cpp
new file mode 100644
index 00000000..9d69347d
--- /dev/null
+++ b/proto-osc/metadata.cpp
@@ -0,0 +1,8 @@
+#include "metadata.hpp"
+#include "proto.hpp"
+#include "dialog.hpp"
+
+QString osc_metadata::name() { return tr("Open Sound Control"); }
+QIcon osc_metadata::icon() { return QIcon(":/images/osc-icon.png"); }
+
+OPENTRACK_DECLARE_PROTOCOL(osc_proto, osc_dialog, osc_metadata)
diff --git a/proto-osc/metadata.hpp b/proto-osc/metadata.hpp
new file mode 100644
index 00000000..c0a947aa
--- /dev/null
+++ b/proto-osc/metadata.hpp
@@ -0,0 +1,10 @@
+#pragma once
+#include "api/plugin-api.hpp"
+
+class osc_metadata : public Metadata
+{
+ Q_OBJECT
+
+ QString name() override;
+ QIcon icon() override;
+};
diff --git a/proto-osc/osc-res.qrc b/proto-osc/osc-res.qrc
new file mode 100644
index 00000000..ade4c629
--- /dev/null
+++ b/proto-osc/osc-res.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>images/osc-icon.png</file>
+ </qresource>
+</RCC>
diff --git a/proto-osc/proto.cpp b/proto-osc/proto.cpp
new file mode 100644
index 00000000..2f90957c
--- /dev/null
+++ b/proto-osc/proto.cpp
@@ -0,0 +1,54 @@
+#include "proto.hpp"
+#include "ui_dialog.h"
+#include "api/plugin-api.hpp"
+#include <QQuaternion>
+#include <QHostAddress>
+#include "osc/OscOutboundPacketStream.h"
+
+osc_proto::osc_proto()
+{
+ auto reload_fn = [this] {
+ dest = QHostAddress{s.address };
+ port = (unsigned short)s.port;
+ };
+ connect(&*s.b, &bundle_::changed, this, reload_fn);
+ connect(&*s.b, &bundle_::reloading, this, reload_fn);
+}
+
+void osc_proto::pose(const double* data, const double*)
+{
+ if (dest.isNull())
+ return;
+
+ static constexpr unsigned buffer_size = 1024;
+ char buffer[buffer_size] = {};
+ osc::OutboundPacketStream p{buffer, buffer_size};
+ auto q = QQuaternion::fromEulerAngles((float)data[Pitch], (float)data[Yaw], (float)-data[Roll]).normalized();
+ p << osc::BeginMessage("/bridge/quat") << q.scalar() << q.x() << q.y() << q.z() << osc::EndMessage;
+ sock.writeDatagram(p.Data(), (int)p.Size(), dest, port);
+}
+
+module_status osc_proto::initialize()
+{
+ QString error;
+
+ dest = QHostAddress{s.address };
+ port = (unsigned short)s.port;
+
+ if (dest.isNull())
+ {
+ error = tr("Invalid destination address '%1'").arg(s.address);
+ goto fail;
+ }
+
+ if (!sock.bind())
+ {
+ error = tr("Error binding socket to INADDR_ANY");
+ goto fail;
+ }
+
+ return status_ok();
+
+fail:
+ return { tr("%1: %2").arg(error, sock.errorString()) };
+}
diff --git a/proto-osc/proto.hpp b/proto-osc/proto.hpp
new file mode 100644
index 00000000..53a5dee4
--- /dev/null
+++ b/proto-osc/proto.hpp
@@ -0,0 +1,29 @@
+#pragma once
+
+/* Copyright (c) 2023 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 "settings.hpp"
+#include "api/plugin-api.hpp"
+#include <QUdpSocket>
+#include <QHostAddress>
+
+class osc_proto : public QObject, public IProtocol
+{
+ Q_OBJECT
+
+ osc_settings s;
+ QUdpSocket sock;
+ QHostAddress dest;
+ unsigned short port = 0;
+
+public:
+ osc_proto();
+ module_status initialize() override;
+ void pose(const double *headpose, const double*) override;
+ QString game_name() override { return tr("Open Sound Control"); }
+};
diff --git a/proto-osc/settings.hpp b/proto-osc/settings.hpp
new file mode 100644
index 00000000..edeb45c6
--- /dev/null
+++ b/proto-osc/settings.hpp
@@ -0,0 +1,14 @@
+#pragma once
+#include <QString>
+#include "options/options.hpp"
+
+using namespace options;
+
+struct osc_settings : opts
+{
+ value<QString> address;
+ value<int> port;
+ osc_settings() : opts("proto-osc"), address{b, "address", "127.0.0.1"},
+ port(b, "port", 53101)
+ {}
+};
diff --git a/proto-simconnect/ftnoir_protocol_sc.cpp b/proto-simconnect/ftnoir_protocol_sc.cpp
index 226a427b..ca76e0ce 100644
--- a/proto-simconnect/ftnoir_protocol_sc.cpp
+++ b/proto-simconnect/ftnoir_protocol_sc.cpp
@@ -43,20 +43,15 @@ void simconnect::run()
qDebug() << "fsx: connect failed, retry in" << sleep_time << "seconds...";
else
{
- Timer tm;
-
- if (!SUCCEEDED(hr = simconnect_subscribe(handle, 0, "Frame")))
+ if (!SUCCEEDED(hr = simconnect_subscribe(handle, 0, "1sec")))
qDebug() << "fsx: can't subscribe to frame event:" << (void*)hr;
else
{
while (!isInterruptionRequested())
{
- constexpr int max_idle_seconds = 2;
-
- if (WaitForSingleObject(event, 0) == WAIT_OBJECT_0)
- tm.start();
+ constexpr int max_idle_ms = 2000;
- if ((int)tm.elapsed_seconds() > max_idle_seconds)
+ if (WaitForSingleObject(event, max_idle_ms) != WAIT_OBJECT_0)
{
qDebug() << "fsx: timeout reached, reconnecting";
break;
@@ -73,7 +68,9 @@ void simconnect::run()
}
}
+ QMutexLocker l(&mtx);
(void)simconnect_close(handle);
+ handle = nullptr;
}
for (unsigned k = 0; k < sleep_time * 25; k++)
@@ -91,8 +88,6 @@ void simconnect::run()
void simconnect::pose(const double* pose, const double*)
{
- QMutexLocker l(&mtx);
-
data[Pitch] = (float)-pose[Pitch];
data[Yaw] = (float)pose[Yaw];
data[Roll] = (float)pose[Roll];
@@ -101,6 +96,12 @@ void simconnect::pose(const double* pose, const double*)
data[TX] = (float)-pose[TX] * to_meters;
data[TY] = (float)pose[TY] * to_meters;
data[TZ] = (float)-pose[TZ] * to_meters;
+
+ QMutexLocker l(&mtx);
+ if (handle)
+ (void)simconnect_set6DOF(handle,
+ data[TX], data[TY], data[TZ],
+ data[Pitch], data[Roll], data[Yaw]);
}
module_status simconnect::initialize()
@@ -148,14 +149,6 @@ module_status simconnect::initialize()
return {};
}
-void simconnect::handler()
-{
- QMutexLocker l(&mtx);
- (void)simconnect_set6DOF(handle,
- data[TX], data[TY], data[TZ],
- data[Pitch], data[Roll], data[Yaw]);
-}
-
void simconnect::event_handler(SIMCONNECT_RECV* pData, DWORD, void* self_)
{
simconnect& self = *reinterpret_cast<simconnect*>(self_);
@@ -172,9 +165,6 @@ void simconnect::event_handler(SIMCONNECT_RECV* pData, DWORD, void* self_)
qDebug() << "fsx: got quit event";
self.reconnect = true;
break;
- case SIMCONNECT_RECV_ID_EVENT_FRAME:
- self.handler();
- break;
}
}
diff --git a/proto-simconnect/ftnoir_protocol_sc.h b/proto-simconnect/ftnoir_protocol_sc.h
index 28b9b1d8..df3a538d 100644
--- a/proto-simconnect/ftnoir_protocol_sc.h
+++ b/proto-simconnect/ftnoir_protocol_sc.h
@@ -47,8 +47,6 @@ public:
void run() override;
private:
- void handler();
-
enum {
SIMCONNECT_RECV_ID_EXCEPTION = 2,
SIMCONNECT_RECV_ID_QUIT = 3,
diff --git a/trackmouse/lang/ru_RU.ts b/proto-simconnect/lang/de_DE.ts
index dd9011c4..3d6a4157 100644
--- a/trackmouse/lang/ru_RU.ts
+++ b/proto-simconnect/lang/de_DE.ts
@@ -1,73 +1,68 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1" language="ru_RU">
+<TS version="2.1" language="de_DE">
<context>
- <name>main_window</name>
+ <name>UICSCControls</name>
<message>
- <source>The Octopus is sad</source>
+ <source>Protocol settings</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source> :: </source>
+ <source>FSX version</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Check permissions for your .ini directory:
-
-%1&quot;%2
-
-Exiting now.</source>
- <translation type="unfinished"></translation>
- </message>
-</context>
-<context>
- <name>window</name>
- <message>
- <source>trackmouse prototype</source>
+ <source>Prepar3d / SP2 XPACK</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Keyboard shortcuts</source>
+ <source>RTM</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>start/stop tracking</source>
+ <source>SP1</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Alt+F10</source>
+ <source>SP2 -- Acceleration</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>center</source>
+ <source>Steam FSX (older)</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Alt+F11</source>
+ <source>Steam FSX (newer)</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Alt+F12</source>
+ <source>You need to install SimConnect SDK for your FSX version. For the Steam version, choose SP1 in the above combo box.</source>
<translation type="unfinished"></translation>
</message>
+</context>
+<context>
+ <name>simconnect</name>
<message>
- <source>Sensitivity</source>
+ <source>dll load failed: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>100%</source>
+ <source>Install FSX/Prepar3D SimConnect SDK.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Start</source>
+ <source>can&apos;t import %1: %2</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Stop</source>
+ <source>FSX / Prepar3D</source>
<translation type="unfinished"></translation>
</message>
+</context>
+<context>
+ <name>simconnect_metadata</name>
<message>
- <source>freeze toggle</source>
+ <source>Microsoft FSX SimConnect</source>
<translation type="unfinished"></translation>
</message>
</context>
diff --git a/proto-simconnect/lang/zh_CN.ts b/proto-simconnect/lang/zh_CN.ts
index 50d0fa8a..86e487f2 100644
--- a/proto-simconnect/lang/zh_CN.ts
+++ b/proto-simconnect/lang/zh_CN.ts
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
<context>
<name>UICSCControls</name>
<message>
diff --git a/proto-udp/ftnoir_protocol_ftn.cpp b/proto-udp/ftnoir_protocol_ftn.cpp
index f04f4f4c..c67c39b8 100644
--- a/proto-udp/ftnoir_protocol_ftn.cpp
+++ b/proto-udp/ftnoir_protocol_ftn.cpp
@@ -16,7 +16,7 @@ udp::udp()
{
set_dest_address();
- QObject::connect(s.b.get(), &bundle_::changed,
+ QObject::connect(&*s.b, &bundle_::changed,
this, &udp::set_dest_address,
Qt::DirectConnection);
}
diff --git a/proto-udp/lang/de_DE.ts b/proto-udp/lang/de_DE.ts
new file mode 100644
index 00000000..442d8683
--- /dev/null
+++ b/proto-udp/lang/de_DE.ts
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>UICFTNControls</name>
+ <message>
+ <source>UDP protocol settings</source>
+ <translation>UDP-Protokolleinstellungen</translation>
+ </message>
+ <message>
+ <source>Remote IP address</source>
+ <translation>Entfernte IP-Adresse</translation>
+ </message>
+ <message>
+ <source>Port</source>
+ <translation>Port</translation>
+ </message>
+</context>
+<context>
+ <name>udp</name>
+ <message>
+ <source>Can&apos;t bind socket: %1</source>
+ <translation>Kann nicht an Socket binden: %1</translation>
+ </message>
+ <message>
+ <source>UDP over network</source>
+ <translation>UDP über Netzwerk</translation>
+ </message>
+</context>
+<context>
+ <name>udp_sender_dll</name>
+ <message>
+ <source>UDP over network</source>
+ <translation>UDP über Netzwerk</translation>
+ </message>
+</context>
+</TS>
diff --git a/proto-udp/lang/zh_CN.ts b/proto-udp/lang/zh_CN.ts
index ca1bc409..b2a750b8 100644
--- a/proto-udp/lang/zh_CN.ts
+++ b/proto-udp/lang/zh_CN.ts
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
<context>
<name>UICFTNControls</name>
<message>
diff --git a/proto-vjoystick/lang/de_DE.ts b/proto-vjoystick/lang/de_DE.ts
new file mode 100644
index 00000000..24f61c52
--- /dev/null
+++ b/proto-vjoystick/lang/de_DE.ts
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>vjoystick</name>
+ <message>
+ <source>vjoystick</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Go to the &lt;a href=&quot;https://github.com/jshafer817/vJoy&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;vJoy&lt;/span&gt;&lt;/a&gt; project site or &lt;a href=&quot;https://github.com/jshafer817/vJoy/releases/tag/v2.1.9.1&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;download directly&lt;/span&gt;&lt;/a&gt; for Windows 10 and 11.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>vjoystick won&apos;t work without the driver installed.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>driver/SDK version mismatch (dll 0x%1, driver 0x%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>BUG: handle leak.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Virtual joystick already in use.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Device missing. Add joystick #1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Unknown error.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Unknown error #%1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>vjoystick driver problem</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Download the driver</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Visit project site</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Driver problem.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Virtual joystick</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>vjoystick_metadata</name>
+ <message>
+ <source>Joystick emulation -- vjoystick</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/proto-vjoystick/lang/nl_NL.ts b/proto-vjoystick/lang/nl_NL.ts
index ff2eb9c9..056563f7 100644
--- a/proto-vjoystick/lang/nl_NL.ts
+++ b/proto-vjoystick/lang/nl_NL.ts
@@ -36,10 +36,6 @@
<translation type="unfinished"></translation>
</message>
<message>
- <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Go to the &lt;a href=&quot;http://vjoystick.sourceforge.net/site/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;vjoystick&lt;/span&gt;&lt;/a&gt; project site or &lt;a href=&quot;https://sourceforge.net/projects/vjoystick/files/latest/download&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;download directly&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
<source>BUG: handle leak.</source>
<translation type="unfinished"></translation>
</message>
@@ -59,6 +55,10 @@
<source>driver/SDK version mismatch (dll 0x%1, driver 0x%2)</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Go to the &lt;a href=&quot;https://github.com/jshafer817/vJoy&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;vJoy&lt;/span&gt;&lt;/a&gt; project site or &lt;a href=&quot;https://github.com/jshafer817/vJoy/releases/tag/v2.1.9.1&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;download directly&lt;/span&gt;&lt;/a&gt; for Windows 10 and 11.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>vjoystick_metadata</name>
diff --git a/proto-vjoystick/lang/ru_RU.ts b/proto-vjoystick/lang/ru_RU.ts
index 3d867d07..4c9aacd3 100644
--- a/proto-vjoystick/lang/ru_RU.ts
+++ b/proto-vjoystick/lang/ru_RU.ts
@@ -36,10 +36,6 @@
<translation type="unfinished"></translation>
</message>
<message>
- <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Go to the &lt;a href=&quot;http://vjoystick.sourceforge.net/site/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;vjoystick&lt;/span&gt;&lt;/a&gt; project site or &lt;a href=&quot;https://sourceforge.net/projects/vjoystick/files/latest/download&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;download directly&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
<source>BUG: handle leak.</source>
<translation type="unfinished"></translation>
</message>
@@ -59,6 +55,10 @@
<source>driver/SDK version mismatch (dll 0x%1, driver 0x%2)</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Go to the &lt;a href=&quot;https://github.com/jshafer817/vJoy&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;vJoy&lt;/span&gt;&lt;/a&gt; project site or &lt;a href=&quot;https://github.com/jshafer817/vJoy/releases/tag/v2.1.9.1&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;download directly&lt;/span&gt;&lt;/a&gt; for Windows 10 and 11.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>vjoystick_metadata</name>
diff --git a/proto-vjoystick/lang/stub.ts b/proto-vjoystick/lang/stub.ts
index 39a74043..8ec5c042 100644
--- a/proto-vjoystick/lang/stub.ts
+++ b/proto-vjoystick/lang/stub.ts
@@ -36,10 +36,6 @@
<translation type="unfinished"></translation>
</message>
<message>
- <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Go to the &lt;a href=&quot;http://vjoystick.sourceforge.net/site/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;vjoystick&lt;/span&gt;&lt;/a&gt; project site or &lt;a href=&quot;https://sourceforge.net/projects/vjoystick/files/latest/download&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;download directly&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
<source>BUG: handle leak.</source>
<translation type="unfinished"></translation>
</message>
@@ -59,6 +55,10 @@
<source>driver/SDK version mismatch (dll 0x%1, driver 0x%2)</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Go to the &lt;a href=&quot;https://github.com/jshafer817/vJoy&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;vJoy&lt;/span&gt;&lt;/a&gt; project site or &lt;a href=&quot;https://github.com/jshafer817/vJoy/releases/tag/v2.1.9.1&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;download directly&lt;/span&gt;&lt;/a&gt; for Windows 10 and 11.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>vjoystick_metadata</name>
diff --git a/proto-vjoystick/lang/zh_CN.ts b/proto-vjoystick/lang/zh_CN.ts
index 39a74043..95192e1d 100644
--- a/proto-vjoystick/lang/zh_CN.ts
+++ b/proto-vjoystick/lang/zh_CN.ts
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
<context>
<name>vjoystick</name>
<message>
@@ -36,10 +36,6 @@
<translation type="unfinished"></translation>
</message>
<message>
- <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Go to the &lt;a href=&quot;http://vjoystick.sourceforge.net/site/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;vjoystick&lt;/span&gt;&lt;/a&gt; project site or &lt;a href=&quot;https://sourceforge.net/projects/vjoystick/files/latest/download&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;download directly&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
<source>BUG: handle leak.</source>
<translation type="unfinished"></translation>
</message>
@@ -59,6 +55,10 @@
<source>driver/SDK version mismatch (dll 0x%1, driver 0x%2)</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Go to the &lt;a href=&quot;https://github.com/jshafer817/vJoy&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;vJoy&lt;/span&gt;&lt;/a&gt; project site or &lt;a href=&quot;https://github.com/jshafer817/vJoy/releases/tag/v2.1.9.1&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;download directly&lt;/span&gt;&lt;/a&gt; for Windows 10 and 11.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>vjoystick_metadata</name>
diff --git a/proto-vjoystick/vjoystick.cpp b/proto-vjoystick/vjoystick.cpp
index 2962a393..292e8259 100644
--- a/proto-vjoystick/vjoystick.cpp
+++ b/proto-vjoystick/vjoystick.cpp
@@ -88,7 +88,7 @@ int vjoystick::to_axis_value(unsigned axis_id, double val) const
const double min = axis_min[axis_id];
const double max = axis_max[axis_id];
- return (int)(clamp((val+minmax) * max / (2*minmax) - min, min, max));
+ return (int)(std::clamp((val+minmax) * max / (2*minmax) - min, min, max));
}
vjoystick::vjoystick() = default;
diff --git a/proto-vjoystick/vjoystick.h b/proto-vjoystick/vjoystick.h
index 39bc14e3..82ebd3e6 100644
--- a/proto-vjoystick/vjoystick.h
+++ b/proto-vjoystick/vjoystick.h
@@ -8,7 +8,6 @@
#pragma once
#include "ui_vjoystick.h"
#include "api/plugin-api.hpp"
-#include "compat/macros.hpp"
enum status
{
diff --git a/proto-vjoystick/vjoystick.ui b/proto-vjoystick/vjoystick.ui
index 5bfdb02a..8092898b 100644
--- a/proto-vjoystick/vjoystick.ui
+++ b/proto-vjoystick/vjoystick.ui
@@ -21,7 +21,7 @@
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
- <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Go to the &lt;a href=&quot;http://vjoystick.sourceforge.net/site/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;vjoystick&lt;/span&gt;&lt;/a&gt; project site or &lt;a href=&quot;https://sourceforge.net/projects/vjoystick/files/latest/download&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;download directly&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Go to the &lt;a href=&quot;https://github.com/jshafer817/vJoy&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;vJoy&lt;/span&gt;&lt;/a&gt; project site or &lt;a href=&quot;https://github.com/jshafer817/vJoy/releases/tag/v2.1.9.1&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;download directly&lt;/span&gt;&lt;/a&gt; for Windows 10 and 11.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
diff --git a/proto-wine/CMakeLists.txt b/proto-wine/CMakeLists.txt
index 0beb50e1..ff4932cc 100644
--- a/proto-wine/CMakeLists.txt
+++ b/proto-wine/CMakeLists.txt
@@ -17,10 +17,14 @@ if(NOT WIN32)
endif()
file(GLOB wine-deps "${CMAKE_CURRENT_SOURCE_DIR}/*.cxx")
#install(FILES ${wine-deps} DESTINATION "${opentrack-src}/proto-wine")
+ set(winegxx-multilib "-m32")
+ if (NOT OPENTRACK_WINE_ARCH STREQUAL "")
+ set(winegxx-multilib "${OPENTRACK_WINE_ARCH}")
+ endif()
add_custom_command(
OUTPUT opentrack-wrapper-wine.exe.so
DEPENDS ${wine-deps}
- COMMAND wineg++ -mconsole -g -DNOMINMAX -O2 -m32 -std=c++17 -fPIC -o
+ COMMAND wineg++ -mconsole -g -DNOMINMAX -O2 ${winegxx-multilib} -std=c++17 -fPIC -o
opentrack-wrapper-wine.exe -I "${CMAKE_SOURCE_DIR}" -I "${CMAKE_BINARY_DIR}"
${wine-deps} -Wall -Wextra -Wpedantic
${my-rt})
diff --git a/proto-wine/ftnoir_protocol_wine.cpp b/proto-wine/ftnoir_protocol_wine.cpp
index af53ff1f..66f7e0fb 100644
--- a/proto-wine/ftnoir_protocol_wine.cpp
+++ b/proto-wine/ftnoir_protocol_wine.cpp
@@ -1,8 +1,8 @@
#include "ftnoir_protocol_wine.h"
+#include <qprocess.h>
#ifndef OTR_WINE_NO_WRAPPER
# include "csv/csv.h"
#endif
-#include "compat/library-path.hpp"
#include <cstring>
#include <cmath>
@@ -10,6 +10,8 @@
#include <QString>
#include <QDebug>
+#include "proton.h"
+
wine::wine() = default;
wine::~wine()
@@ -44,11 +46,12 @@ void wine::pose(const double *headpose, const double*)
#ifndef OTR_WINE_NO_WRAPPER
if (shm->gameid != gameid)
{
+ //qDebug() << "proto/wine: looking up gameData";
QString gamename;
QMutexLocker foo(&game_name_mutex);
/* only EZCA for FSX requires dummy process, and FSX doesn't work on Linux */
/* memory-hacks DLL can't be loaded into a Linux process, either */
- CSV::getGameData(shm->gameid, shm->table, gamename);
+ getGameData(shm->gameid, shm->table, gamename);
gameid = shm->gameid2 = shm->gameid;
connected_game = gamename;
}
@@ -62,54 +65,136 @@ module_status wine::initialize()
#ifndef OTR_WINE_NO_WRAPPER
static const QString library_path(OPENTRACK_BASE_PATH + OPENTRACK_LIBRARY_PATH);
+ /////////////////////////
+ // determine wine path //
+ /////////////////////////
QString wine_path = "wine";
- auto env = QProcessEnvironment::systemEnvironment();
+ if (s.variant_wine) {
+ // NORMAL WINE
+
+ // resolve combo box
+ if (s.wine_select_path().toString() != "WINE") {
+ // if we are not supposed to use system wine then:
+ if (s.wine_select_path().toString() != "CUSTOM") {
+ // if we don't have a custom path then change the wine_path to the path corresponding to the selected version
+ wine_path = s.wine_select_path().toString();
+ }
+ else if (!s.wine_custom_path->isEmpty()) {
+ // if we do have a custom path and it is not empty then
+ wine_path = s.wine_custom_path;
+ }
+ }
+
+ // parse tilde if present
+ if (wine_path[0] == '~')
+ wine_path = qgetenv("HOME") + wine_path.mid(1);
+ }
+ else if (s.variant_proton)
+ {
+ // PROTON
+
+ wine_path = s.proton_path().toString() + "/bin/wine";
+ }
+ qDebug() << "proto/wine: wine_path:" << wine_path;
+
+
+ /////////////////////////////////////
+ // determine environment variables //
+ /////////////////////////////////////
+ QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
+
+ // if proton is used setup proton environment
if (s.variant_proton)
{
+ auto [proton_env, env_error_string, env_success] = make_steam_environ(s.proton_path().toString());
+ env = proton_env;
+
+ if (!env_success)
+ return error(env_error_string);
+ }
+
+ // determine wineprefix
+ if (s.variant_proton && s.variant_proton_steamplay) {
+ // wine prefix is dependend on steam
+
if (s.proton_appid == 0)
return error(tr("Must specify application id for Proton (Steam Play)"));
- std::tuple<QProcessEnvironment, QString, bool> make_steam_environ(const QString& proton_path, int appid);
- QString proton_path(const QString& proton_path);
-
- wine_path = proton_path(s.proton_path().toString());
- auto [proton_env, error_string, success] = make_steam_environ(s.proton_path().toString(), s.proton_appid);
- env = proton_env;
+ auto [prefix, error_string, success] = make_wineprefix(s.proton_appid);
+ qDebug() << "proto/wine: wineprefix:" << prefix;
+ env.insert("WINEPREFIX", prefix);
if (!success)
return error(error_string);
}
- else
- {
- QString wineprefix = "~/.wine";
- if (!s.wineprefix->isEmpty())
+ else {
+ // wine prefix was supplied via path
+
+ QString wineprefix = "";
+
+ // check if prefix was supplied via wine
+ if (s.variant_wine && !s.wineprefix->isEmpty())
wineprefix = s.wineprefix;
+
+ // check if prefix was supplied via proton
+ if (s.variant_proton_external && !s.protonprefix->isEmpty())
+ wineprefix = s.protonprefix;
+
+ // check if the user specified a prefix anywhere
+ if (wineprefix.isEmpty())
+ return error(tr("Prefix has not been defined!").arg(wineprefix));
+
+ // handle tilde
if (wineprefix[0] == '~')
wineprefix = qgetenv("HOME") + wineprefix.mid(1);
+ // return error if relative path is given
if (wineprefix[0] != '/')
return error(tr("Wine prefix must be an absolute path (given '%1')").arg(wineprefix));
+ qDebug() << "proto/wine: wineprefix:" << wineprefix;
+
env.insert("WINEPREFIX", wineprefix);
}
+ // ESYNC and FSYNC
if (s.esync)
env.insert("WINEESYNC", "1");
if (s.fsync)
env.insert("WINEFSYNC", "1");
+ // Headtracking Protocol
env.insert("OTR_WINE_PROTO", QString::number(s.protocol+1));
+
+ ////////////////////////////////
+ // launch the wrapper program //
+ ////////////////////////////////
+
wrapper.setProcessEnvironment(env);
wrapper.setWorkingDirectory(OPENTRACK_BASE_PATH);
wrapper.start(wine_path, { library_path + "opentrack-wrapper-wine.exe.so" });
+ wrapper.waitForStarted();
+ if (wrapper.state() == QProcess::ProcessState::NotRunning) {
+ return error(tr("Failed to start Wine! Make sure the binary is set correctly."));
+ }
#endif
if (lck_shm.success())
{
shm = (WineSHM*) lck_shm.ptr();
memset(shm, 0, sizeof(*shm));
+
+ qDebug() << "proto/wine: shm success";
+
+ // display "waiting for game message" (overwritten once a game is detected)
+#ifndef OTR_WINE_NO_WRAPPER
+ connected_game = "waiting for game...";
+#endif
+ }
+ else {
+ qDebug() << "proto/wine: shm no success";
}
if (lck_shm.success())
diff --git a/proto-wine/ftnoir_protocol_wine.h b/proto-wine/ftnoir_protocol_wine.h
index f7346be9..718699ac 100644
--- a/proto-wine/ftnoir_protocol_wine.h
+++ b/proto-wine/ftnoir_protocol_wine.h
@@ -19,14 +19,19 @@ using namespace options;
struct settings : opts
{
settings() : opts{"proto-wine"} {}
- value<bool> variant_proton{b, "variant-proton", false },
- variant_wine{b, "variant-wine", true },
+ value<bool> variant_wine{b, "variant-wine", true },
+ variant_proton{b, "variant-proton", false },
+ variant_proton_steamplay{b, "variant-proton-steamplay", true },
+ variant_proton_external{b, "variant-proton-external", false },
fsync{b, "fsync", true},
esync{b, "esync", true};
value<int> proton_appid{b, "proton-appid", 0};
value<QVariant> proton_path{b, "proton-version", {} };
- value<QString> wineprefix{b, "wineprefix", "~/.wine"};
+ value<QVariant> wine_select_path{b, "wine-select-version", {"WINE"}};
+ value<QString> wine_custom_path{b, "wine-custom-version", ""};
+ value<QString> wineprefix{b, "wineprefix", "~/.wine/"};
+ value<QString> protonprefix{b, "protonprefix", ""};
value<int> protocol{b, "protocol", 2};
};
@@ -77,6 +82,14 @@ private:
settings s;
private slots:
+ void onWinePathComboUpdated();
+ void onRadioButtonsChanged();
+
+ void doBrowseWine();
+ void doBrowseWinePrefix();
+
+ void doBrowseProtonPrefix();
+
void doOK();
void doCancel();
};
diff --git a/proto-wine/ftnoir_protocol_wine_dialog.cpp b/proto-wine/ftnoir_protocol_wine_dialog.cpp
index a954a752..2b7d08e0 100644
--- a/proto-wine/ftnoir_protocol_wine_dialog.cpp
+++ b/proto-wine/ftnoir_protocol_wine_dialog.cpp
@@ -1,40 +1,222 @@
#include "ftnoir_protocol_wine.h"
#include <QDebug>
+#include <QFileDialog>
#include <QDir>
+#include <QDirIterator>
+#include <qcombobox.h>
+#include <qdebug.h>
+#include <qdir.h>
+#include <qradiobutton.h>
#include "api/plugin-api.hpp"
+#include "options/tie.hpp"
-static const char* proton_paths[] = {
- "/.steam/steam/steamapps/common",
- "/.steam/root/compatibilitytools.d",
- "/.local/share/Steam/steamapps/common",
+/*
+ * 0: path to the directory with wine versions
+ * 1: path from a wine version to the exectuable
+ * 2: name of the application using the wine versions
+ */
+static const char* wine_paths[][3] = {
+ {"/.local/share/lutris/runners/wine/", "/bin/wine", "Lutris"},
+ {"/.var/app/net.lutris.Lutris/data/lutris/runners/wine/", "/bin/wine", "Flatpak Lutris"}
+};
+
+static const char* proton_paths[][2] = {
+ {"/.steam/steam/steamapps/common", "Proton*"},
+ {"/.steam/root/compatibilitytools.d", "*"},
+ {"/.local/share/Steam/steamapps/common", "Proton*"},
+ {"/.local/share/Steam/compatibilitytools.d", "*"},
};
FTControls::FTControls()
{
ui.setupUi(this);
- for (const char* path : proton_paths) {
- QDir dir(QDir::homePath() + path);
+ // populate wine select
+ ui.wine_path_combo->addItem("System Wine", QVariant{"WINE"});
+ for (const char** path : wine_paths) {
+ QDir dir(QDir::homePath() + path[0]);
dir.setFilter(QDir::Dirs);
- dir.setNameFilters({ "Proton*" });
QFileInfoList list = dir.entryInfoList();
for (int i = 0; i < list.size(); ++i) {
QFileInfo fileInfo = list.at(i);
- ui.proton_version->addItem(fileInfo.fileName(), QVariant{fileInfo.filePath()});
+ if (fileInfo.fileName() == "." || fileInfo.fileName() == "..") continue;
+
+ QString name = fileInfo.fileName() + " (" + path[2] + ")";
+ ui.wine_path_combo->addItem(name, QVariant{fileInfo.filePath() + path[1]});
+ }
+ }
+ ui.wine_path_combo->addItem("Custom path to Wine executable", QVariant{"CUSTOM"});
+
+ // populate proton select
+ for (const char** path : proton_paths) {
+ QDir dir(QDir::homePath() + path[0]);
+ dir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
+ dir.setNameFilters({ path[1] });
+
+ QFileInfoList proton_dir_list = dir.entryInfoList();
+ for (int i = 0; i < proton_dir_list.size(); ++i) {
+ const QFileInfo &proton_dir = proton_dir_list.at(i);
+
+ // check if this Proton Version is already present in any way
+ if (ui.proton_version->findText(proton_dir.fileName()) != -1)
+ continue;
+
+ QDirIterator proton_executable_it(proton_dir.canonicalFilePath(), QStringList() << "wine", QDir::Files, QDirIterator::Subdirectories);
+
+ if (proton_executable_it.hasNext()) {
+ QString proton_executable_path = proton_executable_it.next();
+ QDir proton_dist_dir(proton_executable_path);
+ proton_dist_dir.cd("../../");
+
+ ui.proton_version->addItem(proton_dir.fileName(), QVariant{proton_dist_dir.canonicalPath()});
+ }
}
}
- tie_setting(s.proton_path, ui.proton_version);
- tie_setting(s.variant_wine, ui.variant_wine);
- tie_setting(s.variant_proton, ui.variant_proton);
+
+ // settings - wine
+ tie_setting(s.variant_wine, ui.variant_wine); // radio button
+ tie_setting(s.wine_select_path, ui.wine_path_combo); // combo box (dropdown)
+ tie_setting(s.wine_custom_path, ui.wine_path); // line edit (enabled via dropdown)
+ tie_setting(s.wineprefix, ui.wineprefix); // line edit
+
+ // settings - proton
+ tie_setting(s.variant_proton, ui.variant_proton); // radio button
+ tie_setting(s.proton_path, ui.proton_version); // combo box (dropdown)
+ tie_setting(s.variant_proton_steamplay, ui.subvariant_steamplay); // radio button
+ tie_setting(s.proton_appid, ui.proton_appid); // number select
+ tie_setting(s.variant_proton_external, ui.subvariant_external); // radio button
+ tie_setting(s.protonprefix, ui.protonprefix); // line edit
+
+ // settings - advanced
tie_setting(s.esync, ui.esync);
tie_setting(s.fsync, ui.fsync);
- tie_setting(s.proton_appid, ui.proton_appid);
- tie_setting(s.wineprefix, ui.wineprefix);
tie_setting(s.protocol, ui.protocol_selection);
+ // setup signals and slots for UI
+ connect(ui.wine_path_combo, &QComboBox::currentTextChanged, this, &FTControls::onWinePathComboUpdated);
+ connect(ui.browse_wine_path_button, &QPushButton::clicked, this, &FTControls::doBrowseWine);
+ connect(ui.browse_wine_prefix_button, &QPushButton::clicked, this, &FTControls::doBrowseWinePrefix);
+ connect(ui.browse_proton_prefix_button, &QPushButton::clicked, this, &FTControls::doBrowseProtonPrefix);
connect(ui.buttonBox, &QDialogButtonBox::accepted, this, &FTControls::doOK);
connect(ui.buttonBox, &QDialogButtonBox::rejected, this, &FTControls::doCancel);
+
+ // setup signals and slots for UI radio buttons
+ connect(ui.variant_wine, &QRadioButton::clicked, this, &FTControls::onRadioButtonsChanged);
+ connect(ui.variant_proton, &QRadioButton::clicked, this, &FTControls::onRadioButtonsChanged);
+ connect(ui.subvariant_steamplay, &QRadioButton::clicked, this, &FTControls::onRadioButtonsChanged);
+ connect(ui.subvariant_external, &QRadioButton::clicked, this, &FTControls::onRadioButtonsChanged);
+
+ // update state of the combo box and associated ui elements
+ onWinePathComboUpdated();
+ // hide the correct items
+ onRadioButtonsChanged();
+}
+
+void FTControls::onWinePathComboUpdated() {
+ // enable the custom text field if required
+ if (ui.wine_path_combo->currentData() == "CUSTOM") {
+ ui.wine_path->setEnabled(true);
+ ui.browse_wine_path_button->setEnabled(true);
+ }
+ else {
+ ui.wine_path->setEnabled(false);
+ ui.browse_wine_path_button->setEnabled(false);
+ }
+}
+
+void FTControls::onRadioButtonsChanged() {
+ if (ui.variant_wine->isChecked()) {
+ // wine settings selected
+
+ // enable wine settings
+ ui.wine_path_combo->setEnabled(true);
+ ui.wineprefix->setEnabled(true);
+ ui.browse_wine_prefix_button->setEnabled(true);
+ if (ui.wine_path_combo->currentData() == "CUSTOM") {
+ ui.wine_path->setEnabled(true);
+ ui.browse_wine_path_button->setEnabled(true);
+ }
+
+ // disable proton settings
+ ui.proton_version->setEnabled(false);
+ ui.proton_subgroup->setEnabled(false);
+ }
+ else if (ui.variant_proton->isChecked()) {
+ // proton settings selected
+
+ // disable wine settings
+ ui.wine_path_combo->setEnabled(false);
+ ui.wine_path->setEnabled(false);
+ ui.browse_wine_path_button->setEnabled(false);
+ ui.wineprefix->setEnabled(false);
+ ui.browse_wine_prefix_button->setEnabled(false);
+
+ // enable proton settings
+ ui.proton_version->setEnabled(true);
+ ui.proton_subgroup->setEnabled(true);
+
+ // run proton radio buttons logic
+ if (ui.subvariant_steamplay->isChecked()) {
+ // enable steamplay settings
+ ui.proton_appid->setEnabled(true);
+
+ // disable external settings
+ ui.protonprefix->setEnabled(false);
+ ui.browse_proton_prefix_button->setEnabled(false);
+ }
+ else if (ui.subvariant_external->isChecked()) {
+ // disable steamplay settings
+ ui.proton_appid->setEnabled(false);
+
+ // enable external settinsg
+ ui.protonprefix->setEnabled(true);
+ ui.browse_proton_prefix_button->setEnabled(true);
+ }
+ }
+ else {
+ // for some reason QTs auto exclusive feature is not always correctly working
+ // this is a somewhat hacky solution
+ ui.variant_wine->setChecked(ui.wine_path_combo->isEnabled());
+ ui.variant_proton->setChecked(ui.proton_version->isEnabled());
+ }
+}
+
+void FTControls::doBrowseWine() {
+ QFileDialog d(this);
+ d.setFileMode(QFileDialog::FileMode::ExistingFile);
+ d.setWindowTitle(tr("Select path to Wine Binary"));
+ if (s.wine_custom_path->startsWith("~/.local/share/lutris/runners")) {
+ d.selectFile(s.wine_custom_path);
+ }
+ if (d.exec()) {
+ s.wine_custom_path = d.selectedFiles()[0];
+ }
+}
+void FTControls::doBrowseWinePrefix() {
+ QFileDialog d(this);
+ d.setFileMode(QFileDialog::FileMode::Directory);
+ d.setOption(QFileDialog::Option::ShowDirsOnly, true);
+ d.setWindowTitle(tr("Select Wine Prefix"));
+ if (s.wineprefix->startsWith("/") || s.wineprefix->startsWith("~")) {
+ d.selectFile(s.wineprefix);
+ }
+ if (d.exec()) {
+ s.wineprefix = d.selectedFiles()[0];
+ }
+}
+
+void FTControls::doBrowseProtonPrefix() {
+ QFileDialog d(this);
+ d.setFileMode(QFileDialog::FileMode::Directory);
+ d.setOption(QFileDialog::Option::ShowDirsOnly, true);
+ d.setWindowTitle(tr("Select Proton Prefix"));
+ if (s.protonprefix->startsWith("/") || s.protonprefix->startsWith("~")) {
+ d.selectFile(s.protonprefix);
+ }
+ if (d.exec()) {
+ s.protonprefix = d.selectedFiles()[0];
+ }
}
void FTControls::doOK()
diff --git a/proto-wine/ftnoir_winecontrols.ui b/proto-wine/ftnoir_winecontrols.ui
index e9541447..b601d6e5 100644
--- a/proto-wine/ftnoir_winecontrols.ui
+++ b/proto-wine/ftnoir_winecontrols.ui
@@ -9,8 +9,8 @@
<rect>
<x>0</x>
<y>0</y>
- <width>528</width>
- <height>424</height>
+ <width>934</width>
+ <height>466</height>
</rect>
</property>
<property name="windowTitle">
@@ -32,21 +32,93 @@
<property name="title">
<string>Wine variant</string>
</property>
- <layout class="QGridLayout" name="gridLayout">
- <item row="0" column="0">
- <widget class="QRadioButton" name="variant_wine">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Wine (system)</string>
- </property>
- </widget>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="1" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <widget class="QLineEdit" name="wine_path">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>450</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="toolTip">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;wine/runner exectuable path&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="inputMask">
+ <string/>
+ </property>
+ <property name="placeholderText">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="browse_wine_path_button">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Browse Wine Path</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
</item>
- <item row="2" column="1" alignment="Qt::AlignRight">
+ <item row="2" column="1">
+ <layout class="QVBoxLayout" name="verticalLayout_7">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_4">
+ <item>
+ <widget class="QLineEdit" name="wineprefix">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>450</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="toolTip">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;prefix&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="placeholderText">
+ <string>/path_to_the_prefix/</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="browse_wine_prefix_button">
+ <property name="text">
+ <string>Browse Prefix</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item row="3" column="1">
<widget class="QComboBox" name="proton_version">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
@@ -60,26 +132,26 @@
<height>0</height>
</size>
</property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
</widget>
</item>
- <item row="0" column="1">
- <widget class="QLineEdit" name="wineprefix">
+ <item row="3" column="0">
+ <widget class="QRadioButton" name="variant_proton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="minimumSize">
- <size>
- <width>286</width>
- <height>0</height>
- </size>
+ <property name="text">
+ <string>Proton (select version and mode)</string>
</property>
</widget>
</item>
- <item row="2" column="0">
- <widget class="QRadioButton" name="variant_proton">
+ <item row="0" column="0">
+ <widget class="QRadioButton" name="variant_wine">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -87,10 +159,98 @@
</sizepolicy>
</property>
<property name="text">
- <string>Proton (Steam Play)</string>
+ <string>Wine (select version and prefix)</string>
</property>
</widget>
</item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="wine_path_combo">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0" colspan="2">
+ <widget class="QGroupBox" name="proton_subgroup">
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="1">
+ <widget class="QSpinBox" name="proton_appid">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximum">
+ <number>2147483647</number>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QRadioButton" name="subvariant_steamplay">
+ <property name="toolTip">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Steam Play is Steams System to run Windows titles on Linux via Proton.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="text">
+ <string>Steam Play (select Steam Application ID)</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QRadioButton" name="subvariant_external">
+ <property name="toolTip">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;UMU is a launcher that allows the use of Proton outside of Steam. Some game launchers like Lutris may use this to enable Proton support.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="text">
+ <string>UMU enabled Launchers (select prefix)</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_7">
+ <item>
+ <widget class="QLineEdit" name="protonprefix">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>450</width>
+ <height>0</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="browse_proton_prefix_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>105</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Browse Prefix</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
</layout>
</widget>
</item>
@@ -206,32 +366,6 @@
<property name="bottomMargin">
<number>0</number>
</property>
- <item>
- <widget class="QLabel" name="label">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Steam application id</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QSpinBox" name="proton_appid">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="maximum">
- <number>2147483647</number>
- </property>
- </widget>
- </item>
</layout>
</widget>
</item>
@@ -253,6 +387,24 @@
</item>
</layout>
</widget>
+ <tabstops>
+ <tabstop>variant_wine</tabstop>
+ <tabstop>wine_path_combo</tabstop>
+ <tabstop>wine_path</tabstop>
+ <tabstop>browse_wine_path_button</tabstop>
+ <tabstop>wineprefix</tabstop>
+ <tabstop>browse_wine_prefix_button</tabstop>
+ <tabstop>variant_proton</tabstop>
+ <tabstop>proton_version</tabstop>
+ <tabstop>subvariant_steamplay</tabstop>
+ <tabstop>proton_appid</tabstop>
+ <tabstop>subvariant_external</tabstop>
+ <tabstop>protonprefix</tabstop>
+ <tabstop>browse_proton_prefix_button</tabstop>
+ <tabstop>esync</tabstop>
+ <tabstop>fsync</tabstop>
+ <tabstop>protocol_selection</tabstop>
+ </tabstops>
<resources>
<include location="wine-protocol.qrc"/>
</resources>
diff --git a/proto-wine/lang/de_DE.ts b/proto-wine/lang/de_DE.ts
new file mode 100644
index 00000000..072c8626
--- /dev/null
+++ b/proto-wine/lang/de_DE.ts
@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>FTControls</name>
+ <message>
+ <source>Select path to Wine Binary</source>
+ <translation>Pfad des wine-Programms auswählen</translation>
+ </message>
+ <message>
+ <source>Select Wine Prefix</source>
+ <translation>wine-Prefix auswählen</translation>
+ </message>
+ <message>
+ <source>Select Proton Prefix</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>UICFTControls</name>
+ <message>
+ <source>Wine settings</source>
+ <translation>Wine-Einstellungen</translation>
+ </message>
+ <message>
+ <source>Wine variant</source>
+ <translation>Wine-Variante</translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;prefix&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Prefix&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <source>/path_to_the_prefix/</source>
+ <translation>/pfad_zum_prefix/</translation>
+ </message>
+ <message>
+ <source>Browse Prefix</source>
+ <translation>Prefix suchen</translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;wine/runner exectuable path&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Pfad zum ausführbaren wine/runner&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <source>Browse Wine Path</source>
+ <translation>Wine-Pfad suchen</translation>
+ </message>
+ <message>
+ <source>Advanced</source>
+ <translation>Erweitert</translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;When supported.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Falls unterstützt.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <source>ESYNC</source>
+ <translation>ESYNC</translation>
+ </message>
+ <message>
+ <source>FSYNC</source>
+ <translation>FSYNC</translation>
+ </message>
+ <message>
+ <source>Protocol</source>
+ <translation>Protokoll</translation>
+ </message>
+ <message>
+ <source>Freetrack</source>
+ <translation>Freetrack</translation>
+ </message>
+ <message>
+ <source>NPClient</source>
+ <translation>NPClient</translation>
+ </message>
+ <message>
+ <source>Both</source>
+ <translation>Beides</translation>
+ </message>
+ <message>
+ <source>Proton (select version and mode)</source>
+ <translation>Proton (wähle Version und Modus)</translation>
+ </message>
+ <message>
+ <source>Steam Play (select Steam Application ID)</source>
+ <translation>Steam Play (wähle Steam Applikation ID)</translation>
+ </message>
+ <message>
+ <source>UMU enabled Launchers (select prefix)</source>
+ <translation>UMU unterstützte Launcher (wähle Prefix)</translation>
+ </message>
+ <message>
+ <source>Wine (select version and prefix)</source>
+ <translation>Wine (wähle Version und Prefix)</translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Steam Play is Steams System to run Windows titles on Linux via Proton.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Steam Play ist Steams System, um Windows Titel auf Linux, via Proton, auszuführen.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;UMU is a launcher that allows the use of Proton outside of Steam. Some game launchers like Lutris may use this to enable Proton support.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;UMU ist ein Launcher, der es erlaubt Proton außerhalb von Steam zu nutzen. Manche Spiel Launcher, wie Lutris, können dies für Proton Support nutzen.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+</context>
+<context>
+ <name>wine</name>
+ <message>
+ <source>Must specify application id for Proton (Steam Play)</source>
+ <translation>Die Application-ID für Proton (Steam Play) muss angegeben werden</translation>
+ </message>
+ <message>
+ <source>Wine prefix must be an absolute path (given &apos;%1&apos;)</source>
+ <translation>Wine-Prefix muss ein absoluter Pfad sein (&apos;%1&apos; angegeben)</translation>
+ </message>
+ <message>
+ <source>Failed to start Wine! Make sure the binary is set correctly.</source>
+ <translation>Starten von Wine fehlgeschlagen! Stelle sicher, dass der Programmpfad richtig gesetzt ist.</translation>
+ </message>
+ <message>
+ <source>Can&apos;t open shared memory mapping</source>
+ <translation>Shared-Memory-Mapping kann nicht geöffnet werden</translation>
+ </message>
+ <message>
+ <source>Prefix has not been defined!</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>wine_metadata</name>
+ <message>
+ <source>Wine -- Windows layer for Unix</source>
+ <translation>Wine -- Windows-Schicht für Unix</translation>
+ </message>
+ <message>
+ <source>X-Plane</source>
+ <translation>X-Plane</translation>
+ </message>
+</context>
+</TS>
diff --git a/proto-wine/lang/nl_NL.ts b/proto-wine/lang/nl_NL.ts
index e2183cc8..bc3ef604 100644
--- a/proto-wine/lang/nl_NL.ts
+++ b/proto-wine/lang/nl_NL.ts
@@ -2,41 +2,44 @@
<!DOCTYPE TS>
<TS version="2.1" language="nl_NL">
<context>
- <name>UICFTControls</name>
+ <name>FTControls</name>
<message>
- <source>Wine settings</source>
+ <source>Select path to Wine Binary</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Wine variant</source>
+ <source>Select Wine Prefix</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Wine (system)</source>
+ <source>Select Proton Prefix</source>
<translation type="unfinished"></translation>
</message>
+</context>
+<context>
+ <name>UICFTControls</name>
<message>
- <source>Advanced</source>
+ <source>Wine settings</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>ESYNC</source>
+ <source>Wine variant</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>FSYNC</source>
+ <source>Advanced</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;When supported.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <source>ESYNC</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Steam application id</source>
+ <source>FSYNC</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Proton (Steam Play)</source>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;When supported.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -55,6 +58,50 @@
<source>Both</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;prefix&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>/path_to_the_prefix/</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Browse Prefix</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;wine/runner exectuable path&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Browse Wine Path</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Proton (select version and mode)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Steam Play (select Steam Application ID)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>UMU enabled Launchers (select prefix)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Wine (select version and prefix)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Steam Play is Steams System to run Windows titles on Linux via Proton.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;UMU is a launcher that allows the use of Proton outside of Steam. Some game launchers like Lutris may use this to enable Proton support.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>wine</name>
@@ -70,6 +117,14 @@
<source>Wine prefix must be an absolute path (given &apos;%1&apos;)</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Failed to start Wine! Make sure the binary is set correctly.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Prefix has not been defined!</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>wine_metadata</name>
diff --git a/proto-wine/lang/ru_RU.ts b/proto-wine/lang/ru_RU.ts
index f3b44bd2..a7702454 100644
--- a/proto-wine/lang/ru_RU.ts
+++ b/proto-wine/lang/ru_RU.ts
@@ -2,41 +2,44 @@
<!DOCTYPE TS>
<TS version="2.1" language="ru_RU">
<context>
- <name>UICFTControls</name>
+ <name>FTControls</name>
<message>
- <source>Wine settings</source>
+ <source>Select path to Wine Binary</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Wine variant</source>
+ <source>Select Wine Prefix</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Wine (system)</source>
+ <source>Select Proton Prefix</source>
<translation type="unfinished"></translation>
</message>
+</context>
+<context>
+ <name>UICFTControls</name>
<message>
- <source>Advanced</source>
+ <source>Wine settings</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>ESYNC</source>
+ <source>Wine variant</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>FSYNC</source>
+ <source>Advanced</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;When supported.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <source>ESYNC</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Steam application id</source>
+ <source>FSYNC</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Proton (Steam Play)</source>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;When supported.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -55,6 +58,50 @@
<source>Both</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;prefix&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>/path_to_the_prefix/</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Browse Prefix</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;wine/runner exectuable path&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Browse Wine Path</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Proton (select version and mode)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Steam Play (select Steam Application ID)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>UMU enabled Launchers (select prefix)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Wine (select version and prefix)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Steam Play is Steams System to run Windows titles on Linux via Proton.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;UMU is a launcher that allows the use of Proton outside of Steam. Some game launchers like Lutris may use this to enable Proton support.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>wine</name>
@@ -70,6 +117,14 @@
<source>Wine prefix must be an absolute path (given &apos;%1&apos;)</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Failed to start Wine! Make sure the binary is set correctly.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Prefix has not been defined!</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>wine_metadata</name>
diff --git a/proto-wine/lang/stub.ts b/proto-wine/lang/stub.ts
index dd4f946f..2c708749 100644
--- a/proto-wine/lang/stub.ts
+++ b/proto-wine/lang/stub.ts
@@ -2,41 +2,44 @@
<!DOCTYPE TS>
<TS version="2.1">
<context>
- <name>UICFTControls</name>
+ <name>FTControls</name>
<message>
- <source>Wine settings</source>
+ <source>Select path to Wine Binary</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Wine variant</source>
+ <source>Select Wine Prefix</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Wine (system)</source>
+ <source>Select Proton Prefix</source>
<translation type="unfinished"></translation>
</message>
+</context>
+<context>
+ <name>UICFTControls</name>
<message>
- <source>Advanced</source>
+ <source>Wine settings</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>ESYNC</source>
+ <source>Wine variant</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>FSYNC</source>
+ <source>Advanced</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;When supported.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <source>ESYNC</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Steam application id</source>
+ <source>FSYNC</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Proton (Steam Play)</source>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;When supported.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -55,6 +58,50 @@
<source>Both</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;prefix&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>/path_to_the_prefix/</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Browse Prefix</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;wine/runner exectuable path&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Browse Wine Path</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Proton (select version and mode)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Steam Play (select Steam Application ID)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>UMU enabled Launchers (select prefix)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Wine (select version and prefix)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Steam Play is Steams System to run Windows titles on Linux via Proton.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;UMU is a launcher that allows the use of Proton outside of Steam. Some game launchers like Lutris may use this to enable Proton support.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>wine</name>
@@ -70,6 +117,14 @@
<source>Wine prefix must be an absolute path (given &apos;%1&apos;)</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Failed to start Wine! Make sure the binary is set correctly.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Prefix has not been defined!</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>wine_metadata</name>
diff --git a/proto-wine/lang/zh_CN.ts b/proto-wine/lang/zh_CN.ts
index dd4f946f..6884f266 100644
--- a/proto-wine/lang/zh_CN.ts
+++ b/proto-wine/lang/zh_CN.ts
@@ -1,42 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
<context>
- <name>UICFTControls</name>
+ <name>FTControls</name>
<message>
- <source>Wine settings</source>
+ <source>Select path to Wine Binary</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Wine variant</source>
+ <source>Select Wine Prefix</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Wine (system)</source>
+ <source>Select Proton Prefix</source>
<translation type="unfinished"></translation>
</message>
+</context>
+<context>
+ <name>UICFTControls</name>
<message>
- <source>Advanced</source>
+ <source>Wine settings</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>ESYNC</source>
+ <source>Wine variant</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>FSYNC</source>
+ <source>Advanced</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;When supported.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <source>ESYNC</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Steam application id</source>
+ <source>FSYNC</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Proton (Steam Play)</source>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;When supported.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -55,6 +58,50 @@
<source>Both</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;prefix&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>/path_to_the_prefix/</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Browse Prefix</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;wine/runner exectuable path&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Browse Wine Path</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Proton (select version and mode)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Steam Play (select Steam Application ID)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>UMU enabled Launchers (select prefix)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Wine (select version and prefix)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Steam Play is Steams System to run Windows titles on Linux via Proton.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;UMU is a launcher that allows the use of Proton outside of Steam. Some game launchers like Lutris may use this to enable Proton support.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>wine</name>
@@ -70,6 +117,14 @@
<source>Wine prefix must be an absolute path (given &apos;%1&apos;)</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Failed to start Wine! Make sure the binary is set correctly.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Prefix has not been defined!</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>wine_metadata</name>
diff --git a/proto-wine/proton.cpp b/proto-wine/proton.cpp
index 5ecd1f93..998da748 100644
--- a/proto-wine/proton.cpp
+++ b/proto-wine/proton.cpp
@@ -10,32 +10,34 @@
#include <QDebug>
#include <QDir>
#include <QFileInfo>
-#include <QProcessEnvironment>
#include <QtGlobal>
+#include "proton.h"
static const char* steam_paths[] = {
"/.steam/steam/steamapps/compatdata",
"/.local/share/Steam/steamapps/compatdata",
+ "/.steam/debian-installation/steamapps/compatdata",
};
static const char* runtime_paths[] = {
"/.local/share/Steam/ubuntu12_32/steam-runtime",
"/.steam/ubuntu12_32/steam-runtime",
+ "/.steam/debian-installation/ubuntu12_32/steam-runtime",
};
-std::tuple<QProcessEnvironment, QString, bool> make_steam_environ(const QString& proton_path, int appid)
+std::tuple<QProcessEnvironment, QString, bool> make_steam_environ(const QString& proton_dist_path)
{
using ret = std::tuple<QProcessEnvironment, QString, bool>;
auto env = QProcessEnvironment::systemEnvironment();
QString error = "";
QString home = qgetenv("HOME");
- QString runtime_path, app_wineprefix;
+ QString runtime_path;
auto expand = [&](QString x) {
x.replace("HOME", home);
- x.replace("PROTON_PATH", proton_path);
+ x.replace("PROTON_DIST_PATH", proton_dist_path);
x.replace("RUNTIME_PATH", runtime_path);
return x;
};
@@ -49,23 +51,15 @@ std::tuple<QProcessEnvironment, QString, bool> make_steam_environ(const QString&
if (runtime_path.isEmpty())
error = QString("Couldn't find a Steam runtime.");
- for (const char* path : steam_paths) {
- QDir dir(QDir::homePath() + path + expand("/%1/pfx").arg(appid));
- if (dir.exists())
- app_wineprefix = dir.absolutePath();
- }
- if (app_wineprefix.isEmpty())
- error = QString("Couldn't find a Wineprefix for AppId %1").arg(appid);
-
QString path = expand(
- ":PROTON_PATH/dist/bin"
+ ":PROTON_DIST_PATH/bin"
);
path += ':'; path += qgetenv("PATH");
env.insert("PATH", path);
QString library_path = expand(
- ":PROTON_PATH/dist/lib"
- ":PROTON_PATH/dist/lib64"
+ ":PROTON_DIST_PATH/lib"
+ ":PROTON_DIST_PATH/lib64"
":RUNTIME_PATH/pinned_libs_32"
":RUNTIME_PATH/pinned_libs_64"
":RUNTIME_PATH/i386/lib/i386-linux-gnu"
@@ -79,14 +73,25 @@ std::tuple<QProcessEnvironment, QString, bool> make_steam_environ(const QString&
);
library_path += ':'; library_path += qgetenv("LD_LIBRARY_PATH");
env.insert("LD_LIBRARY_PATH", library_path);
- env.insert("WINEPREFIX", app_wineprefix);
return ret(env, error, error.isEmpty());
}
-QString proton_path(const QString& proton_path)
+
+std::tuple<QString, QString, bool> make_wineprefix(int appid)
{
- return proton_path + "/dist/bin/wine";
+ using ret = std::tuple<QString, QString, bool>;
+ QString error = "";
+ QString app_wineprefix;
+ for (const char* path : steam_paths) {
+ QDir dir(QDir::homePath() + path + QString("/%1/pfx").arg(appid));
+ if (dir.exists())
+ app_wineprefix = dir.absolutePath();
+ }
+ if (app_wineprefix.isEmpty())
+ error = QString("Couldn't find a Wineprefix for AppId %1").arg(appid);
+
+ return ret(app_wineprefix, error, error.isEmpty());
}
#endif
diff --git a/proto-wine/proton.h b/proto-wine/proton.h
new file mode 100644
index 00000000..51539305
--- /dev/null
+++ b/proto-wine/proton.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#include <qchar.h>
+#include <qprocess.h>
+
+std::tuple<QProcessEnvironment, QString, bool> make_steam_environ(const QString& proton_dist_path);
+std::tuple<QString, QString, bool> make_wineprefix(int appid); \ No newline at end of file
diff --git a/qxt-mini/lang/de_DE.ts b/qxt-mini/lang/de_DE.ts
new file mode 100644
index 00000000..1552582e
--- /dev/null
+++ b/qxt-mini/lang/de_DE.ts
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+</TS>
diff --git a/qxt-mini/lang/zh_CN.ts b/qxt-mini/lang/zh_CN.ts
index 6401616d..e5ca8aa9 100644
--- a/qxt-mini/lang/zh_CN.ts
+++ b/qxt-mini/lang/zh_CN.ts
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
</TS>
diff --git a/qxt-mini/powerset.hpp b/qxt-mini/powerset.hpp
index d8a8ec9b..901ff0c7 100644
--- a/qxt-mini/powerset.hpp
+++ b/qxt-mini/powerset.hpp
@@ -1,7 +1,5 @@
#pragma once
-#include "compat/macros.hpp"
-
#include <type_traits>
#include <cinttypes>
#include <vector>
diff --git a/qxt-mini/qxtglobalshortcut.cpp b/qxt-mini/qxtglobalshortcut.cpp
index 52758f71..38c97ffd 100644
--- a/qxt-mini/qxtglobalshortcut.cpp
+++ b/qxt-mini/qxtglobalshortcut.cpp
@@ -40,7 +40,7 @@
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
-QHash<QPair<quint32, quint32>, QxtGlobalShortcut*> QxtGlobalShortcutPrivate::shortcuts;
+QMultiMap<QPair<quint32, quint32>, QxtGlobalShortcut*> QxtGlobalShortcutPrivate::shortcuts;
void QxtGlobalShortcutPrivate::event_filter_installer::ensure_event_filter()
{
@@ -180,7 +180,7 @@ void QxtGlobalShortcutPrivate::activateShortcut(quint32 nativeKey, quint32 nativ
shortcut->qxt_d().keystate = false;
if (shortcut->isEnabled())
- emit shortcut->activated(true);
+ emit shortcut->activated(is_down);
}
#endif
}
diff --git a/qxt-mini/qxtglobalshortcut_mac.cpp b/qxt-mini/qxtglobalshortcut_mac.cpp
index d7cd7f84..571a6783 100644
--- a/qxt-mini/qxtglobalshortcut_mac.cpp
+++ b/qxt-mini/qxtglobalshortcut_mac.cpp
@@ -49,13 +49,14 @@ OSStatus qxt_mac_handle_hot_key(EventHandlerCallRef nextHandler, EventRef event,
{
Q_UNUSED(nextHandler);
Q_UNUSED(data);
- if (GetEventClass(event) == kEventClassKeyboard && GetEventKind(event) == kEventHotKeyPressed)
+ if (GetEventClass(event) == kEventClassKeyboard && (GetEventKind(event) == kEventHotKeyPressed || GetEventKind(event) == kEventHotKeyReleased))
{
EventHotKeyID keyID;
GetEventParameter(event, kEventParamDirectObject, typeEventHotKeyID, NULL, sizeof(keyID), NULL, &keyID);
Identifier id = keyIDs.key(keyID.id);
- if(id != Identifier())
- QxtGlobalShortcutPrivate::activateShortcut(id.second, id.first, true);
+ if(id != Identifier()) {
+ QxtGlobalShortcutPrivate::activateShortcut(id.second, id.first, GetEventKind(event) == kEventHotKeyPressed);
+ }
}
return noErr;
}
@@ -233,10 +234,12 @@ bool QxtGlobalShortcutPrivate::registerShortcut(quint32 nativeKey, quint32 nativ
if (!qxt_mac_handler_installed)
{
qxt_mac_handler_installed = true;
- EventTypeSpec t;
- t.eventClass = kEventClassKeyboard;
- t.eventKind = kEventHotKeyPressed;
- InstallApplicationEventHandler(&qxt_mac_handle_hot_key, 1, &t, NULL, NULL);
+ EventTypeSpec t[2];
+ t[0].eventClass = kEventClassKeyboard;
+ t[0].eventKind = kEventHotKeyPressed;
+ t[1].eventClass = kEventClassKeyboard;
+ t[1].eventKind = kEventHotKeyReleased;
+ InstallApplicationEventHandler(&qxt_mac_handle_hot_key, 2, t, NULL, NULL);
}
EventHotKeyID keyID;
@@ -263,7 +266,7 @@ bool QxtGlobalShortcutPrivate::unregisterShortcut(quint32 nativeKey, quint32 nat
return !UnregisterEventHotKey(ref);
}
bool QxtGlobalShortcutPrivate::nativeEventFilter(const QByteArray & eventType,
- void *message, long *result)
+ void *message, qintptr *result)
{
return false;
}
diff --git a/qxt-mini/qxtglobalshortcut_p.h b/qxt-mini/qxtglobalshortcut_p.h
index be708ba6..e6e77809 100644
--- a/qxt-mini/qxtglobalshortcut_p.h
+++ b/qxt-mini/qxtglobalshortcut_p.h
@@ -34,7 +34,7 @@
#include "qxtglobalshortcut.h"
#include <QAbstractEventDispatcher>
#include <QKeySequence>
-#include <QHash>
+#include <QMultiMap>
#include <QAbstractNativeEventFilter>
@@ -54,7 +54,7 @@ public:
bool unsetShortcut();
static bool error;
- bool nativeEventFilter(const QByteArray & eventType, void * message, long * result) override;
+ bool nativeEventFilter(const QByteArray & eventType, void * message, qintptr * result) override;
static void activateShortcut(quint32 nativeKey, quint32 nativeMods, bool is_down);
@@ -70,7 +70,7 @@ private:
static bool registerShortcut(quint32 nativeKey, quint32 nativeMods);
static bool unregisterShortcut(quint32 nativeKey, quint32 nativeMods);
- static QHash<QPair<quint32, quint32>, QxtGlobalShortcut*> shortcuts;
+ static QMultiMap<QPair<quint32, quint32>, QxtGlobalShortcut*> shortcuts;
};
#endif // QXTGLOBALSHORTCUT_P_H
diff --git a/qxt-mini/qxtglobalshortcut_x11.cpp b/qxt-mini/qxtglobalshortcut_x11.cpp
index 01894cfc..4ddf4f6b 100644
--- a/qxt-mini/qxtglobalshortcut_x11.cpp
+++ b/qxt-mini/qxtglobalshortcut_x11.cpp
@@ -339,7 +339,7 @@ QMutex keybinding::lock;
bool QxtX11ErrorHandler::error = false;
bool QxtGlobalShortcutPrivate::nativeEventFilter(const QByteArray & eventType,
- void *message, long *)
+ void *message, qintptr *)
{
QxtX11Data x11;
diff --git a/sdk-paths-runner@GNU-Linux.cmake b/sdk-paths-runner@GNU-Linux.cmake
new file mode 100644
index 00000000..03c87ce4
--- /dev/null
+++ b/sdk-paths-runner@GNU-Linux.cmake
@@ -0,0 +1,3 @@
+set(SDK_WINE ON CACHE BOOL "" FORCE)
+set(OPENTRACK_WINE_ARCH -m64 CACHE STRING "" FORCE)
+set(ENV{PATH} "/usr/lib/wine:$ENV{PATH}")
diff --git a/sdk-paths-runneradmin@MSVC-windows.cmake b/sdk-paths-runneradmin@MSVC-windows.cmake
new file mode 100644
index 00000000..37f8edc8
--- /dev/null
+++ b/sdk-paths-runneradmin@MSVC-windows.cmake
@@ -0,0 +1,33 @@
+#
+# qtbase configure line for safekeeping
+#
+
+# "../configure" -prefix d:\dev\qt-5.10.0 -no-ico -no-gif \
+# -opengl desktop -no-fontconfig -no-harfbuzz \
+# -nomake tests -no-mp -release -opensource -shared -confirm-license \
+# -no-freetype -force-debug-info -separate-debug-info \
+# -make-tool jom -platform win32-msvc -static-runtime
+
+# remember to change -MD to -MT in mkspecs/
+# also add CFLAGS -Zi and LFLAGS -DEBUG
+
+set(__depdir "${CMAKE_CURRENT_LIST_DIR}/opentrack-depends")
+
+function(setq name value)
+ set("${name}" "${__depdir}/${value}" CACHE INTERNAL "" FORCE)
+endfunction()
+
+set(opentrack_install-debug-info FALSE CACHE INTERNAL "" FORCE)
+
+#setq(OpenCV_DIR "../opencv")
+#setq(SDK_ARUCO_LIBPATH "../aruco")
+#setq(SDK_FSUIPC "fsuipc")
+#setq(SDK_HYDRA "SixenseSDK")
+#setq(SDK_KINECT20 "../Kinect-v2.0")
+#setq(SDK_LIBUSB "libusb-msvc-x86")
+#setq(SDK_REALSENSE "../RSSDK-R2")
+#setq(SDK_RIFT_140 "ovr_sdk_win_23.0.0/LibOVR")
+#setq(SDK_VALVE_STEAMVR "steamvr")
+#setq(SDK_VJOYSTICK "vjoystick")
+#setq(ONNXRuntime_DIR "../onnxruntime-1.12.1")
+#setq(SDK_TRACKHAT_SENSOR "../trackhat-c-library-driver")
diff --git a/sdk-paths-sthalik@Clang-windows.cmake b/sdk-paths-sthalik@Clang-windows.cmake
index 895d68b2..97ad67ed 100644
--- a/sdk-paths-sthalik@Clang-windows.cmake
+++ b/sdk-paths-sthalik@Clang-windows.cmake
@@ -18,8 +18,6 @@ set(opentrack_install-debug-info TRUE CACHE INTERNAL "" FORCE)
#setq(OpenCV_DIR "opencv/build-mingw-64")
setq(SDK_ARUCO_LIBPATH "aruco/build-mingw-w64/src/libaruco.a")
-setq(EIGEN3_INCLUDE_DIR "eigen")
-
setq(SDK_FSUIPC "fsuipc")
setq(SDK_HYDRA "SixenseSDK")
diff --git a/sdk-paths-sthalik@GNU-windows.cmake b/sdk-paths-sthalik@GNU-windows.cmake
index b9617719..593f537f 100644
--- a/sdk-paths-sthalik@GNU-windows.cmake
+++ b/sdk-paths-sthalik@GNU-windows.cmake
@@ -18,8 +18,6 @@ set(opentrack_install-debug-info TRUE CACHE INTERNAL "" FORCE)
#setq(OpenCV_DIR "opencv/build-mingw-64")
setq(SDK_ARUCO_LIBPATH "aruco/build-mingw-w64/src/libaruco.a")
-setq(EIGEN3_INCLUDE_DIR "eigen")
-
setq(SDK_FSUIPC "fsuipc")
setq(SDK_HYDRA "SixenseSDK")
diff --git a/sdk-paths-sthalik@MSVC-windows.cmake b/sdk-paths-sthalik@MSVC-windows.cmake
index ad2ddb7e..c5b533da 100644
--- a/sdk-paths-sthalik@MSVC-windows.cmake
+++ b/sdk-paths-sthalik@MSVC-windows.cmake
@@ -18,21 +18,44 @@ function(setq name value)
endfunction()
set(opentrack_install-debug-info TRUE CACHE INTERNAL "" FORCE)
-set(opentrack_maintainer-mode TRUE CACHE INTERNAL "" FORCE)
-list(APPEND CMAKE_PREFIX_PATH "${CMAKE_CURRENT_LIST_DIR}/../qt-msvc-6.1.0")
-
-setq(EIGEN3_INCLUDE_DIR "eigen")
-setq(OpenCV_DIR "opencv/build")
-setq(SDL2_DIR "SDL2-win32")
-setq(SDK_ARUCO_LIBPATH "aruco/build/src/aruco.lib")
+setq(SDK_RIFT_140 "ovr_sdk_win_23.0.0/LibOVR")
+setq(SDK_KINECT20 "nonfree/Kinect-v2.0")
+setq(SDK_VJOYSTICK "vjoystick")
+setq(SDK_PS3EYEDRIVER "PS3EYEDriver")
+setq(SDK_REALSENSE "nonfree/RSSDK-R2")
+setq(SDK_VALVE_STEAMVR "steamvr")
setq(SDK_FSUIPC "fsuipc")
setq(SDK_HYDRA "SixenseSDK")
-setq(SDK_KINECT20 "Kinect-v2.0")
+setq(SDK_EYEWARE_BEAM "eyeware-beam-sdk")
+setq(SDK_TOBII "nonfree/tobii-streamengine")
+if(CMAKE_SIZEOF_VOID_P EQUAL 8)
+setq(Qt6_DIR "../qt6-6.9.0-msvc-amd64/lib/cmake/Qt6")
+setq(OpenCV_DIR "opencv/build/amd64/install")
+setq(SDK_ARUCO_LIBPATH "aruco/build/amd64/src/aruco.lib")
+setq(SDK_LIBUSB "libusb-msvc-amd64")
+setq(ONNXRuntime_DIR "onnxruntime-20250425-msvc-amd64")
+#setq(SDK_TRACKHAT_SENSOR "trackhat-c-library-driver/build/amd64/install")
+set(SDK_TRACKHAT_SENSOR "FALSE" CACHE INTERNAL "" FORCE)
+setq(SDK_OSCPACK "oscpack/build/amd64")
+elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
+setq(Qt6_DIR "../qt/qt6-6.9.0-msvc-x86/lib/cmake/qt6")
+setq(OpenCV_DIR "opencv/build/x86/install")
+setq(SDK_ARUCO_LIBPATH "aruco/build/x86/src/aruco.lib")
setq(SDK_LIBUSB "libusb-msvc-x86")
-setq(SDK_PS3EYEDRIVER "PS3EYEDriver")
-setq(SDK_REALSENSE "RSSDK-R3")
-setq(SDK_RIFT_140 "ovr_sdk_win_1.43.0/LibOVR")
-setq(SDK_VALVE_STEAMVR "steamvr")
-setq(SDK_VJOYSTICK "vjoystick")
-
+setq(ONNXRuntime_DIR "onnxruntime-20250425-msvc-amd64-noavx")
+#setq(SDK_TRACKHAT_SENSOR "trackhat-c-library-driver/build/install")
+set(SDK_TRACKHAT_SENSOR "FALSE" CACHE INTERNAL "" FORCE)
+setq(SDK_OSCPACK "oscpack/build/x86")
+else()
+ message(FATAL_ERROR "unknown word size ${CMAKE_SIZEOF_VOID_P}")
+endif()
+
+set(CMAKE_ASM_NASM_COMPILER nasm.exe CACHE FILEPATH "" FORCE)
+
+set(qt6Core_DIR "${qt6_DIR}Core" CACHE PATH "" FORCE)
+set(qt6Gui_DIR "${qt6_DIR}Gui" CACHE PATH "" FORCE)
+
+if(CMAKE_GENERATOR STREQUAL "NMake Makefiles")
+ set(CMAKE_MAKE_PROGRAM "jom" CACHE STRING "" FORCE)
+endif()
diff --git a/settings/facetracknoir supported games.csv b/settings/facetracknoir supported games.csv
index d5c3ca2d..16c19cbf 100644
--- a/settings/facetracknoir supported games.csv
+++ b/settings/facetracknoir supported games.csv
@@ -697,3 +697,50 @@ No;Game Name;Game protocol;Supported since;Verified;By;INTERNATIONAL_ID;FTN_ID
831;vJoy;FreeTrack20;V160;;;23175;033FBC6527D3DF2DA61600
832;Vox Machinae;FreeTrack20;V160;;;8125;03408C95C383696F38EB00
833;w-hs;FreeTrack20;V160;;;23180;0341D88F5D050D64BB1E00
+834;Assetto Corsa Competizione;FreeTrack20;V160;;;8140;0342E4E8A3BB50E1518200
+835;CFPT;FreeTrack20;V160;;;23290;0343A93E3293CAF36CE300
+836;Escape from Tarkov;FreeTrack20;V160;;;8155;0344F81AF665F954F06900
+837;FlyInside Flight Simulator;FreeTrack20;V160;;;8145;034570BE736C991E163800
+838;Heavy Duty Challenge;FreeTrack20;V160;;;8180;0346CEBAD1615B9B3DF000
+839;Hevelsan;FreeTrack20;V160;;;23315;0347456E97F23237C16400
+840;Infinity: Battlescape;FreeTrack20;V160;;;8165;0348762DABEF4CCF7C7D00
+841;MS Flight Simulator 2020;FreeTrack20;V160;;;8150;034918ED4805F1FA4B7200
+842;Project Wingman;FreeTrack20;V160;;;8185;034A674065C99859032300
+843;Pylon Racer;FreeTrack20;V160;;;23325;034BEB4A3DE88FF4196F00
+844;Reentry - An Orbital Simulator;FreeTrack20;V160;;;8160;034CF1BBC4316F6D883300
+845;Star Wars: Squadrons;FreeTrack20;V160;;;8175;034D4C65426F7385BC9300
+846;Stormworks: Build and Rescue;FreeTrack20;V160;;;8170;034EA8DFCAE674B5E23200
+847;TrackIR SDK;FreeTrack20;V160;;;1000;034FB5D001A7FFDB7FF700
+848;U of Craiova;FreeTrack20;V160;;;23310;0350EEED69C66DD80A0C00
+849;U of Tokyo;FreeTrack20;V160;;;23305;03519E5B8AFF6DC551D200
+850;X-Wing Alliance;FreeTrack20;V160;;;23320;03520929423DC2DC4FA900
+851;XIM;FreeTrack20;V160;;;23295;035347EF0E062076A28000
+852;YZView;FreeTrack20;V160;;;23300;03547AF86EBD6A9F0F4C00
+853;AIRWARS;FreeTrack20;V160;;;8215;0355FDD3D50D5AA6827300
+854;Arma Reforger;FreeTrack20;V160;;;8310;0356F469821FD4D45B5000
+855;Burning Lands;FreeTrack20;V160;;;8240;0357C627B286539FBC2700
+856;Combat Pilot;FreeTrack20;V160;;;8300;035857EC0DC7CF2F47AC00
+857;Construction Simulator;FreeTrack20;V160;;;8260;035983D1CA9E99D6AB8F00
+858;Drift Racing Online;FreeTrack20;V160;;;8305;035AE9AFC8D7D4C2914100
+859;Earth Analog;FreeTrack20;V160;;;8195;035BCAB24F4C1C86A17C00
+860;Everspace 2;FreeTrack20;V160;;;4076;035C221257068D1F149300
+861;eXpanSIM;FreeTrack20;V160;;;8230;035DFF7EB81F8A853A6900
+862;Frontier Pilot Simulator;FreeTrack20;V160;;;8225;035EB613014C8ABC27E700
+863;Frontiers Reach;FreeTrack20;V160;;;8255;035F45D3B6CB3E48298F00
+864;Glider Sim;FreeTrack20;V160;;;8275;03608630388D0E6DC0E900
+865;Helicopter Gunship D.E.X;FreeTrack20;V160;;;8245;036169350A9FC803785500
+866;KartKraft;FreeTrack20;V160;;;8190;03628AC4E0C8DBE189E700
+867;KitHack Model Club;FreeTrack20;V160;;;8295;0363EA18BCA921B5060900
+868;Le Mans Ultimate;FreeTrack20;V160;;;8290;03645C195DB693F8448800
+869;MS Flight Simulator 2024;FreeTrack20;V160;;;8151;0365965F5BA1AE0E41F600
+870;Nuclear Option;FreeTrack20;V160;;;8285;036681C0C5168504285700
+871;Posture123;FreeTrack20;V160;;;8235;036756FE43E4A4615C3A00
+872;Rixer;FreeTrack20;V160;;;8265;0368C7F2C4010E521D9800
+873;Sectaris Command;FreeTrack20;V160;;;8315;03692E9E141F792C950A00
+874;SnowRunner;FreeTrack20;V160;;;8270;036A260A0E12E695F32900
+875;The Bus;FreeTrack20;V160;;;8205;036B8B78E36572E0463400
+876;Tiny Combat Arena;FreeTrack20;V160;;;8280;036C1A586F3FF0120D6F00
+877;Train Driver 2;FreeTrack20;V160;;;8250;036D57CAC3D975EABE0600
+878;Train Sim World 2;FreeTrack20;V160;;;8220;036E5CDED09B7992079800
+879;Truck and Logistics Simulator;FreeTrack20;V160;;;8200;036F7990A626B034410900
+880;World of Aircraft;FreeTrack20;V160;;;8210;037063BEC471EF8B5EA500
diff --git a/spline/axis-opts.cpp b/spline/axis-opts.cpp
index 489008e6..a2b4941d 100644
--- a/spline/axis-opts.cpp
+++ b/spline/axis-opts.cpp
@@ -31,7 +31,8 @@ axis_opts::axis_opts(QString pfx, Axis idx) :
axis_(idx),
zero(b_settings_window, n(pfx, "zero-pos"), 0),
src(b_settings_window, n(pfx, "source-index"), idx),
- invert(b_settings_window, n(pfx, "invert-sign"), false),
+ invert_post(b_settings_window, n(pfx, "invert-sign-post"), false),
+ invert_pre(b_settings_window, n(pfx, "invert-sign"), false),
altp(b_mapping_window, n(pfx, "alt-axis-sign"), false),
clamp_x_(b_mapping_window, n(pfx, "max-value"), get_max_x(idx)),
clamp_y_(b_mapping_window, n(pfx, "max-output-value"), get_max_y(idx))
diff --git a/spline/axis-opts.hpp b/spline/axis-opts.hpp
index c773dd61..437a5faa 100644
--- a/spline/axis-opts.hpp
+++ b/spline/axis-opts.hpp
@@ -29,7 +29,11 @@ public:
r15 = 15,
r10 = 10,
+ t600 = 600,
+ t300 = 300,
+ t150 = 150,
t100 = 100,
+ t75 = 75,
t30 = 30,
t20 = 20,
t15 = 15,
@@ -38,6 +42,10 @@ public:
o_r180 = -180,
o_r90 = -90,
o_t75 = -75,
+ o_t100 = -100,
+ o_t150 = -150,
+ o_t300 = -300,
+ o_t600 = -600,
x1000 = 1000,
};
@@ -47,7 +55,7 @@ public:
bundle b_mapping_window{ make_bundle(axis_ == Axis(-1) ? QString() : "opentrack-mappings") };
value<double> zero;
value<int> src;
- value<bool> invert, altp;
+ value<bool> invert_pre, invert_post, altp;
value<max_clamp> clamp_x_, clamp_y_;
double max_clamp_x() const { return std::fabs(clamp_x_.to<double>()); }
double max_clamp_y() const { return std::fabs(clamp_y_.to<double>()); }
diff --git a/spline/lang/de_DE.ts b/spline/lang/de_DE.ts
new file mode 100644
index 00000000..1552582e
--- /dev/null
+++ b/spline/lang/de_DE.ts
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+</TS>
diff --git a/spline/lang/zh_CN.ts b/spline/lang/zh_CN.ts
index 6401616d..e5ca8aa9 100644
--- a/spline/lang/zh_CN.ts
+++ b/spline/lang/zh_CN.ts
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
</TS>
diff --git a/spline/spline-widget.cpp b/spline/spline-widget.cpp
index 7aeaf590..46e2095c 100644
--- a/spline/spline-widget.cpp
+++ b/spline/spline-widget.cpp
@@ -1,6 +1,5 @@
#include "spline-widget.hpp"
#include "compat/math.hpp"
-#include "compat/macros.hpp"
#include <algorithm>
@@ -14,6 +13,10 @@
#include <QDebug>
+#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
+# define OTR_OBSOLETE_QT_WORKAROUND
+#endif
+
namespace spline_detail {
spline_widget::spline_widget(QWidget *parent) : QWidget(parent)
@@ -45,7 +48,7 @@ void spline_widget::set_config(base_spline* spl)
if (spl)
{
std::shared_ptr<base_settings> s = spl->get_settings();
- connection = connect(s.get(), &base_settings::recomputed,
+ connection = connect(&*s, &base_settings::recomputed,
this, [this] { reload_spline(); },
Qt::QueuedConnection);
}
@@ -80,10 +83,6 @@ bool spline_widget::is_preview_only() const
return preview_only;
}
-#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
-# define OTR_OBSOLETE_QT_WORKAROUND
-#endif
-
void spline_widget::drawBackground()
{
QPainter painter(&background_img);
@@ -113,13 +112,11 @@ void spline_widget::drawBackground()
const int ystep = (int)std::ceil(y_step_), xstep = (int)std::ceil(x_step_);
const double maxx = config->max_input();
const double maxy = config->max_output();
-
#ifndef OTR_OBSOLETE_QT_WORKAROUND
double space_width = metrics.horizontalAdvance(' ');
#else
- double space_width = metrics.averageCharWidth();
+ double space_width = metrics.boundingRect(' ').right();
#endif
-
painter.setPen(palette().text().color());
// vertical grid
@@ -156,9 +153,8 @@ void spline_widget::drawBackground()
#ifndef OTR_OBSOLETE_QT_WORKAROUND
double advance = metrics.horizontalAdvance(text);
#else
- double advance = rect.right();
+ double advance = metrics.boundingRect(text).right();
#endif
-
painter.drawText(QPointF(x - advance/2 - rect.left(),
pixel_bounds.bottom() + metrics.lineSpacing()),
text);
@@ -635,8 +631,8 @@ QPointF spline_widget::pixel_to_point(const QPointF& point)
if (snap_y > 0)
y = snap(y, snap_y);
- x = clamp(x, 0, config->max_input());
- y = clamp(y, 0, config->max_output());
+ x = std::clamp(x, 0., config->max_input());
+ y = std::clamp(y, 0., config->max_output());
return { x, y };
}
diff --git a/spline/spline.cpp b/spline/spline.cpp
index 21044b34..466a9a7f 100644
--- a/spline/spline.cpp
+++ b/spline/spline.cpp
@@ -42,8 +42,10 @@ void spline::set_tracking_active(bool value) const
std::shared_ptr<settings> S;
{
QMutexLocker l(&mtx);
- S = s;
+ if (value == activep)
+ return;
activep = value;
+ S = s;
}
emit S->recomputed();
}
@@ -98,10 +100,10 @@ bool spline::get_last_value(QPointF& point)
double spline::get_value_internal(int x) const
{
- const float sign = signum(x);
+ const auto sign = (f)signum(x);
x = std::abs(x);
- const float ret_ = data[std::min(unsigned(x), value_count - 1)];
- return (double)(sign * clamp(ret_, 0, 1000));
+ const auto ret_ = data[std::min(unsigned(x), value_count - 1)];
+ return (double)(sign * std::clamp(ret_, (f)0, (f)1000));
}
void spline::ensure_in_bounds(const QList<QPointF>& points, int i, f& x, f& y)
@@ -129,7 +131,7 @@ void spline::ensure_in_bounds(const QList<QPointF>& points, int i, f& x, f& y)
int spline::element_count(const QList<QPointF>& points, double max_input)
{
- const unsigned sz = (unsigned)points.size();
+ const int sz = points.size();
for (int k = sz-1; k >= 0; k--)
{
const QPointF& pt = points[k];
@@ -139,7 +141,7 @@ int spline::element_count(const QList<QPointF>& points, double max_input)
return sz;
}
-bool spline::sort_fn(const QPointF& one, const QPointF& two)
+bool spline::sort_fn(QPointF one, QPointF two)
{
return one.x() < two.x();
}
@@ -148,14 +150,14 @@ void spline::update_interp_data() const
{
points_t list = points;
ensure_valid(list);
- const int sz = list.size();
+ int sz = list.size();
if (list.isEmpty())
list.prepend({ max_input(), max_output() });
const double c = bucket_size_coefficient(list);
const double c_ = c * c_interp;
- const float cf = (float)c, c_f = (float)c_;
+ const f cf = (f)c, c_f = (f)c_;
for (unsigned i = 0; i < value_count; i++)
data[i] = magic_fill_value;
@@ -165,15 +167,34 @@ void spline::update_interp_data() const
const QPointF& pt = list[0];
const double x = pt.x();
const double y = pt.y();
- const unsigned max = clamp(uround(x * c), 0, value_count-1);
+ const unsigned max = std::clamp((unsigned)iround(x * c), 1u, value_count-1);
for (unsigned k = 0; k <= max; k++)
- data[k] = float(y * k / max); // no need for bresenham
+ data[k] = f(y * k / max); // no need for bresenham
+ }
+ else if (sz == 2 && list[0].y() < 1e-6)
+ {
+ unsigned start = std::clamp((unsigned)iround(list[0].x() * c), 1u, value_count-1);
+ unsigned end = std::clamp((unsigned)iround(list[1].x() * c), 2u, value_count-1);
+ unsigned max = end - start;
+ for (unsigned x = 0; x < start; x++)
+ data[x] = 0;
+ for (unsigned x = 0; x < max; x++)
+ data[start + x] = (f)(list[1].y() * x / max);
}
else
{
- if (list[0].x() > 1e-2)
- list.push_front({});
+ if (list[0].x() > 1e-6)
+ {
+ double zero_pos = 0;
+ while (list.size() > 1 && list[0].y() <= 1e-6)
+ {
+ zero_pos = list[0].x();
+ list.pop_front();
+ }
+ list.push_front({zero_pos, 0});
+ sz = list.size();
+ }
// now this is hella expensive due to `c_interp'
for (int i = 0; i < sz; i++)
@@ -210,21 +231,32 @@ void spline::update_interp_data() const
const f t2 = t*t;
const f t3 = t*t*t;
- const unsigned x = unsigned(f(.5) * cf * (cx[0] + cx[1] * t + cx[2] * t2 + cx[3] * t3));
- const float y = (float)(f(.5) * (cy[0] + cy[1] * t + cy[2] * t2 + cy[3] * t3));
-
- int ret = std::fpclassify(y);
- if (ret == FP_NAN || ret == FP_INFINITE)
+ const auto x = (unsigned)(f(.5) * cf * (cx[0] + cx[1] * t + cx[2] * t2 + cx[3] * t3));
+ const auto y = (f)(f(.5) * (cy[0] + cy[1] * t + cy[2] * t2 + cy[3] * t3));
+
+ switch (int ret = std::fpclassify(y))
+ {
+ case FP_INFINITE:
+ case FP_NAN:
+ case FP_SUBNORMAL:
+ eval_once(qDebug() << "spline: fpclassify" << y
+ << "returned" << ret
+ << "for bundle" << s->b->name());
continue;
-
- if (x < value_count)
- data[x] = y;
+ case FP_ZERO:
+ case FP_NORMAL:
+ if (x < value_count)
+ data[x] = y;
+ break;
+ default:
+ unreachable();
+ }
}
}
}
- float maxy = (float)max_output();
- float last = 0;
+ auto maxy = (f)max_output();
+ auto last = (f)0;
#ifdef __clang__
# pragma clang diagnostic push
@@ -235,10 +267,21 @@ void spline::update_interp_data() const
{
if (data[i] == magic_fill_value)
data[i] = last;
- data[i] = clamp(data[i], 0, maxy);
+ data[i] = std::clamp(data[i], (f)0, (f)maxy);
last = data[i];
}
+ // make sure empty places stay empty (see #1341)
+ if (auto it = std::find_if(list.cbegin(), list.cend(),
+ [](QPointF x) { return x.x() >= (f)1e-6 && x.y() >= (f)1e-6; });
+ it != list.cend() && it != list.cbegin())
+ {
+ it--;
+ unsigned max = std::clamp((unsigned)iround(it->x() * c), 0u, value_count-1);
+
+ for (unsigned x = 0; x < max; x++)
+ data[x] = 0;
+ }
#ifdef __clang__
# pragma clang diagnostic pop
#endif
@@ -361,11 +404,11 @@ void spline::set_bundle(bundle b, const QString& axis_name, Axis axis)
S = s;
conn_points = QObject::connect(&s->points, value_::value_changed<QList<QPointF>>(),
- ctx.get(), [this] { invalidate_settings(); }, Qt::DirectConnection);
+ &*ctx, [this] { invalidate_settings(); }, Qt::DirectConnection);
conn_maxx = QObject::connect(&s->opts.clamp_x_, value_::value_changed<int>(),
- ctx.get(), [this](double) { invalidate_settings(); }, Qt::DirectConnection);
+ &*ctx, [this](double) { invalidate_settings(); }, Qt::DirectConnection);
conn_maxy = QObject::connect(&s->opts.clamp_y_, value_::value_changed<int>(),
- ctx.get(), [this](double) { invalidate_settings(); }, Qt::DirectConnection);
+ &*ctx, [this](double) { invalidate_settings(); }, Qt::DirectConnection);
}
emit S->recomputed();
@@ -472,7 +515,7 @@ double spline::bucket_size_coefficient(const QList<QPointF>& points) const
const int sz = element_count(points, maxx);
const double last_x = sz ? points[sz - 1].x() : maxx;
- return clamp((value_count-1) / clamp(last_x, eps, maxx), 0., (value_count-1));
+ return std::clamp((value_count-1) / std::clamp(last_x, eps, maxx), 0., (value_count-1.));
}
void spline::disconnect_signals()
diff --git a/spline/spline.hpp b/spline/spline.hpp
index e4f64069..f8748368 100644
--- a/spline/spline.hpp
+++ b/spline/spline.hpp
@@ -105,12 +105,12 @@ struct OTR_SPLINE_EXPORT base_spline : base_spline_, spline_modify_mixin, spline
class OTR_SPLINE_EXPORT spline : public base_spline
{
- using f = float;
+ using f = double;
double bucket_size_coefficient(const QList<QPointF>& points) const;
void update_interp_data() const;
double get_value_internal(int x) const;
- static bool sort_fn(const QPointF& one, const QPointF& two);
+ static bool sort_fn(QPointF one, QPointF two);
static void ensure_in_bounds(const QList<QPointF>& points, int i, f& x, f& y);
static int element_count(const QList<QPointF>& points, double max_input);
@@ -118,20 +118,20 @@ class OTR_SPLINE_EXPORT spline : public base_spline
void disconnect_signals();
void invalidate_settings_();
- mutex mtx { mutex::Recursive };
+ mutex<QRecursiveMutex> mtx;
std::shared_ptr<settings> s;
QMetaObject::Connection conn_points, conn_maxx, conn_maxy;
std::shared_ptr<QObject> ctx { std::make_shared<QObject>() };
mutable QPointF last_input_value{-1, -1};
- mutable std::vector<float> data = std::vector<float>(value_count, magic_fill_value);
+ mutable std::vector<f> data = std::vector<f>(value_count, magic_fill_value);
mutable points_t points;
mutable axis_opts::max_clamp clamp_x = axis_opts::x1000, clamp_y = axis_opts::x1000;
mutable bool activep = false;
static constexpr unsigned value_count = 8192;
- static constexpr float magic_fill_value = -(1 << 24) + 1;
+ static constexpr f magic_fill_value = -(1 << 24) + 1;
static constexpr double c_interp = 5;
public:
diff --git a/tracker-aruco/CMakeLists.txt b/tracker-aruco/CMakeLists.txt
index d3c1f28c..333edb3e 100644
--- a/tracker-aruco/CMakeLists.txt
+++ b/tracker-aruco/CMakeLists.txt
@@ -11,6 +11,15 @@ include(opentrack-opencv)
find_package(OpenCV QUIET)
if(OpenCV_FOUND)
+ set(opencv-modules core calib3d imgproc features2d flann)
+ foreach(k ${opencv-modules})
+ if(NOT TARGET "opencv_${k}")
+ return()
+ endif()
+ endforeach()
+ foreach(k ${opencv-modules})
+ otr_install_lib("opencv_${k}" "${opentrack-libexec}")
+ endforeach()
set(SDK_ARUCO_LIBPATH "" CACHE FILEPATH "Aruco paper marker tracker static library path")
if(SDK_ARUCO_LIBPATH)
set(modules "${SDK_ARUCO_LIBPATH}" opencv_calib3d opencv_imgproc opencv_core)
@@ -22,7 +31,7 @@ if(OpenCV_FOUND)
try_compile(tracker-aruco_has-working-abi "${CMAKE_CURRENT_BINARY_DIR}"
SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/compile-test/abi.cpp"
CMAKE_FLAGS "-DINCLUDE_DIRECTORIES:STRING=${dir}"
- "-DCXX_STANDARD=17" "-DCXX_STANDARD_REQUIRED=1"
+ "-DCXX_STANDARD=20" "-DCXX_STANDARD_REQUIRED=1"
OUTPUT_VARIABLE krap)
if(NOT tracker-aruco_has-working-abi)
message(FATAL_ERROR "${krap}\n" "Must use Aruco fork from <https://github.com/opentrack/aruco>")
diff --git a/tracker-aruco/aruco-trackercontrols.ui b/tracker-aruco/aruco-trackercontrols.ui
index 4d40b6ef..729d4b48 100644
--- a/tracker-aruco/aruco-trackercontrols.ui
+++ b/tracker-aruco/aruco-trackercontrols.ui
@@ -9,8 +9,8 @@
<rect>
<x>0</x>
<y>0</y>
- <width>462</width>
- <height>221</height>
+ <width>457</width>
+ <height>230</height>
</rect>
</property>
<property name="windowTitle">
@@ -64,13 +64,17 @@
<item>
<widget class="QFrame" name="frame">
<layout class="QGridLayout" name="gridLayout_4">
- <item row="2" column="1">
- <widget class="QComboBox" name="cameraName">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
+ <item row="4" column="0">
+ <widget class="QLabel" name="label_11">
+ <property name="text">
+ <string>Resolution</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_10">
+ <property name="text">
+ <string>Camera name</string>
</property>
</widget>
</item>
@@ -93,7 +97,24 @@
</property>
</widget>
</item>
- <item row="3" column="1">
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_12">
+ <property name="text">
+ <string>Frames per second</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QComboBox" name="cameraFPS">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
<widget class="QComboBox" name="resolution">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
@@ -118,32 +139,16 @@
</item>
<item>
<property name="text">
+ <string>1920x1080</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
<string>Default (not recommended!)</string>
</property>
</item>
</widget>
</item>
- <item row="1" column="0">
- <widget class="QLabel" name="label_12">
- <property name="text">
- <string>Frames per second</string>
- </property>
- </widget>
- </item>
- <item row="3" column="0">
- <widget class="QLabel" name="label_11">
- <property name="text">
- <string>Resolution</string>
- </property>
- </widget>
- </item>
- <item row="2" column="0">
- <widget class="QLabel" name="label_10">
- <property name="text">
- <string>Camera name</string>
- </property>
- </widget>
- </item>
<item row="0" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
@@ -151,8 +156,8 @@
</property>
</widget>
</item>
- <item row="1" column="1">
- <widget class="QComboBox" name="cameraFPS">
+ <item row="2" column="1">
+ <widget class="QComboBox" name="cameraName">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -161,7 +166,7 @@
</property>
</widget>
</item>
- <item row="4" column="1">
+ <item row="5" column="1">
<widget class="QPushButton" name="camera_settings">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
@@ -174,6 +179,26 @@
</property>
</widget>
</item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>MJPEG</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QCheckBox" name="use_mjpeg">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
</item>
diff --git a/tracker-aruco/ftnoir_tracker_aruco.cpp b/tracker-aruco/ftnoir_tracker_aruco.cpp
index 6e94e618..5130a889 100644
--- a/tracker-aruco/ftnoir_tracker_aruco.cpp
+++ b/tracker-aruco/ftnoir_tracker_aruco.cpp
@@ -57,6 +57,7 @@ static const resolution_tuple resolution_choices[] =
{ 640, 480 },
{ 320, 240 },
{ 1280, 720 },
+ { 1920, 1080 },
{ 0, 0 }
};
@@ -82,8 +83,8 @@ module_status aruco_tracker::start_tracker(QFrame* videoframe)
videoWidget = std::make_unique<cv_video_widget>(videoframe);
layout = std::make_unique<QHBoxLayout>();
layout->setContentsMargins(0, 0, 0, 0);
- layout->addWidget(videoWidget.get());
- videoframe->setLayout(layout.get());
+ layout->addWidget(&*videoWidget);
+ videoframe->setLayout(&*layout);
videoWidget->show();
start();
@@ -102,8 +103,8 @@ bool aruco_tracker::detect_with_roi()
{
if (last_roi.width > 1 && last_roi.height > 1)
{
- detector.setMinMaxSize(clamp(size_min * grayscale.cols / last_roi.width, .01f, 1.f),
- clamp(size_max * grayscale.cols / last_roi.width, .01f, 1.f));
+ detector.setMinMaxSize(std::clamp(size_min * grayscale.cols / last_roi.width, .01f, 1.f),
+ std::clamp(size_max * grayscale.cols / last_roi.width, .01f, 1.f));
detector.detect(grayscale(last_roi), markers, cv::Mat(), cv::Mat(), -1, false);
@@ -179,6 +180,8 @@ bool aruco_tracker::open_camera()
if (fps)
args.fps = fps;
+ args.use_mjpeg = s.use_mjpeg;
+
if (!camera->start(args))
{
qDebug() << "aruco tracker: can't open camera";
@@ -225,7 +228,7 @@ void aruco_tracker::draw_ar(bool ok)
}
char buf[9];
- ::snprintf(buf, sizeof(buf), "Hz: %d", clamp(int(fps), 0, 9999));
+ ::snprintf(buf, sizeof(buf), "Hz: %d", std::clamp(int(fps), 0, 9999));
cv::putText(frame, buf, cv::Point(10, 32), cv::FONT_HERSHEY_PLAIN, 2, cv::Scalar(0, 255, 0), 1);
}
@@ -379,6 +382,7 @@ void aruco_tracker::run()
if (!res)
{
+ camera_mtx.unlock();
portable::sleep(100);
continue;
}
@@ -388,7 +392,9 @@ void aruco_tracker::run()
switch (img.channels)
{
case 1:
- grayscale.setTo(color); break;
+ grayscale.create(img.height, img.width, CV_8UC1);
+ color.copyTo(grayscale);
+ break;
case 3:
cv::cvtColor(color, grayscale, cv::COLOR_BGR2GRAY);
break;
@@ -523,6 +529,7 @@ aruco_dialog::aruco_dialog() :
tie_setting(s.headpos_x, ui.cx);
tie_setting(s.headpos_y, ui.cy);
tie_setting(s.headpos_z, ui.cz);
+ tie_setting(s.use_mjpeg, ui.use_mjpeg);
connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK()));
connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel()));
diff --git a/tracker-aruco/ftnoir_tracker_aruco.h b/tracker-aruco/ftnoir_tracker_aruco.h
index 3c50ada0..9229a49c 100644
--- a/tracker-aruco/ftnoir_tracker_aruco.h
+++ b/tracker-aruco/ftnoir_tracker_aruco.h
@@ -62,13 +62,14 @@ struct settings : opts {
value<int> resolution { b, "force-resolution", 0 };
value<int> fov { b, "field-of-view", 56 };
value<aruco_fps> force_fps { b, "force-fps", fps_default };
+ value<bool> use_mjpeg { b, "use-mjpeg", false };
settings();
};
class aruco_tracker : protected virtual QThread, public ITracker
{
- Q_OBJECT
+ //Q_OBJECT
static constexpr float c_search_window = 1.3f;
public:
aruco_tracker();
diff --git a/tracker-aruco/lang/de_DE.ts b/tracker-aruco/lang/de_DE.ts
new file mode 100644
index 00000000..f2f7ed7f
--- /dev/null
+++ b/tracker-aruco/lang/de_DE.ts
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>Form</name>
+ <message>
+ <source>Tracker settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Read the &lt;a href=&quot;https://github.com/opentrack/opentrack/wiki/Aruco-tracker&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;wiki page&lt;/span&gt;&lt;/a&gt; and especially the last paragraph before printing markers.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Resolution</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Camera name</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Frames per second</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>640x480</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>320x240</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>1280x720</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>1920x1080</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Default (not recommended!)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Diagonal FOV</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Camera settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>MJPEG</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Head X</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Head Y</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Head Z </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Toggle calibration</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>aruco_dialog</name>
+ <message>
+ <source>Default</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/tracker-aruco/lang/nl_NL.ts b/tracker-aruco/lang/nl_NL.ts
index c9213ea0..e5a94f3b 100644
--- a/tracker-aruco/lang/nl_NL.ts
+++ b/tracker-aruco/lang/nl_NL.ts
@@ -63,6 +63,14 @@
<source>1280x720</source>
<translation>1280x720</translation>
</message>
+ <message>
+ <source>MJPEG</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>1920x1080</source>
+ <translation type="unfinished">1280x1080 {1920x?}</translation>
+ </message>
</context>
<context>
<name>aruco_dialog</name>
diff --git a/tracker-aruco/lang/ru_RU.ts b/tracker-aruco/lang/ru_RU.ts
index f78a7cae..639b59ce 100644
--- a/tracker-aruco/lang/ru_RU.ts
+++ b/tracker-aruco/lang/ru_RU.ts
@@ -63,6 +63,14 @@
<source>1280x720</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>MJPEG</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>1920x1080</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>aruco_dialog</name>
diff --git a/tracker-aruco/lang/stub.ts b/tracker-aruco/lang/stub.ts
index 82ccd296..f213eb41 100644
--- a/tracker-aruco/lang/stub.ts
+++ b/tracker-aruco/lang/stub.ts
@@ -63,6 +63,14 @@
<source>1280x720</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>MJPEG</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>1920x1080</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>aruco_dialog</name>
diff --git a/tracker-aruco/lang/zh_CN.ts b/tracker-aruco/lang/zh_CN.ts
index e060c387..5d5646b7 100644
--- a/tracker-aruco/lang/zh_CN.ts
+++ b/tracker-aruco/lang/zh_CN.ts
@@ -1,47 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
<context>
<name>Form</name>
<message>
<source>Tracker settings</source>
- <translation type="unfinished"></translation>
+ <translation>追踪器设置</translation>
</message>
<message>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Read the &lt;a href=&quot;https://github.com/opentrack/opentrack/wiki/Aruco-tracker&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;wiki page&lt;/span&gt;&lt;/a&gt; and especially the last paragraph before printing markers.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
- <translation type="unfinished"></translation>
+ <translation type="unfinished">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;阅读此&lt;a href=&quot;https://github.com/opentrack/opentrack/wiki/Aruco-tracker&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0077DD;&quot;&gt;Wiki页面&lt;/span&gt;&lt;/a&gt; 特别是打印标记前的最后一段. &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
<source>Diagonal FOV</source>
- <translation type="unfinished"></translation>
+ <translation>对角FOV</translation>
</message>
<message>
<source>640x480</source>
- <translation type="unfinished"></translation>
+ <translation></translation>
</message>
<message>
<source>320x240</source>
- <translation type="unfinished"></translation>
+ <translation></translation>
</message>
<message>
<source>Default (not recommended!)</source>
- <translation type="unfinished"></translation>
+ <translation>默认 (不推荐!)</translation>
</message>
<message>
<source>Camera name</source>
- <translation type="unfinished"></translation>
+ <translation>相机名称</translation>
</message>
<message>
<source>Frames per second</source>
- <translation type="unfinished"></translation>
+ <translation>FPS</translation>
</message>
<message>
<source>Resolution</source>
- <translation type="unfinished"></translation>
+ <translation type="unfinished">分辨率</translation>
</message>
<message>
<source>Camera settings</source>
- <translation type="unfinished"></translation>
+ <translation>相机设置</translation>
</message>
<message>
<source>Head X</source>
@@ -57,12 +57,20 @@
</message>
<message>
<source>Toggle calibration</source>
- <translation type="unfinished"></translation>
+ <translation>切换校准</translation>
</message>
<message>
<source>1280x720</source>
+ <translation></translation>
+ </message>
+ <message>
+ <source>MJPEG</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>1920x1080</source>
+ <translation></translation>
+ </message>
</context>
<context>
<name>aruco_dialog</name>
diff --git a/tracker-easy/CMakeLists.txt b/tracker-easy/CMakeLists.txt
index c377dd9c..ff537877 100644
--- a/tracker-easy/CMakeLists.txt
+++ b/tracker-easy/CMakeLists.txt
@@ -5,17 +5,17 @@ if(OpenCV_FOUND)
try_compile(tracker-easy_ocv-check "${CMAKE_CURRENT_BINARY_DIR}"
SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/ocv-check.cxx"
CMAKE_FLAGS "-DINCLUDE_DIRECTORIES=${OpenCV_INCLUDE_DIRS}"
- "-DCXX_STANDARD=17" "-DCXX_STANDARD_REQUIRED=1"
+ "-DCXX_STANDARD=20" "-DCXX_STANDARD_REQUIRED=1"
OUTPUT_VARIABLE krap)
if(tracker-easy_ocv-check)
- foreach(k video highgui)
+ foreach(k highgui videoio imgcodecs imgproc calib3d video features2d flann)
otr_install_lib("opencv_${k}" "${opentrack-libexec}")
endforeach()
if(CMAKE_COMPILER_IS_GNUCXX)
add_compile_options(-Wno-sign-compare)
elseif(MSVC)
- add_compile_definitions(-wd4018)
+ add_compile_options(-wd4018)
endif()
otr_module(tracker-easy)
diff --git a/tracker-easy/lang/de_DE.ts b/tracker-easy/lang/de_DE.ts
new file mode 100644
index 00000000..81e52cb0
--- /dev/null
+++ b/tracker-easy/lang/de_DE.ts
@@ -0,0 +1,250 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>EasyTracker::Metadata</name>
+ <message>
+ <source>Easy Tracker 1.1</source>
+ <translation>Easy Tracker 1.1</translation>
+ </message>
+</context>
+<context>
+ <name>UICPTClientControls</name>
+ <message>
+ <source>Easy Tracker Settings</source>
+ <translation>Easy-Tracker-Einstellungen</translation>
+ </message>
+ <message>
+ <source>Tracker</source>
+ <translation>Tracker</translation>
+ </message>
+ <message>
+ <source>Camera</source>
+ <translation>Kamera</translation>
+ </message>
+ <message>
+ <source>Desired capture framerate</source>
+ <translation>Angestrebte Aufnahmebildrate</translation>
+ </message>
+ <message>
+ <source> Hz</source>
+ <translation> Hz</translation>
+ </message>
+ <message>
+ <source>Camera settings (when available)</source>
+ <translation>Kamera-Einstellungen (falls verfügbar)</translation>
+ </message>
+ <message>
+ <source>Width</source>
+ <translation>Breite</translation>
+ </message>
+ <message>
+ <source>Height</source>
+ <translation>Höhe</translation>
+ </message>
+ <message>
+ <source>FPS</source>
+ <translation>FPS</translation>
+ </message>
+ <message>
+ <source>Open</source>
+ <translation>Öffnen</translation>
+ </message>
+ <message>
+ <source>°</source>
+ <translation>°</translation>
+ </message>
+ <message>
+ <source>Device</source>
+ <translation>Gerät</translation>
+ </message>
+ <message>
+ <source>Desired capture height</source>
+ <translation>Angestrebte Aufnahmehöhe</translation>
+ </message>
+ <message>
+ <source> px</source>
+ <translation> px</translation>
+ </message>
+ <message>
+ <source>Desired capture width</source>
+ <translation>Angestrebte Aufnahmebreite</translation>
+ </message>
+ <message>
+ <source>Diagonal field of view</source>
+ <translation>Diagonales Sichtfeld</translation>
+ </message>
+ <message>
+ <source>Settings</source>
+ <translation>Einstellungen</translation>
+ </message>
+ <message>
+ <source>Debug (full size preview)</source>
+ <translation>Fehlersuche (Vorschau in voller Größe)</translation>
+ </message>
+ <message>
+ <source>Min size</source>
+ <translation>Minimale Größe</translation>
+ </message>
+ <message>
+ <source>Minimum point diameter</source>
+ <translation>Minimaler Punktdurchmesser</translation>
+ </message>
+ <message>
+ <source>Auto center</source>
+ <translation>Automatisch zentrieren</translation>
+ </message>
+ <message>
+ <source>Max size</source>
+ <translation>Maximale Größe</translation>
+ </message>
+ <message>
+ <source>Size in pixels of half the edge defining deadzone squares around tracked points</source>
+ <translation>Größe in Pixeln der halben Kante, die die Totbereichquadrate um die verfolgten Punkte definiert</translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Use P3P or AP3P for three and four points setup. Use EPNP or ITERATIVE for five points setup. Inconsistent configuration will result in undefined behavior.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Benutze P3P oder AP3P für ein Drei- oder Vierpunkt-Setup. Benutze EPNP oder ITERATIVE für ein Fünfpunkt-Setup. Eine inkonsistente Konfiguration führt zu einem undefinierten Verhalten.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <source>Perspective-N-Point solver</source>
+ <translation>Perspektivischer N-Punkt-Löser</translation>
+ </message>
+ <message>
+ <source>Make sure you pick a solver supporting the number of marker you are using. For three points detection use either P3P or AP3P.</source>
+ <translation>Stelle sicher, einen Löser zu verwenden, der die Anzahl deiner verwendeten Markierungen unterstützt. Für die Erkennung von drei Punkten nutze entweder P3P oder AP3P.</translation>
+ </message>
+ <message>
+ <source>ITERATIVE</source>
+ <translation>ITERATIVE</translation>
+ </message>
+ <message>
+ <source>EPNP</source>
+ <translation>EPNP</translation>
+ </message>
+ <message>
+ <source>P3P</source>
+ <translation>P3P</translation>
+ </message>
+ <message>
+ <source>DLS</source>
+ <translation>DLS</translation>
+ </message>
+ <message>
+ <source>UPNP</source>
+ <translation>UPNP</translation>
+ </message>
+ <message>
+ <source>AP3P</source>
+ <translation>AP3P</translation>
+ </message>
+ <message>
+ <source>Deadzone</source>
+ <translation>Totbereich</translation>
+ </message>
+ <message>
+ <source>Maximum point diameter</source>
+ <translation>Maximaler Punktdurchmesser</translation>
+ </message>
+ <message>
+ <source>Auto center timeout</source>
+ <translation>Timeout für automatische Zentrierung</translation>
+ </message>
+ <message>
+ <source>If no valid pose can be determined after that much time the center pose will be used.</source>
+ <translation>Falls innerhalb dieses Zeitraums keine Pose erkannt wird, wird die zentrierte Pose verwendet.</translation>
+ </message>
+ <message>
+ <source> ms</source>
+ <translation> ms</translation>
+ </message>
+ <message>
+ <source>Model</source>
+ <translation>Modell</translation>
+ </message>
+ <message>
+ <source>Model type:</source>
+ <translation>Modelltyp:</translation>
+ </message>
+ <message>
+ <source>Hat three vertices</source>
+ <translation>Hut drei Eckpunkte</translation>
+ </message>
+ <message>
+ <source>Hat four vertices</source>
+ <translation>Hut vier Eckpunkte</translation>
+ </message>
+ <message>
+ <source>Hat five vertices</source>
+ <translation>Hut fünf Eckpunkte</translation>
+ </message>
+ <message>
+ <source>Clip three vertices</source>
+ <translation>Sticker drei Eckpunkte</translation>
+ </message>
+ <message>
+ <source>Vertices: </source>
+ <translation>Eckpunkte: </translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;X&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;X&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;Y&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;Y&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;Z&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;Z&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <source>Top:</source>
+ <translation>Oben:</translation>
+ </message>
+ <message>
+ <source> mm</source>
+ <translation> mm</translation>
+ </message>
+ <message>
+ <source>Right:</source>
+ <translation>Rechts:</translation>
+ </message>
+ <message>
+ <source>Left:</source>
+ <translation>Links:</translation>
+ </message>
+ <message>
+ <source>Center:</source>
+ <translation>Mitte:</translation>
+ </message>
+ <message>
+ <source>Top right:</source>
+ <translation>Oben rechts:</translation>
+ </message>
+ <message>
+ <source>Top left:</source>
+ <translation>Oben links:</translation>
+ </message>
+ <message>
+ <source>Clip top:</source>
+ <translation>Sticker-Oberkante:</translation>
+ </message>
+ <message>
+ <source>Clip middle:</source>
+ <translation>Sticker-Mitte:</translation>
+ </message>
+ <message>
+ <source>Clip bottom:</source>
+ <translation>Sticker-Unterkante:</translation>
+ </message>
+ <message>
+ <source>About</source>
+ <translation>Über</translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Easy Tracker&lt;br/&gt;Version 1.1&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;by Stéphane Lenclud&lt;/span&gt;&lt;/p&gt;&lt;p&gt;See &lt;a href=&quot;https://github.com/opentrack/opentrack/wiki/Easy-Tracker&quot;&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline; color:#9999aa;&quot;&gt;documentation on GitHub&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Easy Tracker&lt;br/&gt;Version 1.1&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;von Stéphane Lenclud&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Siehe &lt;a href=&quot;https://github.com/opentrack/opentrack/wiki/Easy-Tracker&quot;&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline; color:#9999aa;&quot;&gt;Dokumentation auf GitHub&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+</context>
+</TS>
diff --git a/tracker-easy/preview.cpp b/tracker-easy/preview.cpp
index 1dad7d84..97c8aeaf 100644
--- a/tracker-easy/preview.cpp
+++ b/tracker-easy/preview.cpp
@@ -9,6 +9,7 @@
#include "preview.h"
#include "compat/math.hpp"
+#include "compat/macros.h"
#include <opencv2/imgproc.hpp>
#include <QDebug>
diff --git a/tracker-easy/tracker-easy.cpp b/tracker-easy/tracker-easy.cpp
index 0487d031..7046a918 100644
--- a/tracker-easy/tracker-easy.cpp
+++ b/tracker-easy/tracker-easy.cpp
@@ -56,8 +56,8 @@ namespace EasyTracker
{
opencv_init();
- connect(iSettings.b.get(), &bundle_::saving, this, &Tracker::CheckCamera, Qt::DirectConnection);
- connect(iSettings.b.get(), &bundle_::reloading, this, &Tracker::CheckCamera, Qt::DirectConnection);
+ connect(&*iSettings.b, &bundle_::saving, this, &Tracker::CheckCamera, Qt::DirectConnection);
+ connect(&*iSettings.b, &bundle_::reloading, this, &Tracker::CheckCamera, Qt::DirectConnection);
connect(&iSettings.fov, value_::value_changed<int>(), this, &Tracker::set_fov, Qt::DirectConnection);
set_fov(iSettings.fov);
@@ -842,8 +842,8 @@ namespace EasyTracker
widget = std::make_unique<video_widget>(video_frame);
layout = std::make_unique<QHBoxLayout>(video_frame);
layout->setContentsMargins(0, 0, 0, 0);
- layout->addWidget(widget.get());
- video_frame->setLayout(layout.get());
+ layout->addWidget(&*widget);
+ video_frame->setLayout(&*layout);
//video_widget->resize(video_frame->width(), video_frame->height());
video_frame->show();
diff --git a/tracker-eyeware-beam/CMakeLists.txt b/tracker-eyeware-beam/CMakeLists.txt
new file mode 100644
index 00000000..e041c131
--- /dev/null
+++ b/tracker-eyeware-beam/CMakeLists.txt
@@ -0,0 +1,27 @@
+# The Eyeware Beam SDK can be found at https://beam.eyeware.tech/developers/
+# The latest version can be downloaded at https://eyewarecistorage.blob.core.windows.net/beam-sdk/BeamSDK-Windows64-1.1.0.zip
+set(SDK_EYEWARE_BEAM "" CACHE PATH "Eyeware Beam SDK path")
+if(WIN32 AND SDK_EYEWARE_BEAM)
+ if(MSVC)
+ add_compile_options(-EHsc)
+ endif()
+ otr_module(tracker-eyeware-beam)
+
+ if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4")
+ set(arch "x86")
+ else()
+ set(arch "x64")
+ endif()
+
+ target_include_directories(${self} SYSTEM PRIVATE "${SDK_EYEWARE_BEAM}/API/cpp/include")
+ target_link_directories(${self} PRIVATE "${SDK_EYEWARE_BEAM}/API/cpp/lib/${arch}")
+ set(dll "${SDK_EYEWARE_BEAM}/API/cpp/lib/${arch}/tracker_client.dll")
+ set(lib tracker_client.lib)
+
+ #message(${self})
+ #message(${dll})
+ #message(${lib})
+
+ target_link_libraries(${self} ${lib})
+ install(FILES ${dll} DESTINATION ${opentrack-libexec})
+endif()
diff --git a/tracker-eyeware-beam/eyeware_beam.cpp b/tracker-eyeware-beam/eyeware_beam.cpp
new file mode 100644
index 00000000..f48b5d4d
--- /dev/null
+++ b/tracker-eyeware-beam/eyeware_beam.cpp
@@ -0,0 +1,116 @@
+/* Copyright (c) 2023 Eyeware Tech SA https://www.eyeware.tech
+ *
+ * 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 "eyeware_beam.h"
+
+#include <QMutexLocker>
+
+static constexpr double rad_to_deg = 180.0 / M_PI;
+static constexpr double m_to_cm = 100.0;
+static constexpr double epsilon = 0.000001;
+
+eyeware_beam_tracker::eyeware_beam_tracker()
+{
+}
+
+eyeware_beam_tracker::~eyeware_beam_tracker()
+{
+ QMutexLocker lck(&mtx);
+ release_tracker_instance(tracker_client);
+ tracker_client = nullptr;
+}
+
+module_status eyeware_beam_tracker::start_tracker(QFrame* videoframe)
+{
+ QMutexLocker lck(&mtx);
+ try
+ {
+ tracker_client = create_tracker_instance("127.0.0.1", 12010);
+ }
+ catch (...)
+ {
+ return error("Eyeware Beam initialization has failed");
+ }
+
+ return status_ok();
+}
+
+void eyeware_beam_tracker::extract_translation(const eyeware::Vector3D& t,
+ double& translation_x_cm,
+ double& translation_y_cm,
+ double& translation_z_cm)
+{
+ translation_x_cm = +t.x * m_to_cm;
+ translation_y_cm = -t.y * m_to_cm;
+ translation_z_cm = +t.z * m_to_cm;
+}
+
+void eyeware_beam_tracker::extract_rotation_angles(const eyeware::Matrix3x3& R,
+ double& pitch_deg,
+ double& roll_deg,
+ double& yaw_deg)
+{
+ double r00 = static_cast<double>(R[0][0]);
+ double r01 = static_cast<double>(R[0][1]);
+ double r02 = static_cast<double>(R[0][2]);
+ double r10 = static_cast<double>(R[1][0]);
+ double r11 = static_cast<double>(R[1][1]);
+ double r12 = static_cast<double>(R[1][2]);
+ double r20 = static_cast<double>(R[2][0]);
+ double r21 = static_cast<double>(R[2][1]);
+ double r22 = static_cast<double>(R[2][2]);
+
+ double dy = std::sqrt(r00 * r00 + r10 * r10);
+ last_yaw_deg = -std::atan2(-r20, dy) * rad_to_deg;
+ last_roll_deg = 0.0;
+ if (dy > epsilon)
+ {
+ last_pitch_deg = -std::atan2(r21, r22) * rad_to_deg;
+ last_roll_deg = +std::atan2(r10, r00) * rad_to_deg;
+ }
+ else
+ {
+ last_pitch_deg = -std::atan2(-r12, r11) * rad_to_deg;
+ }
+}
+
+void eyeware_beam_tracker::data(double *data)
+{
+ QMutexLocker lck(&mtx);
+
+ if (connected(tracker_client))
+ {
+ eyeware::HeadPoseInfo head_pose_info = get_head_pose_info(tracker_client);
+ if (!head_pose_info.is_lost)
+ {
+ extract_translation(head_pose_info.transform.translation, last_translation_x_cm,
+ last_translation_y_cm, last_translation_z_cm);
+ extract_rotation_angles(head_pose_info.transform.rotation, last_pitch_deg, last_roll_deg, last_yaw_deg);
+ }
+ }
+
+ data[TX] = last_translation_x_cm;
+ data[TY] = last_translation_y_cm;
+ data[TZ] = last_translation_z_cm;
+ data[Yaw] = last_yaw_deg;
+ data[Pitch] = last_pitch_deg;
+ data[Roll] = last_roll_deg;
+}
+
+eyeware_beam_dialog::eyeware_beam_dialog()
+{
+ ui.setupUi(this);
+
+ connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK()));
+}
+
+void eyeware_beam_dialog::doOK()
+{
+ close();
+}
+
+OPENTRACK_DECLARE_TRACKER(eyeware_beam_tracker, eyeware_beam_dialog, eyeware_beam_metadata)
diff --git a/tracker-eyeware-beam/eyeware_beam.h b/tracker-eyeware-beam/eyeware_beam.h
new file mode 100644
index 00000000..9ebf1f3c
--- /dev/null
+++ b/tracker-eyeware-beam/eyeware_beam.h
@@ -0,0 +1,70 @@
+/* Copyright (c) 2023 Eyeware Tech SA https://www.eyeware.tech
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ */
+
+#pragma once
+
+#include "api/plugin-api.hpp"
+
+#include "ui_eyeware_beam.h"
+
+#include "eyeware/tracker_client.h"
+
+#include <QObject>
+#include <QMutex>
+
+class eyeware_beam_tracker : public QObject, public ITracker
+{
+ Q_OBJECT
+
+public:
+ eyeware_beam_tracker();
+ ~eyeware_beam_tracker() override;
+ module_status start_tracker(QFrame* frame) override;
+ void data(double *data) override;
+
+private:
+ void extract_translation(const eyeware::Vector3D& t,
+ double& translation_x_cm,
+ double& translation_y_cm,
+ double& translation_z_cm);
+ void extract_rotation_angles(const eyeware::Matrix3x3& R, double& pitch_deg, double& roll_deg, double& yaw_deg);
+
+ eyeware::TrackerClient* tracker_client = nullptr;
+
+ QMutex mtx;
+
+ double last_pitch_deg = 0.0;
+ double last_roll_deg = 0.0;
+ double last_yaw_deg = 0.0;
+ double last_translation_x_cm = 0.0;
+ double last_translation_y_cm = 0.0;
+ double last_translation_z_cm = 0.0;
+};
+
+class eyeware_beam_dialog : public ITrackerDialog
+{
+ Q_OBJECT
+
+public:
+ eyeware_beam_dialog();
+ void register_tracker(ITracker * x) override { tracker = static_cast<eyeware_beam_tracker*>(x); }
+ void unregister_tracker() override { tracker = nullptr; }
+
+private:
+ Ui::eyeware_beam_ui ui;
+ eyeware_beam_tracker* tracker = nullptr;
+
+private Q_SLOTS:
+ void doOK();
+};
+
+class eyeware_beam_metadata : public Metadata
+{
+ Q_OBJECT
+ QString name() override { return QString("Eyeware Beam"); }
+ QIcon icon() override { return QIcon(":/images/eyeware_beam_logo.png"); }
+};
diff --git a/tracker-eyeware-beam/eyeware_beam.qrc b/tracker-eyeware-beam/eyeware_beam.qrc
new file mode 100644
index 00000000..ae20865e
--- /dev/null
+++ b/tracker-eyeware-beam/eyeware_beam.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/images">
+ <file>eyeware_beam_logo.png</file>
+ </qresource>
+</RCC>
diff --git a/tracker-eyeware-beam/eyeware_beam.ui b/tracker-eyeware-beam/eyeware_beam.ui
new file mode 100644
index 00000000..475db6a0
--- /dev/null
+++ b/tracker-eyeware-beam/eyeware_beam.ui
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>eyeware_beam_ui</class>
+ <widget class="QWidget" name="eyeware_beam_ui">
+ <property name="windowModality">
+ <enum>Qt::NonModal</enum>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>433</width>
+ <height>180</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="windowTitle">
+ <string>Eyeware Beam</string>
+ </property>
+ <property name="windowIcon">
+ <iconset resource="eyeware_beam.qrc">
+ <normaloff>:/images/eyeware_beam_logo.png</normaloff>:/images/eyeware_beam_logo.png</iconset>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QFrame" name="frame_2">
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <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>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Please make sure the Eyeware Beam application is running and tracking is active.&lt;/p&gt;
+&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;To download, visit &lt;a href=&quot;https://beam.eyeware.tech/opentrack&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;https://beam.eyeware.tech/opentrack&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="openExternalLinks">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </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::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources>
+ <include location="eyeware_beam.qrc"/>
+ </resources>
+ <connections/>
+ <slots>
+ <slot>startEngineClicked()</slot>
+ <slot>stopEngineClicked()</slot>
+ <slot>cameraSettingsClicked()</slot>
+ </slots>
+</ui>
diff --git a/tracker-eyeware-beam/eyeware_beam_logo.png b/tracker-eyeware-beam/eyeware_beam_logo.png
new file mode 100644
index 00000000..6a611cac
--- /dev/null
+++ b/tracker-eyeware-beam/eyeware_beam_logo.png
Binary files differ
diff --git a/tracker-eyeware-beam/lang/de_DE.ts b/tracker-eyeware-beam/lang/de_DE.ts
new file mode 100644
index 00000000..01c6c88d
--- /dev/null
+++ b/tracker-eyeware-beam/lang/de_DE.ts
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>eyeware_beam_ui</name>
+ <message>
+ <source>Eyeware Beam</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:8pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Please make sure the Eyeware Beam application is running and tracking is active.&lt;/p&gt;
+&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;To download, visit &lt;a href=&quot;https://beam.eyeware.tech/opentrack&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;https://beam.eyeware.tech/opentrack&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/tracker-eyeware-beam/lang/nl_NL.ts b/tracker-eyeware-beam/lang/nl_NL.ts
new file mode 100644
index 00000000..d70f58ed
--- /dev/null
+++ b/tracker-eyeware-beam/lang/nl_NL.ts
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="nl_NL">
+<context>
+ <name>eyeware_beam_ui</name>
+ <message>
+ <source>Eyeware Beam</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:8pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Please make sure the Eyeware Beam application is running and tracking is active.&lt;/p&gt;
+&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;To download, visit &lt;a href=&quot;https://beam.eyeware.tech/opentrack&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;https://beam.eyeware.tech/opentrack&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/tracker-eyeware-beam/lang/ru_RU.ts b/tracker-eyeware-beam/lang/ru_RU.ts
new file mode 100644
index 00000000..0bff47ae
--- /dev/null
+++ b/tracker-eyeware-beam/lang/ru_RU.ts
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="ru_RU">
+<context>
+ <name>eyeware_beam_ui</name>
+ <message>
+ <source>Eyeware Beam</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:8pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Please make sure the Eyeware Beam application is running and tracking is active.&lt;/p&gt;
+&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;To download, visit &lt;a href=&quot;https://beam.eyeware.tech/opentrack&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;https://beam.eyeware.tech/opentrack&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/tracker-eyeware-beam/lang/stub.ts b/tracker-eyeware-beam/lang/stub.ts
new file mode 100644
index 00000000..c64ff83e
--- /dev/null
+++ b/tracker-eyeware-beam/lang/stub.ts
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1">
+<context>
+ <name>eyeware_beam_ui</name>
+ <message>
+ <source>Eyeware Beam</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:8pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Please make sure the Eyeware Beam application is running and tracking is active.&lt;/p&gt;
+&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;To download, visit &lt;a href=&quot;https://beam.eyeware.tech/opentrack&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;https://beam.eyeware.tech/opentrack&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/tracker-eyeware-beam/lang/zh_CN.ts b/tracker-eyeware-beam/lang/zh_CN.ts
new file mode 100644
index 00000000..aed44317
--- /dev/null
+++ b/tracker-eyeware-beam/lang/zh_CN.ts
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="zh_CN">
+<context>
+ <name>eyeware_beam_ui</name>
+ <message>
+ <source>Eyeware Beam</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:8pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Please make sure the Eyeware Beam application is running and tracking is active.&lt;/p&gt;
+&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;To download, visit &lt;a href=&quot;https://beam.eyeware.tech/opentrack&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;https://beam.eyeware.tech/opentrack&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/tracker-freepie-udp/ftnoir_tracker_freepie-udp.cpp b/tracker-freepie-udp/ftnoir_tracker_freepie-udp.cpp
index c9d374ed..3950af0c 100644
--- a/tracker-freepie-udp/ftnoir_tracker_freepie-udp.cpp
+++ b/tracker-freepie-udp/ftnoir_tracker_freepie-udp.cpp
@@ -40,9 +40,9 @@ void tracker_freepie::run() {
{
int order[] =
{
- clamp(s.idx_x, 0, 2),
- clamp(s.idx_y, 0, 2),
- clamp(s.idx_z, 0, 2)
+ std::clamp(*s.idx_x, 0, 2),
+ std::clamp(*s.idx_y, 0, 2),
+ std::clamp(*s.idx_z, 0, 2)
};
double orient[3] = {0, 0, 0};
diff --git a/tracker-freepie-udp/lang/de_DE.ts b/tracker-freepie-udp/lang/de_DE.ts
new file mode 100644
index 00000000..27f3bc39
--- /dev/null
+++ b/tracker-freepie-udp/lang/de_DE.ts
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>UI_freepie_udp_dialog</name>
+ <message>
+ <source>Tracker settings</source>
+ <translation>Tracker-Einstellungen</translation>
+ </message>
+ <message>
+ <source>UDP port</source>
+ <translation>UDP-Port</translation>
+ </message>
+ <message>
+ <source>Axis order</source>
+ <translation>Achsen-Reihenfolge</translation>
+ </message>
+ <message>
+ <source>output yaw</source>
+ <translation>Gieren-Ausgabe</translation>
+ </message>
+ <message>
+ <source>input yaw</source>
+ <translation>Gieren-Eingabe</translation>
+ </message>
+ <message>
+ <source>input pitch</source>
+ <translation>Nicken-Eingabe</translation>
+ </message>
+ <message>
+ <source>input roll</source>
+ <translation>Rollen-Eingabe</translation>
+ </message>
+ <message>
+ <source>output pitch</source>
+ <translation>Nicken-Ausgabe</translation>
+ </message>
+ <message>
+ <source>output roll</source>
+ <translation>Rollen-Ausgabe</translation>
+ </message>
+ <message>
+ <source>Add to axis</source>
+ <translation>Zur Achse hinzufügen</translation>
+ </message>
+ <message>
+ <source>yaw</source>
+ <translation>Gieren</translation>
+ </message>
+ <message>
+ <source>0</source>
+ <translation>0</translation>
+ </message>
+ <message>
+ <source>+90</source>
+ <translation>+90</translation>
+ </message>
+ <message>
+ <source>-90</source>
+ <translation>-90</translation>
+ </message>
+ <message>
+ <source>+180</source>
+ <translation>+180</translation>
+ </message>
+ <message>
+ <source>-180</source>
+ <translation>-180</translation>
+ </message>
+ <message>
+ <source>pitch</source>
+ <translation>Nicken</translation>
+ </message>
+ <message>
+ <source>roll</source>
+ <translation>Rollen</translation>
+ </message>
+</context>
+<context>
+ <name>meta_freepie</name>
+ <message>
+ <source>FreePIE UDP receiver</source>
+ <translation>FreePIE-UDP-Empfänger</translation>
+ </message>
+</context>
+</TS>
diff --git a/tracker-freepie-udp/lang/zh_CN.ts b/tracker-freepie-udp/lang/zh_CN.ts
index 1072784a..b886451c 100644
--- a/tracker-freepie-udp/lang/zh_CN.ts
+++ b/tracker-freepie-udp/lang/zh_CN.ts
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
<context>
<name>UI_freepie_udp_dialog</name>
<message>
diff --git a/tracker-fusion/fusion.cpp b/tracker-fusion/fusion.cpp
index 47dc0d13..fba38d3d 100644
--- a/tracker-fusion/fusion.cpp
+++ b/tracker-fusion/fusion.cpp
@@ -14,6 +14,7 @@
#include <QDebug>
#include <QMessageBox>
#include <QApplication>
+#include <cassert>
static const char* own_name = "fusion";
@@ -114,7 +115,7 @@ module_status fusion_tracker::start_tracker(QFrame* frame)
other_frame->setFixedSize(320, 240); // XXX magic frame size
other_frame->setVisible(false);
- rot_tracker->start_tracker(other_frame.get());
+ rot_tracker->start_tracker(&*other_frame);
}
end:
diff --git a/tracker-fusion/lang/de_DE.ts b/tracker-fusion/lang/de_DE.ts
new file mode 100644
index 00000000..3019b655
--- /dev/null
+++ b/tracker-fusion/lang/de_DE.ts
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>fusion_dialog</name>
+ <message>
+ <source>Fusion tracker only works when distinct trackers are selected for rotation and position.</source>
+ <translation>Der Fusion-Tracker funktioniert nur, wenn für Rotation und Position unterschiedliche Tracker ausgewählt wurden.</translation>
+ </message>
+</context>
+<context>
+ <name>fusion_metadata</name>
+ <message>
+ <source>Fusion</source>
+ <translation>Fusion</translation>
+ </message>
+</context>
+<context>
+ <name>fusion_tracker</name>
+ <message>
+ <source>Fusion tracker</source>
+ <translation>Fusion-Tracker</translation>
+ </message>
+ <message>
+ <source>Trackers not selected.</source>
+ <translation>Keine Tracker ausgewählt.</translation>
+ </message>
+ <message>
+ <source>Select different trackers for rotation and position.</source>
+ <translation>Wähle unterschiedliche Tracker für Rotation und Position.</translation>
+ </message>
+</context>
+<context>
+ <name>fusion_ui</name>
+ <message>
+ <source>Fusion</source>
+ <translation>Fusion</translation>
+ </message>
+ <message>
+ <source>Set distinct trackers for rotation and position input.</source>
+ <translation>Wähle unterschiedliche Tracker für Rotations- und Positionseingabe.</translation>
+ </message>
+ <message>
+ <source>Configure the trackers on the main window. It&apos;s required that they&apos;re both distinct, and both are set to something.</source>
+ <translation>Konfiguriere die Tracker im Hauptfenster. Es ist unbedingt erforderlich, dass zwei unterschiedliche Tracker ausgewählt werden, und beides zumindest irgendeinen Tracker auswählt.</translation>
+ </message>
+ <message>
+ <source>Rotation</source>
+ <translation>Rotation</translation>
+ </message>
+ <message>
+ <source>Position</source>
+ <translation>Position</translation>
+ </message>
+</context>
+</TS>
diff --git a/tracker-fusion/lang/zh_CN.ts b/tracker-fusion/lang/zh_CN.ts
index 7aed6201..e1345e4c 100644
--- a/tracker-fusion/lang/zh_CN.ts
+++ b/tracker-fusion/lang/zh_CN.ts
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
<context>
<name>fusion_dialog</name>
<message>
diff --git a/tracker-hatire/CMakeLists.txt b/tracker-hatire/CMakeLists.txt
index 5e18eb2d..e8b34a29 100644
--- a/tracker-hatire/CMakeLists.txt
+++ b/tracker-hatire/CMakeLists.txt
@@ -2,4 +2,7 @@ if(Qt6SerialPort_FOUND)
otr_module(tracker-hatire)
target_link_libraries(${self} ${Qt6SerialPort_LIBRARIES})
target_include_directories(${self} SYSTEM PUBLIC ${Qt6SerialPort_INCLUDE_DIRS})
+ if (WIN32 OR APPLE)
+ otr_install_lib(Qt6::SerialPort .)
+ endif()
endif()
diff --git a/tracker-hatire/ftnoir_hatcontrols.ui b/tracker-hatire/ftnoir_hatcontrols.ui
index ca4cefbc..758d8e51 100644
--- a/tracker-hatire/ftnoir_hatcontrols.ui
+++ b/tracker-hatire/ftnoir_hatcontrols.ui
@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>389</width>
- <height>488</height>
+ <height>497</height>
</rect>
</property>
<property name="minimumSize">
@@ -1136,16 +1136,6 @@ p, li { white-space: pre-wrap; }
<string>Serial Parameters</string>
</property>
<layout class="QGridLayout" name="gridLayout_8">
- <item row="0" column="1">
- <widget class="QComboBox" name="QCB_Serial_baudRate">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- </widget>
- </item>
<item row="3" column="1">
<widget class="QComboBox" name="QCB_Serial_stopBits">
<property name="sizePolicy">
@@ -1163,6 +1153,13 @@ p, li { white-space: pre-wrap; }
</property>
</widget>
</item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="baudRateLabel">
+ <property name="text">
+ <string>BaudRate:</string>
+ </property>
+ </widget>
+ </item>
<item row="2" column="1">
<widget class="QComboBox" name="QCB_Serial_parity">
<property name="sizePolicy">
@@ -1173,13 +1170,10 @@ p, li { white-space: pre-wrap; }
</property>
</widget>
</item>
- <item row="4" column="1">
- <widget class="QComboBox" name="QCB_Serial_flowControl">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
+ <item row="1" column="0">
+ <widget class="QLabel" name="dataBitsLabel">
+ <property name="text">
+ <string>Data bits</string>
</property>
</widget>
</item>
@@ -1190,6 +1184,16 @@ p, li { white-space: pre-wrap; }
</property>
</widget>
</item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="QCB_Serial_baudRate">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
<item row="2" column="0">
<widget class="QLabel" name="parityLabel">
<property name="text">
@@ -1197,15 +1201,18 @@ p, li { white-space: pre-wrap; }
</property>
</widget>
</item>
- <item row="0" column="0">
- <widget class="QLabel" name="baudRateLabel">
- <property name="text">
- <string>BaudRate:</string>
+ <item row="1" column="1">
+ <widget class="QComboBox" name="QCB_Serial_dataBits">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
</property>
</widget>
</item>
- <item row="1" column="1">
- <widget class="QComboBox" name="QCB_Serial_dataBits">
+ <item row="4" column="1">
+ <widget class="QComboBox" name="QCB_Serial_flowControl">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -1214,10 +1221,23 @@ p, li { white-space: pre-wrap; }
</property>
</widget>
</item>
- <item row="1" column="0">
- <widget class="QLabel" name="dataBitsLabel">
+ <item row="5" column="0">
+ <widget class="QLabel" name="label_6">
<property name="text">
- <string>Data bits</string>
+ <string>DTR</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1">
+ <widget class="QCheckBox" name="QCB_Serial_dtr">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
</property>
</widget>
</item>
@@ -1371,6 +1391,7 @@ p, li { white-space: pre-wrap; }
</layout>
</widget>
<tabstops>
+ <tabstop>tabWidget</tabstop>
<tabstop>cbSerialPort</tabstop>
<tabstop>btnZero</tabstop>
<tabstop>btnReset</tabstop>
@@ -1393,24 +1414,27 @@ p, li { white-space: pre-wrap; }
<tabstop>cb_z</tabstop>
<tabstop>chkInvertZ</tabstop>
<tabstop>le_cmd_init</tabstop>
+ <tabstop>spb_BeforeInit</tabstop>
<tabstop>le_cmd_start</tabstop>
+ <tabstop>spb_BeforeStart</tabstop>
<tabstop>spb_AfterStart</tabstop>
<tabstop>le_cmd_stop</tabstop>
<tabstop>le_cmd_center</tabstop>
<tabstop>le_cmd_zero</tabstop>
- <tabstop>le_cmd_reset</tabstop>
<tabstop>spb_Fps</tabstop>
+ <tabstop>le_cmd_reset</tabstop>
<tabstop>cb_Endian</tabstop>
<tabstop>QCB_Serial_baudRate</tabstop>
<tabstop>QCB_Serial_dataBits</tabstop>
<tabstop>QCB_Serial_parity</tabstop>
<tabstop>QCB_Serial_stopBits</tabstop>
<tabstop>QCB_Serial_flowControl</tabstop>
+ <tabstop>QCB_Serial_dtr</tabstop>
+ <tabstop>btn_icone</tabstop>
+ <tabstop>chkEnableLogging</tabstop>
<tabstop>lineSend</tabstop>
<tabstop>btnSend</tabstop>
<tabstop>pteINFO</tabstop>
- <tabstop>tabWidget</tabstop>
- <tabstop>btn_icone</tabstop>
</tabstops>
<resources>
<include location="ftnoir_hat.qrc"/>
diff --git a/tracker-hatire/ftnoir_tracker_hat.cpp b/tracker-hatire/ftnoir_tracker_hat.cpp
index b53a9213..9948c30e 100644
--- a/tracker-hatire/ftnoir_tracker_hat.cpp
+++ b/tracker-hatire/ftnoir_tracker_hat.cpp
@@ -122,7 +122,7 @@ void hatire::data(double *data)
}
for (unsigned k = 0; k < 3; k++)
- HAT.Rot[k] = clamp(HAT.Rot[k], -180, 180);
+ HAT.Rot[k] = std::clamp(HAT.Rot[k], -180.f, 180.f);
const struct
{
diff --git a/tracker-hatire/ftnoir_tracker_hat.h b/tracker-hatire/ftnoir_tracker_hat.h
index b2300556..16e39c7d 100644
--- a/tracker-hatire/ftnoir_tracker_hat.h
+++ b/tracker-hatire/ftnoir_tracker_hat.h
@@ -32,6 +32,7 @@ public:
void send_serial_command(const QByteArray& x);
hatire_thread t;
+
private:
TArduinoData ArduinoData {}, HAT {};
QByteArray Begin;
@@ -42,8 +43,6 @@ private:
int frame_cnt = 0;
std::atomic<int> CptError { 0 };
-
- static inline QByteArray to_latin1(const QString& str) { return str.toLatin1(); }
};
class hatire_metadata : public Metadata
diff --git a/tracker-hatire/ftnoir_tracker_hat_dialog.cpp b/tracker-hatire/ftnoir_tracker_hat_dialog.cpp
index 7cb25d18..7cede5ca 100644
--- a/tracker-hatire/ftnoir_tracker_hat_dialog.cpp
+++ b/tracker-hatire/ftnoir_tracker_hat_dialog.cpp
@@ -32,6 +32,10 @@ dialog_hatire::dialog_hatire() : theTracker(nullptr), timer(this)
ui.QCB_Serial_baudRate->addItem(QLatin1String("38400"),QSerialPort::Baud38400);
ui.QCB_Serial_baudRate->addItem(QLatin1String("57600"),QSerialPort:: Baud57600);
ui.QCB_Serial_baudRate->addItem(QLatin1String("115200"),QSerialPort::Baud115200);
+ ui.QCB_Serial_baudRate->addItem(QLatin1String("230400"),(QSerialPort::BaudRate)230400);
+ ui.QCB_Serial_baudRate->addItem(QLatin1String("250000"),(QSerialPort::BaudRate)250000);
+ ui.QCB_Serial_baudRate->addItem(QLatin1String("500000"),(QSerialPort::BaudRate)500000);
+ ui.QCB_Serial_baudRate->addItem(QLatin1String("1000000"),(QSerialPort::BaudRate)1000000);
ui.QCB_Serial_dataBits->clear();
ui.QCB_Serial_dataBits->addItem(QLatin1String("5"), QSerialPort::Data5);
@@ -91,6 +95,7 @@ dialog_hatire::dialog_hatire() : theTracker(nullptr), timer(this)
tie_setting(s.DelaySeq, ui.spb_AfterStart);
tie_setting(s.BigEndian, ui.cb_Endian);
+ tie_setting(s.pDTR, ui.QCB_Serial_dtr);
tie_setting(s.pBaudRate, ui.QCB_Serial_baudRate);
tie_setting(s.pDataBits, ui.QCB_Serial_dataBits);
diff --git a/tracker-hatire/ftnoir_tracker_hat_settings.h b/tracker-hatire/ftnoir_tracker_hat_settings.h
index a41f906d..b17042c7 100644
--- a/tracker-hatire/ftnoir_tracker_hat_settings.h
+++ b/tracker-hatire/ftnoir_tracker_hat_settings.h
@@ -22,7 +22,7 @@ struct TrackerSettings : opts
value<int> DelayInit, DelayStart, DelaySeq;
- value<bool> BigEndian, EnableLogging;
+ value<bool> BigEndian, EnableLogging, pDTR;
value<QString> QSerialPortName;
@@ -63,6 +63,7 @@ struct TrackerSettings : opts
DelaySeq(b, "after-start-delay", 0),
BigEndian(b, "is-big-endian", false),
EnableLogging(b, "enable-logging", false),
+ pDTR(b, "data-terminal-ready", false),
QSerialPortName(b, "serial-port-name", ""),
pBaudRate(b, "baud-rate", QSerialPort::Baud115200),
pDataBits(b, "data-bits", QSerialPort::Data8),
diff --git a/tracker-hatire/lang/de_DE.ts b/tracker-hatire/lang/de_DE.ts
new file mode 100644
index 00000000..0670cfa7
--- /dev/null
+++ b/tracker-hatire/lang/de_DE.ts
@@ -0,0 +1,353 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>UIHATControls</name>
+ <message>
+ <source>Head Arduino Tracker settings FaceTrackNoIR</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>General</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Serial port</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Zero</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Reset</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Axis Configuration</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Associate Axis</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>RotX</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>RotY</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>RotZ</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Enable Axis</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Pitch:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Enable</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Yaw:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Invert</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Y:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Invert Axis</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>X:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>X</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Y</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Z</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Roll:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Z:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Axis</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Status</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Trame per seconde</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>tps</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Info:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>HAT STOPPED</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Command</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Arduino Commands</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Init</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Start</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Command for Start send sequence</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Command for Initialising Arduino</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Command for Stop send sequence</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Center</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Stop</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Command for read Center Gyro arduino</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Command for Reset Arduino</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Command for reset Center Gyro arduino</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Little or Big Endian for &lt;span style=&quot; font-family:&apos;Arial,Geneva,Helvetica,sans-serif&apos;; font-size:medium; color:#000000;&quot;&gt;the serialization of byte order&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Arial,Geneva,Helvetica,sans-serif&apos;; font-size:medium; color:#000000;&quot;&gt;Arduino is LittleEndian ( unchecked)&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Endian</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Indicate at opentrack speed sketch FPS to adjust CPU &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Delay before Init command in ms&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Delay after Init command in ms&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Delay</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Delay after Start Command in ms&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Delay after startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Serial Parameters</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Flow control</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>BaudRate:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Data bits</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Stop bits</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Parity</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>DTR</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>About</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt; font-weight:600;&quot;&gt;FTNoIR HAT Plugin&lt;br /&gt;by FuraX49&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;a href=&quot;http://hatire.sourceforge.net/&quot;&gt;&lt;span style=&quot; font-size:8pt; font-weight:600; text-decoration: underline; color:#0000ff;&quot;&gt;Manual (external)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Version 1.0.0</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Send</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Disable when not in use, will have a performance impact</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Enable logging to diagnostic file</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>dialog_hatire</name>
+ <message>
+ <source>Version %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>HAT START</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>HAT STOPPED</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>hatire</name>
+ <message>
+ <source>Unable to open ComPort: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Unknown error</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>hatire_metadata</name>
+ <message>
+ <source>Hatire Arduino</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>hatire_thread</name>
+ <message>
+ <source>Timeout during writing command</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>COM port not open</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Setting serial port name</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Opening serial port</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Port Open</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Port Parameters set</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Raising DTR</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Raising RTS</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Waiting on init</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Port setup, waiting for HAT frames to process</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/tracker-hatire/lang/nl_NL.ts b/tracker-hatire/lang/nl_NL.ts
index a1e613b0..af8cbd2c 100644
--- a/tracker-hatire/lang/nl_NL.ts
+++ b/tracker-hatire/lang/nl_NL.ts
@@ -269,6 +269,10 @@ p, li { white-space: pre-wrap; }
<source>Enable logging to diagnostic file</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>DTR</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>dialog_hatire</name>
diff --git a/tracker-hatire/lang/ru_RU.ts b/tracker-hatire/lang/ru_RU.ts
index f7f3024d..16052702 100644
--- a/tracker-hatire/lang/ru_RU.ts
+++ b/tracker-hatire/lang/ru_RU.ts
@@ -269,6 +269,10 @@ p, li { white-space: pre-wrap; }
<source>Enable logging to diagnostic file</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>DTR</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>dialog_hatire</name>
diff --git a/tracker-hatire/lang/stub.ts b/tracker-hatire/lang/stub.ts
index 0d22bf81..90c8e616 100644
--- a/tracker-hatire/lang/stub.ts
+++ b/tracker-hatire/lang/stub.ts
@@ -269,6 +269,10 @@ p, li { white-space: pre-wrap; }
<source>Enable logging to diagnostic file</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>DTR</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>dialog_hatire</name>
diff --git a/tracker-hatire/lang/zh_CN.ts b/tracker-hatire/lang/zh_CN.ts
index 0d22bf81..d80cfa28 100644
--- a/tracker-hatire/lang/zh_CN.ts
+++ b/tracker-hatire/lang/zh_CN.ts
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
<context>
<name>UIHATControls</name>
<message>
@@ -269,6 +269,10 @@ p, li { white-space: pre-wrap; }
<source>Enable logging to diagnostic file</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>DTR</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>dialog_hatire</name>
diff --git a/tracker-hatire/thread.cpp b/tracker-hatire/thread.cpp
index 4938e77f..7d61a90a 100644
--- a/tracker-hatire/thread.cpp
+++ b/tracker-hatire/thread.cpp
@@ -71,7 +71,7 @@ void hatire_thread::Log(const QString& message)
{
QTextStream out(&flDiagnostics);
QString milliSeconds;
- milliSeconds = QString("%1").arg(QTime::currentTime().msec(), 3, 10, QChar('0'));
+ milliSeconds = QStringLiteral("%1").arg(QTime::currentTime().msec(), 3, 10, QChar('0'));
// We have a file
out << QTime::currentTime().toString() << "." << milliSeconds << ": " << message << "\r\n";
flDiagnostics.close();
@@ -111,7 +111,7 @@ void hatire_thread::teardown_serial()
{
QByteArray msg;
Log("Tracker shut down");
- com_port.write(to_latin1(s.CmdStop));
+ com_port.write(s.CmdStop->toUtf8());
if (!com_port.waitForBytesWritten(1000))
{
emit serial_debug_info("TimeOut in writing CMD");
@@ -120,7 +120,7 @@ void hatire_thread::teardown_serial()
{
msg.append("\r\n");
msg.append("SEND '");
- msg.append(s.CmdStop);
+ msg.append(s.CmdStop->toUtf8());
msg.append("'\r\n");
}
emit serial_debug_info(msg);
@@ -168,6 +168,7 @@ serial_result hatire_thread::init_serial_port_impl()
&& com_port.setParity((QSerialPort::Parity)s.pParity)
&& com_port.setStopBits((QSerialPort::StopBits)s.pStopBits)
&& com_port.setFlowControl((QSerialPort::FlowControl)s.pFlowControl)
+ && com_port.setDataTerminalReady(s.pDTR)
&& com_port.clear(QSerialPort::AllDirections)
)
{
@@ -200,7 +201,7 @@ serial_result hatire_thread::init_serial_port_impl()
}
Log(tr("Waiting on init"));
qDebug() << QTime::currentTime() << " HAT send INIT ";
- sendcmd_str(s.CmdInit);
+ emit sendcmd_str(s.CmdInit);
// Wait init MPU sequence
for (int i = 1; i <= s.DelayStart; i+=50)
{
@@ -208,7 +209,7 @@ serial_result hatire_thread::init_serial_port_impl()
}
// Send START cmd to IMU
qDebug() << QTime::currentTime() << " HAT send START ";
- sendcmd_str(s.CmdStart);
+ emit sendcmd_str(s.CmdStart);
// Wait start MPU sequence
for (int i = 1; i <=s.DelaySeq; i+=50)
@@ -241,13 +242,13 @@ void hatire_thread::serial_info_impl()
if (com_port.isOpen())
{
msg.append("\r\n");
- msg.append(com_port.portName());
+ msg.append(com_port.portName().toUtf8());
msg.append("\r\n");
msg.append("BAUDRATE :");
- msg.append(QString::number(com_port.baudRate()));
+ msg.append(QString::number(com_port.baudRate()).toLatin1());
msg.append("\r\n");
msg.append("DataBits :");
- msg.append(QString::number(com_port.dataBits()));
+ msg.append(QString::number(com_port.dataBits()).toLatin1());
msg.append("\r\n");
msg.append("Parity :");
diff --git a/tracker-hatire/thread.hpp b/tracker-hatire/thread.hpp
index 5cecbdce..f6bd8d49 100644
--- a/tracker-hatire/thread.hpp
+++ b/tracker-hatire/thread.hpp
@@ -50,7 +50,6 @@ class hatire_thread : public QThread
char buf[1024];
void run() override;
- static inline QByteArray to_latin1(const QString& str) { return str.toLatin1(); }
void serial_debug_info_str(const QString& str);
diff --git a/tracker-hydra/lang/de_DE.ts b/tracker-hydra/lang/de_DE.ts
new file mode 100644
index 00000000..dba66a05
--- /dev/null
+++ b/tracker-hydra/lang/de_DE.ts
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>UIHydraControls</name>
+ <message>
+ <source>Sixense Hydra tracker settings FaceTrackNoIR</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>None whatsoever</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/tracker-hydra/lang/zh_CN.ts b/tracker-hydra/lang/zh_CN.ts
index f5440284..f1257856 100644
--- a/tracker-hydra/lang/zh_CN.ts
+++ b/tracker-hydra/lang/zh_CN.ts
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
<context>
<name>UIHydraControls</name>
<message>
diff --git a/tracker-joystick/ftnoir_tracker_joystick.cpp b/tracker-joystick/ftnoir_tracker_joystick.cpp
index bd7e51ed..940abfd1 100644
--- a/tracker-joystick/ftnoir_tracker_joystick.cpp
+++ b/tracker-joystick/ftnoir_tracker_joystick.cpp
@@ -56,8 +56,8 @@ void joystick::data(double *data)
if (k < 0 || k >= 8)
data[i] = 0;
else
- data[i] = clamp(axes[k] * limits[i] / AXIS_MAX,
- -limits[i], limits[i]);
+ data[i] = std::clamp(axes[k] * limits[i] / AXIS_MAX,
+ -limits[i], limits[i]);
}
}
}
diff --git a/tracker-joystick/lang/de_DE.ts b/tracker-joystick/lang/de_DE.ts
new file mode 100644
index 00000000..a99afd81
--- /dev/null
+++ b/tracker-joystick/lang/de_DE.ts
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>UIJoystickControls</name>
+ <message>
+ <source>Tracker settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Device</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Mapping</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Disabled</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Joystick axis #1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Joystick axis #2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Joystick axis #3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Joystick axis #4</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Joystick axis #5</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Joystick axis #6</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Joystick axis #7</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Joystick axis #8</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>X</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Y</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Z</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Yaw</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Pitch</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Roll</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>joystickDll</name>
+ <message>
+ <source>Joystick input</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/tracker-joystick/lang/zh_CN.ts b/tracker-joystick/lang/zh_CN.ts
index a169a05c..b4729a64 100644
--- a/tracker-joystick/lang/zh_CN.ts
+++ b/tracker-joystick/lang/zh_CN.ts
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
<context>
<name>UIJoystickControls</name>
<message>
diff --git a/tracker-kinect-face/CMakeLists.txt b/tracker-kinect-face/CMakeLists.txt
index 0a110e5a..86429bd2 100644
--- a/tracker-kinect-face/CMakeLists.txt
+++ b/tracker-kinect-face/CMakeLists.txt
@@ -34,12 +34,12 @@ if (WIN32 AND opentrack-intel)
# Install Kinect Face Database
install(DIRECTORY "${SDK_KINECT20}/Redist/Face/${kinect-arch-dir}/NuiDatabase" DESTINATION "${opentrack-libexec}")
- set(redist-dir "${CMAKE_SOURCE_DIR}/redist/${kinect-arch-dir}")
- install(
- FILES "${redist-dir}/msvcp110.dll" "${redist-dir}/msvcr110.dll"
- DESTINATION "${opentrack-libexec}"
- PERMISSIONS ${opentrack-perms-exec}
- )
+ #set(redist-dir "${CMAKE_SOURCE_DIR}/redist/${kinect-arch-dir}")
+ #install(
+ # FILES "${redist-dir}/msvcp110.dll" "${redist-dir}/msvcr110.dll"
+ # DESTINATION "${opentrack-libexec}"
+ # PERMISSIONS ${opentrack-perms-exec}
+ #)
# Optional OpenCV support
# Needed for Point Tracker to support Kinect V2 IR Sensor
diff --git a/tracker-kinect-face/camera_kinect_ir.cpp b/tracker-kinect-face/camera_kinect_ir.cpp
index 81357a6d..3a33fd14 100644
--- a/tracker-kinect-face/camera_kinect_ir.cpp
+++ b/tracker-kinect-face/camera_kinect_ir.cpp
@@ -48,7 +48,12 @@ namespace Kinect {
std::vector<QString> CamerasProvider::camera_names() const
{
- if (camera_name_to_index("Kinect V2 Video Sensor") != -1)
+ auto list = get_camera_names();
+ auto it = std::find_if(list.cbegin(), list.cend(), [](const auto& x) {
+ const auto& [name, idx] = x;
+ return name.startsWith("Kinect V2 Video Sensor [");
+ });
+ if (it != list.cend())
{
// We found Kinect V2 Video Sensor therefore we have a kinect V2 connected.
// Publish our Kinect V2 IR Sensor implementation then.
@@ -232,6 +237,7 @@ namespace Kinect {
// Release previous frame if any
SafeRelease(iInfraredFrame);
+ Sleep(34); // FIXME
HRESULT hr = iInfraredFrameReader->AcquireLatestFrame(&iInfraredFrame);
if (SUCCEEDED(hr))
diff --git a/tracker-kinect-face/kinect_face_settings.h b/tracker-kinect-face/kinect_face_settings.h
index 40af1eb7..2c5cc55f 100644
--- a/tracker-kinect-face/kinect_face_settings.h
+++ b/tracker-kinect-face/kinect_face_settings.h
@@ -8,7 +8,6 @@
#pragma once
#include "ui_kinect_face_settings.h"
-#include "compat/macros.hpp"
#include "api/plugin-api.hpp"
class KinectFaceSettings : public ITrackerDialog
diff --git a/tracker-kinect-face/kinect_face_tracker.cpp b/tracker-kinect-face/kinect_face_tracker.cpp
index e7b77133..1b43681c 100644
--- a/tracker-kinect-face/kinect_face_tracker.cpp
+++ b/tracker-kinect-face/kinect_face_tracker.cpp
@@ -1,4 +1,4 @@
-/* Copyright (c) 2019, Stphane Lenclud <github@lenclud.com>
+/* Copyright (c) 2019, Stéphane Lenclud <github@lenclud.com>
* Permission to use, copy, modify, and/or distribute this
* software for any purpose with or without fee is hereby granted,
@@ -136,8 +136,8 @@ module_status KinectFaceTracker::start_tracker(QFrame* aFrame)
iVideoWidget = std::make_unique<video_widget>(aFrame);
iLayout = std::make_unique<QHBoxLayout>(aFrame);
iLayout->setContentsMargins(0, 0, 0, 0);
- iLayout->addWidget(iVideoWidget.get());
- aFrame->setLayout(iLayout.get());
+ iLayout->addWidget(&*iVideoWidget);
+ aFrame->setLayout(&*iLayout);
//video_widget->resize(video_frame->width(), video_frame->height());
aFrame->show();
@@ -187,7 +187,7 @@ void KinectFaceTracker::data(double *data)
{
//OutputDebugStringA("Skipping frame!\n");
}
-
+
// Feed our framework our last valid position and rotation
data[0] = (iLastFacePosition.X - iFacePositionCenter.X) * 100; // Convert to centimer to be in a range that suites OpenTrack.
data[1] = (iLastFacePosition.Y - iFacePositionCenter.Y) * 100;
@@ -201,7 +201,7 @@ void KinectFaceTracker::data(double *data)
/// <summary>
-/// Converts rotation quaternion to Euler angles
+/// Converts rotation quaternion to Euler angles
/// And then maps them to a specified range of values to control the refresh rate
/// </summary>
/// <param name="pQuaternion">face rotation quaternion</param>
@@ -215,7 +215,7 @@ void KinectFaceTracker::ExtractFaceRotationInDegrees(const Vector4* pQuaternion,
double z = pQuaternion->z;
double w = pQuaternion->w;
- // convert face rotation quaternion to Euler angles in degrees
+ // convert face rotation quaternion to Euler angles in degrees
double dPitch, dYaw, dRoll;
dPitch = atan2(2 * (y * z + w * x), w * w - x * x - y * y + z * z) / M_PI * 180.0;
dYaw = asin(2 * (w * y - x * z)) / M_PI * 180.0;
@@ -251,7 +251,7 @@ HRESULT KinectFaceTracker::InitializeDefaultSensor()
hr = iKinectSensor->Open();
}
- // Create color frame reader
+ // Create color frame reader
if (SUCCEEDED(hr))
{
UniqueInterface<IColorFrameSource> colorFrameSource;
@@ -263,8 +263,8 @@ HRESULT KinectFaceTracker::InitializeDefaultSensor()
hr = colorFrameSource->OpenReader(&iColorFrameReader);
}
}
-
- // Create body frame reader
+
+ // Create body frame reader
if (SUCCEEDED(hr))
{
UniqueInterface<IBodyFrameSource> bodyFrameSource;
@@ -339,7 +339,7 @@ void KinectFaceTracker::Update()
if (SUCCEEDED(hr))
{
hr = pColorFrame->get_RawColorImageFormat(&imageFormat);
- }
+ }
if (SUCCEEDED(hr))
{
@@ -373,7 +373,7 @@ void KinectFaceTracker::Update()
if (SUCCEEDED(hr))
{
- // Setup our image
+ // Setup our image
QImage image((const unsigned char*)pBuffer, KColorWidth, KColorHeight, sizeof(RGBQUAD)*KColorWidth, QImage::Format_RGBA8888);
if (IsValidRect(iFaceBox))
{
@@ -555,7 +555,7 @@ void KinectFaceTracker::ProcessFaces()
//IFaceFrameResult* pFaceFrameResult = nullptr;
IFaceAlignment* pFaceAlignment = nullptr;
CreateFaceAlignment(&pFaceAlignment); // TODO: check return?
- //D2D1_POINT_2F faceTextLayout;
+ //D2D1_POINT_2F faceTextLayout;
//hr = pFaceFrame->get_FaceFrameResult(&pFaceFrameResult);
@@ -610,5 +610,3 @@ void KinectFaceTracker::ProcessFaces()
}
}
}
-
-
diff --git a/tracker-kinect-face/kinect_face_tracker.h b/tracker-kinect-face/kinect_face_tracker.h
index 8070c519..83b58d71 100644
--- a/tracker-kinect-face/kinect_face_tracker.h
+++ b/tracker-kinect-face/kinect_face_tracker.h
@@ -12,7 +12,6 @@
#include "api/plugin-api.hpp"
#include "compat/timer.hpp"
-#include "compat/macros.hpp"
#include "video/video-widget.hpp"
// Kinect Header files
diff --git a/tracker-kinect-face/lang/de_DE.ts b/tracker-kinect-face/lang/de_DE.ts
new file mode 100644
index 00000000..c3795d79
--- /dev/null
+++ b/tracker-kinect-face/lang/de_DE.ts
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>KinectFaceMetadata</name>
+ <message>
+ <source>Kinect Face 0.1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>KinectFaceSettings</name>
+ <message>
+ <source>Kinect license</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>KinectFaceUi</name>
+ <message>
+ <source>Kinect Face Tracker</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;justify&quot;&gt;Start OpenTrack to check if Kinect Face Tracker is working.&lt;/p&gt;&lt;p align=&quot;justify&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note&lt;/span&gt;: When using OpenTrack with a Kinect for Windows v2 Sensor, Microsoft will collect telemetry data (e.g. operating system, number of processors, graphic chipset, memory, device type, locale, time) in order to improve Microsoft products and services. The data will not be used to identify specific individuals.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/tracker-kinect-face/lang/zh_CN.ts b/tracker-kinect-face/lang/zh_CN.ts
index de0e4f95..1e1b55d1 100644
--- a/tracker-kinect-face/lang/zh_CN.ts
+++ b/tracker-kinect-face/lang/zh_CN.ts
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
<context>
<name>KinectFaceMetadata</name>
<message>
diff --git a/tracker-linux-joystick/ftnoir_tracker_linux_joystick.cpp b/tracker-linux-joystick/ftnoir_tracker_linux_joystick.cpp
index 8fa600e7..54d9b059 100644
--- a/tracker-linux-joystick/ftnoir_tracker_linux_joystick.cpp
+++ b/tracker-linux-joystick/ftnoir_tracker_linux_joystick.cpp
@@ -78,8 +78,8 @@ void joystick::data(double *data)
if (k < 0 || k >= 8)
data[i] = 0;
else
- data[i] = clamp(axes[k] * limits[i] / AXIS_MAX,
- -limits[i], limits[i]);
+ data[i] = std::clamp(axes[k] * limits[i] / AXIS_MAX,
+ -limits[i], limits[i]);
}
}
}
diff --git a/tracker-linux-joystick/lang/de_DE.ts b/tracker-linux-joystick/lang/de_DE.ts
new file mode 100644
index 00000000..9f03ffe9
--- /dev/null
+++ b/tracker-linux-joystick/lang/de_DE.ts
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>UILinuxJoystickControls</name>
+ <message>
+ <source>Tracker settings</source>
+ <translation>Tracker-Einstellungen</translation>
+ </message>
+ <message>
+ <source>Device</source>
+ <translation>Gerät</translation>
+ </message>
+ <message>
+ <source>Mapping</source>
+ <translation>Abbildung</translation>
+ </message>
+ <message>
+ <source>Disabled</source>
+ <translation>Ausgeschaltet</translation>
+ </message>
+ <message>
+ <source>Joystick axis #1</source>
+ <translation>Joystick-Achse #1</translation>
+ </message>
+ <message>
+ <source>Joystick axis #2</source>
+ <translation>Joystick-Achse #2</translation>
+ </message>
+ <message>
+ <source>Joystick axis #3</source>
+ <translation>Joystick-Achse #3</translation>
+ </message>
+ <message>
+ <source>Joystick axis #4</source>
+ <translation>Joystick-Achse #4</translation>
+ </message>
+ <message>
+ <source>Joystick axis #5</source>
+ <translation>Joystick-Achse #5</translation>
+ </message>
+ <message>
+ <source>Joystick axis #6</source>
+ <translation>Joystick-Achse #6</translation>
+ </message>
+ <message>
+ <source>Joystick axis #7</source>
+ <translation>Joystick-Achse #7</translation>
+ </message>
+ <message>
+ <source>Joystick axis #8</source>
+ <translation>Joystick-Achse #8</translation>
+ </message>
+ <message>
+ <source>X</source>
+ <translation>X</translation>
+ </message>
+ <message>
+ <source>Y</source>
+ <translation>Y</translation>
+ </message>
+ <message>
+ <source>Z</source>
+ <translation>Z</translation>
+ </message>
+ <message>
+ <source>Yaw</source>
+ <translation>Gieren</translation>
+ </message>
+ <message>
+ <source>Pitch</source>
+ <translation>Nicken</translation>
+ </message>
+ <message>
+ <source>Roll</source>
+ <translation>Rollen</translation>
+ </message>
+</context>
+<context>
+ <name>joystickDll</name>
+ <message>
+ <source>Linux Joystick input</source>
+ <translation>Linux-Joystick-Eingabe</translation>
+ </message>
+</context>
+</TS>
diff --git a/tracker-linux-joystick/lang/zh_CN.ts b/tracker-linux-joystick/lang/zh_CN.ts
index 12dc1400..e7813c3a 100644
--- a/tracker-linux-joystick/lang/zh_CN.ts
+++ b/tracker-linux-joystick/lang/zh_CN.ts
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
<context>
<name>UILinuxJoystickControls</name>
<message>
diff --git a/tracker-linux-joystick/linux_joystick.cpp b/tracker-linux-joystick/linux_joystick.cpp
index 49718b52..2374451b 100644
--- a/tracker-linux-joystick/linux_joystick.cpp
+++ b/tracker-linux-joystick/linux_joystick.cpp
@@ -3,6 +3,7 @@
#include <QDir>
#include <QFileInfo>
#include <QVariant>
+#include <QRegularExpression>
// Discovery is done by searching for devices in the sys file system.
//
@@ -16,7 +17,7 @@ std::tuple<QString, QString> sysfsDeviceToJsDev(QFileInfo device) {
QString symlink = device.symLinkTarget();
QString js_dev = QString("/dev/input/%1").arg(device.fileName());
- QRegExp sep(QString("[:.%1]").arg(QDir::separator()));
+ QRegularExpression sep(QString("[:.%1]").arg(QDir::separator()));
QString device_id = symlink.section(sep, -6, -5);
return ret(js_dev, device_id);
}
diff --git a/tracker-neuralnet/BUILD.md b/tracker-neuralnet/BUILD.md
index 8bb694dd..b8994b00 100644
--- a/tracker-neuralnet/BUILD.md
+++ b/tracker-neuralnet/BUILD.md
@@ -9,26 +9,12 @@ Source location: https://github.com/microsoft/onnxruntime
In order to build, execute `build.bat` as follows:
```
-$ build.bat --config RelWithDebInfo --x86 --build_dir .\buildx86\ \
- --enable_msvc_static_runtime --build_shared_lib --skip_tests \
- --cmake_generator "Visual Studio 15 2017"
+$ build.bat --config Release --x86 --cmake_extra_defines CMAKE_INSTALL_PREFIX="D:\Dev\onnxruntime-x86-release" --build_dir .\buildx86\ --enable_msvc_static_runtime --build_shared_lib --skip_tests --cmake_generator "Visual Studio 15 2017"
+$ cmake --install .\buildx64\Release
```
-Replace the argument for `--cmake_generator` if needed.
+Replace the argument for `--cmake_generator` if needed. Also adjust the build-and install directories.
-The result is a messy directory `buildx86\RelWithDebInfo\RelWithDebInfo`,
-but no proper distribution. However only a few files are needed. They can
-be copied manually and are listed in the following in their respective folders:
+This should place all required files in the directory specified by CMAKE_INSTALL_PREFIX.
-```
-onnxruntime-x86-release/include:
-cpu_provider_factory.h onnxruntime_cxx_api.h
-experimental_onnxruntime_cxx_api.h onnxruntime_cxx_inline.h
-experimental_onnxruntime_cxx_inline.h onnxruntime_session_options_config_keys.h
-onnxruntime_c_api.h
-
-onnxruntime-x86-release/lib:
-onnxruntime.dll onnxruntime.exp onnxruntime.lib onnxruntime.pdb
-```
-
-See also https://www.onnxruntime.ai/docs/how-to/build.html
+See also https://www.onnxruntime.ai/docs/how-to/build.html. \ No newline at end of file
diff --git a/tracker-neuralnet/CMakeLists.txt b/tracker-neuralnet/CMakeLists.txt
index d06f0c83..3729c789 100644
--- a/tracker-neuralnet/CMakeLists.txt
+++ b/tracker-neuralnet/CMakeLists.txt
@@ -1,23 +1,49 @@
include(opentrack-opencv)
+set(host-spec "${CMAKE_SYSTEM_NAME} ${CMAKE_SYSTEM_PROCESSOR} ${CMAKE_SIZEOF_VOID_P}")
+if(host-spec MATCHES "^Linux i[3-6]86 4$")
+ return()
+endif()
+
find_package(OpenCV QUIET)
find_package(OpenMP QUIET) # Used to control number of onnx threads.
-set(SDK_ONNX_LIBPATH "" CACHE FILEPATH "Full path of onnx library")
+find_package(ONNXRuntime QUIET)
-if(OpenCV_FOUND AND SDK_ONNX_LIBPATH AND OpenMP_FOUND)
- get_filename_component(ONNX_INCLUDE_DIR "${SDK_ONNX_LIBPATH}" DIRECTORY)
- get_filename_component(ONNX_INCLUDE_DIR "${ONNX_INCLUDE_DIR}" ABSOLUTE)
- set(ONNX_INCLUDE_DIR "${ONNX_INCLUDE_DIR}/../include")
+if(OpenCV_FOUND AND ONNXRuntime_FOUND AND OpenMP_FOUND)
+ if(MSVC)
+ add_compile_options(-EHsc)
+ add_definitions(-D_HAS_EXCEPTIONS=1)
+ endif()
otr_module(tracker-neuralnet)
- target_include_directories(${self} SYSTEM PUBLIC
- ${OpenCV_INCLUDE_DIRS} "${ONNX_INCLUDE_DIR}")
- target_link_libraries(${self}
- opentrack-cv "${SDK_ONNX_LIBPATH}" opencv_imgproc opencv_core
- opencv_imgcodecs opencv_calib3d
- OpenMP::OpenMP_C)
+
+ target_link_libraries(${self}
+ opentrack-cv
+ onnxruntime::onnxruntime
+ opencv_calib3d
+ opencv_imgproc
+ opencv_imgcodecs
+ opencv_core
+ OpenMP::OpenMP_CXX
+ )
+
+ # OpenMP::OpenMP_CXX doesn't set up the -fopenmp linking option, so set it up ourselves.
+ if(NOT MSVC)
+ target_link_options(${self} PUBLIC ${OpenMP_CXX_FLAGS})
+ endif()
install(
- FILES "models/head-localizer.onnx" "models/head-pose.onnx"
+ FILES "models/head-localizer.onnx"
+ "models/head-pose-0.2-big.onnx"
+ "models/head-pose-0.2-small.onnx"
+ "models/head-pose-0.3-big-quantized.onnx"
DESTINATION "${opentrack-libexec}/models"
- PERMISSIONS ${opentrack-perms-file})
-endif() \ No newline at end of file
+ PERMISSIONS ${opentrack-perms-file}
+ )
+
+ if(WIN32)
+ otr_install_lib("${ONNXRuntime_RUNTIME}" ".")
+ endif()
+ if(MSVC)
+ otr_install_lib("redist/vcomp140.dll" "${opentrack-bin}")
+ endif()
+endif()
diff --git a/tracker-neuralnet/deadzone_filter.cpp b/tracker-neuralnet/deadzone_filter.cpp
new file mode 100644
index 00000000..fa96eeb3
--- /dev/null
+++ b/tracker-neuralnet/deadzone_filter.cpp
@@ -0,0 +1,173 @@
+#include "deadzone_filter.h"
+#include "model_adapters.h"
+#include "opencv_contrib.h"
+#include "unscented_trafo.h"
+
+#include <tuple>
+#include <opencv2/core/base.hpp>
+#include <opencv2/core/matx.hpp>
+#include <opencv2/core/quaternion.hpp>
+
+namespace neuralnet_tracker_ns
+{
+
+using namespace cvcontrib;
+
+// Number of degrees of freedom of position and rotation
+static constexpr int dofs = 6;
+
+using StateVec = cv::Vec<float,dofs>;
+using StateCov = cv::Matx<float,dofs,dofs>;
+
+static constexpr int num_sigmas = ukf_cv::MerweScaledSigmaPoints<dofs>::num_sigmas;
+// Rescaling factor for position/size living in the space of the face crop.
+// Applied prior to application of UKF to prevent numerical problems.
+static constexpr float img_scale = 200.f;
+// Similar rescaling factor for position/size that live in world space.
+static constexpr float world_scale = 1000.f; // mm
+
+// Fills the 6 DoF covariance factor, as in L L^T factorization.
+// Covariance is given wrt the tangent space of current predictions
+StateCov make_tangent_space_uncertainty_tril(const PoseEstimator::Face &face)
+{
+ StateCov tril = StateCov::eye();
+ set_minor<3,3>(tril, 0, 0, face.center_size_cov_tril / img_scale);
+ set_minor<3,3>(tril, 3, 3, face.rotaxis_cov_tril);
+ return tril;
+}
+
+
+QuatPose apply_offset(const QuatPose& pose, const StateVec& offset)
+{
+ // Unpack
+ const cv::Vec3f dp = { offset[0], offset[1], offset[2] };
+ const cv::Quatf dr = cv::Quatf::createFromRvec(cv::Vec3f{ offset[3], offset[4], offset[5] });
+ const auto p = pose.pos + dp;
+ const auto r = pose.rot * dr;
+ return { r, p };
+}
+
+
+std::tuple<cv::Quatf, cv::Point2f, float> apply_offset(const PoseEstimator::Face& face, const StateVec& offset)
+{
+ const cv::Quatf dr = cv::Quatf::createFromRvec(cv::Vec3f{ offset[3], offset[4], offset[5] });
+ const auto r = face.rotation * dr;
+
+ const cv::Point2f p = {
+ face.center.x + offset[0]*img_scale,
+ face.center.y + offset[1]*img_scale
+ };
+
+ // Intercept the case where the head size stddev became so large that the sigma points
+ // were created with negative head size (mean - constant*stddev ...). Negative head size
+ // is bad. But this is fine. The unscented transform where this function comes into play
+ // is designed to handle non-linearities like this.
+ const float sz = std::max(0.1f*face.size, face.size + offset[2]*img_scale);
+
+ return {
+ r,
+ p,
+ sz,
+ };
+}
+
+
+StateVec relative_to(const QuatPose& reference, const QuatPose& pose)
+{
+ const auto p = pose.pos - reference.pos;
+ const auto r = toRotVec(reference.rot.conjugate()*pose.rot);
+ return StateVec{ p[0], p[1], p[2], r[0], r[1], r[2] };
+}
+
+
+ukf_cv::SigmaPoints<dofs> relative_to(const QuatPose& pose, const std::array<QuatPose,num_sigmas>& sigmas)
+{
+ ukf_cv::SigmaPoints<dofs> out; // Beware, the number of points is != the number of DoFs.
+ std::transform(sigmas.begin(), sigmas.end(), out.begin(), [&pose](const QuatPose& s) {
+ return relative_to(pose, s);
+ });
+ return out;
+}
+
+
+std::array<QuatPose,num_sigmas> compute_world_pose_from_sigma_point(const PoseEstimator::Face& face, const ukf_cv::SigmaPoints<dofs>& sigmas, Face2WorldFunction face2world)
+{
+ std::array<QuatPose,num_sigmas> out;
+ std::transform(sigmas.begin(), sigmas.end(), out.begin(), [face2world=std::move(face2world), &face](const StateVec& sigma_point) {
+ // First unpack the state vector and generate quaternion rotation w.r.t image space.
+ const auto [rotation, center, size] = apply_offset(face, sigma_point);
+ // Then transform ...
+ QuatPose pose = face2world(rotation, center, size);
+ pose.pos /= world_scale;
+ return pose;
+ });
+ return out;
+}
+
+
+StateVec apply_filter_to_offset(const StateVec& offset, const StateCov& offset_cov, float, const FiltParams& params)
+{
+ // Offset and Cov represent a multivariate normal distribution, which is the probability of the new pose measured w.r.t the previous one.
+ // Prob(x) ~exp(-(x-mu)t Cov^-1 (x-mu))
+ // We want to attenuate this offset, or zero it out completely, to obtain a deadzone-filter behaviour. The size of the deadzone shall be
+ // determined by the covariance projected to the offset direction like so:
+ // Take x = mu - mu / |mu| * alpha
+ // p(alpha) ~exp(-alpha^2 / |mu|^2 * mut Cov^-1 mu) = ~exp(-alpha^2 / sigma^2) with sigma^2 = mut Cov^-1 mu / |mu|^2.
+ // So this projection is like a 1d normal distribution with some standard deviation, which we take to scale the deadzone.
+
+ bool ok = true;
+
+ const float len_div_sigma_sqr = offset.dot(offset_cov.inv(cv::DECOMP_CHOLESKY, &ok) * offset);
+
+ const float attenuation = (ok) ? sigmoid((std::sqrt(len_div_sigma_sqr) - params.deadzone_size)*params.deadzone_hardness) : 1.f;
+
+ // {
+ // std::cout << "cov diag: " << offset_cov.diag() << std::endl;
+ // std::cout << "offset: " << cv::norm(offset) << std::endl;
+ // std::cout << "len_div_sigma_sqr: " << cv::norm(len_div_sigma_sqr) << std::endl;
+ // std::cout << "attenuation (" << ok << "): " << attenuation << std::endl;
+ // }
+
+ return offset*attenuation;
+}
+
+
+QuatPose apply_filter(const PoseEstimator::Face &face, const QuatPose& previous_pose_, float dt, Face2WorldFunction face2world, const FiltParams& params)
+{
+ ukf_cv::MerweScaledSigmaPoints<dofs> unscentedtrafo;
+ auto previous_pose = previous_pose_;
+ previous_pose.pos /= world_scale;
+
+ // Get 6 DoF covariance factor for the predictions in the face crop space.
+ const auto cov_tril = make_tangent_space_uncertainty_tril(face);
+
+ // Compute so called sigma points. These represent the distribution from the covariance matrix in terms of
+ // sampling points.
+ const ukf_cv::SigmaPoints<dofs> sigmas = unscentedtrafo.compute_sigmas(to_vec(StateVec::zeros()), cov_tril, true);
+
+ // The filter uses an unscented transform to translate that into a distribution for the offset from the previous pose.
+ // The trick is to transform the sampling points and compute a covariance from them in the output space.
+ // We have many of these sigma points. This is why that callback comes into play here.
+ // The transform to 3d world space is more than Face2WorldFunction because we also need to apply the sigma point (as
+ // a relative offset) to the pose in face crop space.
+ const std::array<QuatPose,num_sigmas> pose_sigmas = compute_world_pose_from_sigma_point(face, sigmas, std::move(face2world));
+
+ // Compute sigma points relative to the previous pose
+ const ukf_cv::SigmaPoints<dofs> deltas_sigmas = relative_to(previous_pose, pose_sigmas);
+
+ // Compute the mean offset from the last pose and the spread due to the networks uncertainty output.
+ const auto [offset, offset_cov] = unscentedtrafo.compute_statistics(deltas_sigmas);
+
+ // Then the deadzone is applied to the offset and finally the previous pose is transformed by the offset to arrive
+ // at the final output.
+ const StateVec scaled_offset = apply_filter_to_offset(offset, offset_cov, dt, params);
+
+ QuatPose new_pose = apply_offset(previous_pose, scaled_offset);
+
+ new_pose.pos *= world_scale;
+
+ return new_pose;
+}
+
+
+} // namespace neuralnet_tracker_ns \ No newline at end of file
diff --git a/tracker-neuralnet/deadzone_filter.h b/tracker-neuralnet/deadzone_filter.h
new file mode 100644
index 00000000..a9b6aada
--- /dev/null
+++ b/tracker-neuralnet/deadzone_filter.h
@@ -0,0 +1,37 @@
+#pragma once
+
+#include "unscented_trafo.h"
+#include "opencv_contrib.h"
+#include "model_adapters.h"
+
+namespace neuralnet_tracker_ns
+{
+
+/// Represents a 6d pose by quaternion rotation and position vector.
+struct QuatPose {
+ cv::Quatf rot;
+ cv::Vec3f pos;
+};
+
+struct FiltParams
+{
+ float deadzone_hardness = 1.f;
+ float deadzone_size = 1.f;
+};
+
+/** Callback type for converting data from the `Face` struct to a 6d pose.
+*
+* This callback is needed because it depends on things that the filter doesn't have to know about and it is called multiple times
+* due to the way how uncertainty estimates are handled
+*/
+using Face2WorldFunction = std::function<QuatPose (const cv::Quatf&, const cv::Point2f&, float)>;
+
+/** Applies a deadzone filter similar to the one used in the Hamilton filter.
+*
+* What sets this apart is that the deadzone size scales with the uncertainty estimate of the network.
+* The rotation uncertainty is represented by a covariance matrix for the distribution of a rotation vector which
+* describes the offset from the mean rotation (the quaternion in the `Face` struct).
+*/
+QuatPose apply_filter(const PoseEstimator::Face &face, const QuatPose& previous_pose, float dt, Face2WorldFunction face2world, const FiltParams& params);
+
+} // namespace neuralnet_tracker_ns \ No newline at end of file
diff --git a/tracker-neuralnet/ftnoir_tracker_neuralnet.cpp b/tracker-neuralnet/ftnoir_tracker_neuralnet.cpp
index 2fad17aa..1fd50a94 100644
--- a/tracker-neuralnet/ftnoir_tracker_neuralnet.cpp
+++ b/tracker-neuralnet/ftnoir_tracker_neuralnet.cpp
@@ -6,16 +6,19 @@
*/
#include "ftnoir_tracker_neuralnet.h"
+#include "deadzone_filter.h"
+#include "opencv_contrib.h"
+
#include "compat/sleep.hpp"
#include "compat/math-imports.hpp"
-#include "cv/init.hpp"
-#include <opencv2/core.hpp>
-#include <opencv2/core/hal/interface.h>
-#include <opencv2/core/types.hpp>
-#include <opencv2/calib3d.hpp>
-#include <opencv2/imgcodecs.hpp>
#include "compat/timer.hpp"
+#include "compat/check-visible.hpp"
+#include "cv/init.hpp"
+
#include <omp.h>
+#include <onnxruntime_cxx_api.h>
+#include <opencv2/core.hpp>
+#include <opencv2/core/quaternion.hpp>
#ifdef _MSC_VER
# pragma warning(disable : 4702)
@@ -24,26 +27,32 @@
#include <QMutexLocker>
#include <QDebug>
#include <QFile>
+#include <QFileDialog>
+#include <QFileInfo>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <chrono>
+#include <string>
+#include <stdexcept>
+#include <unordered_map>
// Some demo code for onnx
// https://github.com/microsoft/onnxruntime/blob/master/csharp/test/Microsoft.ML.OnnxRuntime.EndToEndTests.Capi/C_Api_Sample.cpp
// https://github.com/leimao/ONNX-Runtime-Inference/blob/main/src/inference.cpp
-namespace
+namespace neuralnet_tracker_ns
{
-using numeric_types::vec3;
-using numeric_types::vec2;
-using numeric_types::mat33;
-
-// Minimal difference if at all going from 1 to 2 threads.
-static constexpr int num_threads = 1;
+using namespace cvcontrib;
+using f = float;
+template<int n> using vec = cv::Vec<f, n>;
+template<int y, int x> using mat = cv::Matx<f, y, x>;
+using vec2 = vec<2>;
+using vec3 = vec<3>;
+using mat33 = mat<3, 3>;
#if _MSC_VER
std::wstring convert(const QString &s) { return s.toStdWString(); }
@@ -52,467 +61,405 @@ std::string convert(const QString &s) { return s.toStdString(); }
#endif
-float sigmoid(float x)
+QDir get_default_model_directory()
{
- return 1.f/(1.f + std::exp(-x));
+ return QDir(OPENTRACK_BASE_PATH+ "/" OPENTRACK_LIBRARY_PATH "models");
}
-template<class T>
-cv::Rect_<T> squarize(const cv::Rect_<T> &r)
+int enum_to_fps(int value)
{
- cv::Point_<T> c{r.x + r.width/T(2), r.y + r.height/T(2)};
- const T sz = std::max(r.height, r.width);
- return {c.x - sz/T(2), c.y - sz/T(2), sz, sz};
-}
+ int fps = 0;
+ switch (value)
+ {
+ default: eval_once(qDebug() << "neuralnet tracker: invalid fps enum value");
+ [[fallthrough]];
+ case fps_default: fps = 0; break;
+ case fps_30: fps = 30; break;
+ case fps_60: fps = 60; break;
+ case fps_75: fps = 75; break;
+ case fps_125: fps = 125; break;
+ case fps_200: fps = 200; break;
+ case fps_50: fps = 50; break;
+ case fps_100: fps = 100; break;
+ case fps_120: fps = 120; break;
+ case fps_300: fps = 300; break;
+ case fps_250: fps = 250; break;
+ }
-int compute_padding(const cv::Rect &r, int w, int h)
-{
- using std::max;
- return max({
- max(-r.x, 0),
- max(-r.y, 0),
- max(r.x+r.width-w, 0),
- max(r.y+r.height-h, 0)
- });
+ return fps;
}
-cv::Rect2f unnormalize(const cv::Rect2f &r, int h, int w)
+template<class F>
+struct OnScopeExit
{
- auto unnorm = [](float x) -> float { return 0.5*(x+1); };
- auto tl = r.tl();
- auto br = r.br();
- auto x0 = unnorm(tl.x)*w;
- auto y0 = unnorm(tl.y)*h;
- auto x1 = unnorm(br.x)*w;
- auto y1 = unnorm(br.y)*h;
- return {
- x0, y0, x1-x0, y1-y0
- };
-}
+ explicit OnScopeExit(F&& f) : f_{ f } {}
+ ~OnScopeExit() noexcept
+ {
+ f_();
+ }
+ F f_;
+};
-cv::Point2f normalize(const cv::Point2f &p, int h, int w)
+
+CamIntrinsics make_intrinsics(const cv::Mat& img, const Settings& settings)
{
+ const int w = img.cols, h = img.rows;
+ const double diag_fov = settings.fov * M_PI / 180.;
+ const double fov_w = 2.*atan(tan(diag_fov/2.)/sqrt(1. + h/(double)w * h/(double)w));
+ const double fov_h = 2.*atan(tan(diag_fov/2.)/sqrt(1. + w/(double)h * w/(double)h));
+ const double focal_length_w = 1. / tan(.5 * fov_w);
+ const double focal_length_h = 1. / tan(.5 * fov_h);
+ /* a
+ ______ <--- here is sensor area
+ | /
+ | /
+ f | /
+ | / 2 x angle is the fov
+ |/
+ <--- here is the hole of the pinhole camera
+
+ So, a / f = tan(fov / 2)
+ => f = a/tan(fov/2)
+ What is a?
+ 1 if we define f in terms of clip space where the image plane goes from -1 to 1. Because a is the half-width.
+ */
+
return {
- p.x/w*2.f-1.f,
- p.y/h*2.f-1.f
+ (float)focal_length_w,
+ (float)focal_length_h,
+ (float)fov_w,
+ (float)fov_h
};
}
-mat33 rotation_from_two_vectors(const vec3 &a, const vec3 &b)
+cv::Rect make_crop_rect_multiple_of(const cv::Size &size, int multiple)
{
- vec3 axis = a.cross(b);
- const float len_a = cv::norm(a);
- const float len_b = cv::norm(b);
- const float len_axis = cv::norm(axis);
- const float sin_angle = std::clamp(len_axis / (len_a * len_b), -1.f, 1.f);
- const float angle = std::asin(sin_angle);
- axis *= angle/(1.e-12 + len_axis);
- mat33 out;
- cv::Rodrigues(axis, out);
- return out;
+ const int new_w = (size.width / multiple) * multiple;
+ const int new_h = (size.height / multiple) * multiple;
+ return cv::Rect(
+ (size.width-new_w)/2,
+ (size.height-new_h)/2,
+ new_w,
+ new_h
+ );
}
-
-/* Computes correction due to head being off screen center.
- x, y: In screen space, i.e. in [-1,1]
- focal_length_x: In screen space
-*/
-mat33 compute_rotation_correction(const cv::Point2f &p, float focal_length_x)
+template<class T>
+cv::Rect_<T> squarize(const cv::Rect_<T> &r)
{
- return rotation_from_two_vectors(
- {1.f,0.f,0.f},
- {focal_length_x, p.y, p.x});
+ cv::Point_<T> c{r.x + r.width/T(2), r.y + r.height/T(2)};
+ const T sz = std::max(r.height, r.width);
+ return {c.x - sz/T(2), c.y - sz/T(2), sz, sz};
}
-mat33 quaternion_to_mat33(const std::array<float,4> quat)
+template<class T>
+cv::Rect_<T> expand(const cv::Rect_<T>& r, T factor)
{
- mat33 m;
- const float w = quat[0];
- const float i = quat[1];
- const float j = quat[2];
- const float k = quat[3];
- m(0,0) = 1.f - 2.f*(j*j + k*k);
- m(1,0) = 2.f*(i*j + k*w);
- m(2,0) = 2.f*(i*k - j*w);
- m(0,1) = 2.f*(i*j - k*w);
- m(1,1) = 1.f - 2.f*(i*i + k*k);
- m(2,1) = 2.f*(j*k + i*w);
- m(0,2) = 2.f*(i*k + j*w);
- m(1,2) = 2.f*(j*k - i*w);
- m(2,2) = 1.f - 2.f*(i*i + j*j);
- return m;
+ // xnew = l+.5*w - w*f*0.5 = l + .5*(w - new_w)
+ const cv::Size_<T> new_size = { r.width * factor, r.height * factor };
+ const cv::Point_<T> new_tl = r.tl() + (as_point(r.size()) - as_point(new_size)) / T(2);
+ return cv::Rect_<T>(new_tl, new_size);
}
template<class T>
-T iou(const cv::Rect_<T> &a, const cv::Rect_<T> &b)
+cv::Rect_<T> ewa_filter(const cv::Rect_<T>& last, const cv::Rect_<T>& current, T alpha)
{
- auto i = a & b;
- return double{i.area()} / (a.area()+b.area()-i.area());
+ const auto last_center = T(0.5) * (last.tl() + last.br());
+ const auto cur_center = T(0.5) * (current.tl() + current.br());
+ const cv::Point_<T> new_size = as_point(last.size()) + alpha * (as_point(current.size()) - as_point(last.size()));
+ const cv::Point_<T> new_center = last_center + alpha * (cur_center - last_center);
+ return cv::Rect_<T>(new_center - T(0.5) * new_size, as_size(new_size));
}
-} // namespace
-
-
-namespace neuralnet_tracker_ns
+cv::Vec3f image_to_world(float x, float y, float size, float reference_size_in_mm, const cv::Size2i& image_size, const CamIntrinsics& intrinsics)
{
+ /*
+ Compute the location the network outputs in 3d space.
-
-int enum_to_fps(int value)
-{
- switch (value)
- {
- case fps_30: return 30;
- case fps_60: return 60;
- default: [[fallthrough]];
- case fps_default: return 0;
- }
+ hhhhhh <- head size (meters)
+ \ | -----------------------
+ \ | \
+ \ | |
+ \ | |- x (meters)
+ ____ <- face.size / width |
+ \ | | |
+ \| |- focal length /
+ ------------------------
+ ------------------------------------------------>> z direction
+ z/x = zi / f
+ zi = image position
+ z = world position
+ f = focal length
+
+ We can also do deltas:
+ dz / x = dzi / f
+ => x = dz / dzi * f
+ which means we can compute x from the head size (dzi) if we assume some reference size (dz).
+ */
+ const float head_size_vertical = 2.f*size; // Size from the model is more like half the real vertical size of a human head.
+ const float xpos = -(intrinsics.focal_length_w * image_size.width * 0.5f) / head_size_vertical * reference_size_in_mm;
+ const float zpos = (x / image_size.width * 2.f - 1.f) * xpos / intrinsics.focal_length_w;
+ const float ypos = (y / image_size.height * 2.f - 1.f) * xpos / intrinsics.focal_length_h;
+ return {xpos, ypos, zpos};
}
-Localizer::Localizer(Ort::MemoryInfo &allocator_info, Ort::Session &&session) :
- session{std::move(session)},
- scaled_frame(input_img_height, input_img_width, CV_8U),
- input_mat(input_img_height, input_img_width, CV_32F)
+vec2 world_to_image(const cv::Vec3f& pos, const cv::Size2i& image_size, const CamIntrinsics& intrinsics)
{
- // Only works when input_mat does not reallocated memory ...which it should not.
- // Non-owning memory reference to input_mat?
- // Note: shape = (bach x channels x h x w)
- const std::int64_t input_shape[4] = { 1, 1, input_img_height, input_img_width };
- input_val = Ort::Value::CreateTensor<float>(allocator_info, input_mat.ptr<float>(0), input_mat.total(), input_shape, 4);
-
- const std::int64_t output_shape[2] = { 1, 5 };
- output_val = Ort::Value::CreateTensor<float>(allocator_info, results.data(), results.size(), output_shape, 2);
+ const float xscr = pos[2] / pos[0] * intrinsics.focal_length_w;
+ const float yscr = pos[1] / pos[0] * intrinsics.focal_length_h;
+ const float x = (xscr+1.)*0.5f*image_size.width;
+ const float y = (yscr+1.)*0.5f*image_size.height;
+ return {x, y};
}
-std::pair<float, cv::Rect2f> Localizer::run(
- const cv::Mat &frame)
+cv::Quatf image_to_world(cv::Quatf q)
{
- auto p = input_mat.ptr(0);
-
- cv::resize(frame, scaled_frame, { input_img_width, input_img_height }, 0, 0, cv::INTER_AREA);
- scaled_frame.convertTo(input_mat, CV_32F, 1./255., -0.5);
-
- assert (input_mat.ptr(0) == p);
- assert (!input_mat.empty() && input_mat.isContinuous());
- assert (input_mat.cols == input_img_width && input_mat.rows == input_img_height);
-
- const char* input_names[] = {"x"};
- const char* output_names[] = {"logit_box"};
-
- //Timer t_; t_.start();
-
- const auto nt = omp_get_num_threads();
- omp_set_num_threads(num_threads);
- session.Run(Ort::RunOptions{nullptr}, input_names, &input_val, 1, output_names, &output_val, 1);
- omp_set_num_threads(nt);
-
- //qDebug() << "localizer: " << t_.elapsed_ms() << " ms\n";
-
- const cv::Rect2f roi = unnormalize(cv::Rect2f{
- results[1],
- results[2],
- results[3]-results[1], // Width
- results[4]-results[2] // Height
- }, frame.rows, frame.cols);
- const float score = sigmoid(results[0]);
-
- return { score, roi };
+ std::swap(q[1], q[3]);
+ q[1] = -q[1];
+ q[2] = -q[2];
+ q[3] = -q[3];
+ return q;
}
-PoseEstimator::PoseEstimator(Ort::MemoryInfo &allocator_info, Ort::Session &&session) :
- session{std::move(session)},
- scaled_frame(input_img_height, input_img_width, CV_8U),
- input_mat(input_img_height, input_img_width, CV_32F)
+cv::Point2f normalize(const cv::Point2f &p, int h, int w)
{
- {
- const std::int64_t input_shape[4] = { 1, 1, input_img_height, input_img_width };
- input_val = Ort::Value::CreateTensor<float>(allocator_info, input_mat.ptr<float>(0), input_mat.total(), input_shape, 4);
- }
-
- {
- const std::int64_t output_shape[2] = { 1, 3 };
- output_val[0] = Ort::Value::CreateTensor<float>(
- allocator_info, &output_coord[0], output_coord.rows, output_shape, 2);
- }
-
- {
- const std::int64_t output_shape[2] = { 1, 4 };
- output_val[1] = Ort::Value::CreateTensor<float>(
- allocator_info, &output_quat[0], output_quat.rows, output_shape, 2);
- }
-
- {
- const std::int64_t output_shape[2] = { 1, 4 };
- output_val[2] = Ort::Value::CreateTensor<float>(
- allocator_info, &output_box[0], output_box.rows, output_shape, 2);
- }
+ return {
+ p.x/w*2.f-1.f,
+ p.y/h*2.f-1.f
+ };
}
-int PoseEstimator::find_input_intensity_90_pct_quantile() const
+cv::Quatf rotation_from_two_vectors(const vec3 &a, const vec3 &b)
{
- const int channels[] = { 0 };
- const int hist_size[] = { 255 };
- float range[] = { 0, 256 };
- const float* ranges[] = { range };
- cv::Mat hist;
- cv::calcHist(&scaled_frame, 1, channels, cv::Mat(), hist, 1, hist_size, ranges, true, false);
- int gray_level = 0;
- const int num_pixels_quantile = scaled_frame.total()*0.9f;
- int num_pixels_accum = 0;
- for (int i=0; i<hist_size[0]; ++i)
+ // |axis| = |a| * |b| * sin(alpha)
+ const vec3 axis = a.cross(b);
+ // dot = |a|*|b|*cos(alpha)
+ const float dot = a.dot(b);
+ const float len = cv::norm(axis);
+ vec3 normed_axis = axis / len;
+ float angle = std::atan2(len, dot);
+ if (!(std::isfinite(normed_axis[0]) && std::isfinite(normed_axis[1]) && std::isfinite(normed_axis[2])))
{
- num_pixels_accum += hist.at<float>(i);
- if (num_pixels_accum > num_pixels_quantile)
- {
- gray_level = i;
- break;
- }
+ angle = 0.f;
+ normed_axis = vec3{1.,0.,0.};
}
- return gray_level;
+ return cv::Quatf::createFromAngleAxis(angle, normed_axis);
}
-std::optional<PoseEstimator::Face> PoseEstimator::run(
- const cv::Mat &frame, const cv::Rect &box)
+// Computes correction due to head being off screen center.
+cv::Quatf compute_rotation_correction(const cv::Point3f& p)
{
- cv::Mat cropped;
-
- const int patch_size = std::max(box.width, box.height)*1.05;
- const cv::Point2f patch_center = {
- std::clamp<float>(box.x + 0.5f*box.width, 0.f, frame.cols),
- std::clamp<float>(box.y + 0.5f*box.height, 0.f, frame.rows)
- };
- cv::getRectSubPix(frame, {patch_size, patch_size}, patch_center, cropped);
-
- // Will get failure if patch_center is outside image boundaries.
- // Have to catch this case.
- if (cropped.rows != patch_size || cropped.cols != patch_size)
- return {};
-
- auto p = input_mat.ptr(0);
-
- cv::resize(cropped, scaled_frame, { input_img_width, input_img_height }, 0, 0, cv::INTER_AREA);
-
- // Automatic brightness amplification.
- const int brightness = find_input_intensity_90_pct_quantile();
- const double alpha = brightness<127 ? 0.5/std::max(5,brightness) : 1./255;
- const double beta = -0.5;
-
- scaled_frame.convertTo(input_mat, CV_32F, alpha, beta);
-
- assert (input_mat.ptr(0) == p);
- assert (!input_mat.empty() && input_mat.isContinuous());
- assert (input_mat.cols == input_img_width && input_mat.rows == input_img_height);
-
- const char* input_names[] = {"x"};
- const char* output_names[] = {"pos_size", "quat", "box"};
-
- //Timer t_; t_.start();
+ return rotation_from_two_vectors(
+ {-1.f,0.f,0.f}, p);
+}
- const auto nt = omp_get_num_threads();
- omp_set_num_threads(num_threads);
- session.Run(Ort::RunOptions{nullptr}, input_names, &input_val, 1, output_names, output_val, 3);
- omp_set_num_threads(nt);
- // FIXME: Execution time fluctuates wildly. 19 to 26 ms. Why???
- // The instructions are always the same. Maybe a memory allocation
- // issue. The ONNX api suggests that tensor are allocated in an
- // arena. Does that matter? Maybe the issue is something else?
+// Intersection over union. A value between 0 and 1 which measures the match between the bounding boxes.
+template<class T>
+T iou(const cv::Rect_<T> &a, const cv::Rect_<T> &b)
+{
+ auto i = a & b;
+ return double{i.area()} / (a.area()+b.area()-i.area());
+}
- //qDebug() << "pose net: " << t_.elapsed_ms() << " ms\n";
- // Perform coordinate transformation.
- // From patch-local normalized in [-1,1] to
- // frame unnormalized pixel coordinates.
+class GuardedThreadCountSwitch
+{
+ int old_num_threads_cv_ = 1;
+ int old_num_threads_omp_ = 1;
+ public:
+ GuardedThreadCountSwitch(int num_threads)
+ {
+ old_num_threads_cv_ = cv::getNumThreads();
+ old_num_threads_omp_ = omp_get_num_threads();
+ omp_set_num_threads(num_threads);
+ cv::setNumThreads(num_threads);
+ }
- const cv::Point2f center = patch_center +
- (0.5f*patch_size)*cv::Point2f{output_coord[0], output_coord[1]};
+ ~GuardedThreadCountSwitch()
+ {
+ omp_set_num_threads(old_num_threads_omp_);
+ cv::setNumThreads(old_num_threads_cv_);
+ }
- const float size = patch_size*0.5f*output_coord[2];
+ GuardedThreadCountSwitch(const GuardedThreadCountSwitch&) = delete;
+ GuardedThreadCountSwitch& operator=(const GuardedThreadCountSwitch&) = delete;
+};
- // Following Eigen which uses quat components in the order w, x, y, z.
- const std::array<float,4> rotation = {
- output_quat[3],
- output_quat[0],
- output_quat[1],
- output_quat[2] };
- const cv::Rect2f outbox = {
- patch_center.x + (0.5f*patch_size)*output_box[0],
- patch_center.y + (0.5f*patch_size)*output_box[1],
- 0.5f*patch_size*(output_box[2]-output_box[0]),
- 0.5f*patch_size*(output_box[3]-output_box[1])
- };
+bool NeuralNetTracker::detect()
+{
+ double inference_time = 0.;
- return std::optional<Face>({
- rotation, outbox, center, size
- });
-}
+ OnScopeExit update_inference_time{ [&]() {
+ QMutexLocker lck{ &stats_mtx_ };
+ inference_time_ = inference_time;
+ } };
-cv::Mat PoseEstimator::last_network_input() const
-{
- cv::Mat ret;
- if (!input_mat.empty())
+ // If there is no past ROI from the localizer or if the match of its output
+ // with the current ROI is too poor we have to run it again. This causes a
+ // latency spike of maybe an additional 50%. But it only occurs when the user
+ // moves his head far enough - or when the tracking ist lost ...
+ if (!last_localizer_roi_ || !last_roi_ ||
+ iou(*last_localizer_roi_,*last_roi_)<0.25)
{
- input_mat.convertTo(ret, CV_8U, 255., 127.);
- cv::cvtColor(ret, ret, cv::COLOR_GRAY2RGB);
- }
- return ret;
-}
+ auto [p, rect] = localizer_->run(grayscale_);
+ inference_time += localizer_->last_inference_time_millis();
-
-bool neuralnet_tracker::detect()
-{
- // Note: BGR colors!
- if (!last_localizer_roi || !last_roi ||
- iou(*last_localizer_roi,*last_roi)<0.25)
- {
- auto [p, rect] = localizer->run(grayscale);
- if (p > 0.5)
+ if (last_roi_ && iou(rect,*last_roi_)>=0.25 && p > 0.5)
+ {
+ // The new ROI matches the result from tracking, so the user is
+ // still there and to not disturb recurrent models, we only update
+ // ...
+ last_localizer_roi_ = rect;
+ }
+ else if (p > 0.5 && rect.height > 32 && rect.width > 32)
{
- last_localizer_roi = rect;
- last_roi = rect;
+ // Tracking probably got lost since the ROI's don't match, but the
+ // localizer still finds a face, so we use the ROI from the localizer
+ last_localizer_roi_ = rect;
+ last_roi_ = rect;
+ }
+ else
+ {
+ // Tracking lost and no localization result. The user probably can't be seen.
+ last_roi_.reset();
+ last_localizer_roi_.reset();
}
}
- if (!last_roi)
+ if (!last_roi_)
+ {
+ // Last iteration the tracker failed to generate a trustworthy
+ // roi and the localizer also cannot find a face.
+ draw_gizmos({}, {});
return false;
+ }
+
+ auto face = poseestimator_->run(grayscale_, *last_roi_);
+ inference_time += poseestimator_->last_inference_time_millis();
- auto face = poseestimator->run(grayscale, *last_roi);
-
if (!face)
{
- last_roi.reset();
+ last_roi_.reset();
+ draw_gizmos({}, {});
return false;
}
- last_roi = face->box;
+ cv::Rect2f roi = expand(face->box, (float)settings_.roi_zoom);
- Affine pose = compute_pose(*face);
+ last_roi_ = ewa_filter(*last_roi_, roi, float(settings_.roi_filter_alpha));
- draw_gizmos(frame, *face, pose);
+ QuatPose pose = compute_filtered_pose(*face);
+ last_pose_ = pose;
+
+ Affine pose_affine = {
+ pose.rot.toRotMat3x3(cv::QUAT_ASSUME_UNIT),
+ pose.pos };
{
- QMutexLocker lck(&mtx);
- this->pose_ = pose;
+ QMutexLocker lck(&mtx_);
+ last_pose_affine_ = pose_affine;
}
+ draw_gizmos(*face, last_pose_affine_);
+
return true;
}
-Affine neuralnet_tracker::compute_pose(const PoseEstimator::Face &face) const
+void NeuralNetTracker::draw_gizmos(
+ const std::optional<PoseEstimator::Face> &face,
+ const Affine& pose)
{
- const mat33 rot_correction = compute_rotation_correction(
- normalize(face.center, frame.rows, frame.cols),
- intrinsics.focal_length_w);
+ if (!is_visible_)
+ return;
- const mat33 m = rot_correction * quaternion_to_mat33(face.rotation);
+ preview_.draw_gizmos(
+ face,
+ last_roi_,
+ last_localizer_roi_,
+ world_to_image(pose.t, grayscale_.size(), intrinsics_));
- /*
-
- hhhhhh <- head size (meters)
- \ | -----------------------
- \ | \
- \ | |
- \ | |- tz (meters)
- ____ <- face.size / width |
- \ | | |
- \| |- focal length /
- ------------------------
- */
+ if (settings_.show_network_input)
+ {
+ cv::Mat netinput = poseestimator_->last_network_input();
+ preview_.overlay_netinput(netinput);
+ }
+}
+
+
+QuatPose NeuralNetTracker::transform_to_world_pose(const cv::Quatf &face_rotation, const cv::Point2f& face_xy, const float face_size) const
+{
+ const vec3 face_world_pos = image_to_world(
+ face_xy.x, face_xy.y, face_size, HEAD_SIZE_MM,
+ grayscale_.size(),
+ intrinsics_);
+
+ const cv::Quatf rot_correction = compute_rotation_correction(
+ face_world_pos);
- // Compute the location the network outputs in 3d space.
- const vec3 face_world_pos = image_to_world(face.center.x, face.center.y, face.size, head_size_mm);
+ cv::Quatf rot = rot_correction * image_to_world(face_rotation);
// But this is in general not the location of the rotation joint in the neck.
- // So we need an extra offset. Which we determine by solving
+ // So we need an extra offset. Which we determine by computing
// z,y,z-pos = head_joint_loc + R_face * offset
+ const vec3 local_offset = vec3{
+ static_cast<float>(settings_.offset_fwd),
+ static_cast<float>(settings_.offset_up),
+ static_cast<float>(settings_.offset_right)};
+ const vec3 offset = rotate(rot, local_offset);
+ const vec3 pos = face_world_pos + offset;
- const vec3 pos = face_world_pos
- + m * vec3{
- static_cast<float>(s.offset_fwd),
- static_cast<float>(s.offset_up),
- static_cast<float>(s.offset_right)};
-
- return { m, pos };
+ return { rot, pos };
}
-void neuralnet_tracker::draw_gizmos(
- cv::Mat frame,
- const PoseEstimator::Face &face,
- const Affine& pose) const
+QuatPose NeuralNetTracker::compute_filtered_pose(const PoseEstimator::Face &face)
{
- if (last_roi)
- {
- const int col = 255;
- cv::rectangle(frame, *last_roi, cv::Scalar(0, 255, 0), /*thickness=*/1);
- }
- if (last_localizer_roi)
- {
- const int col = 255;
- cv::rectangle(frame, *last_localizer_roi, cv::Scalar(col, 0, 255-col), /*thickness=*/1);
- }
-
- if (face.size>=1.f)
- cv::circle(frame, static_cast<cv::Point>(face.center), int(face.size), cv::Scalar(255,255,255), 2);
- cv::circle(frame, static_cast<cv::Point>(face.center), 3, cv::Scalar(255,255,255), -1);
-
- auto draw_coord_line = [&](int i, const cv::Scalar& color)
- {
- const float vx = -pose.R(2,i);
- const float vy = -pose.R(1,i);
- static constexpr float len = 100.f;
- cv::Point q = face.center + len*cv::Point2f{vx, vy};
- cv::line(frame, static_cast<cv::Point>(face.center), static_cast<cv::Point>(q), color, 2);
- };
- draw_coord_line(0, {0, 0, 255});
- draw_coord_line(1, {0, 255, 0});
- draw_coord_line(2, {255, 0, 0});
-
- if (s.show_network_input)
+ if (fps_ > 0.001 && last_pose_ && poseestimator_->has_uncertainty())
{
- cv::Mat netinput = poseestimator->last_network_input();
- if (!netinput.empty())
- {
- const int w = std::min(netinput.cols, frame.cols);
- const int h = std::min(netinput.rows, frame.rows);
- cv::Rect roi(0, 0, w, h);
- netinput(roi).copyTo(frame(roi));
- }
+ auto image2world = [this](const cv::Quatf &face_rotation, const cv::Point2f& face_xy, const float face_size) {
+ return this->transform_to_world_pose(face_rotation, face_xy, face_size); };
+
+ return apply_filter(
+ face,
+ *last_pose_,
+ 1./fps_,
+ std::move(image2world),
+ FiltParams{
+ float(settings_.deadzone_hardness),
+ float(settings_.deadzone_size)
+ });
}
+ else
{
- // Draw the computed joint position
- auto xy = world_to_image(pose.t);
- cv::circle(frame, cv::Point(xy[0],xy[1]), 5, cv::Scalar(0,0,255), -1);
+ return transform_to_world_pose(face.rotation, face.center, face.size);
}
-
- char buf[128];
- ::snprintf(buf, sizeof(buf), "%d Hz, Max: %d ms", clamp(int(fps), 0, 9999), int(max_frame_time*1000.));
- cv::putText(frame, buf, cv::Point(10, frame.rows-10), cv::FONT_HERSHEY_PLAIN, 1, cv::Scalar(0, 255, 0), 1);
}
-neuralnet_tracker::neuralnet_tracker()
+NeuralNetTracker::NeuralNetTracker()
{
opencv_init();
- cv::setNumThreads(num_threads);
+ neuralnet_tracker_tests::run();
}
-neuralnet_tracker::~neuralnet_tracker()
+NeuralNetTracker::~NeuralNetTracker()
{
requestInterruption();
wait();
@@ -521,130 +468,107 @@ neuralnet_tracker::~neuralnet_tracker()
}
-module_status neuralnet_tracker::start_tracker(QFrame* videoframe)
+module_status NeuralNetTracker::start_tracker(QFrame* videoframe)
{
videoframe->show();
- videoWidget = std::make_unique<cv_video_widget>(videoframe);
- layout = std::make_unique<QHBoxLayout>();
- layout->setContentsMargins(0, 0, 0, 0);
- layout->addWidget(videoWidget.get());
- videoframe->setLayout(layout.get());
- videoWidget->show();
+ video_widget_ = std::make_unique<cv_video_widget>(videoframe);
+ layout_ = std::make_unique<QHBoxLayout>();
+ layout_->setContentsMargins(0, 0, 0, 0);
+ layout_->addWidget(&*video_widget_);
+ videoframe->setLayout(&*layout_);
+ video_widget_->show();
+ num_threads_ = settings_.num_threads;
start();
return status_ok();
}
-bool neuralnet_tracker::load_and_initialize_model()
+bool NeuralNetTracker::load_and_initialize_model()
{
const QString localizer_model_path_enc =
OPENTRACK_BASE_PATH+"/" OPENTRACK_LIBRARY_PATH "/models/head-localizer.onnx";
- const QString poseestimator_model_path_enc =
- OPENTRACK_BASE_PATH+"/" OPENTRACK_LIBRARY_PATH "/models/head-pose.onnx";
+ const QString poseestimator_model_path_enc = get_posenet_filename();
try
{
- env = Ort::Env{
+ env_ = Ort::Env{
OrtLoggingLevel::ORT_LOGGING_LEVEL_ERROR,
"tracker-neuralnet"
};
auto opts = Ort::SessionOptions{};
// Do thread settings here do anything?
// There is a warning which says to control number of threads via
- // openmp settings. Which is what we do. omp_set_num_threads directly
- // before running the inference pass.
- opts.SetIntraOpNumThreads(num_threads);
- opts.SetInterOpNumThreads(num_threads);
- opts.SetGraphOptimizationLevel(
- GraphOptimizationLevel::ORT_ENABLE_EXTENDED);
-
- opts.EnableCpuMemArena();
- allocator_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
-
- localizer.emplace(
- allocator_info,
- Ort::Session{env, convert(localizer_model_path_enc).c_str(), opts});
-
- poseestimator.emplace(
- allocator_info,
- Ort::Session{env, convert(poseestimator_model_path_enc).c_str(), opts});
+ // openmp settings. Which is what we do.
+ opts.SetIntraOpNumThreads(num_threads_);
+ opts.SetInterOpNumThreads(1);
+ allocator_info_ = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
+
+ localizer_.emplace(
+ allocator_info_,
+ Ort::Session{env_, convert(localizer_model_path_enc).c_str(), opts});
+
+ qDebug() << "Loading pose net " << poseestimator_model_path_enc;
+ poseestimator_.emplace(
+ allocator_info_,
+ Ort::Session{env_, convert(poseestimator_model_path_enc).c_str(), opts});
}
catch (const Ort::Exception &e)
{
- qDebug() << "Failed to initialize the neural network models. ONNX error message: "
+ qDebug() << "Failed to initialize the neural network models. ONNX error message: "
<< e.what();
return false;
}
+ catch (const std::exception &e)
+ {
+ qDebug() << "Failed to initialize the neural network models. Error message: " << e.what();
+ return false;
+ }
+
return true;
}
-bool neuralnet_tracker::open_camera()
+bool NeuralNetTracker::open_camera()
{
- int fps = enum_to_fps(s.force_fps);
+ int rint = std::clamp(*settings_.resolution, 0, (int)std::size(resolution_choices)-1);
+ resolution_tuple res = resolution_choices[rint];
+ int fps = enum_to_fps(settings_.force_fps);
- QMutexLocker l(&camera_mtx);
+ QMutexLocker l(&camera_mtx_);
- camera = video::make_camera(s.camera_name);
+ camera_ = video::make_camera(settings_.camera_name);
- if (!camera)
+ if (!camera_)
return false;
video::impl::camera::info args {};
- args.width = 320;
- args.height = 240;
-
+ if (res.width)
+ {
+ args.width = res.width;
+ args.height = res.height;
+ }
if (fps)
args.fps = fps;
- if (!camera->start(args))
+ args.use_mjpeg = settings_.use_mjpeg;
+
+ if (!camera_->start(args))
{
qDebug() << "neuralnet tracker: can't open camera";
return false;
}
- return true;
-}
-
-
-void neuralnet_tracker::set_intrinsics()
-{
- const int w = grayscale.cols, h = grayscale.rows;
- const double diag_fov = s.fov * M_PI / 180.;
- const double fov_w = 2.*atan(tan(diag_fov/2.)/sqrt(1. + h/(double)w * h/(double)w));
- const double fov_h = 2.*atan(tan(diag_fov/2.)/sqrt(1. + w/(double)h * w/(double)h));
- const double focal_length_w = 1. / tan(.5 * fov_w);
- const double focal_length_h = 1. / tan(.5 * fov_h);
- intrinsics.fov_h = fov_h;
- intrinsics.fov_w = fov_w;
- intrinsics.focal_length_w = focal_length_w;
- intrinsics.focal_length_h = focal_length_h;
+ return true;
}
-vec3 neuralnet_tracker::image_to_world(float x, float y, float size, float real_size) const
+void NeuralNetTracker::run()
{
- // Compute the location the network outputs in 3d space.
- const float xpos = -(intrinsics.focal_length_w * frame.cols * 0.5f) / size * real_size;
- const float zpos = (x / frame.cols * 2.f - 1.f) * xpos / intrinsics.focal_length_w;
- const float ypos = (y / frame.rows * 2.f - 1.f) * xpos / intrinsics.focal_length_h;
- return {xpos, ypos, zpos};
-}
+ preview_.init(*video_widget_);
+ GuardedThreadCountSwitch switch_num_threads_to(num_threads_);
-vec2 neuralnet_tracker::world_to_image(const vec3& pos) const
-{
- const float xscr = pos[2] / pos[0] * intrinsics.focal_length_w;
- const float yscr = pos[1] / pos[0] * intrinsics.focal_length_h;
- const float x = (xscr+1.)*0.5f*frame.cols;
- const float y = (yscr+1.)*0.5f*frame.rows;
- return {x, y};
-}
-
-
-void neuralnet_tracker::run()
-{
if (!open_camera())
return;
@@ -655,11 +579,12 @@ void neuralnet_tracker::run()
while (!isInterruptionRequested())
{
+ is_visible_ = check_is_visible();
auto t = clk.now();
{
- QMutexLocker l(&camera_mtx);
+ QMutexLocker l(&camera_mtx_);
- auto [ img, res ] = camera->get_frame();
+ auto [ img, res ] = camera_->get_frame();
if (!res)
{
@@ -668,16 +593,24 @@ void neuralnet_tracker::run()
continue;
}
- auto color = cv::Mat(img.height, img.width, CV_8UC(img.channels), (void*)img.data, img.stride);
- color.copyTo(frame);
+ {
+ QMutexLocker lck{&stats_mtx_};
+ resolution_ = { img.width, img.height };
+ }
+
+ auto color = prepare_input_image(img);
+
+ if (is_visible_)
+ preview_.copy_video_frame(color);
switch (img.channels)
{
case 1:
- grayscale.setTo(color);
+ grayscale_.create(img.height, img.width, CV_8UC1);
+ color.copyTo(grayscale_);
break;
case 3:
- cv::cvtColor(color, grayscale, cv::COLOR_BGR2GRAY);
+ cv::cvtColor(color, grayscale_, cv::COLOR_BGR2GRAY);
break;
default:
qDebug() << "Can't handle" << img.channels << "color channels";
@@ -685,13 +618,13 @@ void neuralnet_tracker::run()
}
}
- set_intrinsics();
+ intrinsics_ = make_intrinsics(grayscale_, settings_);
detect();
- if (frame.rows > 0)
- videoWidget->update_image(frame);
-
+ if (is_visible_)
+ preview_.copy_to_widget(*video_widget_);
+
update_fps(
std::chrono::duration_cast<std::chrono::milliseconds>(
clk.now() - t).count()*1.e-3);
@@ -699,40 +632,71 @@ void neuralnet_tracker::run()
}
-void neuralnet_tracker::update_fps(double dt)
+cv::Mat NeuralNetTracker::prepare_input_image(const video::frame& frame)
{
- const double alpha = dt/(dt + RC);
+ auto img = cv::Mat(frame.height, frame.width, CV_8UC(frame.channels), (void*)frame.data, frame.stride);
- if (dt > 1e-6)
+ // Crop if aspect ratio is not 4:3
+ if (img.rows*4 != img.cols*3)
+ {
+ img = img(make_crop_rect_for_aspect(img.size(), 4, 3));
+ }
+
+ img = img(make_crop_rect_multiple_of(img.size(), 4));
+
+ if (img.cols > 640)
+ {
+ cv::pyrDown(img, downsized_original_images_[0]);
+ img = downsized_original_images_[0];
+ }
+ if (img.cols > 640)
{
- fps *= 1 - alpha;
- fps += alpha * 1./dt;
+ cv::pyrDown(img, downsized_original_images_[1]);
+ img = downsized_original_images_[1];
}
- max_frame_time = std::max(max_frame_time, dt);
+ return img;
+}
+
+
+void NeuralNetTracker::update_fps(double dt)
+{
+ const double alpha = dt/(dt + RC);
+ if (dt > 1e-6)
+ {
+ QMutexLocker lck{&stats_mtx_};
+ fps_ *= 1 - alpha;
+ fps_ += alpha * 1./dt;
+ }
}
-void neuralnet_tracker::data(double *data)
+void NeuralNetTracker::data(double *data)
{
Affine tmp = [&]()
{
- QMutexLocker lck(&mtx);
- return pose_;
+ QMutexLocker lck(&mtx_);
+ return last_pose_affine_;
}();
const auto& mx = tmp.R.col(0);
const auto& my = tmp.R.col(1);
- const auto& mz = -tmp.R.col(2);
+ const auto& mz = tmp.R.col(2);
+ // For reference: https://en.wikipedia.org/wiki/Euler_angles. Section "Rotation matrix". The relevant matrix is
+ // under "Tait-Bryan angles", row with "Y_alpha Z_beta X_gamma = ...".
+ // Because for the NN tracker x is forward, and y is up. We can see that the x axis is independent of roll. Thus it
+ // is relatively easy to figure out the yaw and pitch angles (alpha and beta).
const float yaw = std::atan2(mx(2), mx(0));
const float pitch = -std::atan2(-mx(1), std::sqrt(mx(2)*mx(2)+mx(0)*mx(0)));
- const float roll = std::atan2(-my(2), mz(2));
+ // For the roll angle we recognize that the matrix entries in the second row contain cos(pitch)*cos(roll), and
+ // cos(pitch)*sin(roll). Using atan2 eliminates the common pitch factor and we obtain the roll angle.
+ const float roll = std::atan2(-mz(1), my(1));
{
constexpr double rad2deg = 180/M_PI;
data[Yaw] = rad2deg * yaw;
data[Pitch] = rad2deg * pitch;
- data[Roll] = rad2deg * roll;
+ data[Roll] = -rad2deg * roll;
// convert to cm
data[TX] = -tmp.t[2] * 0.1;
@@ -742,112 +706,185 @@ void neuralnet_tracker::data(double *data)
}
-Affine neuralnet_tracker::pose()
+Affine NeuralNetTracker::pose()
+{
+ QMutexLocker lck(&mtx_);
+ return last_pose_affine_;
+}
+
+
+std::tuple<cv::Size,double, double> NeuralNetTracker::stats() const
{
- QMutexLocker lck(&mtx);
- return pose_;
+ QMutexLocker lck(&stats_mtx_);
+ return { resolution_, fps_, inference_time_ };
}
-void neuralnet_dialog::make_fps_combobox()
+QString NeuralNetTracker::get_posenet_filename() const
+{
+ QString filename = settings_.posenet_file;
+ if (QFileInfo(filename).isRelative())
+ filename = get_default_model_directory().absoluteFilePath(filename);
+ return filename;
+}
+
+
+void NeuralNetDialog::make_fps_combobox()
{
for (int k = 0; k < fps_MAX; k++)
{
const int hz = enum_to_fps(k);
const QString name = (hz == 0) ? tr("Default") : QString::number(hz);
- ui.cameraFPS->addItem(name, k);
+ ui_.cameraFPS->addItem(name, k);
+ }
+}
+
+void NeuralNetDialog::make_resolution_combobox()
+{
+ int k=0;
+ for (const auto [w, h] : resolution_choices)
+ {
+ const QString s = (w == 0)
+ ? tr("Default")
+ : QString::number(w) + " x " + QString::number(h);
+ ui_.resolution->addItem(s, k++);
}
}
-neuralnet_dialog::neuralnet_dialog() :
- trans_calib(1, 2)
+NeuralNetDialog::NeuralNetDialog() :
+ trans_calib_(1, 2)
{
- ui.setupUi(this);
+ ui_.setupUi(this);
make_fps_combobox();
- tie_setting(s.force_fps, ui.cameraFPS);
+ make_resolution_combobox();
for (const auto& str : video::camera_names())
- ui.cameraName->addItem(str);
+ ui_.cameraName->addItem(str);
- tie_setting(s.camera_name, ui.cameraName);
- tie_setting(s.fov, ui.cameraFOV);
- tie_setting(s.offset_fwd, ui.tx_spin);
- tie_setting(s.offset_up, ui.ty_spin);
- tie_setting(s.offset_right, ui.tz_spin);
- tie_setting(s.show_network_input, ui.showNetworkInput);
+ tie_setting(settings_.camera_name, ui_.cameraName);
+ tie_setting(settings_.fov, ui_.cameraFOV);
+ tie_setting(settings_.offset_fwd, ui_.tx_spin);
+ tie_setting(settings_.offset_up, ui_.ty_spin);
+ tie_setting(settings_.offset_right, ui_.tz_spin);
+ tie_setting(settings_.show_network_input, ui_.showNetworkInput);
+ tie_setting(settings_.roi_filter_alpha, ui_.roiFilterAlpha);
+ tie_setting(settings_.use_mjpeg, ui_.use_mjpeg);
+ tie_setting(settings_.roi_zoom, ui_.roiZoom);
+ tie_setting(settings_.num_threads, ui_.threadCount);
+ tie_setting(settings_.resolution, ui_.resolution);
+ tie_setting(settings_.force_fps, ui_.cameraFPS);
+ tie_setting(settings_.posenet_file, ui_.posenetFileDisplay);
- connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK()));
- connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel()));
- connect(ui.camera_settings, SIGNAL(clicked()), this, SLOT(camera_settings()));
+ connect(ui_.buttonBox, SIGNAL(accepted()), this, SLOT(doOK()));
+ connect(ui_.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel()));
+ connect(ui_.camera_settings, SIGNAL(clicked()), this, SLOT(camera_settings()));
+ connect(ui_.posenetSelectButton, SIGNAL(clicked()), this, SLOT(onSelectPoseNetFile()));
+ connect(&settings_.camera_name, value_::value_changed<QString>(), this, &NeuralNetDialog::update_camera_settings_state);
- connect(&s.camera_name, value_::value_changed<QString>(), this, &neuralnet_dialog::update_camera_settings_state);
+ update_camera_settings_state(settings_.camera_name);
- update_camera_settings_state(s.camera_name);
+ connect(&calib_timer_, &QTimer::timeout, this, &NeuralNetDialog::trans_calib_step);
+ calib_timer_.setInterval(35);
+ connect(ui_.tcalib_button,SIGNAL(toggled(bool)), this, SLOT(startstop_trans_calib(bool)));
+
+ connect(&tracker_status_poll_timer_, &QTimer::timeout, this, &NeuralNetDialog::status_poll);
+ tracker_status_poll_timer_.setInterval(250);
+ tracker_status_poll_timer_.start();
+}
- connect(&calib_timer, &QTimer::timeout, this, &neuralnet_dialog::trans_calib_step);
- calib_timer.setInterval(35);
- connect(ui.tcalib_button,SIGNAL(toggled(bool)), this, SLOT(startstop_trans_calib(bool)));
+void NeuralNetDialog::save()
+{
+ settings_.b->save();
}
+void NeuralNetDialog::reload()
+{
+ settings_.b->reload();
+}
-void neuralnet_dialog::doOK()
+void NeuralNetDialog::doOK()
{
- s.b->save();
+ save();
close();
}
-void neuralnet_dialog::doCancel()
+void NeuralNetDialog::doCancel()
{
close();
}
-void neuralnet_dialog::camera_settings()
+void NeuralNetDialog::camera_settings()
{
- if (tracker)
+ if (tracker_)
{
- QMutexLocker l(&tracker->camera_mtx);
- (void)tracker->camera->show_dialog();
+ QMutexLocker l(&tracker_->camera_mtx_);
+ (void)tracker_->camera_->show_dialog();
}
else
- (void)video::show_dialog(s.camera_name);
+ (void)video::show_dialog(settings_.camera_name);
}
-void neuralnet_dialog::update_camera_settings_state(const QString& name)
+void NeuralNetDialog::update_camera_settings_state(const QString& name)
{
(void)name;
- ui.camera_settings->setEnabled(true);
+ ui_.camera_settings->setEnabled(true);
}
-void neuralnet_dialog::register_tracker(ITracker * x)
+void NeuralNetDialog::register_tracker(ITracker * x)
{
- tracker = static_cast<neuralnet_tracker*>(x);
- ui.tcalib_button->setEnabled(true);
+ tracker_ = static_cast<NeuralNetTracker*>(x);
+ ui_.tcalib_button->setEnabled(true);
}
-void neuralnet_dialog::unregister_tracker()
+void NeuralNetDialog::unregister_tracker()
{
- tracker = nullptr;
- ui.tcalib_button->setEnabled(false);
+ tracker_ = nullptr;
+ ui_.tcalib_button->setEnabled(false);
}
+bool NeuralNetDialog::embeddable() noexcept
+{
+ return true;
+}
-void neuralnet_dialog::trans_calib_step()
+void NeuralNetDialog::set_buttons_visible(bool x)
{
- if (tracker)
+ ui_.buttonBox->setVisible(x);
+}
+
+void NeuralNetDialog::status_poll()
+{
+ QString status;
+ if (!tracker_)
+ {
+ status = tr("Tracker Offline");
+ }
+ else
{
- const Affine X_CM = [&]() {
- QMutexLocker l(&calibrator_mutex);
- return tracker->pose();
+ auto [ res, fps, inference_time ] = tracker_->stats();
+ status = tr("%1x%2 @ %3 FPS / Inference: %4 ms").arg(res.width).arg(res.height).arg(int(fps)).arg(inference_time, 0, 'f', 1);
+ }
+ ui_.resolution_display->setText(status);
+}
+
+
+void NeuralNetDialog::trans_calib_step()
+{
+ if (tracker_)
+ {
+ const Affine X_CM = [&]() {
+ QMutexLocker l(&calibrator_mutex_);
+ return tracker_->pose();
}();
- trans_calib.update(X_CM.R, X_CM.t);
- auto [_, nsamples] = trans_calib.get_estimate();
+ trans_calib_.update(X_CM.R, X_CM.t);
+ auto [_, nsamples] = trans_calib_.get_estimate();
constexpr int min_yaw_samples = 15;
constexpr int min_pitch_samples = 12;
@@ -866,52 +903,77 @@ void neuralnet_dialog::trans_calib_step()
const int nsamples_total = nsamples[0] + nsamples[1];
sample_feedback = tr("%1 samples. Over %2, good!").arg(nsamples_total).arg(min_samples);
}
- ui.sample_count_display->setText(sample_feedback);
+ ui_.sample_count_display->setText(sample_feedback);
}
else
startstop_trans_calib(false);
}
-void neuralnet_dialog::startstop_trans_calib(bool start)
+void NeuralNetDialog::startstop_trans_calib(bool start)
{
- QMutexLocker l(&calibrator_mutex);
- // FIXME: does not work ...
+ QMutexLocker l(&calibrator_mutex_);
+ // FIXME: does not work ...
if (start)
{
qDebug() << "pt: starting translation calibration";
- calib_timer.start();
- trans_calib.reset();
- ui.sample_count_display->setText(QString());
+ calib_timer_.start();
+ trans_calib_.reset();
+ ui_.sample_count_display->setText(QString());
// Tracker must run with zero'ed offset for calibration.
- s.offset_fwd = 0;
- s.offset_up = 0;
- s.offset_right = 0;
+ settings_.offset_fwd = 0;
+ settings_.offset_up = 0;
+ settings_.offset_right = 0;
}
else
{
- calib_timer.stop();
+ calib_timer_.stop();
qDebug() << "pt: stopping translation calibration";
{
- auto [tmp, nsamples] = trans_calib.get_estimate();
- s.offset_fwd = int(tmp[0]);
- s.offset_up = int(tmp[1]);
- s.offset_right = int(tmp[2]);
+ auto [tmp, nsamples] = trans_calib_.get_estimate();
+ settings_.offset_fwd = int(tmp[0]);
+ settings_.offset_up = int(tmp[1]);
+ settings_.offset_right = int(tmp[2]);
}
}
- ui.tx_spin->setEnabled(!start);
- ui.ty_spin->setEnabled(!start);
- ui.tz_spin->setEnabled(!start);
+ ui_.tx_spin->setEnabled(!start);
+ ui_.ty_spin->setEnabled(!start);
+ ui_.tz_spin->setEnabled(!start);
if (start)
- ui.tcalib_button->setText(tr("Stop calibration"));
+ ui_.tcalib_button->setText(tr("Stop calibration"));
else
- ui.tcalib_button->setText(tr("Start calibration"));
+ ui_.tcalib_button->setText(tr("Start calibration"));
+}
+
+
+void NeuralNetDialog::onSelectPoseNetFile()
+{
+ const auto root = get_default_model_directory();
+ // Start with the current setting
+ QString filename = settings_.posenet_file;
+ // If the filename is relative then assume that the file is located under the
+ // model directory. Under regular use this should always be the case.
+ if (QFileInfo(filename).isRelative())
+ filename = root.absoluteFilePath(filename);
+ filename = QFileDialog::getOpenFileName(this,
+ tr("Select Pose Net ONNX"), filename, tr("ONNX Files (*.onnx)"));
+ // In case the user aborted.
+ if (filename.isEmpty())
+ return;
+ // When a file under the model directory was selected we can get rid of the
+ // directory prefix. This is more robust than storing absolute paths, e.g.
+ // in case the user moves the opentrack install folder / reuses old settings.
+ // When the file is not in the model directory, we have to use the absolute path,
+ // which is also fine as developer feature.
+ if (filename.startsWith(root.absolutePath()))
+ filename = root.relativeFilePath(filename);
+ settings_.posenet_file = filename;
}
-settings::settings() : opts("neuralnet-tracker") {}
+Settings::Settings() : opts("neuralnet-tracker") {}
} // neuralnet_tracker_ns
-OPENTRACK_DECLARE_TRACKER(neuralnet_tracker, neuralnet_dialog, neuralnet_metadata)
+OPENTRACK_DECLARE_TRACKER(NeuralNetTracker, NeuralNetDialog, NeuralNetMetadata)
diff --git a/tracker-neuralnet/ftnoir_tracker_neuralnet.h b/tracker-neuralnet/ftnoir_tracker_neuralnet.h
index e26689a4..ce85dcd5 100644
--- a/tracker-neuralnet/ftnoir_tracker_neuralnet.h
+++ b/tracker-neuralnet/ftnoir_tracker_neuralnet.h
@@ -7,11 +7,15 @@
#pragma once
+#include "ui_neuralnet-trackercontrols.h"
+#include "model_adapters.h"
+#include "deadzone_filter.h"
+#include "preview.h"
+
#include "options/options.hpp"
#include "api/plugin-api.hpp"
#include "cv/video-widget.hpp"
#include "cv/translation-calibrator.hpp"
-#include "cv/numeric.hpp"
#include "compat/timer.hpp"
#include "video/camera.hpp"
#include "cv/affine.hpp"
@@ -25,14 +29,11 @@
#include <memory>
#include <cinttypes>
-
-#include <onnxruntime_cxx_api.h>
+#include <array>
#include <opencv2/core.hpp>
-#include <opencv2/core/types.hpp>
#include <opencv2/imgproc.hpp>
-#include "ui_neuralnet-trackercontrols.h"
namespace neuralnet_tracker_ns
{
@@ -46,11 +47,36 @@ enum fps_choices
fps_default = 0,
fps_30 = 1,
fps_60 = 2,
- fps_MAX = 3
+ fps_75 = 3,
+ fps_125 = 4,
+ fps_200 = 5,
+ fps_50 = 6,
+ fps_100 = 7,
+ fps_120 = 8,
+ fps_300 = 9,
+ fps_250 = 10,
+ fps_MAX = 11,
};
+struct resolution_tuple
+{
+ int width;
+ int height;
+};
+
+static const std::array<resolution_tuple, 7> resolution_choices =
+{{
+ { 320, 240 },
+ { 640, 480 },
+ { 800, 600 },
+ { 1024, 768 },
+ { 1280, 720 },
+ { 1920, 1080},
+ { 0, 0 }
+}};
+
-struct settings : opts {
+struct Settings : opts {
value<int> offset_fwd { b, "offset-fwd", 200 }, // Millimeters
offset_up { b, "offset-up", 0 },
offset_right { b, "offset-right", 0 };
@@ -58,7 +84,15 @@ struct settings : opts {
value<int> fov { b, "field-of-view", 56 };
value<fps_choices> force_fps { b, "force-fps", fps_default };
value<bool> show_network_input { b, "show-network-input", false };
- settings();
+ value<double> roi_filter_alpha{ b, "roi-filter-alpha", 1. };
+ value<double> roi_zoom{ b, "roi-zoom", 1. };
+ value<bool> use_mjpeg { b, "use-mjpeg", false };
+ value<int> num_threads { b, "num-threads", 1 };
+ value<int> resolution { b, "force-resolution", 0 };
+ value<double> deadzone_size { b, "deadzone-size", 1. };
+ value<double> deadzone_hardness { b, "deadzone-hardness", 1.5 };
+ value<QString> posenet_file { b, "posenet-file", "head-pose-0.3-big-quantized.onnx" };
+ Settings();
};
@@ -71,152 +105,107 @@ struct CamIntrinsics
};
-class Localizer
-{
- public:
- Localizer(Ort::MemoryInfo &allocator_info,
- Ort::Session &&session);
-
- // Returns bounding wrt image coordinate of the input image
- // The preceeding float is the score for being a face normalized to [0,1].
- std::pair<float, cv::Rect2f> run(
- const cv::Mat &frame);
-
- private:
- inline static constexpr int input_img_width = 288;
- inline static constexpr int input_img_height = 224;
- Ort::Session session{nullptr};
- // Inputs / outputs
- cv::Mat scaled_frame{}, input_mat{};
- Ort::Value input_val{nullptr}, output_val{nullptr};
- std::array<float, 5> results;
-};
-
-
-class PoseEstimator
-{
- public:
- struct Face
- {
- std::array<float,4> rotation; // Quaternion, (w, x, y, z)
- // The following quantities are defined wrt the image space of the input
- cv::Rect2f box;
- cv::Point2f center;
- float size;
- };
-
- PoseEstimator(Ort::MemoryInfo &allocator_info,
- Ort::Session &&session);
- // Inference
- std::optional<Face> run(const cv::Mat &frame, const cv::Rect &box);
- // Returns an image compatible with the 'frame' image for displaying.
- cv::Mat last_network_input() const;
-
- private:
- // Operates on the private image data members
- int find_input_intensity_90_pct_quantile() const;
-
- inline static constexpr int input_img_width = 129;
- inline static constexpr int input_img_height = 129;
- Ort::Session session{nullptr};
- // Inputs
- cv::Mat scaled_frame{}, input_mat{};
- Ort::Value input_val{nullptr};
- // Outputs
- cv::Vec<float, 3> output_coord{};
- cv::Vec<float, 4> output_quat{};
- cv::Vec<float, 4> output_box{};
- Ort::Value output_val[3] = {
- Ort::Value{nullptr},
- Ort::Value{nullptr},
- Ort::Value{nullptr}};
-};
-
-
-class neuralnet_tracker : protected virtual QThread, public ITracker
+class NeuralNetTracker : protected virtual QThread, public ITracker
{
- Q_OBJECT
+ //Q_OBJECT
public:
- neuralnet_tracker();
- ~neuralnet_tracker() override;
+ NeuralNetTracker();
+ ~NeuralNetTracker() override;
module_status start_tracker(QFrame* frame) override;
void data(double *data) override;
void run() override;
Affine pose();
+ std::tuple<cv::Size, double, double> stats() const;
- QMutex camera_mtx;
- std::unique_ptr<video::impl::camera> camera;
+ QMutex camera_mtx_;
+ std::unique_ptr<video::impl::camera> camera_;
private:
bool detect();
bool open_camera();
void set_intrinsics();
+ cv::Mat prepare_input_image(const video::frame& frame);
bool load_and_initialize_model();
void draw_gizmos(
- cv::Mat frame,
- const PoseEstimator::Face &face,
- const Affine& pose) const;
+ const std::optional<PoseEstimator::Face> &face,
+ const Affine& pose);
void update_fps(double dt);
-
- Affine compute_pose(const PoseEstimator::Face &face) const;
- numeric_types::vec3 image_to_world(float x, float y, float size, float real_size) const;
- numeric_types::vec2 world_to_image(const numeric_types::vec3& p) const;
-
- settings s;
- std::optional<Localizer> localizer;
- std::optional<PoseEstimator> poseestimator;
- Ort::Env env{nullptr};
- Ort::MemoryInfo allocator_info{nullptr};
-
- CamIntrinsics intrinsics{};
- cv::Mat frame, grayscale;
- std::optional<cv::Rect2f> last_localizer_roi;
- std::optional<cv::Rect2f> last_roi;
- static constexpr float head_size_mm = 200.f;
-
- double fps = 0;
- double max_frame_time = 0;
+ // Secretly applies filtering while computing the pose in 3d space.
+ QuatPose compute_filtered_pose(const PoseEstimator::Face &face);
+ // Compute the pose in 3d space taking the network outputs
+ QuatPose transform_to_world_pose(const cv::Quatf &face_rotation, const cv::Point2f& face_xy, const float face_size) const;
+ QString get_posenet_filename() const;
+
+ Settings settings_;
+ std::optional<Localizer> localizer_;
+ std::optional<PoseEstimator> poseestimator_;
+ Ort::Env env_{nullptr};
+ Ort::MemoryInfo allocator_info_{nullptr};
+
+ CamIntrinsics intrinsics_{};
+ cv::Mat grayscale_;
+ std::array<cv::Mat,2> downsized_original_images_ = {}; // Image pyramid
+ std::optional<cv::Rect2f> last_localizer_roi_;
+ std::optional<cv::Rect2f> last_roi_;
+ static constexpr float HEAD_SIZE_MM = 200.f; // In the vertical. Approximately.
+
+ mutable QMutex stats_mtx_;
+ double fps_ = 0;
+ double inference_time_ = 0;
+ cv::Size resolution_ = {};
+
static constexpr double RC = .25;
+ int num_threads_ = 1;
+ bool is_visible_ = true;
- QMutex mtx; // Protects the pose
- Affine pose_;
+ QMutex mtx_ = {}; // Protects the pose
+ std::optional<QuatPose> last_pose_ = {};
+ Affine last_pose_affine_ = {};
- std::unique_ptr<cv_video_widget> videoWidget;
- std::unique_ptr<QHBoxLayout> layout;
+ Preview preview_;
+ std::unique_ptr<cv_video_widget> video_widget_;
+ std::unique_ptr<QHBoxLayout> layout_;
};
-class neuralnet_dialog : public ITrackerDialog
+class NeuralNetDialog : public ITrackerDialog
{
Q_OBJECT
public:
- neuralnet_dialog();
+ NeuralNetDialog();
void register_tracker(ITracker * x) override;
void unregister_tracker() override;
+
+ bool embeddable() noexcept override;
+ void set_buttons_visible(bool x) override;
private:
void make_fps_combobox();
+ void make_resolution_combobox();
- Ui::Form ui;
- settings s;
-
+ Ui::Form ui_;
+ Settings settings_;
// Calibration code mostly taken from point tracker
- QTimer calib_timer;
- TranslationCalibrator trans_calib;
- QMutex calibrator_mutex;
-
- neuralnet_tracker* tracker = nullptr;
+ QTimer calib_timer_;
+ TranslationCalibrator trans_calib_;
+ QMutex calibrator_mutex_;
+ QTimer tracker_status_poll_timer_;
+ NeuralNetTracker* tracker_ = nullptr;
private Q_SLOTS:
+ void save() override;
+ void reload() override;
void doOK();
void doCancel();
void camera_settings();
void update_camera_settings_state(const QString& name);
void startstop_trans_calib(bool start);
void trans_calib_step();
+ void status_poll();
+ void onSelectPoseNetFile();
};
-class neuralnet_metadata : public Metadata
+class NeuralNetMetadata : public Metadata
{
Q_OBJECT
QString name() override { return QString("neuralnet tracker"); }
@@ -226,6 +215,15 @@ class neuralnet_metadata : public Metadata
} // neuralnet_tracker_ns
-using neuralnet_tracker_ns::neuralnet_tracker;
-using neuralnet_tracker_ns::neuralnet_dialog;
-using neuralnet_tracker_ns::neuralnet_metadata; \ No newline at end of file
+
+namespace neuralnet_tracker_tests
+{
+
+void run();
+
+}
+
+
+using neuralnet_tracker_ns::NeuralNetTracker;
+using neuralnet_tracker_ns::NeuralNetDialog;
+using neuralnet_tracker_ns::NeuralNetMetadata;
diff --git a/tracker-neuralnet/lang/de_DE.ts b/tracker-neuralnet/lang/de_DE.ts
new file mode 100644
index 00000000..6261eec0
--- /dev/null
+++ b/tracker-neuralnet/lang/de_DE.ts
@@ -0,0 +1,172 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>Form</name>
+ <message>
+ <source>Tracker settings</source>
+ <translation>Tracker-Einstellungen</translation>
+ </message>
+ <message>
+ <source>Head Center Offset</source>
+ <translation>Versatz zur Kopfmitte</translation>
+ </message>
+ <message>
+ <source>Right</source>
+ <translation>Rechts</translation>
+ </message>
+ <message>
+ <source>Forward</source>
+ <translation>Vorwärts</translation>
+ </message>
+ <message>
+ <source>Up</source>
+ <translation>Hoch</translation>
+ </message>
+ <message>
+ <source> mm</source>
+ <translation> mm</translation>
+ </message>
+ <message>
+ <source>Use only yaw and pitch while calibrating.
+Don&apos;t roll or change position.</source>
+ <translation>Während der Kalibrierung nur gieren und nicken.
+Bitte nicht rollen oder die Position ändern.</translation>
+ </message>
+ <message>
+ <source>Start calibration</source>
+ <translation>Kalibrierung starten</translation>
+ </message>
+ <message>
+ <source>Camera Configuration</source>
+ <translation>Kamera-Konfiguration</translation>
+ </message>
+ <message>
+ <source>Diagonal FOV</source>
+ <translation>Diagonales Sichtfeld</translation>
+ </message>
+ <message>
+ <source>Camera name</source>
+ <translation>Kamera-Name</translation>
+ </message>
+ <message>
+ <source>Field of view. Needed to transform the pose to world coordinates.</source>
+ <translation>Sichtfeld. Benötigt, um die Pose in Welt-Koordinaten zu übersetzen.</translation>
+ </message>
+ <message>
+ <source>The requested resolution for cases where the camera delivers maximum frame rate only for a particular resolution. The image may still be downscaled to the internal resolution.</source>
+ <translation>Die angeforderte Auflösung für Fälle, in denen die Kamera die maximale Bildrate nur bei bestimmten Auflösungen ausgibt. Das Bild wird möglicherweise weiterhin herunterskaliert auf die interne Auflösung.</translation>
+ </message>
+ <message>
+ <source>Resolution</source>
+ <translation>Auflösung</translation>
+ </message>
+ <message>
+ <source>Requested video frame rate. Actual setting may not be supported by the camera.</source>
+ <translation>Angeforderte Bildrate. Die tatsächliche Einstellungen wird von der Kamera möglicherweise nicht unterstützt.</translation>
+ </message>
+ <message>
+ <source>Frames per second</source>
+ <translation>Bilder pro Sekunde</translation>
+ </message>
+ <message>
+ <source>MJPEG</source>
+ <translation>MJPEG</translation>
+ </message>
+ <message>
+ <source>Camera settings</source>
+ <translation>Kamera-Einstellungen</translation>
+ </message>
+ <message>
+ <source>Tuning / Debug</source>
+ <translation>Tuning / Fehlersuche</translation>
+ </message>
+ <message>
+ <source>Thread Count</source>
+ <translation>Anzahl der Threads</translation>
+ </message>
+ <message>
+ <source>Number of threads. Can be used to balance the CPU load between the game and the tracker.</source>
+ <translation>Anzahl der Threads. Kann verwendet werden, um die CPU-Last zwischen Spiel und Tracker zu balancieren.</translation>
+ </message>
+ <message>
+ <source>Show the image patch that the pose estimation model sees.</source>
+ <translation>Zeigt den Bildausschnitt, den das Modell zur Posenabschätzung sieht.</translation>
+ </message>
+ <message>
+ <source>Show Network Input</source>
+ <translation>Zeige Netzwerk-Eingabe</translation>
+ </message>
+ <message>
+ <source>ROI Smoothing Alpha</source>
+ <translation>ROI-Glättungsalpha</translation>
+ </message>
+ <message>
+ <source>Amount of smoothing of the face region coordinates. Can help stabilize the pose.</source>
+ <translation>Umfang der Glättung der Gesichtkoordinaten. Kann helfen, die Pose zu stabilisieren.</translation>
+ </message>
+ <message>
+ <source>ROI Zoom</source>
+ <translation>ROI-Zoom</translation>
+ </message>
+ <message>
+ <source>Zoom factor for the face region. Applied before the patch is fed into the pose estimation model. There is a sweet spot near 1.</source>
+ <translation>Zoom-Faktor der Gesichtsregion. Wird angewendet, bevor der Bildausschnitt zum Posen-Abschätzungsmodell gesendet wird. Der Sweet-Spot liegt nahe bei 1.</translation>
+ </message>
+ <message>
+ <source>Select the pose network. Changes take affect on the next tracker start</source>
+ <translation>Wählt das Pose-Netzwerk. Die Änderungen treten beim nächsten Start des Trackers inkraft</translation>
+ </message>
+ <message>
+ <source>Select Pose Net ONNX</source>
+ <translation>Wähle Pose-Netzwerk ONNX</translation>
+ </message>
+ <message>
+ <source>&lt;the pose net file&gt;</source>
+ <translation>&lt;die pose netzwerk datei&gt;</translation>
+ </message>
+</context>
+<context>
+ <name>neuralnet_tracker_ns::NeuralNetDialog</name>
+ <message>
+ <source>Default</source>
+ <translation>Standard</translation>
+ </message>
+ <message>
+ <source>Tracker Offline</source>
+ <translation>Tracker offline</translation>
+ </message>
+ <message>
+ <source>%1x%2 @ %3 FPS / Inference: %4 ms</source>
+ <translation>%1x%2 @ %3 FPS / Inferenz: %4 ms</translation>
+ </message>
+ <message>
+ <source>%1 yaw samples. Yaw more to %2 samples for stable calibration.</source>
+ <translation>%1 Gieren-Proben. Weiterhin gieren bis %2 Proben für eine stabile Kalibrierung.</translation>
+ </message>
+ <message>
+ <source>%1 pitch samples. Pitch more to %2 samples for stable calibration.</source>
+ <translation>%1 Nicken-Proben. Weiterhin nicken bis %2 Proben für eine stabile Kalibrierung.</translation>
+ </message>
+ <message>
+ <source>%1 samples. Over %2, good!</source>
+ <translation>%1 Proben. Mehr als %2, gut!</translation>
+ </message>
+ <message>
+ <source>Stop calibration</source>
+ <translation>Kalibrierung stoppen</translation>
+ </message>
+ <message>
+ <source>Start calibration</source>
+ <translation>Kalibrierung starten</translation>
+ </message>
+ <message>
+ <source>Select Pose Net ONNX</source>
+ <translation>Wähle Pose-Netzwerk ONNX</translation>
+ </message>
+ <message>
+ <source>ONNX Files (*.onnx)</source>
+ <translation>ONNX-Dateien (*.onnx)</translation>
+ </message>
+</context>
+</TS>
diff --git a/tracker-neuralnet/lang/nl_NL.ts b/tracker-neuralnet/lang/nl_NL.ts
index fb6c3348..27da4f5a 100644
--- a/tracker-neuralnet/lang/nl_NL.ts
+++ b/tracker-neuralnet/lang/nl_NL.ts
@@ -60,14 +60,86 @@ Don&apos;t roll or change position.</source>
<source>Show Network Input</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>MJPEG</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Tuning / Debug</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>ROI Smoothing Alpha</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>ROI Zoom</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Thread Count</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Resolution</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Field of view. Needed to transform the pose to world coordinates.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Requested video frame rate. Actual setting may not be supported by the camera.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>The requested resolution for cases where the camera delivers maximum frame rate only for a particular resolution. The image may still be downscaled to the internal resolution.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Number of threads. Can be used to balance the CPU load between the game and the tracker.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Show the image patch that the pose estimation model sees.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Amount of smoothing of the face region coordinates. Can help stabilize the pose.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Zoom factor for the face region. Applied before the patch is fed into the pose estimation model. There is a sweet spot near 1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Select Pose Net ONNX</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;the pose net file&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Select the pose network. Changes take affect on the next tracker start</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
- <name>neuralnet_tracker_ns::neuralnet_dialog</name>
+ <name>neuralnet_tracker_ns::NeuralNetDialog</name>
<message>
<source>Default</source>
<translation type="unfinished">Standaard</translation>
</message>
<message>
+ <source>Tracker Offline</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1x%2 @ %3 FPS / Inference: %4 ms</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
<source>%1 yaw samples. Yaw more to %2 samples for stable calibration.</source>
<translation type="unfinished"></translation>
</message>
@@ -87,5 +159,13 @@ Don&apos;t roll or change position.</source>
<source>Start calibration</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Select Pose Net ONNX</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>ONNX Files (*.onnx)</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
</TS>
diff --git a/tracker-neuralnet/lang/ru_RU.ts b/tracker-neuralnet/lang/ru_RU.ts
index f1ba9a92..c32d4fa7 100644
--- a/tracker-neuralnet/lang/ru_RU.ts
+++ b/tracker-neuralnet/lang/ru_RU.ts
@@ -5,86 +5,169 @@
<name>Form</name>
<message>
<source>Tracker settings</source>
- <translation type="unfinished"></translation>
+ <translation>Настройки трекера</translation>
</message>
<message>
<source>Diagonal FOV</source>
- <translation type="unfinished"></translation>
+ <translation>Угол обзора</translation>
</message>
<message>
<source>Camera settings</source>
- <translation type="unfinished"></translation>
+ <translation>Настройки камеры</translation>
</message>
<message>
<source>Frames per second</source>
- <translation type="unfinished"></translation>
+ <translation>Кадры в секунду</translation>
</message>
<message>
<source>Camera name</source>
- <translation type="unfinished"></translation>
+ <translation>Камера</translation>
</message>
<message>
<source>Camera Configuration</source>
- <translation type="unfinished"></translation>
+ <translation>Конфигурация камеры</translation>
</message>
<message>
<source>Head Center Offset</source>
- <translation type="unfinished"></translation>
+ <translation>Смещение центра головы</translation>
</message>
<message>
<source> mm</source>
- <translation type="unfinished"></translation>
+ <translation> мм</translation>
</message>
<message>
<source>Use only yaw and pitch while calibrating.
Don&apos;t roll or change position.</source>
- <translation type="unfinished"></translation>
+ <translation>Поворачивайте голову влево-вправо и наклоняйте вверх-вниз.
+Не наклоняйте набок и не смещайте голову в сторону.</translation>
</message>
<message>
<source>Start calibration</source>
- <translation type="unfinished"></translation>
+ <translation>Начать калибровку</translation>
</message>
<message>
<source>Right</source>
- <translation type="unfinished"></translation>
+ <translation>Вправо</translation>
</message>
<message>
<source>Forward</source>
- <translation type="unfinished"></translation>
+ <translation>Вперед</translation>
</message>
<message>
<source>Up</source>
- <translation type="unfinished"></translation>
+ <translation>Вверх</translation>
</message>
<message>
<source>Show Network Input</source>
+ <translation>Показать входные данные</translation>
+ </message>
+ <message>
+ <source>MJPEG</source>
+ <translation>Использовать MJPEG</translation>
+ </message>
+ <message>
+ <source>Tuning / Debug</source>
+ <translation>Тонкая настройка</translation>
+ </message>
+ <message>
+ <source>ROI Smoothing Alpha</source>
+ <translation>Сглаживание ROI</translation>
+ </message>
+ <message>
+ <source>ROI Zoom</source>
+ <translation>Масштабирование ROI</translation>
+ </message>
+ <message>
+ <source>Thread Count</source>
+ <translation>Количество потоков</translation>
+ </message>
+ <message>
+ <source>Resolution</source>
+ <translation>Разрешение</translation>
+ </message>
+ <message>
+ <source>Field of view. Needed to transform the pose to world coordinates.</source>
+ <translation>Угол обзора камеры. Требуется для преобразования положения головы в глобальные координаты</translation>
+ </message>
+ <message>
+ <source>Requested video frame rate. Actual setting may not be supported by the camera.</source>
+ <translation>Частота кадров. Реальные значения могут не поддерживаться камерой.</translation>
+ </message>
+ <message>
+ <source>The requested resolution for cases where the camera delivers maximum frame rate only for a particular resolution. The image may still be downscaled to the internal resolution.</source>
+ <translation>Разрешение камеры, для тех случаев, когда быстродействие камеры максимально в определенном разрешении. Может быть масштабировано до внутреннего разрешения.</translation>
+ </message>
+ <message>
+ <source>Number of threads. Can be used to balance the CPU load between the game and the tracker.</source>
+ <translation>Количество потоков. Используется для балансировки нагрузки на процессор между игрой и трекером.</translation>
+ </message>
+ <message>
+ <source>Show the image patch that the pose estimation model sees.</source>
+ <translation>Показать изображение, используемое моделью определения позиции</translation>
+ </message>
+ <message>
+ <source>Amount of smoothing of the face region coordinates. Can help stabilize the pose.</source>
+ <translation>Сглаживание координат области лица. Может помочь стабилизировать позицию.</translation>
+ </message>
+ <message>
+ <source>Zoom factor for the face region. Applied before the patch is fed into the pose estimation model. There is a sweet spot near 1.</source>
+ <translation>Фактор масштабирования области лица. Применяется перед передачей кадра в модель определения позиции. Наилучшие результаты близки к 1</translation>
+ </message>
+ <message>
+ <source>Select Pose Net ONNX</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;the pose net file&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Select the pose network. Changes take affect on the next tracker start</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
- <name>neuralnet_tracker_ns::neuralnet_dialog</name>
+ <name>neuralnet_tracker_ns::NeuralNetDialog</name>
<message>
<source>Default</source>
- <translation type="unfinished"></translation>
+ <translation>По умолчанию</translation>
+ </message>
+ <message>
+ <source>Tracker Offline</source>
+ <translation>Трекер выключен</translation>
+ </message>
+ <message>
+ <source>%1x%2 @ %3 FPS / Inference: %4 ms</source>
+ <translation>%1x%2 @ %3 FPS; Время оценки: %4 мс</translation>
</message>
<message>
<source>%1 yaw samples. Yaw more to %2 samples for stable calibration.</source>
- <translation type="unfinished"></translation>
+ <translation>Сэмплов поворота: %1.
+Поворачивайте голову в стороны до %2 сэмплов для стабильной калибрации.</translation>
</message>
<message>
<source>%1 pitch samples. Pitch more to %2 samples for stable calibration.</source>
- <translation type="unfinished"></translation>
+ <translation>Сэмплов наклона: %1.
+Наклоняйте голову вниз/вверх до %2 сэмплов для стабильной калибрации.</translation>
</message>
<message>
<source>%1 samples. Over %2, good!</source>
- <translation type="unfinished"></translation>
+ <translation>%1 сэмплов. Более %2, достаточно.</translation>
</message>
<message>
<source>Stop calibration</source>
- <translation type="unfinished"></translation>
+ <translation>Остановить калибровку</translation>
</message>
<message>
<source>Start calibration</source>
+ <translation>Начать калибровку</translation>
+ </message>
+ <message>
+ <source>Select Pose Net ONNX</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>ONNX Files (*.onnx)</source>
<translation type="unfinished"></translation>
</message>
</context>
diff --git a/tracker-neuralnet/lang/stub.ts b/tracker-neuralnet/lang/stub.ts
index 52b8aded..9609f05e 100644
--- a/tracker-neuralnet/lang/stub.ts
+++ b/tracker-neuralnet/lang/stub.ts
@@ -60,14 +60,86 @@ Don&apos;t roll or change position.</source>
<source>Show Network Input</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>MJPEG</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Tuning / Debug</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>ROI Smoothing Alpha</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>ROI Zoom</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Thread Count</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Resolution</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Field of view. Needed to transform the pose to world coordinates.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Requested video frame rate. Actual setting may not be supported by the camera.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>The requested resolution for cases where the camera delivers maximum frame rate only for a particular resolution. The image may still be downscaled to the internal resolution.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Number of threads. Can be used to balance the CPU load between the game and the tracker.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Show the image patch that the pose estimation model sees.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Amount of smoothing of the face region coordinates. Can help stabilize the pose.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Zoom factor for the face region. Applied before the patch is fed into the pose estimation model. There is a sweet spot near 1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Select Pose Net ONNX</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;the pose net file&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Select the pose network. Changes take affect on the next tracker start</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
- <name>neuralnet_tracker_ns::neuralnet_dialog</name>
+ <name>neuralnet_tracker_ns::NeuralNetDialog</name>
<message>
<source>Default</source>
<translation type="unfinished"></translation>
</message>
<message>
+ <source>Tracker Offline</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1x%2 @ %3 FPS / Inference: %4 ms</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
<source>%1 yaw samples. Yaw more to %2 samples for stable calibration.</source>
<translation type="unfinished"></translation>
</message>
@@ -87,5 +159,13 @@ Don&apos;t roll or change position.</source>
<source>Start calibration</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Select Pose Net ONNX</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>ONNX Files (*.onnx)</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
</TS>
diff --git a/tracker-neuralnet/lang/zh_CN.ts b/tracker-neuralnet/lang/zh_CN.ts
index 2d0dd8ff..53da04ae 100644
--- a/tracker-neuralnet/lang/zh_CN.ts
+++ b/tracker-neuralnet/lang/zh_CN.ts
@@ -1,35 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
<context>
<name>Form</name>
<message>
<source>Tracker settings</source>
- <translation type="unfinished"></translation>
+ <translation>追踪器设置</translation>
</message>
<message>
<source>Diagonal FOV</source>
- <translation type="unfinished"></translation>
+ <translation>对角FOV</translation>
</message>
<message>
<source>Camera name</source>
- <translation type="unfinished"></translation>
+ <translation>相机名</translation>
</message>
<message>
<source>Frames per second</source>
- <translation type="unfinished"></translation>
+ <translation>FPS</translation>
</message>
<message>
<source>Camera settings</source>
- <translation type="unfinished"></translation>
+ <translation>相机设置</translation>
</message>
<message>
<source>Camera Configuration</source>
- <translation type="unfinished"></translation>
+ <translation>相机配置</translation>
</message>
<message>
<source>Head Center Offset</source>
- <translation type="unfinished"></translation>
+ <translation>头部归中补偿</translation>
</message>
<message>
<source> mm</source>
@@ -38,34 +38,107 @@
<message>
<source>Use only yaw and pitch while calibrating.
Don&apos;t roll or change position.</source>
- <translation type="unfinished"></translation>
+ <translation>在校准时只使用偏航和俯仰,
+不要滚转或是改变位置. </translation>
</message>
<message>
<source>Start calibration</source>
- <translation type="unfinished"></translation>
+ <translation>开始校准</translation>
</message>
<message>
<source>Right</source>
- <translation type="unfinished"></translation>
+ <translation>向右</translation>
</message>
<message>
<source>Forward</source>
- <translation type="unfinished"></translation>
+ <translation>向前</translation>
</message>
<message>
<source>Up</source>
- <translation type="unfinished"></translation>
+ <translation>向上</translation>
</message>
<message>
<source>Show Network Input</source>
+ <translation>展示神经网络输入</translation>
+ </message>
+ <message>
+ <source>MJPEG</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Tuning / Debug</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>ROI Smoothing Alpha</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>ROI Zoom</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Thread Count</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Resolution</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Field of view. Needed to transform the pose to world coordinates.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Requested video frame rate. Actual setting may not be supported by the camera.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>The requested resolution for cases where the camera delivers maximum frame rate only for a particular resolution. The image may still be downscaled to the internal resolution.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Number of threads. Can be used to balance the CPU load between the game and the tracker.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Show the image patch that the pose estimation model sees.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Amount of smoothing of the face region coordinates. Can help stabilize the pose.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Zoom factor for the face region. Applied before the patch is fed into the pose estimation model. There is a sweet spot near 1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Select the pose network. Changes take affect on the next tracker start</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Select Pose Net ONNX</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;the pose net file&gt;</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
- <name>neuralnet_tracker_ns::neuralnet_dialog</name>
+ <name>neuralnet_tracker_ns::NeuralNetDialog</name>
<message>
<source>Default</source>
- <translation type="unfinished"></translation>
+ <translation>默认</translation>
+ </message>
+ <message>
+ <source>Tracker Offline</source>
+ <translation>追踪器离线</translation>
+ </message>
+ <message>
+ <source>%1x%2 @ %3 FPS / Inference: %4 ms</source>
+ <translation>%1x%2 @ %3 FPS / 推理: %4 ms</translation>
</message>
<message>
<source>%1 yaw samples. Yaw more to %2 samples for stable calibration.</source>
@@ -81,10 +154,18 @@ Don&apos;t roll or change position.</source>
</message>
<message>
<source>Stop calibration</source>
- <translation type="unfinished"></translation>
+ <translation>结束校准</translation>
</message>
<message>
<source>Start calibration</source>
+ <translation>开始校准</translation>
+ </message>
+ <message>
+ <source>Select Pose Net ONNX</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>ONNX Files (*.onnx)</source>
<translation type="unfinished"></translation>
</message>
</context>
diff --git a/tracker-neuralnet/model_adapters.cpp b/tracker-neuralnet/model_adapters.cpp
new file mode 100644
index 00000000..f53478af
--- /dev/null
+++ b/tracker-neuralnet/model_adapters.cpp
@@ -0,0 +1,416 @@
+#include "model_adapters.h"
+
+#include "compat/timer.hpp"
+
+#include <opencv2/core.hpp>
+#include <opencv2/core/quaternion.hpp>
+#include <opencv2/imgproc.hpp>
+
+#include <QDebug>
+
+namespace neuralnet_tracker_ns
+{
+
+
+float sigmoid(float x)
+{
+ return 1.f/(1.f + std::exp(-x));
+}
+
+
+// Defined in ftnoir_tracker_neuralnet.cpp
+// Normally we wouldn't need it here. However ... see below.
+cv::Quatf image_to_world(cv::Quatf q);
+
+
+cv::Quatf world_to_image(cv::Quatf q)
+{
+ // It's its own inverse.
+ return image_to_world(q);
+}
+
+
+cv::Rect2f unnormalize(const cv::Rect2f &r, int h, int w)
+{
+ auto unnorm = [](float x) -> float { return 0.5*(x+1); };
+ auto tl = r.tl();
+ auto br = r.br();
+ auto x0 = unnorm(tl.x)*w;
+ auto y0 = unnorm(tl.y)*h;
+ auto x1 = unnorm(br.x)*w;
+ auto y1 = unnorm(br.y)*h;
+ return {
+ x0, y0, x1-x0, y1-y0
+ };
+}
+
+
+// Returns width and height of the input tensor, or throws.
+// Expects the model to take one tensor as input that must
+// have the shape B x C x H x W, where B=C=1.
+cv::Size get_input_image_shape(const Ort::Session &session)
+{
+ if (session.GetInputCount() < 1)
+ throw std::invalid_argument("Model must take at least one input tensor");
+ const std::vector<std::int64_t> shape =
+ session.GetInputTypeInfo(0).GetTensorTypeAndShapeInfo().GetShape();
+ if (shape.size() != 4)
+ throw std::invalid_argument("Model takes the input tensor in the wrong shape");
+ return { static_cast<int>(shape[3]), static_cast<int>(shape[2]) };
+}
+
+
+Ort::Value create_tensor(const Ort::TypeInfo& info, Ort::Allocator& alloc)
+{
+ const auto shape = info.GetTensorTypeAndShapeInfo().GetShape();
+ auto t = Ort::Value::CreateTensor<float>(
+ alloc, shape.data(), shape.size());
+ memset(t.GetTensorMutableData<float>(), 0, sizeof(float)*info.GetTensorTypeAndShapeInfo().GetElementCount());
+ return t;
+}
+
+
+int find_input_intensity_quantile(const cv::Mat& frame, float percentage)
+{
+ const int channels[] = { 0 };
+ const int hist_size[] = { 256 };
+ float range[] = { 0, 256 };
+ const float* ranges[] = { range };
+ cv::Mat hist;
+ cv::calcHist(&frame, 1, channels, cv::Mat(), hist, 1, hist_size, ranges, true, false);
+ int gray_level = 0;
+ const int num_pixels_quantile = frame.total()*percentage*0.01f;
+ int num_pixels_accum = 0;
+ for (int i=0; i<hist_size[0]; ++i)
+ {
+ num_pixels_accum += hist.at<float>(i);
+ if (num_pixels_accum > num_pixels_quantile)
+ {
+ gray_level = i;
+ break;
+ }
+ }
+ return gray_level;
+}
+
+
+// Automatic brightness adjustment. Scales brightness to lie between -.5 and 0.5, roughly.
+void normalize_brightness(const cv::Mat& frame, cv::Mat& out)
+{
+ const float pct = 90;
+
+ const int brightness = find_input_intensity_quantile(frame, pct);
+
+ const double alpha = brightness<127 ? (pct/100.f*0.5f/std::max(5,brightness)) : 1./255;
+ const double beta = -0.5;
+
+ frame.convertTo(out, CV_32F, alpha, beta);
+}
+
+
+
+Localizer::Localizer(Ort::MemoryInfo &allocator_info, Ort::Session &&session) :
+ session_{std::move(session)},
+ scaled_frame_(INPUT_IMG_HEIGHT, INPUT_IMG_WIDTH, CV_8U),
+ input_mat_(INPUT_IMG_HEIGHT, INPUT_IMG_WIDTH, CV_32F)
+{
+ // Only works when input_mat does not reallocated memory ...which it should not.
+ // Non-owning memory reference to input_mat?
+ // Note: shape = (bach x channels x h x w)
+ const std::int64_t input_shape[4] = { 1, 1, INPUT_IMG_HEIGHT, INPUT_IMG_WIDTH };
+ input_val_ = Ort::Value::CreateTensor<float>(allocator_info, input_mat_.ptr<float>(0), input_mat_.total(), input_shape, 4);
+
+ const std::int64_t output_shape[2] = { 1, 5 };
+ output_val_ = Ort::Value::CreateTensor<float>(allocator_info, results_.data(), results_.size(), output_shape, 2);
+}
+
+
+std::pair<float, cv::Rect2f> Localizer::run(
+ const cv::Mat &frame)
+{
+ auto p = input_mat_.ptr(0);
+
+ cv::resize(frame, scaled_frame_, { INPUT_IMG_WIDTH, INPUT_IMG_HEIGHT }, 0, 0, cv::INTER_AREA);
+ scaled_frame_.convertTo(input_mat_, CV_32F, 1./255., -0.5);
+
+ assert (input_mat_.ptr(0) == p);
+ assert (!input_mat_.empty() && input_mat_.isContinuous());
+ assert (input_mat_.cols == INPUT_IMG_WIDTH && input_mat_.rows == INPUT_IMG_HEIGHT);
+
+ const char* input_names[] = {"x"};
+ const char* output_names[] = {"logit_box"};
+
+ Timer t; t.start();
+
+ session_.Run(Ort::RunOptions{nullptr}, input_names, &input_val_, 1, output_names, &output_val_, 1);
+
+ last_inference_time_ = t.elapsed_ms();
+
+ const cv::Rect2f roi = unnormalize(cv::Rect2f{
+ results_[1],
+ results_[2],
+ results_[3]-results_[1], // Width
+ results_[4]-results_[2] // Height
+ }, frame.rows, frame.cols);
+ const float score = sigmoid(results_[0]);
+
+ return { score, roi };
+}
+
+
+double Localizer::last_inference_time_millis() const
+{
+ return last_inference_time_;
+}
+
+
+std::string PoseEstimator::get_network_input_name(size_t i) const
+{
+#if ORT_API_VERSION >= 12
+ return std::string(&*session_.GetInputNameAllocated(i, allocator_));
+#else
+ return std::string(session_.GetInputName(i, allocator_));
+#endif
+}
+
+std::string PoseEstimator::get_network_output_name(size_t i) const
+{
+#if ORT_API_VERSION >= 12
+ return std::string(&*session_.GetOutputNameAllocated(i, allocator_));
+#else
+ return std::string(session_.GetOutputName(i, allocator_));
+#endif
+}
+
+PoseEstimator::PoseEstimator(Ort::MemoryInfo &allocator_info, Ort::Session &&session)
+ : model_version_{session.GetModelMetadata().GetVersion()}
+ , session_{std::move(session)}
+ , allocator_{session_, allocator_info}
+{
+ using namespace std::literals::string_literals;
+
+ if (session_.GetOutputCount() < 2)
+ throw std::runtime_error("Invalid Model: must have at least two outputs");
+
+ // WARNING: Messy model compatibility issues!
+ // When reading the initial model release, it did not have the version field set.
+ // Reading it here will result in some unspecified value. It's probably UB due to
+ // reading uninitialized memory. But there is little choice.
+ // Now, detection of this old version is messy ... we have to guess based on the
+ // number we get. Getting an uninitialized value matching a valid version is unlikely.
+ // But the real problem is that this line must be updated whenever we want to bump the
+ // version number!!
+ if (model_version_ <= 0 || model_version_ > 4)
+ model_version_ = 1;
+
+ const cv::Size input_image_shape = get_input_image_shape(session_);
+
+ scaled_frame_ = cv::Mat(input_image_shape, CV_8U, cv::Scalar(0));
+ input_mat_ = cv::Mat(input_image_shape, CV_32F, cv::Scalar(0.f));
+
+ {
+ const std::int64_t input_shape[4] = { 1, 1, input_image_shape.height, input_image_shape.width };
+ input_val_.push_back(
+ Ort::Value::CreateTensor<float>(allocator_info, input_mat_.ptr<float>(0), input_mat_.total(), input_shape, 4));
+ }
+
+ struct TensorSpec
+ {
+ std::vector<int64_t> shape;
+ float* buffer = nullptr;
+ size_t element_count = 0;
+ bool available = false;
+ };
+
+ std::unordered_map<std::string, TensorSpec> understood_outputs = {
+ { "pos_size", TensorSpec{ { 1, 3 }, &output_coord_[0], output_coord_.rows } },
+ { "quat", TensorSpec{ { 1, 4}, &output_quat_[0], output_quat_.rows } },
+ { "box", TensorSpec{ { 1, 4}, &output_box_[0], output_box_.rows } },
+ { "rotaxis_scales_tril", TensorSpec{ {1, 3, 3}, output_rotaxis_scales_tril_.val, 9 }},
+ { "rotaxis_std", TensorSpec{ {1, 3, 3}, output_rotaxis_scales_tril_.val, 9 }}, // TODO: Delete when old models aren't used any more
+ { "pos_size_std", TensorSpec{ {1, 3}, output_coord_scales_std_.val, output_coord_scales_std_.rows}},
+ { "pos_size_scales", TensorSpec{ {1, 3}, output_coord_scales_std_.val, output_coord_scales_std_.rows}},
+ { "pos_size_scales_tril", TensorSpec{ {1, 3, 3}, output_coord_scales_tril_.val, 9}}
+ };
+
+ qDebug() << "Pose model inputs (" << session_.GetInputCount() << ")";
+ qDebug() << "Pose model outputs (" << session_.GetOutputCount() << "):";
+ output_names_.resize(session_.GetOutputCount());
+ output_c_names_.resize(session_.GetOutputCount());
+ for (size_t i=0; i<session_.GetOutputCount(); ++i)
+ {
+ const std::string name = get_network_output_name(i);
+ const auto& output_info = session_.GetOutputTypeInfo(i);
+ const auto& onnx_tensor_spec = output_info.GetTensorTypeAndShapeInfo();
+ auto my_tensor_spec_it = understood_outputs.find(name);
+
+ qDebug() << "\t" << name.c_str() << " (" << onnx_tensor_spec.GetShape() << ") dtype: " << onnx_tensor_spec.GetElementType() << " " <<
+ (my_tensor_spec_it != understood_outputs.end() ? "ok" : "unknown");
+
+ if (my_tensor_spec_it != understood_outputs.end())
+ {
+ TensorSpec& t = my_tensor_spec_it->second;
+ if (onnx_tensor_spec.GetShape() != t.shape ||
+ onnx_tensor_spec.GetElementType() != Ort::TypeToTensorType<float>::type)
+ throw std::runtime_error("Invalid output tensor spec for "s + name);
+ output_val_.push_back(Ort::Value::CreateTensor<float>(
+ allocator_info, t.buffer, t.element_count, t.shape.data(), t.shape.size()));
+ t.available = true;
+ }
+ else
+ {
+ // Create tensor regardless and ignore output
+ output_val_.push_back(create_tensor(output_info, allocator_));
+ }
+ output_names_[i] = name;
+ output_c_names_[i] = output_names_[i].c_str();
+ }
+
+ has_uncertainty_ = understood_outputs.at("rotaxis_scales_tril").available ||
+ understood_outputs.at("rotaxis_std").available;
+ has_uncertainty_ &= understood_outputs.at("pos_size_std").available ||
+ understood_outputs.at("pos_size_scales").available ||
+ understood_outputs.at("pos_size_scales_tril").available;
+ pos_scale_uncertainty_is_matrix_ = understood_outputs.at("pos_size_scales_tril").available;
+
+ input_names_.resize(session_.GetInputCount());
+ input_c_names_.resize(session_.GetInputCount());
+ for (size_t i = 0; i < session_.GetInputCount(); ++i)
+ {
+ input_names_[i] = get_network_input_name(i);
+ input_c_names_[i] = input_names_[i].c_str();
+ }
+
+ assert (input_names_.size() == input_val_.size());
+ assert (output_names_.size() == output_val_.size());
+}
+
+
+std::optional<PoseEstimator::Face> PoseEstimator::run(
+ const cv::Mat &frame, const cv::Rect &box)
+{
+ cv::Mat cropped;
+
+ const int patch_size = std::max(box.width, box.height)*1.05;
+ const cv::Point2f patch_center = {
+ std::clamp<float>(box.x + 0.5f*box.width, 0.f, frame.cols),
+ std::clamp<float>(box.y + 0.5f*box.height, 0.f, frame.rows)
+ };
+ cv::getRectSubPix(frame, {patch_size, patch_size}, patch_center, cropped);
+
+ // Will get failure if patch_center is outside image boundaries settings.
+ // Have to catch this case.
+ if (cropped.rows != patch_size || cropped.cols != patch_size)
+ return {};
+
+ [[maybe_unused]] auto* p = input_mat_.ptr(0);
+
+ cv::resize(cropped, scaled_frame_, scaled_frame_.size(), 0, 0, cv::INTER_AREA);
+
+ normalize_brightness(scaled_frame_, input_mat_);
+
+ assert (input_mat_.ptr(0) == p);
+ assert (!input_mat_.empty() && input_mat_.isContinuous());
+
+ Timer t; t.start();
+
+ try
+ {
+ session_.Run(
+ Ort::RunOptions{ nullptr },
+ input_c_names_.data(),
+ input_val_.data(),
+ input_val_.size(),
+ output_c_names_.data(),
+ output_val_.data(),
+ output_val_.size());
+ }
+ catch (const Ort::Exception &e)
+ {
+ qDebug() << "Failed to run the model: " << e.what();
+ return {};
+ }
+
+ last_inference_time_ = t.elapsed_ms();
+
+ // Perform coordinate transformation.
+ // From patch-local normalized in [-1,1] to
+ // frame unnormalized pixel.
+
+ cv::Matx33f center_size_cov_tril = {};
+ if (has_uncertainty_)
+ {
+ if (pos_scale_uncertainty_is_matrix_)
+ {
+ center_size_cov_tril = output_coord_scales_tril_;
+ }
+ else
+ {
+ center_size_cov_tril(0,0) = output_coord_scales_std_[0];
+ center_size_cov_tril(1,1) = output_coord_scales_std_[1];
+ center_size_cov_tril(2,2) = output_coord_scales_std_[2];
+ }
+ center_size_cov_tril *= patch_size*0.5f;
+ }
+
+ const cv::Point2f center = patch_center +
+ (0.5f*patch_size)*cv::Point2f{output_coord_[0], output_coord_[1]};
+ const float size = patch_size*0.5f*output_coord_[2];
+
+ // Following Eigen which uses quat components in the order w, x, y, z.
+ // As does OpenCV
+ cv::Quatf rotation = {
+ output_quat_[3],
+ output_quat_[0],
+ output_quat_[1],
+ output_quat_[2] };
+
+ // Should be lower triangular. If not maybe something is wrong with memory layout ... or the model.
+ assert(output_rotaxis_scales_tril_(0, 1) == 0);
+ assert(output_rotaxis_scales_tril_(0, 2) == 0);
+ assert(output_rotaxis_scales_tril_(1, 2) == 0);
+ assert(center_size_cov_tril(0, 1) == 0);
+ assert(center_size_cov_tril(0, 2) == 0);
+ assert(center_size_cov_tril(1, 2) == 0);
+
+ cv::Matx33f rotaxis_scales_tril = output_rotaxis_scales_tril_;
+
+ if (model_version_ < 2)
+ {
+ // Due to a change in coordinate conventions
+ rotation = world_to_image(rotation);
+ }
+
+ const cv::Rect2f outbox = {
+ patch_center.x + (0.5f*patch_size)*output_box_[0],
+ patch_center.y + (0.5f*patch_size)*output_box_[1],
+ 0.5f*patch_size*(output_box_[2]-output_box_[0]),
+ 0.5f*patch_size*(output_box_[3]-output_box_[1])
+ };
+
+ return std::optional<Face>({
+ rotation, rotaxis_scales_tril, outbox, center, size, center_size_cov_tril
+ });
+}
+
+
+cv::Mat PoseEstimator::last_network_input() const
+{
+ assert(!input_mat_.empty());
+ cv::Mat ret;
+ input_mat_.convertTo(ret, CV_8U, 255., 127.);
+ cv::cvtColor(ret, ret, cv::COLOR_GRAY2RGB);
+ return ret;
+}
+
+
+double PoseEstimator::last_inference_time_millis() const
+{
+ return last_inference_time_;
+}
+
+
+
+
+
+} // namespace neuralnet_tracker_ns
diff --git a/tracker-neuralnet/model_adapters.h b/tracker-neuralnet/model_adapters.h
new file mode 100644
index 00000000..c1aaa6de
--- /dev/null
+++ b/tracker-neuralnet/model_adapters.h
@@ -0,0 +1,105 @@
+#pragma once
+
+#include <optional>
+#include <array>
+#include <vector>
+#include <string>
+
+#include <onnxruntime_cxx_api.h>
+#include <opencv2/core.hpp>
+#include "opencv_contrib.h"
+
+
+namespace neuralnet_tracker_ns
+{
+
+// Generally useful sigmoid function
+float sigmoid(float x);
+
+
+class Localizer
+{
+ public:
+ Localizer(Ort::MemoryInfo &allocator_info,
+ Ort::Session &&session);
+
+ // Returns bounding wrt image coordinate of the input image
+ // The preceeding float is the score for being a face normalized to [0,1].
+ std::pair<float, cv::Rect2f> run(
+ const cv::Mat &frame);
+
+ double last_inference_time_millis() const;
+ private:
+ inline static constexpr int INPUT_IMG_WIDTH = 288;
+ inline static constexpr int INPUT_IMG_HEIGHT = 224;
+ Ort::Session session_{nullptr};
+ // Inputs / outputs
+ cv::Mat scaled_frame_{}, input_mat_{};
+ Ort::Value input_val_{nullptr}, output_val_{nullptr};
+ std::array<float, 5> results_;
+ double last_inference_time_ = 0;
+};
+
+
+class PoseEstimator
+{
+ public:
+ struct Face
+ {
+ cv::Quatf rotation;
+ cv::Matx33f rotaxis_cov_tril; // Lower triangular factor of Cholesky decomposition
+ cv::Rect2f box;
+ cv::Point2f center;
+ float size;
+ cv::Matx33f center_size_cov_tril; // Lower triangular factor of Cholesky decomposition
+ };
+
+ PoseEstimator(Ort::MemoryInfo &allocator_info,
+ Ort::Session &&session);
+ /** Inference
+ *
+ * Coordinates are defined wrt. the image space of the input `frame`.
+ * X goes right, Z (depth) into the image, Y points down (like pixel coordinates values increase from top to bottom)
+ */
+ std::optional<Face> run(const cv::Mat &frame, const cv::Rect &box);
+ // Returns an image compatible with the 'frame' image for displaying.
+ cv::Mat last_network_input() const;
+ double last_inference_time_millis() const;
+ bool has_uncertainty() const { return has_uncertainty_; }
+
+ private:
+ std::string get_network_input_name(size_t i) const;
+ std::string get_network_output_name(size_t i) const;
+ int64_t model_version_ = 0; // Queried meta data from the ONNX file
+ Ort::Session session_{nullptr}; // ONNX's runtime context for running the model
+ mutable Ort::Allocator allocator_; // Memory allocator for tensors
+ // Inputs
+ cv::Mat scaled_frame_{}, input_mat_{}; // Input. One is the original crop, the other is rescaled (?)
+ std::vector<Ort::Value> input_val_; // Tensors to put into the model
+ std::vector<std::string> input_names_; // Refers to the names in the onnx model.
+ std::vector<const char *> input_c_names_; // Refers to the C names in the onnx model.
+ // Outputs
+ cv::Vec<float, 3> output_coord_{}; // 2d Coordinate and head size output.
+ cv::Vec<float, 4> output_quat_{}; // Quaternion output
+ cv::Vec<float, 4> output_box_{}; // Bounding box output
+ cv::Matx33f output_rotaxis_scales_tril_{}; // Lower triangular matrix of LLT factorization of covariance of rotation vector as offset from output quaternion
+ cv::Matx33f output_coord_scales_tril_{}; // Lower triangular factor
+ cv::Vec3f output_coord_scales_std_{}; // Depending on the model, alternatively a 3d vector with standard deviations.
+ std::vector<Ort::Value> output_val_; // Tensors to put the model outputs in.
+ std::vector<std::string> output_names_; // Refers to the names in the onnx model.
+ std::vector<const char *> output_c_names_; // Refers to the C names in the onnx model.
+ // More bookkeeping
+ double last_inference_time_ = 0;
+ bool has_uncertainty_ = false;
+ bool pos_scale_uncertainty_is_matrix_ = false;
+};
+
+
+// Finds the intensity where x percent of pixels have less intensity than that.
+int find_input_intensity_quantile(const cv::Mat& frame, float percentage);
+
+// Adjust brightness levels to full range and scales the value range to [-0.5, 0.5]
+void normalize_brightness(const cv::Mat& frame, cv::Mat& out);
+
+
+} // namespace neuralnet_tracker_ns
diff --git a/tracker-neuralnet/models/head-pose-0.2-big.onnx b/tracker-neuralnet/models/head-pose-0.2-big.onnx
new file mode 100644
index 00000000..e53fd831
--- /dev/null
+++ b/tracker-neuralnet/models/head-pose-0.2-big.onnx
Binary files differ
diff --git a/tracker-neuralnet/models/head-pose.onnx b/tracker-neuralnet/models/head-pose-0.2-small.onnx
index dcb55dcc..f2b64219 100644
--- a/tracker-neuralnet/models/head-pose.onnx
+++ b/tracker-neuralnet/models/head-pose-0.2-small.onnx
Binary files differ
diff --git a/tracker-neuralnet/models/head-pose-0.3-big-quantized.onnx b/tracker-neuralnet/models/head-pose-0.3-big-quantized.onnx
new file mode 100644
index 00000000..7f875c63
--- /dev/null
+++ b/tracker-neuralnet/models/head-pose-0.3-big-quantized.onnx
Binary files differ
diff --git a/tracker-neuralnet/neuralnet-trackercontrols.ui b/tracker-neuralnet/neuralnet-trackercontrols.ui
index f16b5807..ae2450b4 100644
--- a/tracker-neuralnet/neuralnet-trackercontrols.ui
+++ b/tracker-neuralnet/neuralnet-trackercontrols.ui
@@ -9,333 +9,226 @@
<rect>
<x>0</x>
<y>0</y>
- <width>727</width>
- <height>202</height>
+ <width>651</width>
+ <height>432</height>
</rect>
</property>
<property name="windowTitle">
<string>Tracker settings</string>
</property>
<layout class="QGridLayout" name="gridLayout">
- <item row="5" column="0">
- <widget class="QDialogButtonBox" name="buttonBox">
- <property name="standardButtons">
- <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ <item row="4" column="0">
+ <widget class="QGroupBox" name="groupBox_10">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
</property>
- </widget>
- </item>
- <item row="3" column="0">
- <widget class="QFrame" name="frame_3">
- <property name="frameShape">
- <enum>QFrame::StyledPanel</enum>
+ <property name="autoFillBackground">
+ <bool>true</bool>
</property>
- <property name="frameShadow">
- <enum>QFrame::Raised</enum>
+ <property name="title">
+ <string>Head Center Offset</string>
</property>
- <layout class="QHBoxLayout" name="horizontalLayout">
- <property name="spacing">
- <number>0</number>
- </property>
- <property name="leftMargin">
- <number>0</number>
- </property>
- <property name="topMargin">
- <number>0</number>
- </property>
- <property name="rightMargin">
- <number>0</number>
- </property>
- <property name="bottomMargin">
- <number>0</number>
- </property>
- <item>
- <widget class="QGroupBox" name="groupBox">
+ <layout class="QGridLayout" name="gridLayout_5">
+ <item row="0" column="0">
+ <widget class="QFrame" name="frame_4">
<property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="title">
- <string>Camera Configuration</string>
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>16777215</height>
+ </size>
</property>
- <layout class="QGridLayout" name="gridLayout_4">
- <item row="2" column="1">
- <widget class="QComboBox" name="cameraName">
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_11">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <property name="verticalSpacing">
+ <number>0</number>
+ </property>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_66">
<property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
+ <property name="text">
+ <string>Right</string>
+ </property>
</widget>
</item>
- <item row="0" column="1">
- <widget class="QSpinBox" name="cameraFOV">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_61">
<property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="locale">
- <locale language="English" country="UnitedStates"/>
- </property>
- <property name="minimum">
- <number>35</number>
- </property>
- <property name="maximum">
- <number>90</number>
+ <property name="text">
+ <string>Forward</string>
</property>
</widget>
</item>
<item row="1" column="0">
- <widget class="QLabel" name="label_12">
- <property name="text">
- <string>Frames per second</string>
+ <widget class="QLabel" name="label_62">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
</property>
- </widget>
- </item>
- <item row="2" column="0">
- <widget class="QLabel" name="label_10">
<property name="text">
- <string>Camera name</string>
+ <string>Up</string>
</property>
</widget>
</item>
- <item row="0" column="0">
- <widget class="QLabel" name="label_9">
- <property name="text">
- <string>Diagonal FOV</string>
+ <item row="0" column="1">
+ <widget class="QSpinBox" name="tx_spin">
+ <property name="maximumSize">
+ <size>
+ <width>150</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="suffix">
+ <string> mm</string>
+ </property>
+ <property name="minimum">
+ <number>-65535</number>
+ </property>
+ <property name="maximum">
+ <number>65536</number>
</property>
</widget>
</item>
<item row="1" column="1">
- <widget class="QComboBox" name="cameraFPS">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
+ <widget class="QSpinBox" name="ty_spin">
+ <property name="maximumSize">
+ <size>
+ <width>150</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="suffix">
+ <string> mm</string>
+ </property>
+ <property name="minimum">
+ <number>-65535</number>
+ </property>
+ <property name="maximum">
+ <number>65536</number>
</property>
</widget>
</item>
- <item row="3" column="1">
- <widget class="QPushButton" name="camera_settings">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
+ <item row="2" column="1">
+ <widget class="QSpinBox" name="tz_spin">
+ <property name="maximumSize">
+ <size>
+ <width>150</width>
+ <height>16777215</height>
+ </size>
</property>
- <property name="text">
- <string>Camera settings</string>
+ <property name="suffix">
+ <string> mm</string>
+ </property>
+ <property name="minimum">
+ <number>-65535</number>
+ </property>
+ <property name="maximum">
+ <number>65536</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
- <item>
- <widget class="QGroupBox" name="groupBox_10">
+ <item row="0" column="1">
+ <widget class="QFrame" name="frame_5">
<property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="title">
- <string>Head Center Offset</string>
+ <property name="minimumSize">
+ <size>
+ <width>260</width>
+ <height>0</height>
+ </size>
</property>
- <layout class="QGridLayout" name="gridLayout_5">
- <item row="0" column="0">
- <widget class="QFrame" name="frame_4">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QLabel" name="label_59">
+ <property name="text">
+ <string>Use only yaw and pitch while calibrating.
+Don't roll or change position.</string>
</property>
- <property name="maximumSize">
- <size>
- <width>16777215</width>
- <height>16777215</height>
- </size>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
</property>
- <property name="frameShape">
- <enum>QFrame::NoFrame</enum>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="openExternalLinks">
+ <bool>false</bool>
</property>
- <property name="frameShadow">
- <enum>QFrame::Raised</enum>
- </property>
- <layout class="QGridLayout" name="gridLayout_11">
- <item row="1" column="1">
- <widget class="QSpinBox" name="ty_spin">
- <property name="maximumSize">
- <size>
- <width>150</width>
- <height>16777215</height>
- </size>
- </property>
- <property name="suffix">
- <string> mm</string>
- </property>
- <property name="minimum">
- <number>-65535</number>
- </property>
- <property name="maximum">
- <number>65536</number>
- </property>
- </widget>
- </item>
- <item row="2" column="0">
- <widget class="QLabel" name="label_66">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Right</string>
- </property>
- </widget>
- </item>
- <item row="2" column="1">
- <widget class="QSpinBox" name="tz_spin">
- <property name="maximumSize">
- <size>
- <width>150</width>
- <height>16777215</height>
- </size>
- </property>
- <property name="suffix">
- <string> mm</string>
- </property>
- <property name="minimum">
- <number>-65535</number>
- </property>
- <property name="maximum">
- <number>65536</number>
- </property>
- </widget>
- </item>
- <item row="0" column="0">
- <widget class="QLabel" name="label_61">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Forward</string>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QSpinBox" name="tx_spin">
- <property name="maximumSize">
- <size>
- <width>150</width>
- <height>16777215</height>
- </size>
- </property>
- <property name="suffix">
- <string> mm</string>
- </property>
- <property name="minimum">
- <number>-65535</number>
- </property>
- <property name="maximum">
- <number>65536</number>
- </property>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QLabel" name="label_62">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Up</string>
- </property>
- </widget>
- </item>
- </layout>
</widget>
</item>
- <item row="0" column="1">
- <widget class="QFrame" name="frame_5">
+ <item>
+ <widget class="QLabel" name="sample_count_display">
<property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="minimumSize">
- <size>
- <width>260</width>
- <height>0</height>
- </size>
- </property>
<property name="frameShape">
- <enum>QFrame::NoFrame</enum>
+ <enum>QFrame::Panel</enum>
</property>
<property name="frameShadow">
- <enum>QFrame::Raised</enum>
+ <enum>QFrame::Sunken</enum>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="tcalib_button">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Start calibration</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
</property>
- <layout class="QVBoxLayout" name="verticalLayout_2">
- <item>
- <widget class="QLabel" name="label_59">
- <property name="text">
- <string>Use only yaw and pitch while calibrating.
-Don't roll or change position.</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
- </property>
- <property name="wordWrap">
- <bool>true</bool>
- </property>
- <property name="openExternalLinks">
- <bool>false</bool>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QLabel" name="sample_count_display">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string/>
- </property>
- <property name="wordWrap">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="tcalib_button">
- <property name="enabled">
- <bool>false</bool>
- </property>
- <property name="text">
- <string>Start calibration</string>
- </property>
- <property name="checkable">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- </layout>
</widget>
</item>
</layout>
@@ -344,13 +237,442 @@ Don't roll or change position.</string>
</layout>
</widget>
</item>
- <item row="4" column="0">
- <widget class="QCheckBox" name="showNetworkInput">
+ <item row="8" column="0">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QGroupBox" name="groupBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="autoFillBackground">
+ <bool>true</bool>
+ </property>
+ <property name="title">
+ <string>Camera Configuration</string>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <property name="checkable">
+ <bool>false</bool>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="spacing">
+ <number>10</number>
+ </property>
+ <property name="bottomMargin">
+ <number>8</number>
+ </property>
+ <item>
+ <layout class="QGridLayout" name="gridLayout_3">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <property name="horizontalSpacing">
+ <number>0</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>2</number>
+ </property>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="cameraName">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_9">
+ <property name="text">
+ <string>Diagonal FOV</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_10">
+ <property name="text">
+ <string>Camera name</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QSpinBox" name="cameraFOV">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip">
+ <string>Field of view. Needed to transform the pose to world coordinates.</string>
+ </property>
+ <property name="locale">
+ <locale language="English" country="UnitedStates"/>
+ </property>
+ <property name="minimum">
+ <number>35</number>
+ </property>
+ <property name="maximum">
+ <number>90</number>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QComboBox" name="resolution">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip">
+ <string>The requested resolution for cases where the camera delivers maximum frame rate only for a particular resolution. The image may still be downscaled to the internal resolution.</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="resolution_label">
+ <property name="text">
+ <string>Resolution</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QGridLayout" name="gridLayout_6">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <property name="horizontalSpacing">
+ <number>0</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>2</number>
+ </property>
+ <item row="4" column="1">
+ <widget class="QComboBox" name="cameraFPS">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip">
+ <string>Requested video frame rate. Actual setting may not be supported by the camera.</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="label_12">
+ <property name="text">
+ <string>Frames per second</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QCheckBox" name="use_mjpeg">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_11">
+ <property name="text">
+ <string>MJPEG</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QPushButton" name="camera_settings">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Camera settings</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="7" column="0">
+ <widget class="QLabel" name="resolution_display">
+ <property name="autoFillBackground">
+ <bool>true</bool>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::Panel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Sunken</enum>
+ </property>
<property name="text">
- <string>Show Network Input</string>
+ <string notr="true"/>
</property>
</widget>
</item>
+ <item row="5" column="0">
+ <widget class="QGroupBox" name="tuningOptionsBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="autoFillBackground">
+ <bool>true</bool>
+ </property>
+ <property name="title">
+ <string>Tuning / Debug</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <widget class="QLabel" name="threadCountLabel">
+ <property name="text">
+ <string>Thread Count</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QSpinBox" name="threadCount">
+ <property name="toolTip">
+ <string>Number of threads. Can be used to balance the CPU load between the game and the tracker.</string>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>32</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="Line" name="line">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="showNetworkInput">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip">
+ <string>Show the image patch that the pose estimation model sees.</string>
+ </property>
+ <property name="text">
+ <string>Show Network Input</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="Line" name="line_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="roiFilterAlphaLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>ROI Smoothing Alpha</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDoubleSpinBox" name="roiFilterAlpha">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>150</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="toolTip">
+ <string>Amount of smoothing of the face region coordinates. Can help stabilize the pose.</string>
+ </property>
+ <property name="wrapping">
+ <bool>false</bool>
+ </property>
+ <property name="decimals">
+ <number>2</number>
+ </property>
+ <property name="maximum">
+ <double>1.000000000000000</double>
+ </property>
+ <property name="singleStep">
+ <double>0.010000000000000</double>
+ </property>
+ <property name="value">
+ <double>1.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="roiZoomLabel">
+ <property name="text">
+ <string>ROI Zoom</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDoubleSpinBox" name="roiZoom">
+ <property name="toolTip">
+ <string>Zoom factor for the face region. Applied before the patch is fed into the pose estimation model. There is a sweet spot near 1.</string>
+ </property>
+ <property name="minimum">
+ <double>0.100000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>2.000000000000000</double>
+ </property>
+ <property name="singleStep">
+ <double>0.010000000000000</double>
+ </property>
+ <property name="value">
+ <double>1.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QFrame" name="network_select_frame">
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="posenetSelectButton">
+ <property name="toolTip">
+ <string>Select the pose network. Changes take affect on the next tracker start</string>
+ </property>
+ <property name="text">
+ <string>Select Pose Net ONNX</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="posenetFileDisplay">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&lt;the pose net file&gt;</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
</layout>
</widget>
<resources/>
diff --git a/tracker-neuralnet/opencv_contrib.h b/tracker-neuralnet/opencv_contrib.h
new file mode 100644
index 00000000..1c199025
--- /dev/null
+++ b/tracker-neuralnet/opencv_contrib.h
@@ -0,0 +1,120 @@
+#pragma once
+
+#include <opencv2/core.hpp>
+#include <opencv2/core/base.hpp>
+#include <opencv2/core/quaternion.hpp>
+
+// Well eventually it might be a contribution
+
+namespace cvcontrib
+{
+
+
+template<class T>
+cv::Point_<T> as_point(const cv::Size_<T>& s)
+{
+ return { s.width, s.height };
+}
+
+
+template<class T>
+cv::Size_<T> as_size(const cv::Point_<T>& p)
+{
+ return { p.x, p.y };
+}
+
+
+template<int n, int m>
+inline bool allfinite(const cv::Matx<float, n, m> &mat)
+{
+ const size_t sz = mat.rows*mat.cols;
+ for (size_t i=0; i<sz; ++i)
+ if (!std::isfinite(mat.val[i]))
+ return false;
+ return true;
+}
+
+
+// Because compiler refuses to convert it automatically
+template<int n>
+inline cv::Vec<float, n> to_vec(const cv::Matx<float, n, 1>& m)
+{
+ return cv::Vec<float,n>{m.val};
+}
+
+
+template<int n, int m, int o>
+inline void set_minor(cv::Vec<float, m> &dst, const int startrow, const cv::Matx<float, o, 1> &src)
+{
+ assert (startrow>=0 && startrow+n <= dst.rows);
+ for (int row=startrow, i=0; row<startrow+n; ++row,++i)
+ {
+ dst[row] = src(i,0);
+ }
+}
+
+
+template<int nrows, int ncols, int m, int n>
+inline void set_minor(cv::Matx<float, m, n>& dst, const int startrow, int startcol, const cv::Matx<float, nrows, ncols> &src)
+{
+ assert (startrow>=0 && startrow+nrows <= dst.rows);
+ assert (startcol>=0 && startcol+ncols <= dst.cols);
+ for (int row=startrow, i=0; row<startrow+nrows; ++row,++i)
+ {
+ for (int col=startcol, j=0; col<startcol+ncols; ++col,++j)
+ {
+ dst(row, col) = src(i,j);
+ }
+ }
+}
+
+
+inline cv::Quatf identity_quat()
+{
+ return cv::Quatf(1,0,0,0);
+}
+
+
+inline cv::Vec3f toRotVec(const cv::Quatf& q)
+{
+ // This is an improved implementation
+#if 1
+ // w = cos(alpha/2)
+ // xyz = sin(alpha/2)*axis
+ static constexpr float eps = 1.e-12f;
+ const cv::Vec3f xyz{q.x, q.y, q.z};
+ const float len = cv::norm(xyz);
+ const float angle = std::atan2(len, q.w)*2.f;
+ return xyz*(angle/(len+eps));
+#else
+ // The opencv implementation fails even the simplest test:
+ // out = toRVec(cv::Quatf{1., 0., 0., 0. });
+ // ASSERT_TRUE(std::isfinite(out[0]) && std::isfinite(out[1]) && std::isfinite(out[2]));
+ return q.toRotVec();
+#endif
+}
+
+
+inline cv::Vec3f rotate(const cv::Quatf& q, const cv::Vec3f &v)
+{
+ const auto r = q * cv::Quatf{0., v[0], v[1], v[2]} * q.conjugate();
+ return { r.x, r.y, r.z };
+}
+
+
+template<int n>
+inline cv::Matx<float, n, n> cholesky(const cv::Matx<float, n, n>& mat)
+{
+ cv::Matx<float, n, n> l = mat;
+ // Der Code ist die Doku!
+ // https://github.com/opencv/opencv/blob/4.5.4/modules/core/src/matrix_decomp.cpp#L95
+ cv::Cholesky(l.val, l.cols * sizeof(float), n, nullptr, 0, 0);
+ // It doesn't clear the upper triangle so we do it for it.
+ for (int row=0; row<n; ++row)
+ for (int col=row+1; col<n; ++col)
+ l(row, col) = 0.f;
+ return l;
+}
+
+
+} // namespace cvcontrib \ No newline at end of file
diff --git a/tracker-neuralnet/preview.cpp b/tracker-neuralnet/preview.cpp
new file mode 100644
index 00000000..76a6bbc0
--- /dev/null
+++ b/tracker-neuralnet/preview.cpp
@@ -0,0 +1,135 @@
+#include "preview.h"
+
+
+namespace neuralnet_tracker_ns
+{
+
+
+cv::Rect make_crop_rect_for_aspect(const cv::Size &size, int aspect_w, int aspect_h)
+{
+ auto [w, h] = size;
+ if ( w*aspect_h > aspect_w*h )
+ {
+ // Image is too wide
+ const int new_w = (aspect_w*h)/aspect_h;
+ return cv::Rect((w - new_w)/2, 0, new_w, h);
+ }
+ else
+ {
+ const int new_h = (aspect_h*w)/aspect_w;
+ return cv::Rect(0, (h - new_h)/2, w, new_h);
+ }
+}
+
+
+
+
+void Preview::init(const cv_video_widget& widget)
+{
+ auto [w,h] = widget.preview_size();
+ preview_size_ = { w, h };
+}
+
+
+void Preview::copy_video_frame(const cv::Mat& frame)
+{
+ cv::Rect roi = make_crop_rect_for_aspect(frame.size(), preview_size_.width, preview_size_.height);
+
+ cv::resize(frame(roi), preview_image_, preview_size_, 0, 0, cv::INTER_NEAREST);
+
+ offset_ = { (float)-roi.x, (float)-roi.y };
+ scale_ = float(preview_image_.cols) / float(roi.width);
+}
+
+
+void Preview::draw_gizmos(
+ const std::optional<PoseEstimator::Face> &face,
+ const std::optional<cv::Rect2f>& last_roi,
+ const std::optional<cv::Rect2f>& last_localizer_roi,
+ const cv::Point2f& neckjoint_position)
+{
+ if (preview_image_.empty())
+ return;
+
+ if (last_roi)
+ {
+ const int col = 255;
+ cv::rectangle(preview_image_, transform(*last_roi), cv::Scalar(0, col, 0), /*thickness=*/1);
+ }
+ if (last_localizer_roi)
+ {
+ const int col = 255;
+ cv::rectangle(preview_image_, transform(*last_localizer_roi), cv::Scalar(col, 0, 255-col), /*thickness=*/1);
+ }
+
+ if (face)
+ {
+ if (face->size>=1.f)
+ cv::circle(preview_image_, static_cast<cv::Point>(transform(face->center)), int(transform(face->size)), cv::Scalar(255,255,255), 2);
+ cv::circle(preview_image_, static_cast<cv::Point>(transform(face->center)), 3, cv::Scalar(255,255,255), -1);
+
+ const cv::Matx33f R = face->rotation.toRotMat3x3(cv::QUAT_ASSUME_UNIT);
+
+ auto draw_coord_line = [&](int i, const cv::Scalar& color)
+ {
+ const float vx = R(0,i);
+ const float vy = R(1,i);
+ static constexpr float len = 100.f;
+ cv::Point q = face->center + len*cv::Point2f{vx, vy};
+ cv::line(preview_image_, static_cast<cv::Point>(transform(face->center)), static_cast<cv::Point>(transform(q)), color, 2);
+ };
+ draw_coord_line(0, {0, 0, 255});
+ draw_coord_line(1, {0, 255, 0});
+ draw_coord_line(2, {255, 0, 0});
+
+ // Draw the computed joint position
+ auto xy = transform(neckjoint_position);
+ cv::circle(preview_image_, cv::Point(xy.x,xy.y), 5, cv::Scalar(0,0,255), -1);
+ }
+
+
+}
+
+void Preview::overlay_netinput(const cv::Mat& netinput)
+{
+ if (netinput.empty())
+ return;
+
+ const int w = std::min(netinput.cols, preview_image_.cols);
+ const int h = std::min(netinput.rows, preview_image_.rows);
+ cv::Rect roi(0, 0, w, h);
+ netinput(roi).copyTo(preview_image_(roi));
+}
+
+void Preview::draw_fps(double fps, double last_inference_time)
+{
+ char buf[128];
+ ::snprintf(buf, sizeof(buf), "%d Hz, pose inference: %d ms", std::clamp(int(fps), 0, 9999), int(last_inference_time));
+ cv::putText(preview_image_, buf, cv::Point(10, preview_image_.rows-10), cv::FONT_HERSHEY_PLAIN, 1, cv::Scalar(0, 255, 0), 1);
+}
+
+
+void Preview::copy_to_widget(cv_video_widget& widget)
+{
+ if (preview_image_.rows > 0)
+ widget.update_image(preview_image_);
+}
+
+
+cv::Rect2f Preview::transform(const cv::Rect2f& r) const
+{
+ return { (r.x - offset_.x)*scale_, (r.y - offset_.y)*scale_, r.width*scale_, r.height*scale_ };
+}
+
+cv::Point2f Preview::transform(const cv::Point2f& p) const
+{
+ return { (p.x - offset_.x)*scale_ , (p.y - offset_.y)*scale_ };
+}
+
+float Preview::transform(float s) const
+{
+ return s * scale_;
+}
+
+
+} \ No newline at end of file
diff --git a/tracker-neuralnet/preview.h b/tracker-neuralnet/preview.h
new file mode 100644
index 00000000..adc12993
--- /dev/null
+++ b/tracker-neuralnet/preview.h
@@ -0,0 +1,60 @@
+/* Copyright (c) 2021 Michael Welter <michael@welter-4d.de>
+ *
+ * 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 "model_adapters.h"
+
+#include "cv/video-widget.hpp"
+
+#include <optional>
+
+#include <opencv2/core.hpp>
+#include <opencv2/imgproc.hpp>
+
+
+namespace neuralnet_tracker_ns
+{
+
+/** Makes a maximum size cropping rect with the given aspect.
+* @param aspect_w: nominator of the aspect ratio
+* @param aspect_h: denom of the aspect ratio
+*/
+cv::Rect make_crop_rect_for_aspect(const cv::Size &size, int aspect_w, int aspect_h);
+
+
+/** This class is responsible for drawing the debug/info gizmos
+*
+* In addition there function to transform the inputs to the size of
+* the preview image which can be different from the camera frame.
+*/
+class Preview
+{
+public:
+ void init(const cv_video_widget& widget);
+ void copy_video_frame(const cv::Mat& frame);
+ void draw_gizmos(
+ const std::optional<PoseEstimator::Face> &face,
+ const std::optional<cv::Rect2f>& last_roi,
+ const std::optional<cv::Rect2f>& last_localizer_roi,
+ const cv::Point2f& neckjoint_position);
+ void overlay_netinput(const cv::Mat& netinput);
+ void draw_fps(double fps, double last_inference_time);
+ void copy_to_widget(cv_video_widget& widget);
+private:
+ // Transform from camera image to preview
+ cv::Rect2f transform(const cv::Rect2f& r) const;
+ cv::Point2f transform(const cv::Point2f& p) const;
+ float transform(float s) const;
+
+ cv::Mat preview_image_;
+ cv::Size preview_size_ = { 0, 0 };
+ float scale_ = 1.f;
+ cv::Point2f offset_ = { 0.f, 0.f};
+};
+
+} // neuralnet_tracker_ns \ No newline at end of file
diff --git a/tracker-neuralnet/redist/vcomp140.dll b/tracker-neuralnet/redist/vcomp140.dll
new file mode 100644
index 00000000..42c069b9
--- /dev/null
+++ b/tracker-neuralnet/redist/vcomp140.dll
Binary files differ
diff --git a/tracker-neuralnet/tests.cpp b/tracker-neuralnet/tests.cpp
new file mode 100644
index 00000000..b1d2a6d0
--- /dev/null
+++ b/tracker-neuralnet/tests.cpp
@@ -0,0 +1,58 @@
+#include "model_adapters.h"
+
+#include <algorithm>
+#include <numeric>
+#include <cstdio>
+
+namespace neuralnet_tracker_tests
+{
+
+
+void assert_(bool ok, const std::string& msg)
+{
+ if (ok)
+ return;
+ std::cout << msg << std::endl;
+ std::exit(-1);
+}
+
+
+void test_find_input_intensity_quantile()
+{
+ cv::Mat data(10,10, CV_8UC1);
+ std::iota(data.begin<uint8_t>(), data.end<uint8_t>(), 0);
+
+ const float pct = 90;
+
+ const int val = neuralnet_tracker_ns::find_input_intensity_quantile(data, pct);
+
+ assert_(val == int(10*10*pct/100.f), "test_find_input_intensity_quantile failed");
+}
+
+
+void test_normalize_brightness()
+{
+ cv::Mat data(10,10, CV_8UC1);
+ std::iota(data.begin<uint8_t>(), data.end<uint8_t>(), 0);
+
+ cv::Mat out;
+ neuralnet_tracker_ns::normalize_brightness(data, out);
+
+ auto [minit,maxit] = std::minmax_element(out.begin<float>(),out.end<float>());
+ const auto minval = *minit;
+ const auto maxval = *maxit;
+ assert_(std::abs(minval + 0.5f) < 0.02, "test_normalize_brightness failed");
+ // If the brightest value is lower than half-max, it will be boosted to half-max.
+ // Otherwise it will just be rescaled to [-.5, 0.5 ]. Here we have the low-brightness case.
+ assert_(std::abs(maxval - 0.0f) < 0.02, "test_normalize_brightness failed");
+}
+
+
+void run()
+{
+ test_find_input_intensity_quantile();
+ test_normalize_brightness();
+}
+
+
+} \ No newline at end of file
diff --git a/tracker-neuralnet/unscented_trafo.h b/tracker-neuralnet/unscented_trafo.h
new file mode 100644
index 00000000..267aa969
--- /dev/null
+++ b/tracker-neuralnet/unscented_trafo.h
@@ -0,0 +1,132 @@
+#pragma once
+
+#include <algorithm>
+#include <opencv2/core.hpp>
+#include <opencv2/core/base.hpp>
+#include <opencv2/core/quaternion.hpp>
+
+#include <cmath>
+#include <vector>
+
+#include "opencv_contrib.h"
+
+namespace ukf_cv
+{
+
+using namespace cvcontrib;
+
+template<int dim, int otherdim = dim>
+using SigmaPoints = std::array<cv::Vec<float,otherdim>,dim*2+1>;
+
+
+// Ported from
+// https://filterpy.readthedocs.io/en/latest/_modules/filterpy/kalman/sigma_points.html
+// Excerpt from the original docu:
+// "
+
+// Generates sigma points and weights according to Van der Merwe's
+// 2004 dissertation[1] for the UnscentedKalmanFilter class.. It
+// parametizes the sigma points using alpha, beta, kappa terms, and
+// is the version seen in most publications.
+
+// Unless you know better, this should be your default choice.
+
+// alpha : float
+// Determins the spread of the sigma points around the mean.
+// Usually a small positive value (1e-3) according to [3].
+
+// beta : float
+// Incorporates prior knowledge of the distribution of the mean. For
+// Gaussian x beta=2 is optimal, according to [3].
+
+// kappa : float, default=0.0
+// Secondary scaling parameter usually set to 0 according to [4],
+// or to 3-n according to [5].
+
+// Reference
+// .. [1] R. Van der Merwe "Sigma-Point Kalman Filters for Probabilitic
+// Inference in Dynamic State-Space Models" (Doctoral dissertation)
+
+// "
+template<int dim>
+class MerweScaledSigmaPoints
+{
+public:
+ static constexpr int num_sigmas = 2*dim+1;
+
+ using Vector = cv::Vec<float,dim>;
+ using Matrix = cv::Matx<float,dim,dim>;
+
+ MerweScaledSigmaPoints(float alpha = 0.01, float beta = 2., int kappa = 3-dim)
+ {
+ lambda = alpha*alpha * (dim + kappa) - dim;
+ const float c = .5 / (dim + lambda);
+ Wc_i = c;
+ Wm_i = c;
+ Wm_0 = lambda / (dim+lambda);
+ Wc_0 = Wm_0 + (1.-alpha*alpha + beta);
+ }
+
+ SigmaPoints<dim> compute_sigmas(const Vector &mu, const Matrix &mat, bool is_tril_factor) const
+ {
+ const Matrix triu_factor = is_tril_factor ? mat.t() : cholesky(mat).t();
+
+ const Matrix U = triu_factor*std::sqrt(lambda+dim);
+
+ SigmaPoints<dim> sigmas;
+
+ sigmas[0] = mu;
+ for (int k=0; k<dim; ++k)
+ {
+ sigmas[k+1] = to_vec(mu + U.row(k).t());
+ sigmas[dim+k+1] = to_vec(mu - U.row(k).t());
+ }
+ return sigmas;
+ }
+
+ template<int otherdim>
+ std::tuple<cv::Vec<float,otherdim> , cv::Matx<float,otherdim,otherdim>> compute_statistics(const SigmaPoints<dim,otherdim> &sigmas) const
+ {
+ cv::Vec<float,otherdim> mu{}; // Zero initializes
+ for (size_t i=0; i<sigmas.size(); ++i)
+ {
+ mu += to_vec((i==0 ? Wm_0 : Wm_i) * sigmas[i]);
+ }
+
+ cv::Matx<float,otherdim,otherdim> cov{};
+ for (size_t i=0; i<sigmas.size(); ++i)
+ {
+ const auto p = sigmas[i] - mu;
+ cov += (i==0 ? Wc_0 : Wc_i)*p*p.t();
+ }
+
+ return { mu, cov };
+ }
+
+ template<int otherdim>
+ cv::Matx<float,dim,otherdim> compute_cov(const SigmaPoints<dim,dim> &sigmas, const SigmaPoints<dim,otherdim> &othersigmas) const
+ {
+ cv::Vec<float,dim> mu{}; // Zero initializes
+ cv::Vec<float,otherdim> mu_other{}; // Zero initializes
+ for (size_t i=0; i<sigmas.size(); ++i)
+ {
+ mu += to_vec((i==0 ? Wm_0 : Wm_i) * sigmas[i]);
+ mu_other += to_vec((i==0 ? Wm_0 : Wm_i) * othersigmas[i]);
+ }
+
+ cv::Matx<float,dim,otherdim> cov{};
+ for (size_t i=0; i<sigmas.size(); ++i)
+ {
+ const auto p = sigmas[i] - mu;
+ const auto q = othersigmas[i] - mu_other;
+ cov += (i==0 ? Wc_0 : Wc_i)*p*q.t();
+ }
+
+ return cov;
+ }
+private:
+ float Wc_i, Wm_i, Wm_0, Wc_0, lambda;
+};
+
+
+} // namespace ukf_cv \ No newline at end of file
diff --git a/tracker-pt/CMakeLists.txt b/tracker-pt/CMakeLists.txt
index 078cd4bc..0c2e9ce3 100644
--- a/tracker-pt/CMakeLists.txt
+++ b/tracker-pt/CMakeLists.txt
@@ -1,9 +1,12 @@
include(opentrack-opencv)
find_package(OpenCV QUIET)
if(OpenCV_FOUND)
+ foreach(k core)
+ otr_install_lib("opencv_${k}" "${opentrack-libexec}")
+ endforeach()
otr_module(tracker-pt-base STATIC)
target_include_directories(${self} SYSTEM PUBLIC ${OpenCV_INCLUDE_DIRS})
- target_link_libraries(${self} opencv_imgproc opentrack-cv opencv_core opentrack-video)
+ target_link_libraries(${self} opentrack-cv opencv_core)
set_property(TARGET ${self} PROPERTY OUTPUT_NAME "pt-base")
endif()
add_subdirectory(module)
diff --git a/tracker-pt/FTNoIR_PT_Controls.ui b/tracker-pt/FTNoIR_PT_Controls.ui
index f683d7c3..53a29c1a 100644
--- a/tracker-pt/FTNoIR_PT_Controls.ui
+++ b/tracker-pt/FTNoIR_PT_Controls.ui
@@ -9,12 +9,12 @@
<rect>
<x>0</x>
<y>0</y>
- <width>418</width>
- <height>724</height>
+ <width>413</width>
+ <height>630</height>
</rect>
</property>
<property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
@@ -32,85 +32,24 @@
<property name="autoFillBackground">
<bool>false</bool>
</property>
- <layout class="QGridLayout" name="gridLayout_9">
- <property name="sizeConstraint">
- <enum>QLayout::SetFixedSize</enum>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <property name="spacing">
+ <number>0</number>
</property>
- <item row="1" column="0" alignment="Qt::AlignVCenter">
- <widget class="QGroupBox" name="groupBox_5">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="title">
- <string>Status</string>
- </property>
- <layout class="QGridLayout" name="gridLayout_10">
- <item row="1" column="0">
- <widget class="QLabel" name="label_3">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Extracted Points:</string>
- </property>
- </widget>
- </item>
- <item row="0" column="0">
- <widget class="QLabel" name="label_38">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Camera Info:</string>
- </property>
- </widget>
- </item>
- <item row="1" column="1">
- <widget class="QLabel" name="pointinfo_label">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QLabel" name="caminfo_label">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item row="0" column="0">
+ <property name="leftMargin">
+ <number>4</number>
+ </property>
+ <property name="topMargin">
+ <number>4</number>
+ </property>
+ <property name="rightMargin">
+ <number>4</number>
+ </property>
+ <property name="bottomMargin">
+ <number>4</number>
+ </property>
+ <item>
<widget class="QTabWidget" name="tabWidget">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
<property name="locale">
<locale language="English" country="UnitedStates"/>
</property>
@@ -118,6 +57,12 @@
<number>0</number>
</property>
<widget class="QWidget" name="tab_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
<attribute name="title">
<string>Camera</string>
</attribute>
@@ -134,64 +79,100 @@
<string>Camera settings</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
- <item row="0" column="1">
- <widget class="QComboBox" name="camdevice_combo">
+ <item row="11" column="1">
+ <widget class="QCheckBox" name="chroma_key_overexposed">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0">
+ <widget class="QLabel" name="label_4">
<property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="minimumContentsLength">
- <number>10</number>
+ <property name="cursor">
+ <cursorShape>WhatsThisCursor</cursorShape>
+ </property>
+ <property name="toolTip">
+ <string>This should be 56° or 76° for the PS3 Eye, dependent upon the physical lens setting. It's only neccessary to get position correspond to real-world values.</string>
+ </property>
+ <property name="text">
+ <string>Diagonal field of view</string>
</property>
</widget>
</item>
- <item row="4" column="1">
- <widget class="QSpinBox" name="fov">
+ <item row="6" column="0">
+ <widget class="QLabel" name="label_5">
<property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="suffix">
- <string>°</string>
+ <property name="text">
+ <string>Dynamic pose (for caps only, never clips)</string>
</property>
- <property name="prefix">
- <string/>
+ </widget>
+ </item>
+ <item row="9" column="0">
+ <widget class="QLabel" name="label_12">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
</property>
- <property name="minimum">
- <number>10</number>
+ <property name="cursor">
+ <cursorShape>WhatsThisCursor</cursorShape>
</property>
- <property name="maximum">
- <number>90</number>
+ <property name="toolTip">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;For LEDs, 'Natural' is the fastest grayscale mode thanks to optimized SIMD code. Color key allows to track regular pieces of colored paper.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="text">
+ <string>Color channels used</string>
</property>
</widget>
</item>
- <item row="4" column="0">
- <widget class="QLabel" name="label_4">
+ <item row="8" column="1">
+ <widget class="QPushButton" name="camera_settings">
<property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
- <string>Diagonal field of view</string>
+ <string>Open</string>
</property>
</widget>
</item>
- <item row="1" column="0">
- <widget class="QLabel" name="label_36">
+ <item row="4" column="1">
+ <widget class="QCheckBox" name="use_mjpeg">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="label_13">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
+ <property name="cursor">
+ <cursorShape>WhatsThisCursor</cursorShape>
+ </property>
+ <property name="toolTip">
+ <string>Enable MJPEG compression for high-speed cameras other than the PS3 Eye. Windows only.</string>
+ </property>
<property name="text">
- <string>Width</string>
+ <string>MJPEG compression</string>
</property>
</widget>
</item>
@@ -211,30 +192,28 @@
</property>
</widget>
</item>
- <item row="2" column="1">
- <widget class="QSpinBox" name="res_y_spin">
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_36">
<property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="toolTip">
- <string>Desired capture height</string>
- </property>
- <property name="suffix">
- <string> px</string>
- </property>
- <property name="maximum">
- <number>2000</number>
+ <property name="text">
+ <string>Width</string>
</property>
- <property name="singleStep">
- <number>10</number>
+ </widget>
+ </item>
+ <item row="11" column="0">
+ <widget class="QLabel" name="label_16">
+ <property name="text">
+ <string>Chroma key includes overexposed pixels</string>
</property>
</widget>
</item>
- <item row="6" column="0">
- <widget class="QLabel" name="label_6">
+ <item row="8" column="0">
+ <widget class="QLabel" name="label_9">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Maximum">
<horstretch>0</horstretch>
@@ -242,30 +221,30 @@
</sizepolicy>
</property>
<property name="text">
- <string>Dynamic pose timeout</string>
+ <string>Camera settings (when available)</string>
</property>
</widget>
</item>
- <item row="3" column="1">
- <widget class="QSpinBox" name="fps_spin">
+ <item row="7" column="1">
+ <widget class="QSpinBox" name="init_phase_timeout">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="toolTip">
- <string>Desired capture framerate</string>
- </property>
<property name="suffix">
- <string> Hz</string>
+ <string> ms</string>
+ </property>
+ <property name="minimum">
+ <number>50</number>
</property>
<property name="maximum">
- <number>2000</number>
+ <number>5000</number>
</property>
</widget>
</item>
- <item row="5" column="1">
+ <item row="6" column="1">
<widget class="QCheckBox" name="dynamic_pose">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
@@ -300,8 +279,8 @@
</property>
</widget>
</item>
- <item row="2" column="0">
- <widget class="QLabel" name="label_41">
+ <item row="7" column="0">
+ <widget class="QLabel" name="label_6">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Maximum">
<horstretch>0</horstretch>
@@ -309,26 +288,20 @@
</sizepolicy>
</property>
<property name="text">
- <string>Height</string>
+ <string>Dynamic pose timeout</string>
</property>
</widget>
</item>
- <item row="6" column="1">
- <widget class="QSpinBox" name="init_phase_timeout">
+ <item row="0" column="1">
+ <widget class="QComboBox" name="camdevice_combo">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="suffix">
- <string> ms</string>
- </property>
- <property name="minimum">
- <number>50</number>
- </property>
- <property name="maximum">
- <number>5000</number>
+ <property name="minimumContentsLength">
+ <number>10</number>
</property>
</widget>
</item>
@@ -348,46 +321,39 @@
</property>
</widget>
</item>
- <item row="7" column="1">
- <widget class="QPushButton" name="camera_settings">
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_41">
<property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
- <string>Open</string>
+ <string>Height</string>
</property>
</widget>
</item>
- <item row="7" column="0">
- <widget class="QLabel" name="label_9">
+ <item row="3" column="1">
+ <widget class="QSpinBox" name="fps_spin">
<property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="text">
- <string>Camera settings (when available)</string>
+ <property name="toolTip">
+ <string>Desired capture framerate</string>
</property>
- </widget>
- </item>
- <item row="8" column="0">
- <widget class="QLabel" name="label_12">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
+ <property name="suffix">
+ <string> Hz</string>
</property>
- <property name="text">
- <string>Color channels used</string>
+ <property name="maximum">
+ <number>2000</number>
</property>
</widget>
</item>
- <item row="8" column="1">
+ <item row="9" column="1">
<widget class="QComboBox" name="blob_color">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
@@ -397,12 +363,12 @@
</property>
<item>
<property name="text">
- <string>Average</string>
+ <string>Grayscale BT.709</string>
</property>
</item>
<item>
<property name="text">
- <string>Natural</string>
+ <string>Grayscale (from hardware)</string>
</property>
</item>
<item>
@@ -452,19 +418,93 @@
</item>
</widget>
</item>
- <item row="5" column="0">
- <widget class="QLabel" name="label_5">
+ <item row="5" column="1">
+ <widget class="QSpinBox" name="fov">
<property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
+ <property name="suffix">
+ <string>°</string>
+ </property>
+ <property name="prefix">
+ <string/>
+ </property>
+ <property name="minimum">
+ <number>10</number>
+ </property>
+ <property name="maximum">
+ <number>90</number>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QSpinBox" name="res_y_spin">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip">
+ <string>Desired capture height</string>
+ </property>
+ <property name="suffix">
+ <string> px</string>
+ </property>
+ <property name="maximum">
+ <number>2000</number>
+ </property>
+ <property name="singleStep">
+ <number>10</number>
+ </property>
+ </widget>
+ </item>
+ <item row="10" column="0">
+ <widget class="QLabel" name="label_17">
<property name="text">
- <string>Dynamic pose (for caps only, never clips)</string>
+ <string>Chroma key strength</string>
</property>
</widget>
</item>
+ <item row="10" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QSlider" name="chroma_key_strength_slider">
+ <property name="minimum">
+ <number>5</number>
+ </property>
+ <property name="maximum">
+ <number>40</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDoubleSpinBox" name="chroma_key_strength_label">
+ <property name="focusPolicy">
+ <enum>Qt::NoFocus</enum>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="buttonSymbols">
+ <enum>QAbstractSpinBox::NoButtons</enum>
+ </property>
+ <property name="minimum">
+ <double>0.500000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>4.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
</layout>
</widget>
</item>
@@ -504,6 +544,12 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
+ <property name="cursor">
+ <cursorShape>WhatsThisCursor</cursorShape>
+ </property>
+ <property name="toolTip">
+ <string>Set minimum size to avoid small stray lights from being treated as points.</string>
+ </property>
<property name="text">
<string>Min size</string>
</property>
@@ -580,6 +626,12 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
+ <property name="cursor">
+ <cursorShape>WhatsThisCursor</cursorShape>
+ </property>
+ <property name="toolTip">
+ <string>Track dependent on point size and not absolute brightness. This may allow more stable tracking.</string>
+ </property>
<property name="text">
<string>Automatic threshold</string>
</property>
@@ -607,6 +659,26 @@
</property>
</widget>
</item>
+ <item row="2" column="1">
+ <widget class="QLabel" name="threshold_value_display">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_14">
+ <property name="text">
+ <string>Value</string>
+ </property>
+ </widget>
+ </item>
<item row="3" column="1">
<widget class="QDoubleSpinBox" name="mindiam_spin">
<property name="sizePolicy">
@@ -629,29 +701,22 @@
</property>
</widget>
</item>
- <item row="2" column="1">
- <widget class="QLabel" name="threshold_value_display">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="2" column="0">
- <widget class="QLabel" name="label_14">
- <property name="text">
- <string>Value</string>
- </property>
- </widget>
- </item>
</layout>
</widget>
</item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>1</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
</layout>
</widget>
<widget class="QWidget" name="tab_4">
@@ -1353,6 +1418,231 @@ Don't roll or change position.</string>
</layout>
</widget>
</item>
+ <item row="2" column="0">
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>1</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab">
+ <attribute name="title">
+ <string>Filter</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout_9">
+ <item row="0" column="0">
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Point filter</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_12">
+ <item row="1" column="1">
+ <widget class="QSlider" name="point_filter_limit_slider">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimum">
+ <number>0</number>
+ </property>
+ <property name="maximum">
+ <number>99</number>
+ </property>
+ <property name="singleStep">
+ <number>1</number>
+ </property>
+ <property name="pageStep">
+ <number>10</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QDoubleSpinBox" name="point_filter_label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>2</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="focusPolicy">
+ <enum>Qt::NoFocus</enum>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="buttonSymbols">
+ <enum>QAbstractSpinBox::NoButtons</enum>
+ </property>
+ <property name="maximum">
+ <double>999.990000000000009</double>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_15">
+ <property name="text">
+ <string>Limit</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QDoubleSpinBox" name="point_filter_deadzone_label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="focusPolicy">
+ <enum>Qt::NoFocus</enum>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="buttonSymbols">
+ <enum>QAbstractSpinBox::NoButtons</enum>
+ </property>
+ <property name="suffix">
+ <string> px</string>
+ </property>
+ <property name="maximum">
+ <double>1.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QSlider" name="point_filter_slider">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>10</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimum">
+ <number>0</number>
+ </property>
+ <property name="maximum">
+ <number>400</number>
+ </property>
+ <property name="singleStep">
+ <number>1</number>
+ </property>
+ <property name="pageStep">
+ <number>10</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QSlider" name="point_filter_deadzone_slider">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimum">
+ <number>0</number>
+ </property>
+ <property name="maximum">
+ <number>100</number>
+ </property>
+ <property name="singleStep">
+ <number>1</number>
+ </property>
+ <property name="pageStep">
+ <number>3</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_30">
+ <property name="text">
+ <string>Deadzone</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QDoubleSpinBox" name="point_filter_limit_label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="focusPolicy">
+ <enum>Qt::NoFocus</enum>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="buttonSymbols">
+ <enum>QAbstractSpinBox::NoButtons</enum>
+ </property>
+ <property name="maximum">
+ <double>1.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QCheckBox" name="enable_point_filter">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip">
+ <string>Filter point centers prior to pose estimation.</string>
+ </property>
+ <property name="text">
+ <string>Enable</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <spacer name="verticalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
</layout>
</widget>
<widget class="QWidget" name="tab_3">
@@ -1390,7 +1680,74 @@ Don't roll or change position.</string>
</widget>
</widget>
</item>
- <item row="2" column="0">
+ <item>
+ <widget class="QGroupBox" name="groupBox_5">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Status</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_10">
+ <item row="1" column="1">
+ <widget class="QLabel" name="pointinfo_label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="caminfo_label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Extracted Points:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_38">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Camera Info:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
@@ -1411,11 +1768,14 @@ Don't roll or change position.</string>
<tabstop>res_x_spin</tabstop>
<tabstop>res_y_spin</tabstop>
<tabstop>fps_spin</tabstop>
+ <tabstop>use_mjpeg</tabstop>
<tabstop>fov</tabstop>
<tabstop>dynamic_pose</tabstop>
<tabstop>init_phase_timeout</tabstop>
<tabstop>camera_settings</tabstop>
<tabstop>blob_color</tabstop>
+ <tabstop>chroma_key_strength_slider</tabstop>
+ <tabstop>chroma_key_overexposed</tabstop>
<tabstop>auto_threshold</tabstop>
<tabstop>threshold_slider</tabstop>
<tabstop>mindiam_spin</tabstop>
@@ -1426,8 +1786,8 @@ Don't roll or change position.</string>
<tabstop>clip_bheight_spin</tabstop>
<tabstop>clip_blength_spin</tabstop>
<tabstop>cap_length_spin</tabstop>
- <tabstop>cap_height_spin</tabstop>
<tabstop>cap_width_spin</tabstop>
+ <tabstop>cap_height_spin</tabstop>
<tabstop>m1x_spin</tabstop>
<tabstop>m1y_spin</tabstop>
<tabstop>m1z_spin</tabstop>
@@ -1438,6 +1798,14 @@ Don't roll or change position.</string>
<tabstop>ty_spin</tabstop>
<tabstop>tz_spin</tabstop>
<tabstop>tcalib_button</tabstop>
+ <tabstop>enable_point_filter</tabstop>
+ <tabstop>point_filter_slider</tabstop>
+ <tabstop>point_filter_limit_slider</tabstop>
+ <tabstop>point_filter_deadzone_slider</tabstop>
+ <tabstop>maxdiam_spin</tabstop>
+ <tabstop>mindiam_spin</tabstop>
+ <tabstop>auto_threshold</tabstop>
+ <tabstop>threshold_slider</tabstop>
</tabstops>
<resources>
<include location="module/tracker_pt.qrc"/>
diff --git a/tracker-pt/ftnoir_tracker_pt.cpp b/tracker-pt/ftnoir_tracker_pt.cpp
index de95a0d4..0f8495d9 100644
--- a/tracker-pt/ftnoir_tracker_pt.cpp
+++ b/tracker-pt/ftnoir_tracker_pt.cpp
@@ -6,6 +6,7 @@
* copyright notice and this permission notice appear in all copies.
*/
+#undef NDEBUG
#include "ftnoir_tracker_pt.h"
#include "pt-api.hpp"
#include "cv/init.hpp"
@@ -13,7 +14,9 @@
#include "compat/math-imports.hpp"
#include "compat/check-visible.hpp"
#include "compat/thread-name.hpp"
+#include "compat/qt-dpi.hpp"
+#include <cassert>
#include <QHBoxLayout>
#include <QDebug>
#include <QFile>
@@ -27,17 +30,11 @@ Tracker_PT::Tracker_PT(pointer<pt_runtime_traits> const& traits) :
traits { traits },
s { traits->get_module_name() },
point_extractor { traits->make_point_extractor() },
- camera { traits->make_camera() },
- frame { traits->make_frame() },
- preview_frame { traits->make_preview(preview_width, preview_height) }
+ frame { traits->make_frame() }
{
opencv_init();
- connect(s.b.get(), &bundle_::saving, this, &Tracker_PT::maybe_reopen_camera, Qt::DirectConnection);
- connect(s.b.get(), &bundle_::reloading, this, &Tracker_PT::maybe_reopen_camera, Qt::DirectConnection);
-
- connect(&s.fov, value_::value_changed<int>(), this, &Tracker_PT::set_fov, Qt::DirectConnection);
- set_fov(s.fov);
+ connect(&*s.b, &bundle_::saving, this, [this]{ reopen_camera_flag = true; }, Qt::DirectConnection);
}
Tracker_PT::~Tracker_PT()
@@ -45,24 +42,41 @@ Tracker_PT::~Tracker_PT()
requestInterruption();
wait();
- QMutexLocker l(&camera_mtx);
- camera->stop();
+ if (camera)
+ camera->stop();
+}
+
+bool Tracker_PT::check_camera()
+{
+ if (reopen_camera_flag)
+ {
+ reopen_camera_flag = false;
+
+ camera = nullptr;
+ camera = traits->make_camera();
+ if (!camera || !camera->start(s))
+ return false;
+ }
+ assert(camera);
+ if (progn(bool x = true; return open_camera_dialog_flag.compare_exchange_strong(x, false);))
+ run_in_thread_sync(qApp->thread(), [this] { camera->show_camera_settings(); });
+ return true;
}
void Tracker_PT::run()
{
portable::set_curthread_name("tracker/pt");
- if (!maybe_reopen_camera())
- return;
-
while(!isInterruptionRequested())
{
+ if (!check_camera())
+ break;
+
pt_camera_info info;
bool new_frame = false;
{
- QMutexLocker l(&camera_mtx);
+ camera->set_fov(s.fov);
std::tie(new_frame, info) = camera->get_frame(*frame);
}
@@ -70,10 +84,10 @@ void Tracker_PT::run()
{
const bool preview_visible = check_is_visible();
- if (preview_visible)
- *preview_frame = *frame;
+ if (preview_visible && !widget->fresh())
+ preview_frame->set_last_frame(*frame);
- point_extractor->extract_points(*frame, *preview_frame, points);
+ point_extractor->extract_points(*frame, *preview_frame, preview_visible && !widget->fresh(), points);
point_count.store(points.size(), std::memory_order_relaxed);
const bool success = points.size() >= PointModel::N_POINTS;
@@ -87,10 +101,7 @@ void Tracker_PT::run()
{
int dynamic_pose_ms = s.dynamic_pose ? s.init_phase_timeout : 0;
- point_tracker.track(points,
- PointModel(s),
- info,
- dynamic_pose_ms);
+ point_tracker.track(points, PointModel(s), info, dynamic_pose_ms, filter, camera->deadzone_amount());
ever_success.store(true, std::memory_order_relaxed);
}
@@ -98,7 +109,7 @@ void Tracker_PT::run()
X_CM = point_tracker.pose();
}
- if (preview_visible)
+ if (preview_visible && !widget->fresh())
{
const f fx = pt_camera_info::get_focal_length(info.fov, info.res_x, info.res_y);
Affine X_MH(mat33::eye(), vec3(s.t_MH_x, s.t_MH_y, s.t_MH_z));
@@ -109,44 +120,31 @@ void Tracker_PT::run()
preview_frame->draw_head_center((p[0] * fx) / p[2], (p[1] * fx) / p[2]);
widget->update_image(preview_frame->get_bitmap());
-
- auto [ w, h ] = widget->preview_size();
- if (w != preview_width || h != preview_height)
- {
- preview_width = w; preview_height = h;
- preview_frame = traits->make_preview(w, h);
- }
}
}
}
}
-bool Tracker_PT::maybe_reopen_camera()
-{
- QMutexLocker l(&camera_mtx);
-
- return camera->start(s.camera_name,
- s.cam_fps, s.cam_res_x, s.cam_res_y);
-}
-
-void Tracker_PT::set_fov(int value)
-{
- QMutexLocker l(&camera_mtx);
- camera->set_fov(value);
-}
-
module_status Tracker_PT::start_tracker(QFrame* video_frame)
{
- //video_frame->setAttribute(Qt::WA_NativeWindow);
+ {
+ auto camera = traits->make_camera();
+ if (!camera || !camera->start(s))
+ return error(tr("Failed to open camera '%1'").arg(s.camera_name));
+ }
widget = std::make_unique<video_widget>(video_frame);
layout = std::make_unique<QHBoxLayout>(video_frame);
layout->setContentsMargins(0, 0, 0, 0);
- layout->addWidget(widget.get());
- video_frame->setLayout(layout.get());
+ layout->addWidget(&*widget);
+ video_frame->setLayout(&*layout);
//video_widget->resize(video_frame->width(), video_frame->height());
video_frame->show();
+ double dpi = screen_dpi(video_frame);
+ preview_frame = traits->make_preview(iround(preview_width * dpi),
+ iround(preview_height * dpi));
+
start(QThread::HighPriority);
return {};
@@ -212,10 +210,10 @@ int Tracker_PT::get_n_points()
bool Tracker_PT::get_cam_info(pt_camera_info& info)
{
- QMutexLocker l(&camera_mtx);
- bool ret;
+ bool ret = false;
- std::tie(ret, info) = camera->get_info();
+ if (camera)
+ std::tie(ret, info) = camera->get_info();
return ret;
}
diff --git a/tracker-pt/ftnoir_tracker_pt.h b/tracker-pt/ftnoir_tracker_pt.h
index 210c6a01..a793f94b 100644
--- a/tracker-pt/ftnoir_tracker_pt.h
+++ b/tracker-pt/ftnoir_tracker_pt.h
@@ -13,13 +13,12 @@
#include "point_tracker.h"
#include "cv/numeric.hpp"
#include "video/video-widget.hpp"
+#include "point-filter.hpp"
#include <atomic>
#include <memory>
#include <vector>
-#include <opencv2/core.hpp>
-
#include <QThread>
#include <QMutex>
#include <QLayout>
@@ -48,14 +47,10 @@ struct Tracker_PT : QThread, ITracker
private:
void run() override;
-
- bool maybe_reopen_camera();
- void set_fov(int value);
+ [[nodiscard]] bool check_camera();
pointer<pt_runtime_traits> traits;
- QMutex camera_mtx;
-
PointTracker point_tracker;
pt_settings s;
@@ -73,7 +68,10 @@ private:
std::atomic<unsigned> point_count { 0 };
std::atomic<bool> ever_success = false;
+ std::atomic<bool> reopen_camera_flag = true;
+ std::atomic<bool> open_camera_dialog_flag = false;
mutable QMutex center_lock, data_lock;
+ point_filter filter{s};
};
} // ns pt_impl
diff --git a/tracker-pt/ftnoir_tracker_pt_dialog.cpp b/tracker-pt/ftnoir_tracker_pt_dialog.cpp
index 32916cc5..d67f79a7 100644
--- a/tracker-pt/ftnoir_tracker_pt_dialog.cpp
+++ b/tracker-pt/ftnoir_tracker_pt_dialog.cpp
@@ -10,8 +10,6 @@
#include "compat/math.hpp"
#include "video/camera.hpp"
-#include <opencv2/core.hpp>
-
#include <QString>
#include <QtGlobal>
#include <QDebug>
@@ -39,6 +37,7 @@ TrackerDialog_PT::TrackerDialog_PT(const QString& module_name) :
tie_setting(s.cam_res_x, ui.res_x_spin);
tie_setting(s.cam_res_y, ui.res_y_spin);
tie_setting(s.cam_fps, ui.fps_spin);
+ tie_setting(s.use_mjpeg, ui.use_mjpeg);
tie_setting(s.threshold_slider, ui.threshold_slider);
@@ -92,11 +91,9 @@ TrackerDialog_PT::TrackerDialog_PT(const QString& module_name) :
poll_tracker_info_impl();
- connect(this, &TrackerDialog_PT::poll_tracker_info, this, &TrackerDialog_PT::poll_tracker_info_impl, Qt::DirectConnection);
-
constexpr pt_color_type color_types[] = {
- pt_color_average,
- pt_color_natural,
+ pt_color_bt709,
+ pt_color_hardware,
pt_color_red_only,
pt_color_green_only,
pt_color_blue_only,
@@ -113,14 +110,38 @@ TrackerDialog_PT::TrackerDialog_PT(const QString& module_name) :
tie_setting(s.blob_color, ui.blob_color);
+ tie_setting(s.chroma_key_strength, ui.chroma_key_strength_slider);
+ connect(&s.chroma_key_strength, value_::value_changed<slider_value>(), ui.chroma_key_strength_label,
+ [this] { ui.chroma_key_strength_label->setValue(*s.chroma_key_strength); });
+ ui.chroma_key_strength_label->setValue(*s.chroma_key_strength);
+
+ tie_setting(s.chroma_key_overexposed, ui.chroma_key_overexposed);
+ connect(ui.blob_color, &QComboBox::currentTextChanged, this, &TrackerDialog_PT::chroma_key_controls_enable);
+
+ chroma_key_controls_enable("");
+
tie_setting(s.threshold_slider, ui.threshold_value_display, [this](const slider_value& val) {
return threshold_display_text(int(val));
});
// refresh threshold display on auto-threshold checkbox state change
- tie_setting(s.auto_threshold,
- this,
- [this](bool) { s.threshold_slider.notify(); });
+ tie_setting(s.auto_threshold, this, [this](bool) { s.threshold_slider.notify_(); });
+
+ tie_setting(s.enable_point_filter, ui.enable_point_filter);
+ tie_setting(s.point_filter_coefficient, ui.point_filter_slider);
+ tie_setting(s.point_filter_limit, ui.point_filter_limit_slider);
+ connect(&s.point_filter_coefficient, value_::value_changed<slider_value>(),
+ ui.point_filter_label, [this] { ui.point_filter_label->setValue(*s.point_filter_coefficient); } );
+ connect(&s.point_filter_limit, value_::value_changed<slider_value>(), ui.point_filter_limit_label,
+ [this] { ui.point_filter_limit_label->setValue(*s.point_filter_limit); }, Qt::QueuedConnection);
+ ui.point_filter_label->setValue(*s.point_filter_coefficient);
+ ui.point_filter_limit_label->setValue(*s.point_filter_limit);
+
+ tie_setting(s.point_filter_deadzone, ui.point_filter_deadzone_slider);
+ ui.point_filter_deadzone_label->setValue(*s.point_filter_deadzone);
+
+ connect(&s.point_filter_deadzone, value_::value_changed<slider_value>(), ui.point_filter_deadzone_label,
+ [this] { ui.point_filter_deadzone_label->setValue(*s.point_filter_deadzone); }, Qt::QueuedConnection);
}
QString TrackerDialog_PT::threshold_display_text(int threshold_value)
@@ -232,14 +253,25 @@ void TrackerDialog_PT::set_camera_settings_available(const QString& /* camera_na
void TrackerDialog_PT::show_camera_settings()
{
if (tracker)
- {
- QMutexLocker l(&tracker->camera_mtx);
- tracker->camera->show_camera_settings();
- }
+ tracker->open_camera_dialog_flag = true;
else
(void)video::show_dialog(s.camera_name);
}
+void TrackerDialog_PT::chroma_key_controls_enable(const QString&)
+{
+ bool enabled = false;
+ QVariant data = ui.blob_color->currentData();
+ if (data.isValid())
+ {
+ pt_color_type blob_color = pt_color_type(data.toInt());
+ enabled = blob_color >= pt_color_red_chromakey && blob_color <= pt_color_magenta_chromakey;
+ }
+ ui.chroma_key_strength_slider->setEnabled(enabled);
+ ui.chroma_key_strength_label->setEnabled(enabled);
+ ui.chroma_key_overexposed->setEnabled(enabled);
+}
+
void TrackerDialog_PT::trans_calib_step()
{
QMutexLocker l(&calibrator_mutex);
@@ -273,7 +305,7 @@ void TrackerDialog_PT::register_tracker(ITracker *t)
{
tracker = static_cast<Tracker_PT*>(t);
ui.tcalib_button->setEnabled(true);
- poll_tracker_info();
+ poll_tracker_info_impl();
timer.start();
}
@@ -281,8 +313,18 @@ void TrackerDialog_PT::unregister_tracker()
{
tracker = nullptr;
ui.tcalib_button->setEnabled(false);
- poll_tracker_info();
+ poll_tracker_info_impl();
timer.stop();
}
+void TrackerDialog_PT::set_buttons_visible(bool x)
+{
+ ui.buttonBox->setVisible(x);
+}
+
+void TrackerDialog_PT::reload()
+{
+ s.b->reload();
+}
+
} // ns pt_impl
diff --git a/tracker-pt/ftnoir_tracker_pt_dialog.h b/tracker-pt/ftnoir_tracker_pt_dialog.h
index 66981ee6..79cd91bd 100644
--- a/tracker-pt/ftnoir_tracker_pt_dialog.h
+++ b/tracker-pt/ftnoir_tracker_pt_dialog.h
@@ -26,7 +26,10 @@ public:
TrackerDialog_PT(const QString& module_name);
void register_tracker(ITracker *tracker) override;
void unregister_tracker() override;
- void save();
+ bool embeddable() noexcept override { return true; }
+ void set_buttons_visible(bool x) override;
+ void save() override;
+ void reload() override;
public slots:
void doOK();
void doCancel();
@@ -36,8 +39,8 @@ public slots:
void poll_tracker_info_impl();
void set_camera_settings_available(const QString& camera_name);
void show_camera_settings();
-signals:
- void poll_tracker_info();
+ void chroma_key_controls_enable(const QString&);
+
protected:
QString threshold_display_text(int threshold_value);
diff --git a/tracker-pt/lang/de_DE.ts b/tracker-pt/lang/de_DE.ts
new file mode 100644
index 00000000..30ab42c2
--- /dev/null
+++ b/tracker-pt/lang/de_DE.ts
@@ -0,0 +1,378 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>UICPTClientControls</name>
+ <message>
+ <source>PointTracker Settings</source>
+ <translation>PointTracker-Einstellungen</translation>
+ </message>
+ <message>
+ <source>Camera</source>
+ <translation>Kamera</translation>
+ </message>
+ <message>
+ <source>Camera settings</source>
+ <translation>Kamera-Einstellungen</translation>
+ </message>
+ <message>
+ <source>This should be 56° or 76° for the PS3 Eye, dependent upon the physical lens setting. It&apos;s only neccessary to get position correspond to real-world values.</source>
+ <translation>Für die PS3 Eye sollte dies 56° oder 76° sein, abhängig von der Einstellung der physikalischen Linse. Dies wird nur benutzt, um die Position aus den Welt-Koordinaten zur ermitteln.</translation>
+ </message>
+ <message>
+ <source>Diagonal field of view</source>
+ <translation>Diagonales Sichtfeld</translation>
+ </message>
+ <message>
+ <source>Dynamic pose (for caps only, never clips)</source>
+ <translation>Dynamische Pose (nur für Hüte, niemals Sticker)</translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;For LEDs, &apos;Natural&apos; is the fastest grayscale mode thanks to optimized SIMD code. Color key allows to track regular pieces of colored paper.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;‚Natürlich‘ ist, dank des optimierten SIMD-Codes, die schnellste Methode für LEDs. Mit dem Farbschlüssel kann man normale farbige Papierstücke tracken.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <source>Color channels used</source>
+ <translation>Benutzte Farbkanäle</translation>
+ </message>
+ <message>
+ <source>Open</source>
+ <translation>Öffnen</translation>
+ </message>
+ <message>
+ <source>Enable MJPEG compression for high-speed cameras other than the PS3 Eye. Windows only.</source>
+ <translation>Aktiviert MJPEG-Kompression für Hochgeschwindigkeitskameras, PS3 Eye ausgenommen. Nur für Windows.</translation>
+ </message>
+ <message>
+ <source>MJPEG compression</source>
+ <translation>MJPEG-Kompression</translation>
+ </message>
+ <message>
+ <source>FPS</source>
+ <translation>FPS</translation>
+ </message>
+ <message>
+ <source>Width</source>
+ <translation>Breite</translation>
+ </message>
+ <message>
+ <source>Chroma key includes overexposed pixels</source>
+ <translation>Chroma-Key enthält überbelichtete Pixel</translation>
+ </message>
+ <message>
+ <source>Camera settings (when available)</source>
+ <translation>Kamera-Einstellungen (falls verfügbar)</translation>
+ </message>
+ <message>
+ <source> ms</source>
+ <translation> ms</translation>
+ </message>
+ <message>
+ <source>Desired capture width</source>
+ <translation>Angestrebte Aufnahmebreite</translation>
+ </message>
+ <message>
+ <source> px</source>
+ <translation> px</translation>
+ </message>
+ <message>
+ <source>Dynamic pose timeout</source>
+ <translation>Dynamisches Pose-Timeout</translation>
+ </message>
+ <message>
+ <source>Device</source>
+ <translation>Gerät</translation>
+ </message>
+ <message>
+ <source>Height</source>
+ <translation>Höhe</translation>
+ </message>
+ <message>
+ <source>Desired capture framerate</source>
+ <translation>Angestrebte Aufnahme-Bildrate</translation>
+ </message>
+ <message>
+ <source> Hz</source>
+ <translation> Hz</translation>
+ </message>
+ <message>
+ <source>Grayscale BT.709</source>
+ <translation>Graustufen BT.709</translation>
+ </message>
+ <message>
+ <source>Grayscale (from hardware)</source>
+ <translation>Graustufen (gemäß Hardware)</translation>
+ </message>
+ <message>
+ <source>Red only</source>
+ <translation>nur Rot</translation>
+ </message>
+ <message>
+ <source>Green only</source>
+ <translation>nur Grün</translation>
+ </message>
+ <message>
+ <source>Blue only</source>
+ <translation>nur Blau</translation>
+ </message>
+ <message>
+ <source>Red chroma key</source>
+ <translation>Roter Chroma-Key</translation>
+ </message>
+ <message>
+ <source>Green chroma key</source>
+ <translation>Grüner Chroma-Key</translation>
+ </message>
+ <message>
+ <source>Blue chroma key</source>
+ <translation>Blauer Chroma-Key</translation>
+ </message>
+ <message>
+ <source>Cyan chroma key</source>
+ <translation>Cyan-Chroma-Key</translation>
+ </message>
+ <message>
+ <source>Yellow chroma key</source>
+ <translation>Gelber Chroma-Key</translation>
+ </message>
+ <message>
+ <source>Magenta chroma key</source>
+ <translation>Magenta-Chroma-Key</translation>
+ </message>
+ <message>
+ <source>°</source>
+ <translation>°</translation>
+ </message>
+ <message>
+ <source>Desired capture height</source>
+ <translation>Angestrebte Aufnahmehöhe</translation>
+ </message>
+ <message>
+ <source>Chroma key strength</source>
+ <translation>Chroma-Key-Stärke</translation>
+ </message>
+ <message>
+ <source>Point extraction</source>
+ <translation>Punktextraktion</translation>
+ </message>
+ <message>
+ <source>Threshold</source>
+ <translation>Schwelle</translation>
+ </message>
+ <message>
+ <source>Set minimum size to avoid small stray lights from being treated as points.</source>
+ <translation>Setzt die Mindestgröße, um zu verhindern, dass kleine Streulichter als Punkte erkannt werden.</translation>
+ </message>
+ <message>
+ <source>Min size</source>
+ <translation>Mindestgröße</translation>
+ </message>
+ <message>
+ <source>Max size</source>
+ <translation>Maximale Größe</translation>
+ </message>
+ <message>
+ <source>Intensity threshold for point extraction</source>
+ <translation>Intensitätsschwelle für die Punktextraktion</translation>
+ </message>
+ <message>
+ <source>Enable, slider sets point size</source>
+ <translation>Aktivieren, Regler setzt die Punktgröße</translation>
+ </message>
+ <message>
+ <source>Track dependent on point size and not absolute brightness. This may allow more stable tracking.</source>
+ <translation>Tracking abhängig von der Punktgröße anstelle der absoluten Helligkeit durchführen. Dies kann stabileres Tracking ermöglichen.</translation>
+ </message>
+ <message>
+ <source>Automatic threshold</source>
+ <translation>Automatische Schwelle</translation>
+ </message>
+ <message>
+ <source>Maximum point diameter</source>
+ <translation>Maximaler Punktdurchmesser</translation>
+ </message>
+ <message>
+ <source>Value</source>
+ <translation>Wert</translation>
+ </message>
+ <message>
+ <source>Minimum point diameter</source>
+ <translation>Minimaler Punktdurchmesser</translation>
+ </message>
+ <message>
+ <source>Model</source>
+ <translation>Modell</translation>
+ </message>
+ <message>
+ <source>Clip</source>
+ <translation>Sticker</translation>
+ </message>
+ <message>
+ <source>Model Dimensions</source>
+ <translation>Modellabmessungen</translation>
+ </message>
+ <message>
+ <source> mm</source>
+ <translation> mm</translation>
+ </message>
+ <message>
+ <source>Side</source>
+ <translation>Seitlich</translation>
+ </message>
+ <message>
+ <source>Front</source>
+ <translation>Vorne</translation>
+ </message>
+ <message>
+ <source>Cap</source>
+ <translation>Hut</translation>
+ </message>
+ <message>
+ <source>Custom</source>
+ <translation>Benutzerdefiniert</translation>
+ </message>
+ <message>
+ <source>z:</source>
+ <translation>z:</translation>
+ </message>
+ <message>
+ <source>x:</source>
+ <translation>x:</translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Location of the two remaining model points&lt;br/&gt;with respect to the reference point in default pose&lt;/p&gt;&lt;p&gt;Use any units you want, not necessarily centimeters.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Ort der verbleibenden zwei Modellpunkte&lt;br/&gt;unter Berücksichtigung der Referenzpunkte der Standard-Pose&lt;/p&gt;&lt;p&gt;Dies können beliebige Einheiten sein, nicht zwingend Zentimeter.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <source>y:</source>
+ <translation>y:</translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:16pt;&quot;&gt;P&lt;/span&gt;&lt;span style=&quot; font-size:16pt; vertical-align:sub;&quot;&gt;3&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:16pt;&quot;&gt;P&lt;/span&gt;&lt;span style=&quot; font-size:16pt; vertical-align:sub;&quot;&gt;3&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:16pt;&quot;&gt;P&lt;/span&gt;&lt;span style=&quot; font-size:16pt; vertical-align:sub;&quot;&gt;2&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:16pt;&quot;&gt;P&lt;/span&gt;&lt;span style=&quot; font-size:16pt; vertical-align:sub;&quot;&gt;2&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <source>Model position</source>
+ <translation>Modell-Position</translation>
+ </message>
+ <message>
+ <source>Use only yaw and pitch while calibrating.
+Don&apos;t roll or change position.</source>
+ <translation>Bitte nur gieren oder nicken während der Kalibrierung.
+Bitte nicht rollen oder die Position ändern.</translation>
+ </message>
+ <message>
+ <source>Start calibration</source>
+ <translation>Kalibrierung starten</translation>
+ </message>
+ <message>
+ <source>Filter</source>
+ <translation>Filter</translation>
+ </message>
+ <message>
+ <source>Point filter</source>
+ <translation>Punktfilter</translation>
+ </message>
+ <message>
+ <source>Limit</source>
+ <translation>Grenze</translation>
+ </message>
+ <message>
+ <source>Deadzone</source>
+ <translation>Totbereich</translation>
+ </message>
+ <message>
+ <source>Filter point centers prior to pose estimation.</source>
+ <translation>Punktmitten vor der Posen-Abschätzung filtern.</translation>
+ </message>
+ <message>
+ <source>Enable</source>
+ <translation>Einschalten</translation>
+ </message>
+ <message>
+ <source>About</source>
+ <translation>Über</translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;FTNoIR PointTracker Plugin&lt;br/&gt;Version 1.1&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;by Patrick Ruoff&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;http://ftnoirpt.sourceforge.net/&quot;&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline; color:#0000ff;&quot;&gt;Manual (external)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;FTNoIR PointTracker Plugin&lt;br/&gt;Version 1.1&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;von Patrick Ruoff&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;http://ftnoirpt.sourceforge.net/&quot;&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline; color:#0000ff;&quot;&gt;Anleitung (extern)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <source>Status</source>
+ <translation>Status</translation>
+ </message>
+ <message>
+ <source>Extracted Points:</source>
+ <translation>Extrahierte Punkte:</translation>
+ </message>
+ <message>
+ <source>Camera Info:</source>
+ <translation>Kamera-Info:</translation>
+ </message>
+</context>
+<context>
+ <name>pt_impl::TrackerDialog_PT</name>
+ <message>
+ <source>Brightness %1/255</source>
+ <translation>Helligkeit %1/255</translation>
+ </message>
+ <message>
+ <source>LED radius %1 pixels</source>
+ <translation>LED-Radius %1 Pixel</translation>
+ </message>
+ <message>
+ <source>%1 yaw samples. Yaw more to %2 samples for stable calibration.</source>
+ <translation>%1 Gieren-Proben. Weiterhin gieren bis %2 Proben für eine stabile Kalibrierung.</translation>
+ </message>
+ <message>
+ <source>%1 pitch samples. Pitch more to %2 samples for stable calibration.</source>
+ <translation>%1 Nicken-Proben. Weiterhin nicken bis %2 Proben für eine stabile Kalibrierung.</translation>
+ </message>
+ <message>
+ <source>%1 samples. Over %2, good!</source>
+ <translation>%1 Proben. Mehr als %2, gut!</translation>
+ </message>
+ <message>
+ <source>Stop calibration</source>
+ <translation>Kalibrierung stoppen</translation>
+ </message>
+ <message>
+ <source>Start calibration</source>
+ <translation>Kalibrierung starten</translation>
+ </message>
+ <message>
+ <source>%1x%2 @ %3 FPS</source>
+ <translation>%1x%2 @ %3 FPS</translation>
+ </message>
+ <message>
+ <source>%1 OK!</source>
+ <translation>%1 OKAY!</translation>
+ </message>
+ <message>
+ <source>%1 BAD!</source>
+ <translation>%1 SCHLECHT!</translation>
+ </message>
+ <message>
+ <source>Tracker offline</source>
+ <translation>Tracker offline</translation>
+ </message>
+</context>
+<context>
+ <name>pt_impl::Tracker_PT</name>
+ <message>
+ <source>Failed to open camera &apos;%1&apos;</source>
+ <translation>Öffnen der Kamera ‚%1‘ fehlgeschlagen</translation>
+ </message>
+</context>
+<context>
+ <name>pt_module::metadata_pt</name>
+ <message>
+ <source>PointTracker 1.1</source>
+ <translation>PointTracker 1.1</translation>
+ </message>
+</context>
+</TS>
diff --git a/tracker-pt/lang/nl_NL.ts b/tracker-pt/lang/nl_NL.ts
index 3d12a7ea..fc44b0f1 100644
--- a/tracker-pt/lang/nl_NL.ts
+++ b/tracker-pt/lang/nl_NL.ts
@@ -108,14 +108,6 @@
<translation type="unfinished"></translation>
</message>
<message>
- <source>Average</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>Natural</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
<source>Red only</source>
<translation type="unfinished"></translation>
</message>
@@ -256,6 +248,70 @@ Don&apos;t roll or change position.</source>
<source>Magenta chroma key</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>This should be 56° or 76° for the PS3 Eye, dependent upon the physical lens setting. It&apos;s only neccessary to get position correspond to real-world values.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Enable MJPEG compression for high-speed cameras other than the PS3 Eye. Windows only.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Set minimum size to avoid small stray lights from being treated as points.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Track dependent on point size and not absolute brightness. This may allow more stable tracking.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;For LEDs, &apos;Natural&apos; is the fastest grayscale mode thanks to optimized SIMD code. Color key allows to track regular pieces of colored paper.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>MJPEG compression</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Point filter</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Filter point centers prior to pose estimation.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Enable</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Limit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Grayscale BT.709</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Grayscale (from hardware)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Deadzone</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Filter</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Chroma key includes overexposed pixels</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Chroma key strength</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>pt_impl::TrackerDialog_PT</name>
@@ -305,6 +361,13 @@ Don&apos;t roll or change position.</source>
</message>
</context>
<context>
+ <name>pt_impl::Tracker_PT</name>
+ <message>
+ <source>Failed to open camera &apos;%1&apos;</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
<name>pt_module::metadata_pt</name>
<message>
<source>PointTracker 1.1</source>
diff --git a/tracker-pt/lang/ru_RU.ts b/tracker-pt/lang/ru_RU.ts
index 0315d493..7ff4657e 100644
--- a/tracker-pt/lang/ru_RU.ts
+++ b/tracker-pt/lang/ru_RU.ts
@@ -112,14 +112,6 @@
<translation type="unfinished"></translation>
</message>
<message>
- <source>Average</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>Natural</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
<source>Red only</source>
<translation type="unfinished"></translation>
</message>
@@ -261,6 +253,70 @@ ROLL или X/Y-смещения.</translation>
<source>Magenta chroma key</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>This should be 56° or 76° for the PS3 Eye, dependent upon the physical lens setting. It&apos;s only neccessary to get position correspond to real-world values.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Enable MJPEG compression for high-speed cameras other than the PS3 Eye. Windows only.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Set minimum size to avoid small stray lights from being treated as points.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Track dependent on point size and not absolute brightness. This may allow more stable tracking.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;For LEDs, &apos;Natural&apos; is the fastest grayscale mode thanks to optimized SIMD code. Color key allows to track regular pieces of colored paper.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>MJPEG compression</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Point filter</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Filter point centers prior to pose estimation.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Enable</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Limit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Grayscale BT.709</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Grayscale (from hardware)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Deadzone</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Filter</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Chroma key includes overexposed pixels</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Chroma key strength</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>pt_impl::TrackerDialog_PT</name>
@@ -310,6 +366,13 @@ ROLL или X/Y-смещения.</translation>
</message>
</context>
<context>
+ <name>pt_impl::Tracker_PT</name>
+ <message>
+ <source>Failed to open camera &apos;%1&apos;</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
<name>pt_module::metadata_pt</name>
<message>
<source>PointTracker 1.1</source>
diff --git a/tracker-pt/lang/stub.ts b/tracker-pt/lang/stub.ts
index 4c8c4f82..3dbe208d 100644
--- a/tracker-pt/lang/stub.ts
+++ b/tracker-pt/lang/stub.ts
@@ -108,14 +108,6 @@
<translation type="unfinished"></translation>
</message>
<message>
- <source>Average</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>Natural</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
<source>Red only</source>
<translation type="unfinished"></translation>
</message>
@@ -256,6 +248,70 @@ Don&apos;t roll or change position.</source>
<source>Magenta chroma key</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>This should be 56° or 76° for the PS3 Eye, dependent upon the physical lens setting. It&apos;s only neccessary to get position correspond to real-world values.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Enable MJPEG compression for high-speed cameras other than the PS3 Eye. Windows only.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Set minimum size to avoid small stray lights from being treated as points.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Track dependent on point size and not absolute brightness. This may allow more stable tracking.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;For LEDs, &apos;Natural&apos; is the fastest grayscale mode thanks to optimized SIMD code. Color key allows to track regular pieces of colored paper.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>MJPEG compression</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Point filter</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Filter point centers prior to pose estimation.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Enable</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Limit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Grayscale BT.709</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Grayscale (from hardware)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Deadzone</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Filter</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Chroma key includes overexposed pixels</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Chroma key strength</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>pt_impl::TrackerDialog_PT</name>
@@ -305,6 +361,13 @@ Don&apos;t roll or change position.</source>
</message>
</context>
<context>
+ <name>pt_impl::Tracker_PT</name>
+ <message>
+ <source>Failed to open camera &apos;%1&apos;</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
<name>pt_module::metadata_pt</name>
<message>
<source>PointTracker 1.1</source>
diff --git a/tracker-pt/lang/zh_CN.ts b/tracker-pt/lang/zh_CN.ts
index bbbc7f8d..3519d719 100644
--- a/tracker-pt/lang/zh_CN.ts
+++ b/tracker-pt/lang/zh_CN.ts
@@ -200,14 +200,6 @@
<translation type="unfinished"></translation>
</message>
<message>
- <source>Average</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>Natural</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
<source>Red only</source>
<translation type="unfinished"></translation>
</message>
@@ -256,6 +248,70 @@ Don&apos;t roll or change position.</source>
<source>Magenta chroma key</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>This should be 56° or 76° for the PS3 Eye, dependent upon the physical lens setting. It&apos;s only neccessary to get position correspond to real-world values.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Enable MJPEG compression for high-speed cameras other than the PS3 Eye. Windows only.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Set minimum size to avoid small stray lights from being treated as points.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Track dependent on point size and not absolute brightness. This may allow more stable tracking.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;For LEDs, &apos;Natural&apos; is the fastest grayscale mode thanks to optimized SIMD code. Color key allows to track regular pieces of colored paper.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>MJPEG compression</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Point filter</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Filter point centers prior to pose estimation.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Enable</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Limit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Grayscale BT.709</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Grayscale (from hardware)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Deadzone</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Filter</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Chroma key includes overexposed pixels</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Chroma key strength</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>pt_impl::TrackerDialog_PT</name>
@@ -305,6 +361,13 @@ Don&apos;t roll or change position.</source>
</message>
</context>
<context>
+ <name>pt_impl::Tracker_PT</name>
+ <message>
+ <source>Failed to open camera &apos;%1&apos;</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
<name>pt_module::metadata_pt</name>
<message>
<source>PointTracker 1.1</source>
diff --git a/tracker-pt/module/CMakeLists.txt b/tracker-pt/module/CMakeLists.txt
index f7a6cc7f..b7fc974f 100644
--- a/tracker-pt/module/CMakeLists.txt
+++ b/tracker-pt/module/CMakeLists.txt
@@ -1,7 +1,10 @@
include(opentrack-opencv)
find_package(OpenCV QUIET)
if(OpenCV_FOUND)
+ foreach(k core imgproc)
+ otr_install_lib("opencv_${k}" "${opentrack-libexec}")
+ endforeach()
otr_module(tracker-pt)
- target_link_libraries(${self} opentrack-tracker-pt-base)
+ target_link_libraries(${self} opentrack-video opencv_imgproc opentrack-tracker-pt-base)
target_include_directories(${self} PUBLIC "${CMAKE_SOURCE_DIR}/tracker-pt")
endif()
diff --git a/tracker-pt/module/camera.cpp b/tracker-pt/module/camera.cpp
index a70698de..1beba474 100644
--- a/tracker-pt/module/camera.cpp
+++ b/tracker-pt/module/camera.cpp
@@ -7,10 +7,7 @@
#include "camera.h"
#include "frame.hpp"
-
-#include "compat/math-imports.hpp"
-
-#include <opencv2/core.hpp>
+#include <opencv2/core/mat.hpp>
namespace pt_module {
@@ -73,23 +70,29 @@ Camera::result Camera::get_frame(pt_frame& frame_)
return { false, {} };
}
-bool Camera::start(const QString& name, int fps, int res_x, int res_y)
+bool Camera::start(const pt_settings& s)
{
+ int fps = s.cam_fps, res_x = s.cam_res_x, res_y = s.cam_res_y;
+ QString name = s.camera_name;
+ bool use_mjpeg = s.use_mjpeg;
+
if (fps >= 0 && res_x >= 0 && res_y >= 0)
{
if (cam_desired.name != name ||
(int)cam_desired.fps != fps ||
cam_desired.res_x != res_x ||
cam_desired.res_y != res_y ||
+ cam_desired.use_mjpeg != use_mjpeg ||
!cap || !cap->is_open())
{
stop();
cam_desired.name = name;
- cam_desired.fps = fps;
+ cam_desired.fps = (f)fps;
cam_desired.res_x = res_x;
cam_desired.res_y = res_y;
cam_desired.fov = fov;
+ cam_desired.use_mjpeg = use_mjpeg;
cap = video::make_camera(name);
@@ -100,12 +103,16 @@ bool Camera::start(const QString& name, int fps, int res_x, int res_y)
info.fps = fps;
info.width = res_x;
info.height = res_y;
+ info.use_mjpeg = use_mjpeg;
+ info.num_channels = s.blob_color == pt_color_hardware ? 1 : 3;
if (!cap->start(info))
goto fail;
cam_info = pt_camera_info();
cam_info.name = name;
+ cam_info.use_mjpeg = use_mjpeg;
+ cam_info.fov = (f)s.fov;
dt_mean = 0;
cv::Mat tmp;
@@ -141,7 +148,7 @@ bool Camera::get_frame_(cv::Mat& img)
int stride = frame.stride;
if (stride == 0)
stride = cv::Mat::AUTO_STEP;
- img = cv::Mat(frame.height, frame.width, CV_8UC(frame.channels), (void*)frame.data, stride);
+ img = cv::Mat(frame.height, frame.width, CV_8UC(frame.channels), (void*)frame.data, (size_t)stride);
return true;
}
}
diff --git a/tracker-pt/module/camera.h b/tracker-pt/module/camera.h
index e2ba159f..e4772178 100644
--- a/tracker-pt/module/camera.h
+++ b/tracker-pt/module/camera.h
@@ -13,8 +13,6 @@
#include <memory>
-#include <opencv2/core.hpp>
-
#include <QString>
namespace pt_module {
@@ -23,7 +21,7 @@ struct Camera final : pt_camera
{
Camera(const QString& module_name);
- bool start(const QString& name, int fps, int res_x, int res_y) override;
+ bool start(const pt_settings& s) override;
void stop() override;
result get_frame(pt_frame& Frame) override;
diff --git a/tracker-pt/module/frame.cpp b/tracker-pt/module/frame.cpp
index ab110871..1a276f16 100644
--- a/tracker-pt/module/frame.cpp
+++ b/tracker-pt/module/frame.cpp
@@ -1,43 +1,52 @@
#include "frame.hpp"
-
#include "compat/math.hpp"
-
#include <opencv2/imgproc.hpp>
namespace pt_module {
-Preview& Preview::operator=(const pt_frame& frame_)
+void Preview::set_last_frame(const pt_frame& frame_)
{
const cv::Mat& frame = frame_.as_const<const Frame>()->mat;
+ const bool need_resize = frame.size != frame_copy.size;
- if (frame.channels() != 3)
+ if (frame.channels() == 1)
{
- eval_once(qDebug() << "tracker/pt: camera frame depth: 3 !=" << frame.channels());
- return *this;
+ if (need_resize)
+ {
+ frame_tmp.create(frame.size(), CV_8UC3);
+ cv::cvtColor(frame, frame_tmp, cv::COLOR_GRAY2BGR);
+ cv::resize(frame_tmp, frame_copy, frame_copy.size(), 0, 0, cv::INTER_NEAREST);
+ }
+ else
+ cv::cvtColor(frame, frame_copy, cv::COLOR_GRAY2BGR);
+ }
+ else if (frame.channels() == 3)
+ {
+ if (need_resize)
+ cv::resize(frame, frame_copy, frame_copy.size(), 0, 0, cv::INTER_NEAREST);
+ else
+ frame.copyTo(frame_copy);
}
-
- const bool need_resize = frame.cols != frame_out.cols || frame.rows != frame_out.rows;
- if (need_resize)
- cv::resize(frame, frame_copy, cv::Size(frame_out.cols, frame_out.rows), 0, 0, cv::INTER_NEAREST);
else
- frame.copyTo(frame_copy);
-
- return *this;
+ {
+ eval_once(qDebug() << "tracker/pt: camera frame depth" << frame.channels() << "!= 3");
+ frame_copy.create(frame_copy.size(), CV_8UC3);
+ frame_copy.setTo({0});
+ }
}
Preview::Preview(int w, int h)
{
- ensure_size(frame_out, w, h, CV_8UC4);
- ensure_size(frame_copy, w, h, CV_8UC3);
-
- frame_copy.setTo(cv::Scalar(0, 0, 0));
+ frame_out.create(h, w, CV_8UC4);
+ frame_copy.create(h, w, CV_8UC3);
+ frame_copy.setTo({0});
}
QImage Preview::get_bitmap()
{
- int stride = frame_out.step.p[0];
+ int stride = (int)frame_out.step.p[0];
- if (stride < 64 || stride < frame_out.cols * 4)
+ if (stride < frame_out.cols * 4)
{
eval_once(qDebug() << "bad stride" << stride
<< "for bitmap size" << frame_copy.cols << frame_copy.rows);
@@ -71,10 +80,4 @@ void Preview::draw_head_center(f x, f y)
color, 1);
}
-void Preview::ensure_size(cv::Mat& frame, int w, int h, int type)
-{
- if (frame.cols != w || frame.rows != h || frame.type() != type)
- frame = cv::Mat(h, w, type);
-}
-
} // ns pt_module
diff --git a/tracker-pt/module/frame.hpp b/tracker-pt/module/frame.hpp
index 89334599..0569a323 100644
--- a/tracker-pt/module/frame.hpp
+++ b/tracker-pt/module/frame.hpp
@@ -2,7 +2,7 @@
#include "pt-api.hpp"
-#include <opencv2/core.hpp>
+#include <opencv2/core/mat.hpp>
#include <QImage>
#ifdef __clang__
@@ -24,7 +24,7 @@ struct Preview final : pt_preview
{
Preview(int w, int h);
- Preview& operator=(const pt_frame& frame) override;
+ void set_last_frame(const pt_frame& frame) override;
QImage get_bitmap() override;
void draw_head_center(f x, f y) override;
@@ -32,9 +32,7 @@ struct Preview final : pt_preview
operator cv::Mat const&() const { return frame_copy; }
private:
- static void ensure_size(cv::Mat& frame, int w, int h, int type);
-
- cv::Mat frame_copy, frame_out;
+ cv::Mat frame_copy, frame_out, frame_tmp;
};
} // ns pt_module
diff --git a/tracker-pt/module/lang/de_DE.ts b/tracker-pt/module/lang/de_DE.ts
new file mode 100644
index 00000000..6c548aba
--- /dev/null
+++ b/tracker-pt/module/lang/de_DE.ts
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>pt_module::metadata_pt</name>
+ <message>
+ <source>PointTracker 1.1</source>
+ <translation>PointTracker 1.1</translation>
+ </message>
+</context>
+</TS>
diff --git a/tracker-pt/module/lang/zh_CN.ts b/tracker-pt/module/lang/zh_CN.ts
index 03d19f4e..c39728a1 100644
--- a/tracker-pt/module/lang/zh_CN.ts
+++ b/tracker-pt/module/lang/zh_CN.ts
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
<context>
<name>pt_module::metadata_pt</name>
<message>
diff --git a/tracker-pt/module/point_extractor.cpp b/tracker-pt/module/point_extractor.cpp
index a92c87c9..3329fafc 100644
--- a/tracker-pt/module/point_extractor.cpp
+++ b/tracker-pt/module/point_extractor.cpp
@@ -9,9 +9,11 @@
#include "point_extractor.h"
#include "point_tracker.h"
#include "frame.hpp"
-
#include "cv/numeric.hpp"
#include "compat/math.hpp"
+#include "compat/math-imports.hpp"
+
+#include <opencv2/imgproc.hpp>
#undef PREVIEW
//#define PREVIEW
@@ -87,21 +89,16 @@ PointExtractor::PointExtractor(const QString& module_name) : s(module_name)
void PointExtractor::ensure_channel_buffers(const cv::Mat& orig_frame)
{
- if (ch[0].rows != orig_frame.rows || ch[0].cols != orig_frame.cols)
- for (cv::Mat1b& x : ch)
- x = cv::Mat1b(orig_frame.rows, orig_frame.cols);
+ for (cv::Mat1b& x : ch)
+ x.create(orig_frame.rows, orig_frame.cols);
}
void PointExtractor::ensure_buffers(const cv::Mat& frame)
{
const int W = frame.cols, H = frame.rows;
- if (frame_gray.rows != W || frame_gray.cols != H)
- {
- frame_gray = cv::Mat1b(H, W);
- frame_bin = cv::Mat1b(H, W);
- frame_gray_unmasked = cv::Mat1b(H, W);
- }
+ frame_gray.create(H, W);
+ frame_bin.create(H, W);
}
void PointExtractor::extract_single_channel(const cv::Mat& orig_frame, int idx, cv::Mat1b& dest)
@@ -115,15 +112,43 @@ void PointExtractor::extract_single_channel(const cv::Mat& orig_frame, int idx,
cv::mixChannels(&orig_frame, 1, &dest, 1, from_to, 1);
}
-void PointExtractor::filter_single_channel(const cv::Mat& orig_frame, float r, float g, float b, cv::Mat1b& dest)
+void PointExtractor::filter_single_channel(const cv::Mat& orig_frame, float r, float g, float b, bool overexp, cv::Mat1b& dest)
{
ensure_channel_buffers(orig_frame);
- cv::transform(orig_frame, dest, cv::Mat(cv::Matx13f(b, g, r)));
+ // just filter for colour or also include overexposed regions?
+ if (!overexp)
+ cv::transform(orig_frame, dest, cv::Mat(cv::Matx13f(b, g, r)));
+ else
+ {
+ for (int i = 0; i < orig_frame.rows; i++)
+ {
+ cv::Vec3b const* const __restrict orig_ptr = orig_frame.ptr<cv::Vec3b>(i);
+ uint8_t* const __restrict dest_ptr = dest.ptr(i);
+ for (int j = 0; j < orig_frame.cols; j++)
+ {
+ // get the intensity of the key color (i.e. +ve coefficients)
+ uchar blue = orig_ptr[j][0], green = orig_ptr[j][1], red = orig_ptr[j][2];
+ float key = std::max(b, 0.0f) * blue + std::max(g, 0.0f) * green + std::max(r, 0.0f) * red;
+ // get the intensity of the non-key color (i.e. -ve coefficients)
+ float nonkey = std::max(-b, 0.0f) * blue + std::max(-g, 0.0f) * green + std::max(-r, 0.0f) * red;
+ // the result is key color minus non-key color inversely weighted by key colour intensity
+ dest_ptr[j] = std::max(0.0f, std::min(255.0f, key - (255.0f - key) / 255.0f * nonkey));
+ }
+ }
+ }
}
void PointExtractor::color_to_grayscale(const cv::Mat& frame, cv::Mat1b& output)
{
+ if (frame.channels() == 1)
+ {
+ output.create(frame.rows, frame.cols);
+ frame.copyTo(output);
+ return;
+ }
+
+ const float half_chr_key_str = *s.chroma_key_strength * 0.5;
switch (s.blob_color)
{
case pt_color_green_only:
@@ -143,46 +168,42 @@ void PointExtractor::color_to_grayscale(const cv::Mat& frame, cv::Mat1b& output)
}
case pt_color_red_chromakey:
{
- filter_single_channel(frame, 1, -0.5, -0.5, output);
+ filter_single_channel(frame, 1, -half_chr_key_str, -half_chr_key_str, s.chroma_key_overexposed, output);
break;
}
case pt_color_green_chromakey:
{
- filter_single_channel(frame, -0.5, 1, -0.5, output);
+ filter_single_channel(frame, -half_chr_key_str, 1, -half_chr_key_str, s.chroma_key_overexposed, output);
break;
}
case pt_color_blue_chromakey:
{
- filter_single_channel(frame, -0.5, -0.5, 1, output);
+ filter_single_channel(frame, -half_chr_key_str, -half_chr_key_str, 1, s.chroma_key_overexposed, output);
break;
}
case pt_color_cyan_chromakey:
{
- filter_single_channel(frame, -1, 0.5, 0.5, output);
+ filter_single_channel(frame, -*s.chroma_key_strength, 0.5, 0.5, s.chroma_key_overexposed, output);
break;
}
case pt_color_yellow_chromakey:
{
- filter_single_channel(frame, 0.5, 0.5, -1, output);
+ filter_single_channel(frame, 0.5, 0.5, -*s.chroma_key_strength, s.chroma_key_overexposed, output);
break;
}
case pt_color_magenta_chromakey:
{
- filter_single_channel(frame, 0.5, -1, 0.5, output);
- break;
- }
- case pt_color_average:
- {
- const int W = frame.cols, H = frame.rows, sz = W*H;
- cv::reduce(frame.reshape(1, sz),
- output.reshape(1, sz),
- 1, cv::REDUCE_AVG);
+ filter_single_channel(frame, 0.5, -*s.chroma_key_strength, 0.5, s.chroma_key_overexposed, output);
break;
}
+ case pt_color_hardware:
+ eval_once(qDebug() << "camera driver doesn't support grayscale");
+ goto do_grayscale;
default:
eval_once(qDebug() << "wrong pt_color_type enum value" << int(s.blob_color));
[[fallthrough]];
- case pt_color_natural:
+ case pt_color_bt709:
+do_grayscale:
cv::cvtColor(frame, output, cv::COLOR_BGR2GRAY);
break;
}
@@ -214,11 +235,10 @@ void PointExtractor::threshold_image(const cv::Mat& frame_gray, cv::Mat1b& outpu
const f radius = threshold_radius_value(frame_gray.cols, frame_gray.rows, threshold_slider_value);
float const* const __restrict ptr = hist.ptr<float>(0);
- const unsigned area = uround(3 * pi * radius*radius);
+ const unsigned area = unsigned(iround(3 * pi * radius*radius));
const unsigned sz = unsigned(hist.cols * hist.rows);
- constexpr unsigned min_thres = 64;
- unsigned thres = min_thres;
- for (unsigned i = sz-1, cnt = 0; i > 32; i--)
+ unsigned thres = 1;
+ for (unsigned i = sz-1, cnt = 0; i > 1; i--)
{
cnt += (unsigned)ptr[i];
if (cnt >= area)
@@ -246,20 +266,15 @@ static void draw_blobs(cv::Mat& preview_frame, const blob* blobs, unsigned nblob
cy = preview_frame.rows / f(size.height),
c = std::fmax(f(1), cx+cy)/2;
- constexpr unsigned fract_bits = 8;
- constexpr int c_fract(1 << fract_bits);
-
- cv::Point p(iround(b.pos[0] * cx * c_fract), iround(b.pos[1] * cy * c_fract));
-
- auto circle_color = k >= PointModel::N_POINTS
- ? cv::Scalar(192, 192, 192)
- : cv::Scalar(255, 255, 0);
+ cv::Point p(iround(b.pos[0] * cx), iround(b.pos[1] * cy));
- const int overlay_size = iround(dpi);
+ auto outline_color = k >= PointModel::N_POINTS
+ ? cv::Scalar(192, 192, 192)
+ : cv::Scalar(255, 255, 0);
- cv::circle(preview_frame, p, iround((b.radius + f(3.3) * c) * c_fract),
- circle_color, overlay_size,
- cv::LINE_AA, fract_bits);
+ cv::ellipse(preview_frame, p,
+ {iround(b.rect.width/(f)2+2*c), iround(b.rect.height/(f)2+2*c)},
+ 0, 0, 360, outline_color, iround(dpi), cv::LINE_AA);
char buf[16];
std::snprintf(buf, sizeof(buf), "%.2fpx", (double)b.radius);
@@ -270,25 +285,51 @@ static void draw_blobs(cv::Mat& preview_frame, const blob* blobs, unsigned nblob
cv::Point pos(iround(b.pos[0]*cx+offx), iround(b.pos[1]*cy+offy));
cv::putText(preview_frame, buf, pos,
- cv::FONT_HERSHEY_PLAIN, overlay_size, text_color,
+ cv::FONT_HERSHEY_PLAIN, iround(dpi), text_color,
1);
}
}
-void PointExtractor::extract_points(const pt_frame& frame_, pt_preview& preview_frame_, std::vector<vec2>& points)
+static vec2 meanshift_initial_guess(const cv::Rect rect, cv::Mat& frame_roi)
+{
+ vec2 ret = {rect.width/(f)2, rect.height/(f)2};
+
+ // compute center initial guess
+ double ynorm = 0, xnorm = 0, y = 0, x = 0;
+ for (int j = 0; j < rect.height; j++)
+ {
+ const unsigned char* __restrict ptr = frame_roi.ptr<unsigned char>(j);
+ for (int i = 0; i < rect.width; i++)
+ {
+ double val = ptr[i] * 1./255;
+ x += i * val;
+ y += j * val;
+ xnorm += val;
+ ynorm += val;
+ }
+ }
+ constexpr double eps = 1e-4;
+ if (xnorm > eps && ynorm > eps)
+ ret = { (f)(x / xnorm), (f)(y / ynorm) };
+ return ret;
+}
+
+void PointExtractor::extract_points(const pt_frame& frame_,
+ pt_preview& preview_frame_,
+ bool preview_visible,
+ std::vector<vec2>& points)
{
const cv::Mat& frame = frame_.as_const<Frame>()->mat;
ensure_buffers(frame);
- color_to_grayscale(frame, frame_gray_unmasked);
+ color_to_grayscale(frame, frame_gray);
#if defined PREVIEW
cv::imshow("capture", frame_gray);
cv::waitKey(1);
#endif
- threshold_image(frame_gray_unmasked, frame_bin);
- frame_gray_unmasked.copyTo(frame_gray, frame_bin);
+ threshold_image(frame_gray, frame_bin);
const f region_size_min = (f)s.min_point_size;
const f region_size_max = (f)s.max_point_size;
@@ -336,7 +377,7 @@ void PointExtractor::extract_points(const pt_frame& frame_, pt_preview& preview_
}
}
- const f radius = std::sqrt(cnt / pi);
+ const f radius = std::sqrt((f)cnt) / std::sqrt(pi);
if (radius > region_size_max || radius < region_size_min)
continue;
@@ -366,21 +407,14 @@ end:
for (idx = 0; idx < sz; ++idx)
{
blob& b = blobs[idx];
- cv::Rect rect = b.rect;
-
- rect.x -= rect.width / 2;
- rect.y -= rect.height / 2;
- rect.width *= 2;
- rect.height *= 2;
- rect &= cv::Rect(0, 0, W, H); // crop at frame boundaries
-
+ cv::Rect rect = b.rect & cv::Rect(0, 0, W, H); // crop at frame boundaries
cv::Mat frame_roi = frame_gray(rect);
// smaller values mean more changes. 1 makes too many changes while 1.5 makes about .1
static constexpr f radius_c = f(1.75);
const f kernel_radius = b.radius * radius_c;
- vec2 pos(rect.width/f(2), rect.height/f(2)); // position relative to ROI.
+ vec2 pos = meanshift_initial_guess(rect, frame_roi); // position relative to ROI.
for (int iter = 0; iter < 10; ++iter)
{
@@ -395,9 +429,10 @@ end:
b.pos[1] = pos[1] + rect.y;
}
- draw_blobs(preview_frame_.as<Frame>()->mat,
- blobs.data(), blobs.size(),
- frame_gray.size());
+ if (preview_visible)
+ draw_blobs(preview_frame_.as<Frame>()->mat,
+ blobs.data(), blobs.size(),
+ frame_gray.size());
// End of mean shift code. At this point, blob positions are updated with hopefully less noisy less biased values.
diff --git a/tracker-pt/module/point_extractor.h b/tracker-pt/module/point_extractor.h
index 9c97b6ce..fbfdbb0b 100644
--- a/tracker-pt/module/point_extractor.h
+++ b/tracker-pt/module/point_extractor.h
@@ -9,12 +9,10 @@
#pragma once
#include "pt-api.hpp"
-
+#include <opencv2/core/mat.hpp>
+#include <opencv2/core/types.hpp>
#include <vector>
-#include <opencv2/core.hpp>
-#include <opencv2/imgproc.hpp>
-
namespace pt_module {
using namespace numeric_types;
@@ -33,14 +31,18 @@ class PointExtractor final : public pt_point_extractor
public:
// extracts points from frame and draws some processing info into frame, if draw_output is set
// dt: time since last call in seconds
- void extract_points(const pt_frame& frame, pt_preview& preview_frame, std::vector<vec2>& points) override;
- PointExtractor(const QString& module_name);
+ void extract_points(const pt_frame& frame,
+ pt_preview& preview_frame, bool preview_visible,
+ std::vector<vec2>& points) override;
+
+ explicit PointExtractor(const QString& module_name);
+
private:
static constexpr int max_blobs = 16;
pt_settings s;
- cv::Mat1b frame_gray_unmasked, frame_bin, frame_gray;
+ cv::Mat1b frame_bin, frame_gray;
cv::Mat1f hist;
std::vector<blob> blobs;
cv::Mat1b ch[3];
@@ -49,7 +51,7 @@ private:
void ensure_buffers(const cv::Mat& frame);
void extract_single_channel(const cv::Mat& orig_frame, int idx, cv::Mat1b& dest);
- void filter_single_channel(const cv::Mat& orig_frame, float r, float g, float b, cv::Mat1b& dest);
+ void filter_single_channel(const cv::Mat& orig_frame, float r, float g, float b, bool overexp, cv::Mat1b& dest);
void color_to_grayscale(const cv::Mat& frame, cv::Mat1b& output);
void threshold_image(const cv::Mat& frame_gray, cv::Mat1b& output);
diff --git a/tracker-pt/point-filter.cpp b/tracker-pt/point-filter.cpp
new file mode 100644
index 00000000..10448fe2
--- /dev/null
+++ b/tracker-pt/point-filter.cpp
@@ -0,0 +1,72 @@
+#include "point-filter.hpp"
+#include <algorithm>
+#include <cmath>
+#include <QDebug>
+
+namespace pt_point_filter_impl {
+
+void point_filter::reset()
+{
+ t = std::nullopt;
+}
+
+const PointOrder& point_filter::operator()(const PointOrder& input, f deadzone_amount)
+{
+ using std::fmod;
+ using std::sqrt;
+ using std::pow;
+ using std::clamp;
+
+ if (!s.enable_point_filter)
+ {
+ t = std::nullopt;
+ state_ = input;
+ return state_;
+ }
+
+ if (!t)
+ {
+ t.emplace();
+ state_ = input;
+ return state_;
+ }
+
+ constexpr auto E = (f)1.75;
+ const f limit = (f)*s.point_filter_limit;
+ const f C = progn(
+ constexpr int A = 1'000'000;
+ double K = *s.point_filter_coefficient;
+ f log10_pos = -2 + (int)K, rest = (f)(.999-fmod(K, 1.)*.9);
+ return A * pow((f)10, (f)-log10_pos) * rest;
+ );
+
+ f dist = 0, dz = deadzone_amount * (f)s.point_filter_deadzone / 800; // sqrt(640^2 + 480^2)
+
+ for (unsigned i = 0; i < 3; i++)
+ {
+ vec2 tmp = input[i] - state_[i];
+ f x = sqrt(tmp.dot(tmp));
+ x = std::max((f)0, x - dz);
+ dist = std::max(dist, x);
+ }
+
+ if (dist < (f)1e-6)
+ return state_;
+
+ f dt = (f)t->elapsed_seconds(); t->start();
+ f delta = pow(dist, E) * C * dt; // gain
+
+ //qDebug() << "gain" << std::min((f)1, delta);
+
+ for (unsigned i = 0; i < 3; i++)
+ {
+ f x = clamp(delta, (f)0, limit);
+ state_[i] += x*(input[i] - state_[i]);
+ }
+
+ return state_;
+}
+
+point_filter::point_filter(const pt_settings& s) : s{s} {}
+
+} // ns pt_point_filter_impl
diff --git a/tracker-pt/point-filter.hpp b/tracker-pt/point-filter.hpp
new file mode 100644
index 00000000..c3c045dd
--- /dev/null
+++ b/tracker-pt/point-filter.hpp
@@ -0,0 +1,32 @@
+#pragma once
+
+#include "cv/numeric.hpp"
+#include "compat/timer.hpp"
+#include "pt-settings.hpp"
+#include <optional>
+#include <array>
+
+namespace pt_point_filter_impl {
+
+using namespace numeric_types;
+using PointOrder = std::array<numeric_types::vec2, 3>;
+
+class point_filter final
+{
+ PointOrder state_;
+ std::optional<Timer> t;
+ const pt_settings& s;
+
+public:
+ void reset();
+ const PointOrder& operator()(const PointOrder& input, f deadzone_amount);
+
+ explicit point_filter(const pt_settings& s);
+ ~point_filter() = default;
+
+ OTR_DISABLE_MOVE_COPY(point_filter);
+};
+
+} // ns pt_point_filter_impl
+
+using point_filter = pt_point_filter_impl::point_filter;
diff --git a/tracker-pt/point_tracker.cpp b/tracker-pt/point_tracker.cpp
index e209938f..39e96038 100644
--- a/tracker-pt/point_tracker.cpp
+++ b/tracker-pt/point_tracker.cpp
@@ -68,7 +68,7 @@ void PointModel::set_model(const pt_settings& s)
}
}
-void PointModel::get_d_order(const vec2* points, unsigned* d_order, const vec2& d) const
+void PointModel::get_d_order(const vec2* points, unsigned* d_order, const vec2& d)
{
constexpr unsigned cnt = PointModel::N_POINTS;
// fit line to orthographically projected points
@@ -76,11 +76,10 @@ void PointModel::get_d_order(const vec2* points, unsigned* d_order, const vec2&
t d_vals[cnt];
// get sort indices with respect to d scalar product
for (unsigned i = 0; i < cnt; ++i)
- d_vals[i] = t(d.dot(points[i]), i);
+ d_vals[i] = {d.dot(points[i]), i};
- std::sort(d_vals,
- d_vals + 3,
- [](const t& a, const t& b) { return a.first < b.first; });
+ std::sort(std::begin(d_vals), std::end(d_vals),
+ [](t a, t b) { return a.first < b.first; });
for (unsigned i = 0; i < cnt; ++i)
d_order[i] = d_vals[i].second;
@@ -94,10 +93,11 @@ PointTracker::PointOrder PointTracker::find_correspondences_previous(const vec2*
const pt_camera_info& info)
{
const f fx = pt_camera_info::get_focal_length(info.fov, info.res_x, info.res_y);
- PointTracker::PointOrder p;
- p[0] = project(vec3(0,0,0), fx);
- p[1] = project(model.M01, fx);
- p[2] = project(model.M02, fx);
+ PointTracker::PointOrder p {
+ project(vec3(0,0,0), fx),
+ project(model.M01, fx),
+ project(model.M02, fx)
+ };
constexpr unsigned sz = PointModel::N_POINTS;
@@ -136,7 +136,9 @@ PointTracker::PointOrder PointTracker::find_correspondences_previous(const vec2*
void PointTracker::track(const std::vector<vec2>& points,
const PointModel& model,
const pt_camera_info& info,
- int init_phase_timeout)
+ int init_phase_timeout,
+ point_filter& filter,
+ f deadzone_amount)
{
const f fx = pt_camera_info::get_focal_length(info.fov, info.res_x, info.res_y);
PointOrder order;
@@ -149,7 +151,7 @@ void PointTracker::track(const std::vector<vec2>& points,
else
order = find_correspondences_previous(points.data(), model, info);
- if (POSIT(model, order, fx) != -1)
+ if (POSIT(model, filter(order, deadzone_amount), fx) != -1)
{
init_phase = false;
t.start();
diff --git a/tracker-pt/point_tracker.h b/tracker-pt/point_tracker.h
index 70c7a9fc..7492b4eb 100644
--- a/tracker-pt/point_tracker.h
+++ b/tracker-pt/point_tracker.h
@@ -11,14 +11,11 @@
#include "cv/affine.hpp"
#include "cv/numeric.hpp"
#include "pt-api.hpp"
+#include "point-filter.hpp"
-#include <cstddef>
-#include <memory>
#include <vector>
#include <array>
-#include <opencv2/core.hpp>
-
#include <QObject>
namespace pt_impl {
@@ -46,7 +43,7 @@ struct PointModel final
explicit PointModel(const pt_settings& s);
void set_model(const pt_settings& s);
- void get_d_order(const vec2* points, unsigned* d_order, const vec2& d) const;
+ static void get_d_order(const vec2* points, unsigned* d_order, const vec2& d);
};
// ----------------------------------------------------------------------------
@@ -60,7 +57,12 @@ public:
// track the pose using the set of normalized point coordinates (x pos in range -0.5:0.5)
// f : (focal length)/(sensor width)
// dt : time since last call
- void track(const std::vector<vec2>& projected_points, const PointModel& model, const pt_camera_info& info, int init_phase_timeout);
+ void track(const std::vector<vec2>& projected_points,
+ const PointModel& model,
+ const pt_camera_info& info,
+ int init_phase_timeout,
+ point_filter& filter,
+ f deadzone_amount);
Affine pose() const { return X_CM; }
vec2 project(const vec3& v_M, f focal_length);
vec2 project(const vec3& v_M, f focal_length, const Affine& X_CM);
@@ -70,14 +72,13 @@ private:
// the points in model order
using PointOrder = std::array<vec2, 3>;
- PointOrder find_correspondences(const vec2* projected_points, const PointModel &model);
+ static PointOrder find_correspondences(const vec2* projected_points, const PointModel &model);
PointOrder find_correspondences_previous(const vec2* points, const PointModel &model, const pt_camera_info& info);
// The POSIT algorithm, returns the number of iterations
int POSIT(const PointModel& point_model, const PointOrder& order, f focal_length);
Affine X_CM; // transform from model to camera
Affine X_CM_expected;
- PointOrder prev_positions;
Timer t;
bool init_phase = true;
};
diff --git a/tracker-pt/pt-api.cpp b/tracker-pt/pt-api.cpp
index f64d5c9a..d71c6e13 100644
--- a/tracker-pt/pt-api.cpp
+++ b/tracker-pt/pt-api.cpp
@@ -31,7 +31,7 @@ f pt_point_extractor::threshold_radius_value(int w, int h, int threshold)
f cx = w / f{640}, cy = h / f{480};
const f min_radius = f{1.75} * cx;
- const f max_radius = f{15} * cy;
+ const f max_radius = f{30} * cy;
const f radius = std::fmax(f{0}, (max_radius-min_radius) * threshold / f(255) + min_radius);
@@ -40,15 +40,14 @@ f pt_point_extractor::threshold_radius_value(int w, int h, int threshold)
std::tuple<f, f> pt_pixel_pos_mixin::to_pixel_pos(f x, f y, int w, int h)
{
- return std::make_tuple(w*(x+f{.5}), f{.5}*(h - 2*y*w));
+ return { w*(x+f{.5}), f{.5}*(h - 2*y*w) };
}
std::tuple<f, f> pt_pixel_pos_mixin::to_screen_pos(f px, f py, int w, int h)
{
px *= w/(w-f{1}); py *= h/(h-f{1});
- return std::make_tuple((px - w/f{2})/w, -(py - h/f{2})/w);
+ return { (px - w/f{2})/w, -(py - h/f{2})/w };
}
pt_frame::pt_frame() = default;
-
pt_frame::~pt_frame() = default;
diff --git a/tracker-pt/pt-api.hpp b/tracker-pt/pt-api.hpp
index 741576a1..15021ff3 100644
--- a/tracker-pt/pt-api.hpp
+++ b/tracker-pt/pt-api.hpp
@@ -6,11 +6,9 @@
#include "options/options.hpp"
#include <tuple>
-#include <type_traits>
+#include <vector>
#include <memory>
-#include <opencv2/core.hpp>
-
#include <QImage>
#include <QString>
@@ -32,6 +30,7 @@ struct pt_camera_info final
int res_x = 0;
int res_y = 0;
QString name;
+ bool use_mjpeg = false;
};
struct pt_pixel_pos_mixin
@@ -58,11 +57,21 @@ struct pt_frame : pt_pixel_pos_mixin
{
return static_cast<t const*>(this);
}
+
+protected:
+ pt_frame(const pt_frame&) = default;
+ pt_frame(pt_frame&&) = default;
+ pt_frame& operator=(const pt_frame&) = default;
+ pt_frame& operator=(pt_frame&&) = default;
};
struct pt_preview : pt_frame
{
- virtual pt_preview& operator=(const pt_frame&) = 0;
+ pt_preview() = default;
+
+ OTR_DISABLE_MOVE_COPY(pt_preview);
+
+ virtual void set_last_frame(const pt_frame&) = 0;
virtual QImage get_bitmap() = 0;
virtual void draw_head_center(f x, f y) = 0;
};
@@ -75,7 +84,9 @@ struct pt_camera
pt_camera();
virtual ~pt_camera();
- [[nodiscard]] virtual bool start(const QString& name, int fps, int res_x, int res_y) = 0;
+ OTR_DISABLE_MOVE_COPY(pt_camera);
+
+ [[nodiscard]] virtual bool start(const pt_settings& s) = 0;
virtual void stop() = 0;
virtual result get_frame(pt_frame& frame) = 0;
@@ -87,6 +98,7 @@ struct pt_camera
virtual void set_fov(f value) = 0;
virtual void show_camera_settings() = 0;
+ virtual f deadzone_amount() const { return 1; }
};
struct pt_point_extractor : pt_pixel_pos_mixin
@@ -94,9 +106,11 @@ struct pt_point_extractor : pt_pixel_pos_mixin
using vec2 = numeric_types::vec2;
using f = numeric_types::f;
+ OTR_DISABLE_MOVE_COPY(pt_point_extractor);
+
pt_point_extractor();
virtual ~pt_point_extractor();
- virtual void extract_points(const pt_frame& image, pt_preview& preview_frame, std::vector<vec2>& points) = 0;
+ virtual void extract_points(const pt_frame& image, pt_preview& preview_frame, bool preview_visible, std::vector<vec2>& points) = 0;
static f threshold_radius_value(int w, int h, int threshold);
};
@@ -105,6 +119,8 @@ struct pt_runtime_traits
{
template<typename t> using pointer = std::shared_ptr<t>;
+ OTR_DISABLE_MOVE_COPY(pt_runtime_traits);
+
pt_runtime_traits();
virtual ~pt_runtime_traits();
diff --git a/tracker-pt/pt-settings.hpp b/tracker-pt/pt-settings.hpp
index ed13a1ec..5d16d973 100644
--- a/tracker-pt/pt-settings.hpp
+++ b/tracker-pt/pt-settings.hpp
@@ -8,9 +8,9 @@ enum pt_color_type
{
// explicit values, gotta preserve the numbering in .ini
// don't reuse when removing some of the modes
- pt_color_natural = 2,
+ pt_color_bt709 = 2,
+ pt_color_hardware = 14,
pt_color_red_only = 3,
- pt_color_average = 5,
pt_color_blue_only = 6,
pt_color_green_only = 7,
pt_color_red_chromakey = 8,
@@ -63,10 +63,18 @@ struct pt_settings final : options::opts
value<bool> dynamic_pose { b, "dynamic-pose-resolution", false };
value<int> init_phase_timeout { b, "init-phase-timeout", 250 };
value<bool> auto_threshold { b, "automatic-threshold", true };
- value<pt_color_type> blob_color { b, "blob-color", pt_color_natural };
+ value<pt_color_type> blob_color { b, "blob-color", pt_color_bt709 };
+ value<bool> use_mjpeg { b, "use-mjpeg", false };
+ value<slider_value> chroma_key_strength{ b, "chroma-key-strength", { 1.0, 0.5, 4. } };
+ value<bool> chroma_key_overexposed{ b, "chroma-key-overexposed", false };
value<slider_value> threshold_slider { b, "threshold-slider", { 128, 0, 255 } };
+ value<bool> enable_point_filter{ b, "enable-point-filter", false };
+ value<slider_value> point_filter_coefficient { b, "point-filter-coefficient", { 1.0, 0, 4 } };
+ value<slider_value> point_filter_limit { b, "point-filter-limit", { 0.1, 0.01, 1 }};
+ value<slider_value> point_filter_deadzone { b, "point-filter-deadzone", {0, 0, 1} };
+
explicit pt_settings(const QString& name) : opts(name) {}
};
diff --git a/tracker-rift-140/lang/de_DE.ts b/tracker-rift-140/lang/de_DE.ts
new file mode 100644
index 00000000..31fb1354
--- /dev/null
+++ b/tracker-rift-140/lang/de_DE.ts
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>dialog_rift_140</name>
+ <message>
+ <source>Oculus Rift tracker settings FaceTrackNoIR</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>rift_140Dll</name>
+ <message>
+ <source>Oculus Rift runtime 1.4.0 -- HMD</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/tracker-rift-140/lang/zh_CN.ts b/tracker-rift-140/lang/zh_CN.ts
index 26ab2040..ff552d19 100644
--- a/tracker-rift-140/lang/zh_CN.ts
+++ b/tracker-rift-140/lang/zh_CN.ts
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
<context>
<name>dialog_rift_140</name>
<message>
diff --git a/tracker-rs/ftnoir_tracker_rs.cpp b/tracker-rs/ftnoir_tracker_rs.cpp
index 0f458725..f48f58d1 100644
--- a/tracker-rs/ftnoir_tracker_rs.cpp
+++ b/tracker-rs/ftnoir_tracker_rs.cpp
@@ -84,27 +84,16 @@ void RSTracker::handleTrackingEnded(int exitCode){
showRealSenseErrorMessageBox(exitCode);
}
-bool RSTracker::startSdkInstallationProcess()
-{
- bool pStarted = QDesktopServices::openUrl({"https://software.intel.com/en-us/realsense-sdk-windows-eol"});
- if(!pStarted){
- QMessageBox::warning(nullptr,
- tr("Intel® RealSense™ Runtime Installation"),
- tr("Installation process failed to start."),
- QMessageBox::Ok);
- }
- return pStarted;
-}
-
void RSTracker::showRealSenseErrorMessageBox(int exitCode)
{
QMessageBox msgBox;
msgBox.setIcon(QMessageBox::Critical);
msgBox.setText("RealSense Tracking Error");
- switch(exitCode){
+ switch(exitCode)
+ {
case -101: //The implementation got an invalid handle from the RealSense SDK session/modules
- msgBox.setInformativeText(tr("Couldn't initialize RealSense tracking. Please make sure SDK Runtime 2016 R3 is installed."));
+ msgBox.setInformativeText(tr("Couldn't initialize RealSense tracking. Open the tracker settings dialog for links to the camera driver and the SDK."));
break;
case -301: //RealSense SDK runtime execution aborted.
msgBox.setInformativeText(tr("Tracking stopped after the RealSense SDK Runtime execution has aborted."));
@@ -113,15 +102,11 @@ void RSTracker::showRealSenseErrorMessageBox(int exitCode)
msgBox.setInformativeText(tr("Tracking stopped after another program changed camera streams configuration."));
break;
default:
- msgBox.setInformativeText("Status code: " + QString::number(exitCode) + ".\n\nNote that you need the latest camera drivers and the SDK runtime 2016 R3 to be installed.");
+ msgBox.setInformativeText("Status code: " + QString::number(exitCode) + ".\n\nNote that you need the latest camera drivers and the SDK runtime 2016 R2 to be installed.");
}
- QPushButton* triggerSdkInstallation = msgBox.addButton(tr("Install SDK 2016 R3 Runtime"), QMessageBox::ActionRole);
msgBox.addButton(QMessageBox::Ok);
msgBox.exec();
-
- if(msgBox.clickedButton() == triggerSdkInstallation)
- startSdkInstallationProcess();
}
void RSTracker::data(double *data)
diff --git a/tracker-rs/ftnoir_tracker_rs.h b/tracker-rs/ftnoir_tracker_rs.h
index f322d0f6..8c540ff8 100644
--- a/tracker-rs/ftnoir_tracker_rs.h
+++ b/tracker-rs/ftnoir_tracker_rs.h
@@ -24,9 +24,6 @@ public:
module_status start_tracker(QFrame *) override;
void data(double *data) override;
-public slots:
- static bool startSdkInstallationProcess();
-
protected:
void configurePreviewFrame();
diff --git a/tracker-rs/ftnoir_tracker_rs_controls.cpp b/tracker-rs/ftnoir_tracker_rs_controls.cpp
index a5018957..230fd9a4 100644
--- a/tracker-rs/ftnoir_tracker_rs_controls.cpp
+++ b/tracker-rs/ftnoir_tracker_rs_controls.cpp
@@ -14,11 +14,9 @@ RSdialog_realsense::RSdialog_realsense()
connect(ui.buttonBox, &QDialogButtonBox::rejected, this, &QDialog::close);
}
-void RSdialog_realsense::doInstallRSRuntime()
+void RSdialog_realsense::set_buttons_visible(bool x)
{
- bool pStarted = RSTracker::startSdkInstallationProcess();
- if(pStarted)
- close();
+ ui.buttonBox->setVisible(x);
}
void RSdialog_realsense::doOK()
diff --git a/tracker-rs/ftnoir_tracker_rs_controls.h b/tracker-rs/ftnoir_tracker_rs_controls.h
index eeb6ac29..b613518d 100644
--- a/tracker-rs/ftnoir_tracker_rs_controls.h
+++ b/tracker-rs/ftnoir_tracker_rs_controls.h
@@ -16,10 +16,11 @@ public:
RSdialog_realsense();
void register_tracker(ITracker *) {}
void unregister_tracker() {}
+ bool embeddable() noexcept override { return true; }
+ void set_buttons_visible(bool x) override;
private:
Ui::UIRSControls ui;
private slots:
void doOK();
void doCancel();
- void doInstallRSRuntime();
};
diff --git a/tracker-rs/ftnoir_tracker_rs_controls.ui b/tracker-rs/ftnoir_tracker_rs_controls.ui
index af25f74d..7dce262f 100644
--- a/tracker-rs/ftnoir_tracker_rs_controls.ui
+++ b/tracker-rs/ftnoir_tracker_rs_controls.ui
@@ -6,10 +6,16 @@
<rect>
<x>0</x>
<y>0</y>
- <width>339</width>
- <height>155</height>
+ <width>596</width>
+ <height>182</height>
</rect>
</property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
<property name="minimumSize">
<size>
<width>339</width>
@@ -29,8 +35,32 @@
<layout class="QVBoxLayout" name="verticalLayout">
<item alignment="Qt::AlignTop">
<widget class="QLabel" name="label">
+ <property name="minimumSize">
+ <size>
+ <width>578</width>
+ <height>0</height>
+ </size>
+ </property>
<property name="text">
- <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;In order to use this tracker, you need a PC equipped with an Intel® RealSense™ F200 or SR300 camera and the RealSense™ &lt;a href=&quot;https://software.intel.com/en-us/realsense-sdk-windows-eol&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;SDK 2016 R3 runtime&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Scroll down to the bottom of the page, choosing &lt;span style=&quot; font-weight:600;&quot;&gt;2016 R3 Full SDK&lt;/span&gt; on the right-hand side. Unfortunately it's necessary to create an account prior to downloading.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;
+In order to use this tracker, you need:
+
+&lt;ul&gt;
+&lt;li&gt;
+ A PC equipped with an Intel® RealSense™ F200 or SR300 camera
+&lt;/li&gt;
+&lt;li&gt;
+ Depth Camera Manager &lt;a href=&quot;https://www.intel.com/content/www/us/en/download/18309/intel-realsense-depth-camera-manager.html&quot;&gt;&lt;span&gt;driver&lt;/span&gt;&lt;/a&gt;
+ &lt;blockquote&gt;
+ To avoid problems with the DCM installer updating the camera's firmware it is recomended to run the DCM installer from the command line with the following flags:&lt;br/&gt;
+ &lt;span style=&quot; font-weight: bold; font-family:monospace, 'Courier New';&quot;&gt;--ignore-fw-update --silent --no-progress --acceptlicense=yes&lt;/span&gt;
+ &lt;/blockquote&gt;
+&lt;/li&gt;
+&lt;li&gt;
+ RealSense™ &lt;a href=&quot;http://registrationcenter-download.intel.com/akdlm/irc_nas/vcp/9078/intel_rs_sdk_offline_package_10.0.26.0396.exe&quot;&gt;&lt;span&gt;SDK 2016 R2 runtime&lt;/span&gt;&lt;/a&gt;
+&lt;/li&gt;
+&lt;/ul&gt;
+&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
@@ -42,6 +72,12 @@
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
<property name="standardButtons">
<set>QDialogButtonBox::Ok</set>
</property>
diff --git a/tracker-rs/lang/de_DE.ts b/tracker-rs/lang/de_DE.ts
new file mode 100644
index 00000000..46e247da
--- /dev/null
+++ b/tracker-rs/lang/de_DE.ts
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>RSTracker</name>
+ <message>
+ <source>Couldn&apos;t initialize RealSense tracking. Open the tracker settings dialog for links to the camera driver and the SDK.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Tracking stopped after the RealSense SDK Runtime execution has aborted.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Tracking stopped after another program changed camera streams configuration.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>RSTrackerMetaData</name>
+ <message>
+ <source>Intel® RealSense™ Technology</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>UIRSControls</name>
+ <message>
+ <source>Intel RealSense</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;
+In order to use this tracker, you need:
+
+&lt;ul&gt;
+&lt;li&gt;
+ A PC equipped with an Intel® RealSense™ F200 or SR300 camera
+&lt;/li&gt;
+&lt;li&gt;
+ Depth Camera Manager &lt;a href=&quot;https://www.intel.com/content/www/us/en/download/18309/intel-realsense-depth-camera-manager.html&quot;&gt;&lt;span&gt;driver&lt;/span&gt;&lt;/a&gt;
+ &lt;blockquote&gt;
+ To avoid problems with the DCM installer updating the camera&apos;s firmware it is recomended to run the DCM installer from the command line with the following flags:&lt;br/&gt;
+ &lt;span style=&quot; font-weight: bold; font-family:monospace, &apos;Courier New&apos;;&quot;&gt;--ignore-fw-update --silent --no-progress --acceptlicense=yes&lt;/span&gt;
+ &lt;/blockquote&gt;
+&lt;/li&gt;
+&lt;li&gt;
+ RealSense™ &lt;a href=&quot;http://registrationcenter-download.intel.com/akdlm/irc_nas/vcp/9078/intel_rs_sdk_offline_package_10.0.26.0396.exe&quot;&gt;&lt;span&gt;SDK 2016 R2 runtime&lt;/span&gt;&lt;/a&gt;
+&lt;/li&gt;
+&lt;/ul&gt;
+&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/tracker-rs/lang/nl_NL.ts b/tracker-rs/lang/nl_NL.ts
index a4c28187..f129ea58 100644
--- a/tracker-rs/lang/nl_NL.ts
+++ b/tracker-rs/lang/nl_NL.ts
@@ -4,15 +4,7 @@
<context>
<name>RSTracker</name>
<message>
- <source>Intel® RealSense™ Runtime Installation</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>Installation process failed to start.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>Couldn&apos;t initialize RealSense tracking. Please make sure SDK Runtime 2016 R3 is installed.</source>
+ <source>Couldn&apos;t initialize RealSense tracking. Open the tracker settings dialog for links to the camera driver and the SDK.</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -23,10 +15,6 @@
<source>Tracking stopped after another program changed camera streams configuration.</source>
<translation type="unfinished"></translation>
</message>
- <message>
- <source>Install SDK 2016 R3 Runtime</source>
- <translation type="unfinished"></translation>
- </message>
</context>
<context>
<name>RSTrackerMetaData</name>
@@ -42,7 +30,25 @@
<translation type="unfinished"></translation>
</message>
<message>
- <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;In order to use this tracker, you need a PC equipped with an Intel® RealSense™ F200 or SR300 camera and the RealSense™ &lt;a href=&quot;https://software.intel.com/en-us/realsense-sdk-windows-eol&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;SDK 2016 R3 runtime&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Scroll down to the bottom of the page, choosing &lt;span style=&quot; font-weight:600;&quot;&gt;2016 R3 Full SDK&lt;/span&gt; on the right-hand side. Unfortunately it&apos;s necessary to create an account prior to downloading.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;
+In order to use this tracker, you need:
+
+&lt;ul&gt;
+&lt;li&gt;
+ A PC equipped with an Intel® RealSense™ F200 or SR300 camera
+&lt;/li&gt;
+&lt;li&gt;
+ Depth Camera Manager &lt;a href=&quot;https://www.intel.com/content/www/us/en/download/18309/intel-realsense-depth-camera-manager.html&quot;&gt;&lt;span&gt;driver&lt;/span&gt;&lt;/a&gt;
+ &lt;blockquote&gt;
+ To avoid problems with the DCM installer updating the camera&apos;s firmware it is recomended to run the DCM installer from the command line with the following flags:&lt;br/&gt;
+ &lt;span style=&quot; font-weight: bold; font-family:monospace, &apos;Courier New&apos;;&quot;&gt;--ignore-fw-update --silent --no-progress --acceptlicense=yes&lt;/span&gt;
+ &lt;/blockquote&gt;
+&lt;/li&gt;
+&lt;li&gt;
+ RealSense™ &lt;a href=&quot;http://registrationcenter-download.intel.com/akdlm/irc_nas/vcp/9078/intel_rs_sdk_offline_package_10.0.26.0396.exe&quot;&gt;&lt;span&gt;SDK 2016 R2 runtime&lt;/span&gt;&lt;/a&gt;
+&lt;/li&gt;
+&lt;/ul&gt;
+&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
</context>
diff --git a/tracker-rs/lang/ru_RU.ts b/tracker-rs/lang/ru_RU.ts
index 592c72a3..4836eb8f 100644
--- a/tracker-rs/lang/ru_RU.ts
+++ b/tracker-rs/lang/ru_RU.ts
@@ -4,15 +4,7 @@
<context>
<name>RSTracker</name>
<message>
- <source>Intel® RealSense™ Runtime Installation</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>Installation process failed to start.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>Couldn&apos;t initialize RealSense tracking. Please make sure SDK Runtime 2016 R3 is installed.</source>
+ <source>Couldn&apos;t initialize RealSense tracking. Open the tracker settings dialog for links to the camera driver and the SDK.</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -23,10 +15,6 @@
<source>Tracking stopped after another program changed camera streams configuration.</source>
<translation type="unfinished"></translation>
</message>
- <message>
- <source>Install SDK 2016 R3 Runtime</source>
- <translation type="unfinished"></translation>
- </message>
</context>
<context>
<name>RSTrackerMetaData</name>
@@ -42,7 +30,25 @@
<translation type="unfinished"></translation>
</message>
<message>
- <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;In order to use this tracker, you need a PC equipped with an Intel® RealSense™ F200 or SR300 camera and the RealSense™ &lt;a href=&quot;https://software.intel.com/en-us/realsense-sdk-windows-eol&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;SDK 2016 R3 runtime&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Scroll down to the bottom of the page, choosing &lt;span style=&quot; font-weight:600;&quot;&gt;2016 R3 Full SDK&lt;/span&gt; on the right-hand side. Unfortunately it&apos;s necessary to create an account prior to downloading.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;
+In order to use this tracker, you need:
+
+&lt;ul&gt;
+&lt;li&gt;
+ A PC equipped with an Intel® RealSense™ F200 or SR300 camera
+&lt;/li&gt;
+&lt;li&gt;
+ Depth Camera Manager &lt;a href=&quot;https://www.intel.com/content/www/us/en/download/18309/intel-realsense-depth-camera-manager.html&quot;&gt;&lt;span&gt;driver&lt;/span&gt;&lt;/a&gt;
+ &lt;blockquote&gt;
+ To avoid problems with the DCM installer updating the camera&apos;s firmware it is recomended to run the DCM installer from the command line with the following flags:&lt;br/&gt;
+ &lt;span style=&quot; font-weight: bold; font-family:monospace, &apos;Courier New&apos;;&quot;&gt;--ignore-fw-update --silent --no-progress --acceptlicense=yes&lt;/span&gt;
+ &lt;/blockquote&gt;
+&lt;/li&gt;
+&lt;li&gt;
+ RealSense™ &lt;a href=&quot;http://registrationcenter-download.intel.com/akdlm/irc_nas/vcp/9078/intel_rs_sdk_offline_package_10.0.26.0396.exe&quot;&gt;&lt;span&gt;SDK 2016 R2 runtime&lt;/span&gt;&lt;/a&gt;
+&lt;/li&gt;
+&lt;/ul&gt;
+&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
</context>
diff --git a/tracker-rs/lang/stub.ts b/tracker-rs/lang/stub.ts
index fe0faa6a..8ac8297f 100644
--- a/tracker-rs/lang/stub.ts
+++ b/tracker-rs/lang/stub.ts
@@ -4,15 +4,7 @@
<context>
<name>RSTracker</name>
<message>
- <source>Intel® RealSense™ Runtime Installation</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>Installation process failed to start.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>Couldn&apos;t initialize RealSense tracking. Please make sure SDK Runtime 2016 R3 is installed.</source>
+ <source>Couldn&apos;t initialize RealSense tracking. Open the tracker settings dialog for links to the camera driver and the SDK.</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -23,10 +15,6 @@
<source>Tracking stopped after another program changed camera streams configuration.</source>
<translation type="unfinished"></translation>
</message>
- <message>
- <source>Install SDK 2016 R3 Runtime</source>
- <translation type="unfinished"></translation>
- </message>
</context>
<context>
<name>RSTrackerMetaData</name>
@@ -42,7 +30,25 @@
<translation type="unfinished"></translation>
</message>
<message>
- <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;In order to use this tracker, you need a PC equipped with an Intel® RealSense™ F200 or SR300 camera and the RealSense™ &lt;a href=&quot;https://software.intel.com/en-us/realsense-sdk-windows-eol&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;SDK 2016 R3 runtime&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Scroll down to the bottom of the page, choosing &lt;span style=&quot; font-weight:600;&quot;&gt;2016 R3 Full SDK&lt;/span&gt; on the right-hand side. Unfortunately it&apos;s necessary to create an account prior to downloading.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;
+In order to use this tracker, you need:
+
+&lt;ul&gt;
+&lt;li&gt;
+ A PC equipped with an Intel® RealSense™ F200 or SR300 camera
+&lt;/li&gt;
+&lt;li&gt;
+ Depth Camera Manager &lt;a href=&quot;https://www.intel.com/content/www/us/en/download/18309/intel-realsense-depth-camera-manager.html&quot;&gt;&lt;span&gt;driver&lt;/span&gt;&lt;/a&gt;
+ &lt;blockquote&gt;
+ To avoid problems with the DCM installer updating the camera&apos;s firmware it is recomended to run the DCM installer from the command line with the following flags:&lt;br/&gt;
+ &lt;span style=&quot; font-weight: bold; font-family:monospace, &apos;Courier New&apos;;&quot;&gt;--ignore-fw-update --silent --no-progress --acceptlicense=yes&lt;/span&gt;
+ &lt;/blockquote&gt;
+&lt;/li&gt;
+&lt;li&gt;
+ RealSense™ &lt;a href=&quot;http://registrationcenter-download.intel.com/akdlm/irc_nas/vcp/9078/intel_rs_sdk_offline_package_10.0.26.0396.exe&quot;&gt;&lt;span&gt;SDK 2016 R2 runtime&lt;/span&gt;&lt;/a&gt;
+&lt;/li&gt;
+&lt;/ul&gt;
+&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
</context>
diff --git a/tracker-rs/lang/zh_CN.ts b/tracker-rs/lang/zh_CN.ts
index fe0faa6a..dc9b34b5 100644
--- a/tracker-rs/lang/zh_CN.ts
+++ b/tracker-rs/lang/zh_CN.ts
@@ -1,18 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
<context>
<name>RSTracker</name>
<message>
- <source>Intel® RealSense™ Runtime Installation</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>Installation process failed to start.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>Couldn&apos;t initialize RealSense tracking. Please make sure SDK Runtime 2016 R3 is installed.</source>
+ <source>Couldn&apos;t initialize RealSense tracking. Open the tracker settings dialog for links to the camera driver and the SDK.</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -23,10 +15,6 @@
<source>Tracking stopped after another program changed camera streams configuration.</source>
<translation type="unfinished"></translation>
</message>
- <message>
- <source>Install SDK 2016 R3 Runtime</source>
- <translation type="unfinished"></translation>
- </message>
</context>
<context>
<name>RSTrackerMetaData</name>
@@ -42,7 +30,25 @@
<translation type="unfinished"></translation>
</message>
<message>
- <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;In order to use this tracker, you need a PC equipped with an Intel® RealSense™ F200 or SR300 camera and the RealSense™ &lt;a href=&quot;https://software.intel.com/en-us/realsense-sdk-windows-eol&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;SDK 2016 R3 runtime&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Scroll down to the bottom of the page, choosing &lt;span style=&quot; font-weight:600;&quot;&gt;2016 R3 Full SDK&lt;/span&gt; on the right-hand side. Unfortunately it&apos;s necessary to create an account prior to downloading.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;
+In order to use this tracker, you need:
+
+&lt;ul&gt;
+&lt;li&gt;
+ A PC equipped with an Intel® RealSense™ F200 or SR300 camera
+&lt;/li&gt;
+&lt;li&gt;
+ Depth Camera Manager &lt;a href=&quot;https://www.intel.com/content/www/us/en/download/18309/intel-realsense-depth-camera-manager.html&quot;&gt;&lt;span&gt;driver&lt;/span&gt;&lt;/a&gt;
+ &lt;blockquote&gt;
+ To avoid problems with the DCM installer updating the camera&apos;s firmware it is recomended to run the DCM installer from the command line with the following flags:&lt;br/&gt;
+ &lt;span style=&quot; font-weight: bold; font-family:monospace, &apos;Courier New&apos;;&quot;&gt;--ignore-fw-update --silent --no-progress --acceptlicense=yes&lt;/span&gt;
+ &lt;/blockquote&gt;
+&lt;/li&gt;
+&lt;li&gt;
+ RealSense™ &lt;a href=&quot;http://registrationcenter-download.intel.com/akdlm/irc_nas/vcp/9078/intel_rs_sdk_offline_package_10.0.26.0396.exe&quot;&gt;&lt;span&gt;SDK 2016 R2 runtime&lt;/span&gt;&lt;/a&gt;
+&lt;/li&gt;
+&lt;/ul&gt;
+&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
</context>
diff --git a/tracker-rs/rs_impl/ftnoir_tracker_rs_impl.cpp b/tracker-rs/rs_impl/ftnoir_tracker_rs_impl.cpp
index eb97380c..f54531a6 100644
--- a/tracker-rs/rs_impl/ftnoir_tracker_rs_impl.cpp
+++ b/tracker-rs/rs_impl/ftnoir_tracker_rs_impl.cpp
@@ -9,18 +9,21 @@
#ifdef _WIN32
# include <windows.h>
#endif
+#include <cstdlib>
#include <pxcsensemanager.h>
+#if 0
#include <RealSense/Face/FaceModule.h>
#include <RealSense/Face/FaceConfiguration.h>
-
-#include <cstdlib>
-
-const size_t kPreviewStreamWidth = 640;
-const size_t kPreviewStreamHeight = 480;
-
using PXCFaceData = Intel::RealSense::Face::FaceData;
using PXCFaceConfiguration = Intel::RealSense::Face::FaceConfiguration;
+#else
+#include <pxcfacemodule.h>
+#include <pxcfaceconfiguration.h>
+#endif
+
+constexpr size_t kPreviewStreamWidth = 640;
+constexpr size_t kPreviewStreamHeight = 480;
PXCSenseManager* g_senseManager = NULL;
PXCFaceData* g_faceData = NULL;
diff --git a/tracker-s2bot/ftnoir_tracker_s2bot.cpp b/tracker-s2bot/ftnoir_tracker_s2bot.cpp
index c9d684aa..53762c66 100644
--- a/tracker-s2bot/ftnoir_tracker_s2bot.cpp
+++ b/tracker-s2bot/ftnoir_tracker_s2bot.cpp
@@ -38,7 +38,10 @@ void tracker_s2bot::run() {
timer.setInterval((int)(1000./freq));
timer.setSingleShot(false);
connect(&timer, &QTimer::timeout, [this] {
- auto reply = m_nam->get(QNetworkRequest(QUrl("http://localhost:17317/poll")));
+ QNetworkRequest req{QUrl("http://localhost:17317/poll")};
+ req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
+ auto* reply = m_nam->get(req);
+
connect(reply, &QNetworkReply::finished, [this, reply] {
if (reply->error() == QNetworkReply::NoError) {
//qDebug() << "Request submitted OK";
@@ -48,15 +51,15 @@ void tracker_s2bot::run() {
return;
}
- const QStringList slist = QString::fromLatin1(reply->readAll()).split(QRegExp("[\r\n]+"), QString::SkipEmptyParts);
+ const QStringList slist = QString::fromLatin1(reply->readAll()).split(QRegularExpression("[\r\n]+"), Qt::SkipEmptyParts);
reply->close();
reply->deleteLater();
int order[] =
{
- clamp(s.idx_x, 0, 3),
- clamp(s.idx_y, 0, 3),
- clamp(s.idx_z, 0, 3),
+ std::clamp(*s.idx_x, 0, 3),
+ std::clamp(*s.idx_y, 0, 3),
+ std::clamp(*s.idx_z, 0, 3),
};
int add_indices[] = { s.add_yaw, s.add_pitch, s.add_roll, };
diff --git a/tracker-s2bot/lang/de_DE.ts b/tracker-s2bot/lang/de_DE.ts
new file mode 100644
index 00000000..4071d117
--- /dev/null
+++ b/tracker-s2bot/lang/de_DE.ts
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>UI_s2bot_dialog</name>
+ <message>
+ <source>Tracker settings</source>
+ <translation>Tracker-Einstellungen</translation>
+ </message>
+ <message>
+ <source>Update frequency (Hz)</source>
+ <translation>Aktualisierungsfrequenz (Hz)</translation>
+ </message>
+ <message>
+ <source>Axis order</source>
+ <translation>Achsen-Reihenfolge</translation>
+ </message>
+ <message>
+ <source>output yaw</source>
+ <translation>Gieren-Ausgabe</translation>
+ </message>
+ <message>
+ <source>input yaw</source>
+ <translation>Gieren-Eingabe</translation>
+ </message>
+ <message>
+ <source>input pitch</source>
+ <translation>Nicken-Eingabe</translation>
+ </message>
+ <message>
+ <source>input roll</source>
+ <translation>Rollen-Eingabe</translation>
+ </message>
+ <message>
+ <source>input bearing</source>
+ <translation>Peilung-Eingabe</translation>
+ </message>
+ <message>
+ <source>output pitch</source>
+ <translation>Nicken-Ausgabe</translation>
+ </message>
+ <message>
+ <source>output roll</source>
+ <translation>Rollen-Ausgabe</translation>
+ </message>
+ <message>
+ <source>Add to axis</source>
+ <translation>Zur Achse hinzufügen</translation>
+ </message>
+ <message>
+ <source>yaw</source>
+ <translation>Gieren</translation>
+ </message>
+ <message>
+ <source>0</source>
+ <translation>0</translation>
+ </message>
+ <message>
+ <source>+90</source>
+ <translation>+90</translation>
+ </message>
+ <message>
+ <source>-90</source>
+ <translation>-90</translation>
+ </message>
+ <message>
+ <source>+180</source>
+ <translation>+180</translation>
+ </message>
+ <message>
+ <source>-180</source>
+ <translation>-180</translation>
+ </message>
+ <message>
+ <source>pitch</source>
+ <translation>Nicken</translation>
+ </message>
+ <message>
+ <source>roll</source>
+ <translation>Rollen</translation>
+ </message>
+</context>
+<context>
+ <name>meta_s2bot</name>
+ <message>
+ <source>S2Bot receiver</source>
+ <translation>S2Bot-Empfänger</translation>
+ </message>
+</context>
+</TS>
diff --git a/tracker-s2bot/lang/zh_CN.ts b/tracker-s2bot/lang/zh_CN.ts
index babca884..f9cc2a4b 100644
--- a/tracker-s2bot/lang/zh_CN.ts
+++ b/tracker-s2bot/lang/zh_CN.ts
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
<context>
<name>UI_s2bot_dialog</name>
<message>
@@ -9,27 +9,27 @@
</message>
<message>
<source>Update frequency (Hz)</source>
- <translation type="unfinished"></translation>
+ <translation>刷新率(Hz)</translation>
</message>
<message>
<source>Axis order</source>
- <translation type="unfinished"></translation>
+ <translation>输出重映射</translation>
</message>
<message>
<source>output yaw</source>
- <translation type="unfinished"></translation>
+ <translation>输出的航向</translation>
</message>
<message>
<source>input yaw</source>
- <translation type="unfinished"></translation>
+ <translation type="unfinished">输入航向</translation>
</message>
<message>
<source>input pitch</source>
- <translation type="unfinished"></translation>
+ <translation type="unfinished">输入俯仰</translation>
</message>
<message>
<source>input roll</source>
- <translation type="unfinished"></translation>
+ <translation type="unfinished">输入滚转</translation>
</message>
<message>
<source>input bearing</source>
@@ -37,47 +37,47 @@
</message>
<message>
<source>output pitch</source>
- <translation type="unfinished"></translation>
+ <translation>输出的俯仰</translation>
</message>
<message>
<source>output roll</source>
- <translation type="unfinished"></translation>
+ <translation>输出的滚转</translation>
</message>
<message>
<source>Add to axis</source>
- <translation type="unfinished"></translation>
+ <translation>添加到轴</translation>
</message>
<message>
<source>yaw</source>
- <translation type="unfinished"></translation>
+ <translation>航向</translation>
</message>
<message>
<source>0</source>
- <translation type="unfinished"></translation>
+ <translation></translation>
</message>
<message>
<source>+90</source>
- <translation type="unfinished"></translation>
+ <translation></translation>
</message>
<message>
<source>-90</source>
- <translation type="unfinished"></translation>
+ <translation></translation>
</message>
<message>
<source>+180</source>
- <translation type="unfinished"></translation>
+ <translation></translation>
</message>
<message>
<source>-180</source>
- <translation type="unfinished"></translation>
+ <translation></translation>
</message>
<message>
<source>pitch</source>
- <translation type="unfinished"></translation>
+ <translation>俯仰</translation>
</message>
<message>
<source>roll</source>
- <translation type="unfinished"></translation>
+ <translation>滚转</translation>
</message>
</context>
<context>
diff --git a/tracker-steamvr/lang/de_DE.ts b/tracker-steamvr/lang/de_DE.ts
new file mode 100644
index 00000000..1282ec9a
--- /dev/null
+++ b/tracker-steamvr/lang/de_DE.ts
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>dialog</name>
+ <message>
+ <source>Valve SteamVR</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Device</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>steamvr</name>
+ <message>
+ <source>No HMD connected</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Can&apos;t find device with that serial</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>steamvr_metadata</name>
+ <message>
+ <source>Valve SteamVR</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/tracker-steamvr/lang/zh_CN.ts b/tracker-steamvr/lang/zh_CN.ts
index 7b68034b..2c6f7230 100644
--- a/tracker-steamvr/lang/zh_CN.ts
+++ b/tracker-steamvr/lang/zh_CN.ts
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
<context>
<name>dialog</name>
<message>
diff --git a/tracker-steamvr/steamvr.cpp b/tracker-steamvr/steamvr.cpp
index 3bcfcc25..bd0c9c1e 100644
--- a/tracker-steamvr/steamvr.cpp
+++ b/tracker-steamvr/steamvr.cpp
@@ -26,7 +26,7 @@
#include <QMessageBox>
#include <QDebug>
-QMutex device_list::mtx(QMutex::Recursive);
+QRecursiveMutex device_list::mtx;
template<typename F>
auto with_vr_lock(F&& fun) -> decltype(fun(vr_t(), vr_error_t()))
@@ -89,12 +89,17 @@ void device_list::fill_device_specs(QList<device_spec>& list)
switch (v->GetTrackedDeviceClass(k))
{
- case vr::ETrackedDeviceClass::TrackedDeviceClass_HMD:
+ using enum vr::ETrackedDeviceClass;
+ case TrackedDeviceClass_HMD:
dev.type = "HMD"; break;
- case vr::ETrackedDeviceClass::TrackedDeviceClass_Controller:
+ case TrackedDeviceClass_Controller:
dev.type = "Controller"; break;
- case vr::ETrackedDeviceClass::TrackedDeviceClass_TrackingReference:
- dev.type = "Tracker"; break;
+ case TrackedDeviceClass_TrackingReference:
+ dev.type = "Tracking reference"; break;
+ case TrackedDeviceClass_DisplayRedirect:
+ dev.type = "Display redirect"; break;
+ case TrackedDeviceClass_GenericTracker:
+ dev.type = "Generic"; break;
default:
dev.type = "Unknown"; break;
}
@@ -216,9 +221,17 @@ module_status steamvr::start_tracker(QFrame*)
err = tr("Can't find device with that serial");
if (err.isEmpty())
- return status_ok();
- else
- return error(err);
+ {
+ if (auto* c = vr::VRCompositor(); c != nullptr)
+ {
+ c->SetTrackingSpace(origin::TrackingUniverseSeated);
+ return status_ok();
+ }
+ else
+ return error("vr::VRCompositor == NULL");
+ }
+
+ return error(err);
});
}
@@ -253,12 +266,19 @@ bool steamvr::center()
{
if (v->GetTrackedDeviceClass(device_index) == vr::ETrackedDeviceClass::TrackedDeviceClass_HMD)
{
- // Reset yaw and position
- v->ResetSeatedZeroPose();
-
- // Use chaperone universe real world up instead of opentrack's initial pose centering
- // Note: Controllers will be centered based on initial headset position.
- return true;
+ auto* c = vr::VRChaperone();
+ if (!c)
+ {
+ eval_once(qDebug() << "steamvr: vr::VRChaperone == NULL");
+ return false;
+ }
+ else
+ {
+ c->ResetZeroPose(origin::TrackingUniverseSeated);
+ // Use chaperone universe real world up instead of opentrack's initial pose centering
+ // Note: Controllers will be centered based on initial headset position.
+ return true;
+ }
}
else
// with controllers, resetting the seated pose does nothing
diff --git a/tracker-steamvr/steamvr.hpp b/tracker-steamvr/steamvr.hpp
index 61da2e05..cc70ffc5 100644
--- a/tracker-steamvr/steamvr.hpp
+++ b/tracker-steamvr/steamvr.hpp
@@ -8,7 +8,7 @@
#include <climits>
#include <QString>
-#include <QMutex>
+#include <QRecursiveMutex>
#include <QList>
#include <openvr.h>
@@ -57,7 +57,7 @@ struct device_list final
private:
QList<device_spec> device_specs;
- static QMutex mtx;
+ static QRecursiveMutex mtx;
static tt vr_init_();
static void fill_device_specs(QList<device_spec>& list);
static tt vr_init();
diff --git a/tracker-test/lang/de_DE.ts b/tracker-test/lang/de_DE.ts
new file mode 100644
index 00000000..e11372b6
--- /dev/null
+++ b/tracker-test/lang/de_DE.ts
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>test_metadata</name>
+ <message>
+ <source>Test tracker</source>
+ <translation>Test-Tracker</translation>
+ </message>
+</context>
+<context>
+ <name>test_ui</name>
+ <message>
+ <source>Sine wave</source>
+ <translation>Sinus-Welle</translation>
+ </message>
+ <message>
+ <source>Pressing &quot;Abort&quot; will immediately crash the application.</source>
+ <translation>Beim Klick auf „Abbrechen“ wird die Anwendung sofort abstürzen.</translation>
+ </message>
+</context>
+</TS>
diff --git a/tracker-test/lang/zh_CN.ts b/tracker-test/lang/zh_CN.ts
index 78f647f5..5df28f2f 100644
--- a/tracker-test/lang/zh_CN.ts
+++ b/tracker-test/lang/zh_CN.ts
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
<context>
<name>test_metadata</name>
<message>
diff --git a/tracker-test/test.h b/tracker-test/test.h
index 7f06968e..320145ad 100644
--- a/tracker-test/test.h
+++ b/tracker-test/test.h
@@ -2,7 +2,6 @@
#include "ui_test.h"
#include "api/plugin-api.hpp"
#include "compat/timer.hpp"
-#include "compat/macros.hpp"
#include <cmath>
diff --git a/tracker-tobii/CMakeLists.txt b/tracker-tobii/CMakeLists.txt
index 8551c8fa..8273d43e 100644
--- a/tracker-tobii/CMakeLists.txt
+++ b/tracker-tobii/CMakeLists.txt
@@ -1,17 +1,22 @@
-# https://developer.tobii.com/download-packages/stream-engine-4-1-0-for-windows-x86
-# https://developer.tobii.com/download-packages/stream-engine-4-1-0-for-windows-x64
-set(SDK_TOBII "" CACHE PATH "Tobii SDK path")
-if(SDK_TOBII)
+if(WIN32)
+ # https://www.nuget.org/packages/Tobii.StreamEngine.Native/
+ set(SDK_TOBII "" CACHE PATH "Tobii Stream Engine path")
+endif()
+if(WIN32 AND SDK_TOBII)
otr_module(tracker-tobii)
- target_include_directories(${self} SYSTEM PRIVATE "${SDK_TOBII}/include")
- set(dll "${SDK_TOBII}/lib/tobii/tobii_stream_engine.dll")
- set(lib "${SDK_TOBII}/lib/tobii/tobii_stream_engine.lib")
+ if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4")
+ set(arch "x86")
+ else()
+ set(arch "x64")
+ endif()
+
+ target_include_directories(${self} SYSTEM PRIVATE "${SDK_TOBII}/include")
+ target_link_directories(${self} PRIVATE "${SDK_TOBII}/lib/${arch}")
- message(${self})
- message(${dll})
- message(${lib})
+ set(dll "${SDK_TOBII}/lib/${arch}/tobii_stream_engine.dll")
+ set(lib tobii_stream_engine.lib)
- install(FILES ${dll} DESTINATION ${opentrack-hier-pfx})
target_link_libraries(${self} ${lib})
+ install(FILES ${dll} DESTINATION ${opentrack-libexec})
endif()
diff --git a/tracker-tobii/ftnoir_tracker_tobii.cpp b/tracker-tobii/ftnoir_tracker_tobii.cpp
deleted file mode 100644
index 43cb1662..00000000
--- a/tracker-tobii/ftnoir_tracker_tobii.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-#include "ftnoir_tracker_tobii.h"
-#include "api/plugin-api.hpp"
-#include "compat/math.hpp"
-
-tobii::~tobii() = default;
-
-module_status tobii::start_tracker(QFrame*)
-{
- t.start();
- return status_ok();
-}
-
-void tobii::data(double *data)
-{
- if (t.head_pose) {
- tobii_head_pose_t p = *t.head_pose;
- if (p.position_validity == TOBII_VALIDITY_VALID) {
- if (center_pose.position_validity == TOBII_VALIDITY_VALID) {
- p.position_xyz[0] = p.position_xyz[0] - center_pose.position_xyz[0];
- p.position_xyz[1] = p.position_xyz[1] - center_pose.position_xyz[1];
- p.position_xyz[2] = p.position_xyz[2] - center_pose.position_xyz[2];
- }
- else {
- center_pose = p;
- }
- data[0] = clamp(p.position_xyz[0] * 30.0 / 300.0, -30.0, 30.0);
- data[1] = clamp(p.position_xyz[1] * 30.0 / 300.0, -30.0, 30.0);
- data[2] = clamp(p.position_xyz[2] * 30.0 / 300.0, -30.0, 30.0);
- }
-
- double max_yaw = 90.0;
- if (p.rotation_validity_xyz[1] == TOBII_VALIDITY_VALID) {
- if (center_pose.rotation_validity_xyz[1] == TOBII_VALIDITY_VALID) {
- p.rotation_xyz[1] = p.rotation_xyz[1] - center_pose.rotation_xyz[1];
- }
- data[3] = clamp(p.rotation_xyz[1] * 100.0 * max_yaw / 90.0, -max_yaw, max_yaw);
- }
-
- double max_pitch = 90.0;
- if (p.rotation_validity_xyz[0] == TOBII_VALIDITY_VALID) {
- if (center_pose.rotation_validity_xyz[0] == TOBII_VALIDITY_VALID) {
- p.rotation_xyz[0] = p.rotation_xyz[0] - center_pose.rotation_xyz[0];
- }
- data[4] = clamp(p.rotation_xyz[0] * 100.0 * max_pitch / 90.0, -max_pitch, max_pitch);
- }
-
- double max_roll = 90.0;
- if (p.rotation_validity_xyz[2] == TOBII_VALIDITY_VALID) {
- if (center_pose.rotation_validity_xyz[2] == TOBII_VALIDITY_VALID) {
- p.rotation_xyz[2] = p.rotation_xyz[2] - center_pose.rotation_xyz[2];
- }
- data[5] = clamp(p.rotation_xyz[2] * 100.0 * max_roll / 90.0, -max_roll, max_roll);
- }
- }
-}
-
-bool tobii::center ()
-{
- if (t.head_pose) {
- center_pose = *t.head_pose;
- }
- return false;
-}
-
-OPENTRACK_DECLARE_TRACKER(tobii, dialog_tobii, tobiiDll)
diff --git a/tracker-tobii/ftnoir_tracker_tobii.h b/tracker-tobii/ftnoir_tracker_tobii.h
deleted file mode 100644
index bd7a04a2..00000000
--- a/tracker-tobii/ftnoir_tracker_tobii.h
+++ /dev/null
@@ -1,50 +0,0 @@
-#pragma once
-#include "ui_ftnoir_tracker_tobii_controls.h"
-#include <QComboBox>
-#include <QCheckBox>
-#include <QSpinBox>
-#include <QMessageBox>
-#include <QSettings>
-#include <QList>
-#include <QFrame>
-#include <QStringList>
-#include <cmath>
-#include "api/plugin-api.hpp"
-#include "options/options.hpp"
-
-#include "thread.hpp"
-
-class tobii : public ITracker
-{
-public:
- ~tobii();
- module_status start_tracker(QFrame*) override;
- void data(double* data) override;
- virtual bool center() override;
-private:
- tobii_thread t;
- tobii_head_pose_t center_pose;
-};
-
-class dialog_tobii: public ITrackerDialog
-{
- Q_OBJECT
-public:
- dialog_tobii();
- ~dialog_tobii() = default;
- void register_tracker(ITracker *) {}
- void unregister_tracker() {}
- Ui::UITobiiControls ui;
- tobii* tracker;
-private slots:
- void doOK();
- void doCancel();
-};
-
-class tobiiDll : public Metadata
-{
- Q_OBJECT
-
- QString name() { return tr("Tobii input"); }
- QIcon icon() { return QIcon(":/images/opentrack.png"); }
-};
diff --git a/tracker-tobii/ftnoir_tracker_tobii_controls.ui b/tracker-tobii/ftnoir_tracker_tobii_controls.ui
deleted file mode 100644
index 6cd00121..00000000
--- a/tracker-tobii/ftnoir_tracker_tobii_controls.ui
+++ /dev/null
@@ -1,56 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>UITobiiControls</class>
- <widget class="QWidget" name="UITobiiControls">
- <property name="windowModality">
- <enum>Qt::NonModal</enum>
- </property>
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>498</width>
- <height>303</height>
- </rect>
- </property>
- <property name="windowTitle">
- <string>Tobii settings</string>
- </property>
- <property name="windowIcon">
- <iconset>
- <normaloff>../gui/images/opentrack.png</normaloff>../gui/images/opentrack.png
- </iconset>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout">
- <property name="leftMargin">
- <number>12</number>
- </property>
- <property name="topMargin">
- <number>6</number>
- </property>
- <property name="rightMargin">
- <number>12</number>
- </property>
- <property name="bottomMargin">
- <number>6</number>
- </property>
- <item>
- <widget class="QDialogButtonBox" name="buttonBox">
- <property name="standardButtons">
- <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- <tabstops>
- <tabstop>buttonBox</tabstop>
- </tabstops>
- <resources/>
- <connections/>
- <slots>
- <slot>startEngineClicked()</slot>
- <slot>stopEngineClicked()</slot>
- <slot>cameraSettingsClicked()</slot>
- </slots>
-</ui>
diff --git a/tracker-tobii/ftnoir_tracker_tobii_dialog.cpp b/tracker-tobii/ftnoir_tracker_tobii_dialog.cpp
deleted file mode 100644
index 5cd4bad0..00000000
--- a/tracker-tobii/ftnoir_tracker_tobii_dialog.cpp
+++ /dev/null
@@ -1,19 +0,0 @@
-#include "ftnoir_tracker_tobii.h"
-#include "api/plugin-api.hpp"
-
-dialog_tobii::dialog_tobii() : tracker(nullptr)
-{
- ui.setupUi( this );
-
- // Connect Qt signals to member-functions
- connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK()));
- connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel()));
-}
-
-void dialog_tobii::doOK() {
- close();
-}
-
-void dialog_tobii::doCancel() {
- close();
-}
diff --git a/tracker-tobii/lang/de_DE.ts b/tracker-tobii/lang/de_DE.ts
new file mode 100644
index 00000000..27b430a1
--- /dev/null
+++ b/tracker-tobii/lang/de_DE.ts
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>tobii_metadata</name>
+ <message>
+ <source>Tobii Eye Tracker</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>tobii_ui</name>
+ <message>
+ <source>Tobii Eye Tracker</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Please make sure the Tobii Experience application is running and tracking is active.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/tracker-tobii/lang/nl_NL.ts b/tracker-tobii/lang/nl_NL.ts
index 45e4974f..538a31f1 100644
--- a/tracker-tobii/lang/nl_NL.ts
+++ b/tracker-tobii/lang/nl_NL.ts
@@ -2,16 +2,20 @@
<!DOCTYPE TS>
<TS version="2.1" language="nl_NL">
<context>
- <name>UITobiiControls</name>
+ <name>tobii_metadata</name>
<message>
- <source>Tobii settings</source>
+ <source>Tobii Eye Tracker</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
- <name>tobiiDll</name>
+ <name>tobii_ui</name>
<message>
- <source>Tobii input</source>
+ <source>Tobii Eye Tracker</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Please make sure the Tobii Experience application is running and tracking is active.</source>
<translation type="unfinished"></translation>
</message>
</context>
diff --git a/tracker-tobii/lang/ru_RU.ts b/tracker-tobii/lang/ru_RU.ts
index ee73b375..b86ba010 100644
--- a/tracker-tobii/lang/ru_RU.ts
+++ b/tracker-tobii/lang/ru_RU.ts
@@ -2,16 +2,20 @@
<!DOCTYPE TS>
<TS version="2.1" language="ru_RU">
<context>
- <name>UITobiiControls</name>
+ <name>tobii_metadata</name>
<message>
- <source>Tobii settings</source>
+ <source>Tobii Eye Tracker</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
- <name>tobiiDll</name>
+ <name>tobii_ui</name>
<message>
- <source>Tobii input</source>
+ <source>Tobii Eye Tracker</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Please make sure the Tobii Experience application is running and tracking is active.</source>
<translation type="unfinished"></translation>
</message>
</context>
diff --git a/tracker-tobii/lang/stub.ts b/tracker-tobii/lang/stub.ts
index cd8a2bdc..566900a3 100644
--- a/tracker-tobii/lang/stub.ts
+++ b/tracker-tobii/lang/stub.ts
@@ -2,16 +2,20 @@
<!DOCTYPE TS>
<TS version="2.1">
<context>
- <name>UITobiiControls</name>
+ <name>tobii_metadata</name>
<message>
- <source>Tobii settings</source>
+ <source>Tobii Eye Tracker</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
- <name>tobiiDll</name>
+ <name>tobii_ui</name>
<message>
- <source>Tobii input</source>
+ <source>Tobii Eye Tracker</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Please make sure the Tobii Experience application is running and tracking is active.</source>
<translation type="unfinished"></translation>
</message>
</context>
diff --git a/tracker-tobii/lang/zh_CN.ts b/tracker-tobii/lang/zh_CN.ts
index cd8a2bdc..908770e3 100644
--- a/tracker-tobii/lang/zh_CN.ts
+++ b/tracker-tobii/lang/zh_CN.ts
@@ -1,18 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
<context>
- <name>UITobiiControls</name>
+ <name>tobii_metadata</name>
<message>
- <source>Tobii settings</source>
+ <source>Tobii Eye Tracker</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
- <name>tobiiDll</name>
+ <name>tobii_ui</name>
<message>
- <source>Tobii input</source>
+ <source>Tobii Eye Tracker</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Please make sure the Tobii Experience application is running and tracking is active.</source>
+ <translation type="unfinished">请确保 Tobii 应用程序正在运行,跟踪功能处于激活状态. </translation>
+ </message>
</context>
</TS>
diff --git a/tracker-tobii/thread.cpp b/tracker-tobii/thread.cpp
deleted file mode 100644
index bdd48125..00000000
--- a/tracker-tobii/thread.cpp
+++ /dev/null
@@ -1,109 +0,0 @@
-#include "thread.hpp"
-#include "compat/sleep.hpp"
-
-tobii_thread::~tobii_thread()
-{
- exit_thread = true;
- wait();
-
- if (device) tobii_device_destroy(device);
- if (api) tobii_api_destroy(api);
-
- quit();
-}
-
-void tobii_thread::run()
-{
- /* See https://developer.tobii.com/consumer-eye-trackers/stream-engine/ */
- if (tobii_api_create(&api, nullptr, nullptr) != TOBII_ERROR_NO_ERROR)
- {
- error_last = "Failed to initialize the Tobii Stream Engine API.";
- exit_thread = true;
- }
-
- std::vector<std::string> devices;
- if (tobii_enumerate_local_device_urls(api,
- [](char const* url, void* user_data)
- {
- auto list = (std::vector<std::string>*) user_data;
- list->push_back(url);
- }, &devices) != TOBII_ERROR_NO_ERROR)
- {
- error_last = "Failed to enumerate devices.";
- exit_thread = true;
- }
-
- if (devices.size() == 0)
- {
- tobii_api_destroy(api);
- error_last = "No stream engine compatible device(s) found.";
- exit_thread = true;
- }
- std::string selected_device = devices[0];
-
- unsigned int retry = 0;
- tobii_error_t tobii_error = TOBII_ERROR_NO_ERROR;
- do
- {
- tobii_error = tobii_device_create(api, selected_device.c_str(), TOBII_FIELD_OF_USE_INTERACTIVE, &device);
- if ((tobii_error != TOBII_ERROR_CONNECTION_FAILED) && (tobii_error != TOBII_ERROR_FIRMWARE_UPGRADE_IN_PROGRESS)) break;
- portable::sleep(interval);
- ++retry;
- } while (retry < retries);
- if (tobii_error != TOBII_ERROR_NO_ERROR) {
- tobii_api_destroy(api);
- error_last = "Failed to connect to device.";
- exit_thread = true;
- }
-
- if (tobii_head_pose_subscribe(device, [](tobii_head_pose_t const* head_pose, void* user_data) {
-
- if ((*head_pose).position_validity != TOBII_VALIDITY_VALID
- || (*head_pose).rotation_validity_xyz[0] != TOBII_VALIDITY_VALID
- || (*head_pose).rotation_validity_xyz[1] != TOBII_VALIDITY_VALID
- || (*head_pose).rotation_validity_xyz[2] != TOBII_VALIDITY_VALID) return;
-
- tobii_head_pose_t* tobii_head_pose_storage = (tobii_head_pose_t*)user_data;
- *tobii_head_pose_storage = *head_pose;
-
- }, head_pose) != TOBII_ERROR_NO_ERROR)
- {
- error_last = "Failed to subscribe to head pose stream.";
- exit_thread = true;
- }
-
- tobii_error = TOBII_ERROR_NO_ERROR;
- while (!exit_thread)
- {
- tobii_error = tobii_wait_for_callbacks(1, &device);
- if (tobii_error == TOBII_ERROR_TIMED_OUT) continue;
- else if (tobii_error != TOBII_ERROR_NO_ERROR)
- {
- error_last = "tobii_wait_for_callbacks failed.";
- }
-
- tobii_error = tobii_device_process_callbacks(device);
- if (tobii_error == TOBII_ERROR_CONNECTION_FAILED)
- {
- unsigned int retry = 0;
- auto tobii_error = TOBII_ERROR_NO_ERROR;
- do
- {
- tobii_error = tobii_device_reconnect(device);
- if ((tobii_error != TOBII_ERROR_CONNECTION_FAILED) && (tobii_error != TOBII_ERROR_FIRMWARE_UPGRADE_IN_PROGRESS)) break;
- portable::sleep(interval);
- ++retry;
- } while (retry < retries);
- if (tobii_error != TOBII_ERROR_NO_ERROR)
- {
- error_last = "Connection was lost and reconnection failed.";
- exit_thread = true;
- }
- continue;
- }
- else if (tobii_error != TOBII_ERROR_NO_ERROR)
- {
- error_last = "tobii_device_process_callbacks failed.";
- }
- }
-}
diff --git a/tracker-tobii/thread.hpp b/tracker-tobii/thread.hpp
deleted file mode 100644
index d311db87..00000000
--- a/tracker-tobii/thread.hpp
+++ /dev/null
@@ -1,35 +0,0 @@
-#pragma once
-
-#include <QThread>
-#include <QCoreApplication>
-
-#include <tobii/tobii.h>
-#include <tobii/tobii_streams.h>
-#include <atomic>
-#include <vector>
-#include <string>
-
-class tobii_thread : public QThread
-{
- Q_OBJECT
- void run() override;
-
-public:
- tobii_thread()
- {
- head_pose = new tobii_head_pose_t();
- }
- ~tobii_thread() override;
-
- tobii_head_pose_t* head_pose;
-
-private:
- tobii_api_t* api;
- tobii_device_t* device;
-
- static constexpr unsigned int retries = 300;
- static constexpr unsigned int interval = 100;
-
- QString error_last = "";
- std::atomic<bool> exit_thread = false;
-};
diff --git a/tracker-tobii/tobii.cpp b/tracker-tobii/tobii.cpp
new file mode 100644
index 00000000..8cf59335
--- /dev/null
+++ b/tracker-tobii/tobii.cpp
@@ -0,0 +1,121 @@
+/* Copyright (c) 2023, Khoa Nguyen <khoanguyen@3forcom.com>
+
+ * 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 "tobii.h"
+#include "compat/math-imports.hpp"
+
+#include <QMutexLocker>
+
+static constexpr double rad_to_deg = 180.0 * M_1_PI;
+static constexpr double mm_to_cm = 0.1;
+
+static void url_receiver(char const* url, void* user_data)
+{
+ char* buffer = (char*)user_data;
+ if (*buffer != '\0')
+ return; // only keep first value
+
+ if (strlen(url) < 256)
+ strcpy(buffer, url);
+}
+
+static void head_pose_callback(tobii_head_pose_t const* head_pose, void* user_data)
+{
+ // Store the latest head pose data in the supplied storage
+ tobii_head_pose_t* head_pose_storage = (tobii_head_pose_t*)user_data;
+ *head_pose_storage = *head_pose;
+}
+
+tobii_tracker::tobii_tracker() = default;
+
+tobii_tracker::~tobii_tracker()
+{
+ QMutexLocker lck(&mtx);
+ if (device)
+ {
+ tobii_head_pose_unsubscribe(device);
+ tobii_device_destroy(device);
+ }
+ if (api)
+ {
+ tobii_api_destroy(api);
+ }
+}
+
+module_status tobii_tracker::start_tracker(QFrame*)
+{
+ QMutexLocker lck(&mtx);
+ tobii_error_t tobii_error = tobii_api_create(&api, nullptr, nullptr);
+ if (tobii_error != TOBII_ERROR_NO_ERROR)
+ {
+ return error("Failed to initialize the Tobii Stream Engine API.");
+ }
+
+ char url[256] = { 0 };
+ tobii_error = tobii_enumerate_local_device_urls(api, url_receiver, url);
+ if (tobii_error != TOBII_ERROR_NO_ERROR || url[0] == '\0')
+ {
+ tobii_api_destroy(api);
+ return error("No stream engine compatible device(s) found.");
+ }
+
+ tobii_error = tobii_device_create(api, url, &device);
+ if (tobii_error != TOBII_ERROR_NO_ERROR)
+ {
+ tobii_api_destroy(api);
+ return error(QString("Failed to connect to %1.").arg(url));
+ }
+
+ tobii_error = tobii_head_pose_subscribe(device, head_pose_callback, &latest_head_pose);
+ if (tobii_error != TOBII_ERROR_NO_ERROR)
+ {
+ tobii_device_destroy(device);
+ tobii_api_destroy(api);
+ return error("Failed to subscribe to head pose stream.");
+ }
+
+ return status_ok();
+}
+
+void tobii_tracker::data(double* data)
+{
+ QMutexLocker lck(&mtx);
+ tobii_error_t tobii_error = tobii_device_process_callbacks(device);
+ if (tobii_error != TOBII_ERROR_NO_ERROR)
+ {
+ return;
+ }
+
+ // Tobii coordinate system is different from OpenTrack's
+ // Tobii: +x is to the right, +y is up, +z is towards the user
+ // Rotation xyz is in radians, x is pitch, y is yaw, z is roll
+
+ if (latest_head_pose.position_validity == TOBII_VALIDITY_VALID)
+ {
+ data[TX] = -latest_head_pose.position_xyz[0] * mm_to_cm;
+ data[TY] = latest_head_pose.position_xyz[1] * mm_to_cm;
+ data[TZ] = latest_head_pose.position_xyz[2] * mm_to_cm;
+ }
+
+ if (latest_head_pose.rotation_validity_xyz[0] == TOBII_VALIDITY_VALID)
+ {
+ data[Pitch] = latest_head_pose.rotation_xyz[0] * rad_to_deg;
+ }
+
+ if (latest_head_pose.rotation_validity_xyz[1] == TOBII_VALIDITY_VALID)
+ {
+ data[Yaw] = -latest_head_pose.rotation_xyz[1] * rad_to_deg;
+ }
+
+ if (latest_head_pose.rotation_validity_xyz[2] == TOBII_VALIDITY_VALID)
+ {
+ data[Roll] = latest_head_pose.rotation_xyz[2] * rad_to_deg;
+ }
+}
+
+OPENTRACK_DECLARE_TRACKER(tobii_tracker, tobii_dialog, tobii_metadata)
diff --git a/tracker-tobii/tobii.h b/tracker-tobii/tobii.h
new file mode 100644
index 00000000..414f9f64
--- /dev/null
+++ b/tracker-tobii/tobii.h
@@ -0,0 +1,61 @@
+/* Copyright (c) 2023, Khoa Nguyen <khoanguyen@3forcom.com>
+
+ * Permission to use, copy, modify, and/or distribute this
+ * software for any purpose with or without fee is hereby granted,
+ * provided that the above copyright notice and this permission
+ * notice appear in all copies.
+ */
+
+#pragma once
+#include "api/plugin-api.hpp"
+#include "ui_tobii.h"
+
+#include <tobii/tobii.h>
+#include <tobii/tobii_streams.h>
+
+#include <QMutex>
+
+class tobii_tracker : public ITracker
+{
+public:
+ tobii_tracker();
+ ~tobii_tracker() override;
+ module_status start_tracker(QFrame*) override;
+ void data(double* data) override;
+
+private:
+ tobii_api_t* api = nullptr;
+ tobii_device_t* device = nullptr;
+
+ tobii_head_pose_t latest_head_pose{
+ .timestamp_us = 0LL,
+ .position_validity = TOBII_VALIDITY_INVALID,
+ .position_xyz = { 0.f, 0.f, 0.f },
+ .rotation_validity_xyz = { TOBII_VALIDITY_INVALID, TOBII_VALIDITY_INVALID, TOBII_VALIDITY_INVALID },
+ .rotation_xyz = { 0.f, 0.f, 0.f },
+ };
+
+ QMutex mtx;
+};
+
+class tobii_dialog : public ITrackerDialog
+{
+ Q_OBJECT
+
+ Ui::tobii_ui ui;
+
+public:
+ tobii_dialog();
+
+private slots:
+ void doOK();
+ void doCancel();
+};
+
+class tobii_metadata : public Metadata
+{
+ Q_OBJECT
+
+ QString name() override { return tr("Tobii Eye Tracker"); }
+ QIcon icon() override { return QIcon(":/images/tobii_logo.png"); }
+};
diff --git a/tracker-tobii/tobii.qrc b/tracker-tobii/tobii.qrc
new file mode 100644
index 00000000..3e785ef7
--- /dev/null
+++ b/tracker-tobii/tobii.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/images">
+ <file>tobii_logo.png</file>
+ </qresource>
+</RCC>
diff --git a/tracker-tobii/tobii.ui b/tracker-tobii/tobii.ui
new file mode 100644
index 00000000..71b29c12
--- /dev/null
+++ b/tracker-tobii/tobii.ui
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>tobii_ui</class>
+ <widget class="QWidget" name="tobii_ui">
+ <property name="windowModality">
+ <enum>Qt::NonModal</enum>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>278</width>
+ <height>58</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Tobii Eye Tracker</string>
+ </property>
+ <property name="windowIcon">
+ <iconset>
+ <normaloff>:/images/tobii_logo.png</normaloff>:/images/tobii_logo.png</iconset>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </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>Please make sure the Tobii Experience application is running and tracking is active.</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::Close</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources>
+ <include location="tobii.qrc"/>
+ </resources>
+ <connections/>
+ <slots>
+ <slot>startEngineClicked()</slot>
+ <slot>stopEngineClicked()</slot>
+ <slot>cameraSettingsClicked()</slot>
+ </slots>
+</ui>
diff --git a/tracker-tobii/tobii_dialog.cpp b/tracker-tobii/tobii_dialog.cpp
new file mode 100644
index 00000000..689cae38
--- /dev/null
+++ b/tracker-tobii/tobii_dialog.cpp
@@ -0,0 +1,20 @@
+#include "tobii.h"
+
+tobii_dialog::tobii_dialog() // NOLINT(cppcoreguidelines-pro-type-member-init)
+{
+ ui.setupUi(this);
+
+ connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK()));
+ connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel()));
+}
+
+void tobii_dialog::doOK()
+{
+ // s.b->save();
+ close();
+}
+
+void tobii_dialog::doCancel()
+{
+ close();
+}
diff --git a/tracker-tobii/tobii_logo.png b/tracker-tobii/tobii_logo.png
new file mode 100644
index 00000000..3502b37b
--- /dev/null
+++ b/tracker-tobii/tobii_logo.png
Binary files differ
diff --git a/tracker-trackhat/CMakeLists.txt b/tracker-trackhat/CMakeLists.txt
new file mode 100644
index 00000000..483bf4c9
--- /dev/null
+++ b/tracker-trackhat/CMakeLists.txt
@@ -0,0 +1,16 @@
+if(WIN32)
+ include(opentrack-opencv)
+ find_package(OpenCV QUIET)
+ if(OpenCV_FOUND)
+ foreach(k core imgproc)
+ otr_install_lib("opencv_${k}" "${opentrack-libexec}")
+ endforeach()
+ set(SDK_TRACKHAT_SENSOR CACHE PATH "")
+ if(SDK_TRACKHAT_SENSOR)
+ include_directories("${SDK_TRACKHAT_SENSOR}/include" ${OpenCV_INCLUDE_DIRS})
+ link_directories("${SDK_TRACKHAT_SENSOR}/lib")
+ link_libraries(opencv_imgproc opencv_core opentrack-tracker-pt-base track-hat)
+ otr_module(tracker-trackhat)
+ endif()
+ endif()
+endif()
diff --git a/tracker-trackhat/camera.cpp b/tracker-trackhat/camera.cpp
new file mode 100644
index 00000000..d7079a96
--- /dev/null
+++ b/tracker-trackhat/camera.cpp
@@ -0,0 +1,140 @@
+#include "trackhat.hpp"
+#include "compat/sleep.hpp"
+#include <algorithm>
+#include <cstdio>
+
+namespace trackhat_impl {
+
+TH_ErrorCode log_error(TH_ErrorCode error, const char* source,
+ const char* file, int line, const char* function)
+{
+ if (error == TH_ERROR_DEVICE_ALREADY_OPEN)
+ error = TH_SUCCESS;
+ if (error)
+ {
+ auto logger = QMessageLogger(file, line, function).warning();
+ logger << "tracker/trackhat: error" << (void*)-error << "in" << source;
+ }
+ return error;
+}
+
+} // ns trackhat_impl
+
+pt_camera::result trackhat_camera::get_info() const
+{
+ return {true, get_desired() };
+}
+
+pt_camera_info trackhat_camera::get_desired() const
+{
+ pt_camera_info ret = {};
+
+ ret.fov = sensor_fov;
+ ret.fps = 250;
+ ret.res_x = sensor_size;
+ ret.res_y = sensor_size;
+
+ return ret;
+}
+
+QString trackhat_camera::get_desired_name() const
+{
+ return QStringLiteral("TrackHat sensor v1");
+}
+
+QString trackhat_camera::get_active_name() const
+{
+ return get_desired_name();
+}
+
+void trackhat_camera::set_fov(pt_camera::f) {}
+void trackhat_camera::show_camera_settings() {}
+
+trackhat_camera::trackhat_camera()
+{
+ s.set_raii_dtor_state(false);
+ t.set_raii_dtor_state(false);
+
+ for (auto* slider : { &t.exposure, &t.gain, /*&t.threshold,*/ })
+ {
+ QObject::connect(slider, options::value_::value_changed<options::slider_value>(),
+ &sig, &trackhat_impl::setting_receiver::settings_changed,
+ Qt::DirectConnection);
+ }
+}
+
+trackhat_camera::~trackhat_camera()
+{
+ stop();
+}
+
+pt_camera::result trackhat_camera::get_frame(pt_frame& frame_)
+{
+ if (!device.ensure_connected())
+ goto error;
+
+ if (sig.test_and_clear() && !init_regs())
+ goto error;
+
+ set_pt_options();
+
+ {
+ trackHat_ExtendedPoints_t points;
+ if (!!th_check(trackHat_GetDetectedPointsExtended(&*device, &points)))
+ goto error;
+ auto& frame = *frame_.as<trackhat_frame>();
+ frame.init_points(points, t.min_pt_size, t.max_pt_size);
+
+ using trackhat_impl::led_state;
+ int count =
+ std::count_if(frame.points.cbegin(), frame.points.cend(),
+ [](const point& pt) { return pt.ok; });
+ led.update(&*device, *t.led,
+ count == 3
+ ? led_state::tracking
+ : led_state::not_tracking);
+ }
+
+ return {true, get_desired()};
+
+error:
+ stop();
+ return {false, {}};
+}
+
+static void log_handler(const char* file, int line, const char* function, char level, const char* str, size_t len)
+{
+ if (level != 'E')
+ return;
+ char file_[128];
+ snprintf(file_, std::size(file_), "trackhat/%s", file);
+ auto logger = QMessageLogger(file_, line, function).debug();
+ logger << "tracker/trackhat:";
+ logger.noquote() << QLatin1String(str, (int)len);
+}
+
+bool trackhat_camera::start(const pt_settings&)
+{
+ trackHat_SetDebugHandler(log_handler);
+
+ if constexpr(debug_mode)
+ trackHat_EnableDebugMode();
+ else
+ trackHat_DisableDebugMode();
+
+ if (!device.ensure_device_exists())
+ return false;
+
+ set_pt_options();
+
+ led.update(&*device, *t.led, trackhat_impl::led_state::stopped);
+
+ return true;
+}
+
+void trackhat_camera::stop()
+{
+ if (device)
+ led.update(&*device, *t.led, trackhat_impl::led_state::stopped);
+ device.disconnect();
+}
diff --git a/tracker-trackhat/dialog.cpp b/tracker-trackhat/dialog.cpp
new file mode 100644
index 00000000..4ef64f50
--- /dev/null
+++ b/tracker-trackhat/dialog.cpp
@@ -0,0 +1,165 @@
+#include "dialog.hpp"
+
+using namespace options;
+
+trackhat_dialog::trackhat_dialog()
+{
+ ui.setupUi(this);
+ poll_tracker_info();
+ poll_timer.setInterval(100);
+
+ const std::tuple<QString, model_type> model_types[] = {
+ { tr("Cap"), model_cap },
+ { tr("Clip (left)"), model_clip_left },
+ { tr("Clip (right)"), model_clip_right },
+ { tr("Mini Clip (left)"), model_mini_clip_left },
+ { tr("Mini Clip (right)"), model_mini_clip_right },
+ { tr("Custom"), model_mystery_meat },
+ };
+
+ ui.model_type->clear();
+
+ for (const auto& [name, type] : model_types)
+ ui.model_type->addItem(QIcon{}, name, (QVariant)(int)type);
+
+ // model
+
+ tie_setting(t.model, ui.model_type);
+ tie_setting(t.min_pt_size, ui.min_point_size);
+ tie_setting(t.max_pt_size, ui.max_point_size);
+ tie_setting(t.point_filter_limit, ui.point_filter_limit);
+
+ // exposure
+
+ ui.exposure_slider->setMinimum((int)t.exposure->min());
+ ui.exposure_slider->setMaximum((int)t.exposure->max());
+
+ tie_setting(t.exposure, ui.exposure_slider);
+ ui.exposure_label->setValue((int)*t.exposure);
+
+ connect(&t.exposure, value_::value_changed<slider_value>(), ui.exposure_label,
+ [this] { ui.exposure_label->setValue((int)*t.exposure); }, Qt::QueuedConnection);
+
+ // gain
+
+ ui.gain_slider->setMinimum((int)t.gain->min());
+ ui.gain_slider->setMaximum((int)t.gain->max());
+
+ tie_setting(t.gain, ui.gain_slider);
+ ui.gain_label->setValue((int)*t.gain);
+
+ connect(&t.gain, value_::value_changed<slider_value>(), ui.gain_label,
+ [this] { ui.gain_label->setValue((int)*t.gain); }, Qt::QueuedConnection);
+
+#if 0
+ // threshold
+
+ tie_setting(t.threshold, ui.threshold_slider);
+
+ ui.threshold_label->setValue((int)*t.threshold);
+
+ connect(&t.threshold, value_::value_changed<slider_value>(), ui.threshold_label, [=] {
+ ui.threshold_label->setValue((int)*t.threshold);
+ }, Qt::QueuedConnection);
+#endif
+
+ // point filter
+
+ ui.point_filter_limit_label->setValue(*t.point_filter_limit);
+ connect(&t.point_filter_limit, value_::value_changed<slider_value>(), ui.point_filter_limit_label,
+ [this] { ui.point_filter_limit_label->setValue(*t.point_filter_limit); }, Qt::QueuedConnection);
+
+ tie_setting(t.enable_point_filter, ui.enable_point_filter);
+ tie_setting(t.point_filter_coefficient, ui.point_filter_slider);
+ ui.point_filter_label->setValue(*t.point_filter_coefficient);
+
+ connect(&t.point_filter_coefficient, value_::value_changed<slider_value>(), ui.point_filter_label,
+ [this] { ui.point_filter_label->setValue(*t.point_filter_coefficient); }, Qt::QueuedConnection);
+
+ tie_setting(t.point_filter_deadzone, ui.point_filter_deadzone);
+ ui.point_filter_deadzone_label->setValue(*t.point_filter_deadzone);
+
+ connect(&t.point_filter_deadzone, value_::value_changed<slider_value>(), ui.point_filter_deadzone_label,
+ [this] { ui.point_filter_deadzone_label->setValue(*t.point_filter_deadzone); }, Qt::QueuedConnection);
+
+ // led
+
+ using trackhat_impl::led_mode;
+ ui.led_mode->setItemData(0, (int)led_mode::off);
+ ui.led_mode->setItemData(1, (int)led_mode::constant);
+ ui.led_mode->setItemData(2, (int)led_mode::dynamic);
+
+ tie_setting(t.led, ui.led_mode);
+
+ // stuff
+
+ connect(&poll_timer, &QTimer::timeout, this, &trackhat_dialog::poll_tracker_info);
+ connect(ui.buttonBox, &QDialogButtonBox::accepted, this, &trackhat_dialog::doOK);
+ connect(ui.buttonBox, &QDialogButtonBox::rejected, this, &trackhat_dialog::doCancel);
+}
+
+void trackhat_dialog::register_tracker(ITracker* tracker_)
+{
+ tracker = static_cast<Tracker_PT*>(tracker_);
+ poll_tracker_info();
+ poll_timer.start();
+}
+
+void trackhat_dialog::unregister_tracker()
+{
+ tracker = nullptr;
+ poll_tracker_info();
+ poll_timer.stop();
+ update_raw_data();
+}
+
+void trackhat_dialog::save()
+{
+ s.b->save();
+ t.b->save();
+}
+
+void trackhat_dialog::reload()
+{
+ s.b->reload();
+ t.b->reload();
+}
+
+void trackhat_dialog::doCancel() { reload(); close(); }
+void trackhat_dialog::doOK() { save(); close(); }
+
+trackhat_dialog::~trackhat_dialog()
+{
+}
+
+void trackhat_dialog::poll_tracker_info()
+{
+ if (!tracker)
+ ui.status_label->setText(tr("Status: Tracking stopped."));
+ else if (tracker->get_n_points() == 3)
+ ui.status_label->setText(tr("Status: %1 points detected. Good!").arg(tracker->get_n_points()));
+ else
+ ui.status_label->setText(tr("Status: %1 points detected. BAD!").arg(tracker->get_n_points()));
+ update_raw_data();
+}
+
+void trackhat_dialog::set_buttons_visible(bool x)
+{
+ ui.buttonBox->setVisible(x);
+ adjustSize();
+}
+void trackhat_dialog::update_raw_data()
+{
+ QLabel* labels[] = { ui.label_x, ui.label_y, ui.label_z, ui.label_yaw, ui.label_pitch, ui.label_roll };
+ if (tracker)
+ {
+ QString str; str.reserve(16);
+ double data[6] {};
+ tracker->data(data);
+ for (unsigned i = 0; i < std::size(labels); i++)
+ labels[i]->setText(str.sprintf("%.2f%s", data[i], i >= 3 ? "°" : " mm"));
+ }
+ else
+ for (QLabel* x : labels)
+ x->setText(QStringLiteral("-"));
+}
diff --git a/tracker-trackhat/dialog.hpp b/tracker-trackhat/dialog.hpp
new file mode 100644
index 00000000..35ca866b
--- /dev/null
+++ b/tracker-trackhat/dialog.hpp
@@ -0,0 +1,36 @@
+#pragma once
+#include "trackhat.hpp"
+#include "ui_dialog.h"
+#include "tracker-pt/ftnoir_tracker_pt.h"
+#include "api/plugin-api.hpp"
+#include <QTimer>
+
+class trackhat_dialog final : public ITrackerDialog
+{
+ Q_OBJECT
+
+protected:
+ Ui_trackhat_dialog ui;
+ Tracker_PT* tracker = nullptr;
+ QTimer poll_timer{this};
+
+ pt_settings s{trackhat_metadata::module_name};
+ trackhat_settings t;
+
+ void set_buttons_visible(bool x) override;
+ void update_raw_data();
+
+public:
+ trackhat_dialog();
+ ~trackhat_dialog() override;
+ void register_tracker(ITracker *tracker) override;
+ void unregister_tracker() override;
+ bool embeddable() noexcept override { return true; }
+ void save() override;
+ void reload() override;
+
+public slots:
+ void doOK();
+ void doCancel();
+ void poll_tracker_info();
+};
diff --git a/tracker-trackhat/dialog.ui b/tracker-trackhat/dialog.ui
new file mode 100644
index 00000000..7c2ea8ee
--- /dev/null
+++ b/tracker-trackhat/dialog.ui
@@ -0,0 +1,602 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>trackhat_dialog</class>
+ <widget class="QDialog" name="trackhat_dialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>365</width>
+ <height>485</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>365</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="windowTitle">
+ <string>TrackHat</string>
+ </property>
+ <property name="windowIcon">
+ <iconset resource="trackhat-res.qrc">
+ <normaloff>:/images/trackhat-64x64.png</normaloff>:/images/trackhat-64x64.png</iconset>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Camera</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="1" column="2">
+ <widget class="QSpinBox" name="gain_label">
+ <property name="focusPolicy">
+ <enum>Qt::NoFocus</enum>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="buttonSymbols">
+ <enum>QAbstractSpinBox::NoButtons</enum>
+ </property>
+ <property name="maximum">
+ <number>999</number>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Exposure</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QSlider" name="gain_slider">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximum">
+ <number>239</number>
+ </property>
+ <property name="pageStep">
+ <number>10</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QSlider" name="exposure_slider">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximum">
+ <number>239</number>
+ </property>
+ <property name="pageStep">
+ <number>10</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QSpinBox" name="exposure_label">
+ <property name="focusPolicy">
+ <enum>Qt::NoFocus</enum>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="buttonSymbols">
+ <enum>QAbstractSpinBox::NoButtons</enum>
+ </property>
+ <property name="maximum">
+ <number>999</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Gain</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>LED</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QComboBox" name="led_mode">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <item>
+ <property name="text">
+ <string>Off</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Constant</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Dynamic</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_2">
+ <property name="title">
+ <string>Model</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_6">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>10</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Type</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="model_type">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Maximum">
+ <horstretch>9</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Min point size</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>Max point size</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1" alignment="Qt::AlignRight">
+ <widget class="QDoubleSpinBox" name="max_point_size">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="suffix">
+ <string> px</string>
+ </property>
+ <property name="decimals">
+ <number>1</number>
+ </property>
+ <property name="minimum">
+ <double>1.000000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>999.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1" alignment="Qt::AlignRight">
+ <widget class="QDoubleSpinBox" name="min_point_size">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="suffix">
+ <string> px</string>
+ </property>
+ <property name="decimals">
+ <number>1</number>
+ </property>
+ <property name="minimum">
+ <double>1.000000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>999.000000000000000</double>
+ </property>
+ <property name="value">
+ <double>1.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_3">
+ <property name="title">
+ <string>Tracking</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QWidget" name="widget" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_3">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item row="0" column="2">
+ <widget class="QDoubleSpinBox" name="point_filter_label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="focusPolicy">
+ <enum>Qt::NoFocus</enum>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="buttonSymbols">
+ <enum>QAbstractSpinBox::NoButtons</enum>
+ </property>
+ <property name="suffix">
+ <string> px</string>
+ </property>
+ <property name="decimals">
+ <number>2</number>
+ </property>
+ <property name="maximum">
+ <double>300.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QSlider" name="point_filter_limit">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximum">
+ <number>99</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_8">
+ <property name="text">
+ <string>Limit</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_10">
+ <property name="text">
+ <string>Deadzone</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QCheckBox" name="enable_point_filter">
+ <property name="text">
+ <string>Point filter</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QDoubleSpinBox" name="point_filter_limit_label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="focusPolicy">
+ <enum>Qt::NoFocus</enum>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="buttonSymbols">
+ <enum>QAbstractSpinBox::NoButtons</enum>
+ </property>
+ <property name="prefix">
+ <string/>
+ </property>
+ <property name="suffix">
+ <string/>
+ </property>
+ <property name="decimals">
+ <number>2</number>
+ </property>
+ <property name="maximum">
+ <double>1.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QSlider" name="point_filter_slider">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximum">
+ <number>300</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QSlider" name="point_filter_deadzone">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximum">
+ <number>100</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QDoubleSpinBox" name="point_filter_deadzone_label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="focusPolicy">
+ <enum>Qt::NoFocus</enum>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="buttonSymbols">
+ <enum>QAbstractSpinBox::NoButtons</enum>
+ </property>
+ <property name="suffix">
+ <string> px</string>
+ </property>
+ <property name="decimals">
+ <number>2</number>
+ </property>
+ <property name="maximum">
+ <double>300.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="status_label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Status</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_4">
+ <property name="title">
+ <string>Raw data</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_4">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_9">
+ <property name="text">
+ <string>X</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLabel" name="label_y">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLabel" name="label_z">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QLabel" name="label_13">
+ <property name="text">
+ <string>Yaw</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QLabel" name="label_15">
+ <property name="text">
+ <string>Roll</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_11">
+ <property name="text">
+ <string>Y</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="label_x">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_12">
+ <property name="text">
+ <string>Z</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QLabel" name="label_14">
+ <property name="text">
+ <string>Pitch</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <widget class="QLabel" name="label_yaw">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <widget class="QLabel" name="label_pitch">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="3">
+ <widget class="QLabel" name="label_roll">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Expanding</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>10</width>
+ <height>1</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <tabstops>
+ <tabstop>exposure_slider</tabstop>
+ <tabstop>gain_slider</tabstop>
+ <tabstop>led_mode</tabstop>
+ <tabstop>model_type</tabstop>
+ <tabstop>min_point_size</tabstop>
+ <tabstop>max_point_size</tabstop>
+ <tabstop>enable_point_filter</tabstop>
+ <tabstop>point_filter_slider</tabstop>
+ <tabstop>point_filter_limit</tabstop>
+ <tabstop>point_filter_deadzone</tabstop>
+ </tabstops>
+ <resources>
+ <include location="trackhat-res.qrc"/>
+ </resources>
+ <connections/>
+</ui>
diff --git a/tracker-trackhat/extractor.cpp b/tracker-trackhat/extractor.cpp
new file mode 100644
index 00000000..58b9fd05
--- /dev/null
+++ b/tracker-trackhat/extractor.cpp
@@ -0,0 +1,21 @@
+#include "trackhat.hpp"
+#include <algorithm>
+#include <iterator>
+
+void trackhat_extractor::extract_points(const pt_frame& data,
+ pt_preview&, bool,
+ std::vector<vec2>& points)
+{
+ points.clear();
+ points.reserve(trackhat_camera::point_count);
+ const auto& copy = data.as_const<trackhat_frame>()->points;
+
+ for (const auto& pt : copy)
+ {
+ if (!pt.ok)
+ continue;
+ constexpr int sz = trackhat_camera::sensor_size;
+ auto [ x, y ] = to_screen_pos(pt.x, pt.y, sz, sz);
+ points.push_back({x, y});
+ }
+}
diff --git a/tracker-trackhat/frame.cpp b/tracker-trackhat/frame.cpp
new file mode 100644
index 00000000..0e97ed0e
--- /dev/null
+++ b/tracker-trackhat/frame.cpp
@@ -0,0 +1,128 @@
+#include "trackhat.hpp"
+#include <opencv2/imgproc.hpp>
+#include "compat/math.hpp"
+
+trackhat_preview::trackhat_preview(int w, int h)
+{
+ frame_bgr.create(h, w, CV_8UC3);
+ frame_bgra.create(h, w, CV_8UC4);
+}
+
+void trackhat_preview::set_last_frame(const pt_frame& frame_)
+{
+ center = {-1, -1};
+ points = frame_.as_const<trackhat_frame>()->points;
+}
+
+void trackhat_preview::draw_head_center(pt_pixel_pos_mixin::f x, pt_pixel_pos_mixin::f y)
+{
+ center = {x, y};
+}
+
+QImage trackhat_preview::get_bitmap()
+{
+ frame_bgr.setTo({0});
+
+ draw_points();
+ draw_center();
+
+ cv::cvtColor(frame_bgr, frame_bgra, cv::COLOR_BGR2BGRA);
+
+ return QImage((const unsigned char*) frame_bgra.data,
+ frame_bgra.cols, frame_bgra.rows,
+ (int)frame_bgra.step.p[0],
+ QImage::Format_ARGB32);
+}
+
+void trackhat_preview::draw_center()
+{
+ if (center == numeric_types::vec2(-1, -1))
+ return;
+
+ auto [px_, py_] = to_pixel_pos(center[0], center[1], frame_bgr.cols, frame_bgr.rows);
+ int px = iround(px_), py = iround(py_);
+
+ const f dpi = (f)frame_bgr.cols / f(320);
+ constexpr int len_ = 9;
+ int len = iround(len_ * dpi);
+
+ static const cv::Scalar color(0, 255, 255);
+ cv::line(frame_bgr,
+ cv::Point(px - len, py),
+ cv::Point(px + len, py),
+ color, 1);
+ cv::line(frame_bgr,
+ cv::Point(px, py - len),
+ cv::Point(px, py + len),
+ color, 1);
+}
+
+void trackhat_preview::draw_points()
+{
+ for (const auto& pt : points)
+ {
+ if (pt.brightness == 0)
+ continue;
+
+ constexpr int sz = trackhat_camera::sensor_size;
+ constexpr f scaling_factor = 10;
+ const int x = pt.x * frame_bgr.cols / sz, y = pt.y * frame_bgr.rows / sz;
+ const f dpi = (f)frame_bgr.cols / f(320);
+ const int W = std::max(1, iround(pt.W * frame_bgr.cols * scaling_factor / sz)),
+ H = std::max(1, iround(pt.H * frame_bgr.rows * scaling_factor / sz));
+ const auto point_color = progn(double c = pt.brightness; return cv::Scalar{c, c, c};);
+ const auto outline_color = pt.ok
+ ? cv::Scalar{255, 255, 0}
+ : cv::Scalar{192, 192, 192};
+
+ cv::ellipse(frame_bgr, {x, y}, {W, H},
+ 0, 0, 360, point_color, -1, cv::LINE_AA);
+ cv::ellipse(frame_bgr, {x, y}, {iround(W + 2*dpi), iround(H + 2*dpi)},
+ 0, 0, 360, outline_color, iround(dpi), cv::LINE_AA);
+
+ char buf[16];
+ std::snprintf(buf, sizeof(buf), "%dpx", pt.area);
+ auto text_color = pt.ok
+ ? cv::Scalar(0, 0, 255)
+ : cv::Scalar(160, 160, 160);
+ const int offx = iround(W + 9*dpi), offy = H*3/2;
+
+ cv::putText(frame_bgr, buf, {x+offx, y+offy},
+ cv::FONT_HERSHEY_PLAIN, iround(dpi), text_color,
+ 1);
+ }
+}
+
+void trackhat_frame::init_points(const trackHat_ExtendedPoints_t& points_, double min_size, double max_size)
+{
+ trackHat_ExtendedPoints_t copy = points_;
+ points = {};
+
+ std::sort(std::begin(copy.m_point), std::end(copy.m_point),
+ [](trackHat_ExtendedPoint_t p1, trackHat_ExtendedPoint_t p2) {
+ return p1.m_averageBrightness > p2.m_averageBrightness;
+ });
+
+ unsigned i = 0;
+
+ for (const trackHat_ExtendedPoint_t& pt : copy.m_point)
+ {
+ if (pt.m_averageBrightness == 0)
+ continue;
+
+ point p = {};
+
+ if (pt.m_area >= min_size && pt.m_area <= max_size)
+ p.ok = true;
+ constexpr f c = (f)2941/trackhat_camera::sensor_size;
+
+ p.brightness = pt.m_averageBrightness;
+ p.area = pt.m_area * c;
+ p.W = std::max(1, pt.m_boundryRigth - pt.m_boundryLeft);
+ p.H = std::max(1, pt.m_boundryDown - pt.m_boundryUp);
+ p.x = trackhat_camera::sensor_size-1-pt.m_coordinateX;
+ p.y = pt.m_coordinateY;
+
+ points[i++] = p;
+ }
+}
diff --git a/tracker-trackhat/handle.cpp b/tracker-trackhat/handle.cpp
new file mode 100644
index 00000000..42902e76
--- /dev/null
+++ b/tracker-trackhat/handle.cpp
@@ -0,0 +1,72 @@
+#undef NDEBUG
+#include "trackhat.hpp"
+#include "compat/sleep.hpp"
+#include "compat/timer.hpp"
+#include <cassert>
+
+bool camera_handle::ensure_connected()
+{
+ if (state_ >= st_streaming)
+ return true;
+ else if (state_ == st_stopped)
+ return false;
+
+ Timer t;
+
+ constexpr int max_attempts = 5;
+
+ if (!ensure_device_exists())
+ goto error;
+
+ for (int i = 0; i < max_attempts; i++)
+ {
+ if (!th_check(trackHat_Connect(&device_, TH_FRAME_EXTENDED)))
+ {
+ state_ = st_streaming;
+ if (int ms = (int)t.elapsed_ms(); ms > 1000)
+ qDebug() << "tracker/trackhat: connecting took" << ms << "ms";
+ return true;
+ }
+
+ auto dbg = qDebug();
+ dbg << "tracker/trackhat: connect failed, retry";
+ dbg.space(); dbg.nospace();
+ dbg << (i+1) << "/" << max_attempts;
+ portable::sleep(50);
+ }
+
+error:
+ disconnect();
+ return false;
+}
+
+bool camera_handle::ensure_device_exists()
+{
+ switch (state_)
+ {
+ case st_streaming:
+ return true;
+ case st_detected:
+ disconnect();
+ [[fallthrough]];
+ case st_stopped:
+ assert(!th_check(trackHat_Initialize(&device_)) && device_.m_pInternal);
+ if (auto error = th_check(trackHat_DetectDevice(&device_)); error)
+ {
+ disconnect();
+ return false;
+ }
+ state_ = st_detected;
+ return true;
+ }
+}
+
+void camera_handle::disconnect()
+{
+ state_ = st_stopped;
+ if (device_.m_pInternal)
+ {
+ (void)!th_check(trackHat_Disconnect(&device_));
+ trackHat_Deinitialize(&device_);
+ }
+}
diff --git a/tracker-trackhat/images/trackhat-64x64.png b/tracker-trackhat/images/trackhat-64x64.png
new file mode 100644
index 00000000..9e856c23
--- /dev/null
+++ b/tracker-trackhat/images/trackhat-64x64.png
Binary files differ
diff --git a/tracker-trackhat/images/trackhat.ico b/tracker-trackhat/images/trackhat.ico
new file mode 100644
index 00000000..b5f34db3
--- /dev/null
+++ b/tracker-trackhat/images/trackhat.ico
Binary files differ
diff --git a/tracker-trackhat/images/trackhat.png b/tracker-trackhat/images/trackhat.png
new file mode 100644
index 00000000..4f17de81
--- /dev/null
+++ b/tracker-trackhat/images/trackhat.png
Binary files differ
diff --git a/tracker-trackhat/lang/nl_NL.ts b/tracker-trackhat/lang/nl_NL.ts
new file mode 100644
index 00000000..ebc53a27
--- /dev/null
+++ b/tracker-trackhat/lang/nl_NL.ts
@@ -0,0 +1,154 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="nl_NL">
+<context>
+ <name>trackhat_dialog</name>
+ <message>
+ <source>Cap</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Clip (left)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Clip (right)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Mini Clip (left)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Mini Clip (right)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Custom</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Status: Tracking stopped.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Status: %1 points detected. Good!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Status: %1 points detected. BAD!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>TrackHat</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Camera</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Exposure</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Model</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Type</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Min point size</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Max point size</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source> px</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Tracking</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Point filter</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Limit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Status</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Raw data</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>X</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Yaw</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Roll</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Y</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Z</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Pitch</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Deadzone</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Gain</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>LED</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Off</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Constant</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Dynamic</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>trackhat_module</name>
+ <message>
+ <source>TrackHat v1 Sensor</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/tracker-trackhat/lang/ru_RU.ts b/tracker-trackhat/lang/ru_RU.ts
new file mode 100644
index 00000000..49d247be
--- /dev/null
+++ b/tracker-trackhat/lang/ru_RU.ts
@@ -0,0 +1,154 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="ru_RU">
+<context>
+ <name>trackhat_dialog</name>
+ <message>
+ <source>Cap</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Clip (left)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Clip (right)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Mini Clip (left)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Mini Clip (right)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Custom</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Status: Tracking stopped.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Status: %1 points detected. Good!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Status: %1 points detected. BAD!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>TrackHat</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Camera</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Exposure</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Model</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Type</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Min point size</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Max point size</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source> px</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Tracking</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Point filter</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Limit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Status</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Raw data</source>
+ <translation>Исходные данные</translation>
+ </message>
+ <message>
+ <source>X</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Yaw</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Roll</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Y</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Z</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Pitch</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Deadzone</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Gain</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>LED</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Off</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Constant</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Dynamic</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>trackhat_module</name>
+ <message>
+ <source>TrackHat v1 Sensor</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/tracker-trackhat/lang/stub.ts b/tracker-trackhat/lang/stub.ts
new file mode 100644
index 00000000..adb23557
--- /dev/null
+++ b/tracker-trackhat/lang/stub.ts
@@ -0,0 +1,154 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="stub">
+<context>
+ <name>trackhat_dialog</name>
+ <message>
+ <source>TrackHat</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Camera</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Exposure</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Model</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Type</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Min point size</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Max point size</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source> px</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Tracking</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Limit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Deadzone</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Point filter</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Status</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Raw data</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>X</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Yaw</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Roll</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Y</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Z</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Pitch</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cap</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Clip (left)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Clip (right)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Mini Clip (left)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Mini Clip (right)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Custom</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Status: Tracking stopped.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Status: %1 points detected. Good!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Status: %1 points detected. BAD!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Gain</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>LED</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Off</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Constant</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Dynamic</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>trackhat_module</name>
+ <message>
+ <source>TrackHat v1 Sensor</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/tracker-trackhat/lang/zh_CN.ts b/tracker-trackhat/lang/zh_CN.ts
new file mode 100644
index 00000000..5837eeb1
--- /dev/null
+++ b/tracker-trackhat/lang/zh_CN.ts
@@ -0,0 +1,154 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="zh_CN">
+<context>
+ <name>trackhat_dialog</name>
+ <message>
+ <source>Cap</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Clip (left)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Clip (right)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Mini Clip (left)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Mini Clip (right)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Custom</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Status: Tracking stopped.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Status: %1 points detected. Good!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Status: %1 points detected. BAD!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>TrackHat</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Camera</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Exposure</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Model</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Type</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Min point size</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Max point size</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source> px</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Tracking</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Point filter</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Limit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Status</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Raw data</source>
+ <translation>跟踪器原始数据</translation>
+ </message>
+ <message>
+ <source>X</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Yaw</source>
+ <translation>偏航</translation>
+ </message>
+ <message>
+ <source>Roll</source>
+ <translation>横滚</translation>
+ </message>
+ <message>
+ <source>Y</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Z</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Pitch</source>
+ <translation>仰俯</translation>
+ </message>
+ <message>
+ <source>Deadzone</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Gain</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>LED</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Off</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Constant</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Dynamic</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>trackhat_module</name>
+ <message>
+ <source>TrackHat v1 Sensor</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/tracker-trackhat/led.cpp b/tracker-trackhat/led.cpp
new file mode 100644
index 00000000..db5ab6c2
--- /dev/null
+++ b/tracker-trackhat/led.cpp
@@ -0,0 +1,66 @@
+#include "trackhat.hpp"
+
+namespace trackhat_impl {
+
+void led_updater::update_(trackHat_Device_t* device, trackHat_SetLeds_t leds)
+{
+ if (leds_.ledRedState == leds.ledRedState &&
+ leds_.ledGreenState == leds.ledGreenState &&
+ leds_.ledBlueState == leds.ledBlueState)
+ return;
+ (void)trackHat_SetLeds(device, &leds);
+ leds_ = leds;
+}
+
+trackHat_SetLeds_t led_updater::next_state(led_mode mode, led_state new_state)
+{
+ switch (mode)
+ {
+ case led_mode::off:
+ state_ = led_state::stopped;
+ timer_ = std::nullopt;
+ return LED_off;
+ default:
+ case led_mode::constant:
+ state_ = led_state::stopped;
+ timer_ = std::nullopt;
+ return LED_idle;
+ case led_mode::dynamic:
+ break;
+ }
+
+ if (new_state <= led_state::stopped)
+ {
+ state_ = new_state;
+ timer_ = std::nullopt;
+ return LED_idle;
+ }
+ else if (new_state == state_)
+ {
+ timer_ = std::nullopt;
+ return leds_;
+ }
+ else if (!timer_)
+ {
+ timer_ = Timer{};
+ return leds_;
+ }
+ else if (timer_->elapsed_ms() > SWITCH_TIME_MS)
+ {
+ state_ = new_state;
+ timer_ = std::nullopt;
+ return new_state == led_state::not_tracking
+ ? LED_not_tracking
+ : LED_tracking;
+ }
+ else
+ return leds_;
+}
+
+void led_updater::update(trackHat_Device_t* device, led_mode mode, led_state new_state)
+{
+ auto leds = next_state(mode, new_state);
+ update_(device, leds);
+}
+
+} // namespace trackhat_impl
diff --git a/tracker-trackhat/metadata.cpp b/tracker-trackhat/metadata.cpp
new file mode 100644
index 00000000..8c6f4de0
--- /dev/null
+++ b/tracker-trackhat/metadata.cpp
@@ -0,0 +1,39 @@
+#include "metadata.hpp"
+#include "api/plugin-api.hpp"
+#include "trackhat.hpp"
+#include "dialog.hpp"
+
+// XXX TODO
+const QString trackhat_metadata::module_name = QStringLiteral("tracker-trackhat/pt");
+
+pt_runtime_traits::pointer<pt_camera> trackhat_metadata::make_camera() const
+{
+ return std::make_shared<trackhat_camera>();
+}
+
+pt_runtime_traits::pointer<pt_point_extractor> trackhat_metadata::make_point_extractor() const
+{
+ return std::make_shared<trackhat_extractor>();
+}
+
+pt_runtime_traits::pointer<pt_frame> trackhat_metadata::make_frame() const
+{
+ return std::make_shared<trackhat_frame>();
+}
+
+pt_runtime_traits::pointer<pt_preview> trackhat_metadata::make_preview(int w, int h) const
+{
+ return std::make_shared<trackhat_preview>(w, h);
+}
+
+QString trackhat_metadata::get_module_name() const
+{
+ return trackhat_metadata::module_name;
+}
+
+trackhat_pt::trackhat_pt() :
+ Tracker_PT(pt_runtime_traits::pointer<pt_runtime_traits>(new trackhat_metadata))
+{
+}
+
+OPENTRACK_DECLARE_TRACKER(trackhat_pt, trackhat_dialog, trackhat_module)
diff --git a/tracker-trackhat/metadata.hpp b/tracker-trackhat/metadata.hpp
new file mode 100644
index 00000000..f1355064
--- /dev/null
+++ b/tracker-trackhat/metadata.hpp
@@ -0,0 +1,21 @@
+#pragma once
+#include "trackhat.hpp"
+#include "../tracker-pt/ftnoir_tracker_pt.h"
+
+class trackhat_pt final : public Tracker_PT
+{
+ Q_OBJECT
+
+public:
+ trackhat_pt();
+};
+
+class trackhat_module final : public Metadata
+{
+ Q_OBJECT
+
+public:
+ QString name() override { return tr("TrackHat v1 Sensor"); }
+ QIcon icon() override { return QIcon(":/images/trackhat-64x64.png"); }
+ static const QString module_name;
+};
diff --git a/tracker-trackhat/settings.cpp b/tracker-trackhat/settings.cpp
new file mode 100644
index 00000000..dcbf6b37
--- /dev/null
+++ b/tracker-trackhat/settings.cpp
@@ -0,0 +1,144 @@
+#include "trackhat.hpp"
+#include "compat/sleep.hpp"
+#include "compat/timer.hpp"
+
+namespace trackhat_impl {
+
+trackhat_settings::trackhat_settings() : opts{"tracker-trackhat"}
+{
+}
+
+void setting_receiver::settings_changed()
+{
+ changed = true;
+}
+
+bool setting_receiver::test_and_clear()
+{
+ bool x = true;
+ return changed.compare_exchange_strong(x, false);
+}
+
+setting_receiver::setting_receiver(bool value) : changed{value}
+{
+}
+
+} // ns trackhat_impl
+
+void trackhat_camera::set_pt_options()
+{
+ s.min_point_size = t.min_pt_size;
+ s.max_point_size = t.max_pt_size;
+
+ switch (t.model)
+ {
+ default:
+ case model_cap:
+ s.t_MH_x = 0; s.t_MH_y = 0; s.t_MH_z = 0;
+ break;
+ case model_mystery_meat:
+ break;
+ case model_clip_left:
+ case model_mini_clip_left:
+ s.t_MH_x = -135; s.t_MH_y = 0; s.t_MH_z = 0;
+ break;
+ case model_clip_right:
+ case model_mini_clip_right:
+ s.t_MH_x = 135; s.t_MH_y = 0; s.t_MH_z = 0;
+ break;
+ }
+
+ switch (t.model)
+ {
+ default:
+ eval_once(qDebug() << "tracker/trackhat: unknown model");
+ [[fallthrough]];
+ case model_clip_left:
+ case model_clip_right:
+ s.clip_tz = 27; s.clip_ty = 43; s.clip_by = 62; s.clip_bz = 74;
+ s.active_model_panel = 0;
+ break;
+ case model_mini_clip_left:
+ case model_mini_clip_right:
+ s.clip_tz = 13; s.clip_ty = 42; s.clip_by = 60; s.clip_bz = 38;
+ s.active_model_panel = 0;
+ break;
+ case model_cap:
+ s.cap_x = 60; s.cap_y = 90; s.cap_z = 95;
+ s.active_model_panel = 1;
+ break;
+ case model_mystery_meat:
+ break;
+ }
+
+ s.dynamic_pose = t.model == model_cap;
+ s.init_phase_timeout = 500;
+
+ s.camera_name = QStringLiteral("TrackHat Sensor");
+
+ s.enable_point_filter = t.enable_point_filter;
+ s.point_filter_coefficient = *t.point_filter_coefficient;
+ s.point_filter_limit = *t.point_filter_limit;
+ s.point_filter_deadzone = *t.point_filter_deadzone;
+}
+
+bool trackhat_camera::init_regs()
+{
+ auto exp = (uint8_t)t.exposure;
+ auto exp2 = (uint8_t)(exp == 0xff ? 0xf0 : 0xff);
+ auto thres = (uint8_t)0xfe;
+ auto thres2 = (uint8_t)3;
+
+ auto gain = (uint8_t)t.gain;
+ auto gain_c = (uint8_t)(gain/0x10);
+ gain %= 0x10; gain_c %= 4;
+
+ trackHat_SetRegisterGroup_t regs = {
+ {
+ { 0x0c, 0x0f, exp2 }, // exposure lo
+ { 0x0c, 0x10, exp }, // exposure hi
+ { 0x00, 0x0b, 0xff }, // blob area max size
+ { 0x00, 0x0c, 0x03 }, // blob area min size
+ { 0x0c, 0x08, gain }, // gain
+ { 0x0c, 0x0c, gain_c }, // gain multiplier
+ { 0x0c, 0x47, thres }, // min brightness
+ { 0x00, 0x0f, thres2 }, // brightness margin, formula is `thres >= px > thres - fuzz'
+ { 0x0c, 0x60, 0xff }, // scale resolution lo
+ { 0x0c, 0x61, 0x0f }, // scale resolution hi
+ { 0x00, 0x01, 0x01 }, // bank0 sync
+ { 0x01, 0x01, 0x01 }, // bank1 sync
+ },
+ 12,
+ };
+
+ Timer t;
+
+ constexpr int max = 5;
+ int i = 0;
+ for (i = 0; i < max; i++)
+ {
+ TH_ErrorCode status = TH_SUCCESS;
+ status = th_check(trackHat_SetRegisterGroupValue(&*device, &regs));
+ if (status == TH_SUCCESS)
+ break;
+ else if (status != TH_FAILED_TO_SET_REGISTER &&
+ status != TH_ERROR_DEVICE_COMMUNICATION_TIMEOUT)
+ return false;
+ else
+ {
+ auto dbg = qDebug();
+ dbg << "tracker/trackhat: set register retry attempt";
+ dbg.space(); dbg.nospace();
+ dbg << i << "/" << max;
+ portable::sleep(50);
+ }
+ }
+
+ if (i == max)
+ return false;
+
+ if (int elapsed = (int)t.elapsed_ms(); elapsed > 100)
+ qDebug() << "tracker/trackhat: setting registers took" << elapsed << "ms";
+
+ return true;
+}
diff --git a/tracker-trackhat/tracker_trackhat.qrc b/tracker-trackhat/tracker_trackhat.qrc
new file mode 100644
index 00000000..d54010a0
--- /dev/null
+++ b/tracker-trackhat/tracker_trackhat.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>images/trackhat-64x64.png</file>
+ </qresource>
+</RCC>
diff --git a/tracker-trackhat/trackhat-res.qrc b/tracker-trackhat/trackhat-res.qrc
new file mode 100644
index 00000000..9aeb8879
--- /dev/null
+++ b/tracker-trackhat/trackhat-res.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource>
+ <file>images/trackhat-64x64.png</file>
+ </qresource>
+</RCC>
diff --git a/tracker-trackhat/trackhat.hpp b/tracker-trackhat/trackhat.hpp
new file mode 100644
index 00000000..ea00b15e
--- /dev/null
+++ b/tracker-trackhat/trackhat.hpp
@@ -0,0 +1,209 @@
+#pragma once
+
+#include "../tracker-pt/pt-api.hpp"
+#include "compat/timer.hpp"
+#include "options/options.hpp"
+
+#include <track_hat_driver.h>
+
+#include <array>
+#include <atomic>
+#include <optional>
+#include <opencv2/core/mat.hpp>
+
+enum model_type : int
+{
+ model_cap = 1,
+ model_clip_left,
+ model_clip_right,
+ model_mini_clip_left,
+ model_mini_clip_right,
+ model_mystery_meat,
+};
+
+namespace trackhat_impl
+{
+using namespace options;
+
+TH_ErrorCode log_error(TH_ErrorCode error, const char* source, const char* file, int line, const char* function);
+#define th_check_(expr, expr2) ::trackhat_impl::log_error((expr), expr2)
+#define th_check(expr) ::trackhat_impl::log_error((expr), #expr, __FILE__, __LINE__, function_name)
+
+enum class led_mode : unsigned char {
+ off, constant, dynamic,
+};
+
+struct trackhat_settings : opts
+{
+ static constexpr int min_gain = 1, max_gain = 47,
+ min_exposure = 0x10, max_exposure = 0xff;
+ static constexpr int num_exposure_steps = max_gain + max_exposure - min_gain - min_exposure;
+ trackhat_settings();
+ value<slider_value> exposure{b, "exposure", {min_exposure + max_exposure*2/3, min_exposure, max_exposure}};
+ value<slider_value> gain{b, "gain", {min_gain + max_gain*2/3, min_gain, max_gain}};
+ //value<slider_value> threshold{b, "threshold", {0x97, 64, 0xfe}};
+ value<model_type> model{b, "model", model_mini_clip_left};
+ value<double> min_pt_size{b, "min-point-size", 10};
+ value<double> max_pt_size{b, "max-point-size", 50};
+ value<bool> enable_point_filter{b, "enable-point-filter", true };
+ value<slider_value> point_filter_coefficient{b, "point-filter-coefficient", { 1.5, 1, 4 }};
+ value<slider_value> point_filter_limit { b, "point-filter-limit", { 0.1, 0.01, 1 }};
+ value<slider_value> point_filter_deadzone { b, "point-filter-deadzone", {0, 0, 1}};
+ value<led_mode> led { b, "led-mode", led_mode::dynamic };
+};
+
+class setting_receiver : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit setting_receiver(bool value);
+ bool test_and_clear();
+public slots:
+ void settings_changed();
+private:
+ std::atomic<bool> changed{false};
+};
+
+enum class led_state : unsigned char {
+ invalid, stopped, not_tracking, tracking,
+};
+
+struct led_updater final {
+ trackHat_SetLeds_t leds_ {TH_UNCHANGED, TH_UNCHANGED, TH_UNCHANGED};
+ std::optional<Timer> timer_;
+ led_state state_ = led_state::invalid;
+
+ trackHat_SetLeds_t next_state(led_mode mode, led_state new_state);
+ void update_(trackHat_Device_t* device, trackHat_SetLeds_t leds);
+ void update(trackHat_Device_t* device, led_mode mode, led_state new_state);
+
+ static constexpr int SWITCH_TIME_MS = 2000;
+ static constexpr
+ trackHat_SetLeds_t LED_idle = {TH_OFF, TH_SOLID, TH_OFF},
+ LED_off = {TH_OFF, TH_OFF, TH_OFF},
+ LED_tracking {TH_OFF, TH_SOLID, TH_SOLID},
+ LED_not_tracking {TH_SOLID, TH_OFF, TH_OFF};
+};
+
+} // ns trackhat_impl
+
+using trackhat_impl::trackhat_settings;
+using trackhat_impl::led_updater;
+
+struct trackhat_metadata final : pt_runtime_traits
+{
+ pt_runtime_traits::pointer<pt_camera> make_camera() const override;
+ pt_runtime_traits::pointer<pt_point_extractor> make_point_extractor() const override;
+ pt_runtime_traits::pointer<pt_frame> make_frame() const override;
+ pt_runtime_traits::pointer<pt_preview> make_preview(int w, int h) const override;
+ QString get_module_name() const override;
+
+ OTR_DISABLE_MOVE_COPY(trackhat_metadata);
+
+ trackhat_metadata() = default;
+ ~trackhat_metadata() override = default;
+
+ static const QString module_name;
+};
+
+struct point
+{
+ int brightness = 0, area, x, y, W, H;
+ bool ok = false;
+};
+
+struct camera_handle final
+{
+ OTR_DISABLE_MOVE_COPY(camera_handle);
+ trackHat_Device_t* operator->() { return &device_; }
+ trackHat_Device_t& operator*() { return device_; }
+
+ camera_handle() = default;
+ ~camera_handle() = default;
+
+ constexpr operator bool() const { return state_ >= st_streaming; }
+ [[nodiscard]] bool ensure_connected();
+ [[nodiscard]] bool ensure_device_exists();
+ void disconnect();
+private:
+ trackHat_Device_t device_ = {};
+ enum state { st_stopped, st_detected, st_streaming, };
+ state state_ = st_stopped;
+};
+
+struct trackhat_camera final : pt_camera
+{
+ trackhat_camera();
+ ~trackhat_camera() override;
+
+ OTR_DISABLE_MOVE_COPY(trackhat_camera);
+
+ bool start(const pt_settings& s) override;
+ void stop() override;
+
+ pt_camera::result get_frame(pt_frame& frame) override;
+ pt_camera::result get_info() const override;
+ pt_camera_info get_desired() const override;
+
+ QString get_desired_name() const override;
+ QString get_active_name() const override;
+
+ void set_fov(f value) override;
+ void show_camera_settings() override;
+
+ f deadzone_amount() const override { return 10; }
+
+ static constexpr int sensor_size = 4096;
+ static constexpr int sensor_fov = 52;
+ static constexpr int point_count = TRACK_HAT_NUMBER_OF_POINTS;
+ static constexpr bool debug_mode = true;
+
+private:
+ trackhat_impl::setting_receiver sig{true};
+
+ [[nodiscard]] bool init_regs();
+ void set_pt_options();
+
+ camera_handle device;
+ pt_settings s{trackhat_metadata::module_name};
+ trackhat_settings t;
+ led_updater led;
+};
+
+struct trackhat_frame final : pt_frame
+{
+ void init_points(const trackHat_ExtendedPoints_t& points, double min_size, double max_size);
+ trackhat_frame() = default;
+ ~trackhat_frame() override = default;
+
+ std::array<point, trackhat_camera::point_count> points;
+};
+
+struct trackhat_preview final : pt_preview
+{
+ QImage get_bitmap() override;
+ void draw_head_center(f x, f y) override;
+ void set_last_frame(const pt_frame&) override; // NOLINT(misc-unconventional-assign-operator)
+
+ trackhat_preview(int w, int h);
+ ~trackhat_preview() override = default;
+ void draw_points();
+ void draw_center();
+
+ OTR_DISABLE_MOVE_COPY(trackhat_preview);
+
+ cv::Mat frame_bgr, frame_bgra;
+ numeric_types::vec2 center{-1, -1};
+ std::array<point, trackhat_camera::point_count> points;
+};
+
+struct trackhat_extractor final : pt_point_extractor
+{
+ void extract_points(const pt_frame& data, pt_preview&, bool, std::vector<vec2>& points) override;
+
+ OTR_DISABLE_MOVE_COPY(trackhat_extractor);
+
+ trackhat_extractor() = default;
+ ~trackhat_extractor() override = default;
+};
diff --git a/tracker-udp/ftnoir_tracker_udp.cpp b/tracker-udp/ftnoir_tracker_udp.cpp
index 6badaa0f..f1f9f033 100644
--- a/tracker-udp/ftnoir_tracker_udp.cpp
+++ b/tracker-udp/ftnoir_tracker_udp.cpp
@@ -70,7 +70,7 @@ void udp::run()
module_status udp::start_tracker(QFrame*)
{
- if (!sock.bind(QHostAddress::Any, quint16(s.port), QUdpSocket::DontShareAddress))
+ if (!sock.bind(QHostAddress::Any, quint16(s.port), QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint))
return error(tr("Can't bind socket -- %1").arg(sock.errorString()));
sock.moveToThread(this);
diff --git a/tracker-udp/lang/de_DE.ts b/tracker-udp/lang/de_DE.ts
new file mode 100644
index 00000000..a609cd25
--- /dev/null
+++ b/tracker-udp/lang/de_DE.ts
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>UICFTNClientControls</name>
+ <message>
+ <source>UDP tracker settings</source>
+ <translation>UDP-Tracker-Einstellungen</translation>
+ </message>
+ <message>
+ <source>Port</source>
+ <translation>Port</translation>
+ </message>
+ <message>
+ <source>Add to axis</source>
+ <translation>Zur Achse hinzufügen</translation>
+ </message>
+ <message>
+ <source>yaw</source>
+ <translation>Gieren</translation>
+ </message>
+ <message>
+ <source>0</source>
+ <translation>0</translation>
+ </message>
+ <message>
+ <source>+90</source>
+ <translation>+90</translation>
+ </message>
+ <message>
+ <source>-90</source>
+ <translation>-90</translation>
+ </message>
+ <message>
+ <source>+180</source>
+ <translation>+180</translation>
+ </message>
+ <message>
+ <source>-180</source>
+ <translation>-180</translation>
+ </message>
+ <message>
+ <source>pitch</source>
+ <translation>Nicken</translation>
+ </message>
+ <message>
+ <source>roll</source>
+ <translation>Rollen</translation>
+ </message>
+</context>
+<context>
+ <name>udp</name>
+ <message>
+ <source>Can&apos;t bind socket -- %1</source>
+ <translation>Kann nicht an Socket binden -- %1</translation>
+ </message>
+</context>
+<context>
+ <name>udp_receiver_dll</name>
+ <message>
+ <source>UDP over network</source>
+ <translation>UDP über Netzwerk</translation>
+ </message>
+</context>
+</TS>
diff --git a/tracker-udp/lang/zh_CN.ts b/tracker-udp/lang/zh_CN.ts
index 0ebbf95f..6730c9b8 100644
--- a/tracker-udp/lang/zh_CN.ts
+++ b/tracker-udp/lang/zh_CN.ts
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
<context>
<name>UICFTNClientControls</name>
<message>
diff --git a/tracker-wii/CMakeLists.txt b/tracker-wii/CMakeLists.txt
index 00cc2f9f..fb840385 100644
--- a/tracker-wii/CMakeLists.txt
+++ b/tracker-wii/CMakeLists.txt
@@ -2,9 +2,12 @@ if(WIN32)
include(opentrack-opencv)
find_package(OpenCV QUIET)
if(OpenCV_FOUND)
+ foreach(k core imgproc)
+ otr_install_lib("opencv_${k}" "${opentrack-libexec}")
+ endforeach()
add_subdirectory(wiiyourself)
otr_module(tracker-wii)
- target_link_libraries(${self} opentrack-tracker-pt-base opentrack-wiiyourself bthprops)
+ target_link_libraries(${self} opencv_imgproc opentrack-tracker-pt-base opentrack-wiiyourself bthprops)
if(MINGW32)
target_link_libraries(${self} hid)
endif()
diff --git a/tracker-wii/lang/de_DE.ts b/tracker-wii/lang/de_DE.ts
new file mode 100644
index 00000000..fe05cb8e
--- /dev/null
+++ b/tracker-wii/lang/de_DE.ts
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>wii_metadata_pt</name>
+ <message>
+ <source>WiiPointTracker 1.1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/tracker-wii/lang/zh_CN.ts b/tracker-wii/lang/zh_CN.ts
index d67c57ad..6959d95b 100644
--- a/tracker-wii/lang/zh_CN.ts
+++ b/tracker-wii/lang/zh_CN.ts
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
<context>
<name>wii_metadata_pt</name>
<message>
diff --git a/tracker-wii/wii_camera.cpp b/tracker-wii/wii_camera.cpp
index 973d7fab..bec47651 100644
--- a/tracker-wii/wii_camera.cpp
+++ b/tracker-wii/wii_camera.cpp
@@ -15,11 +15,9 @@
#include "wii_camera.h"
#include "wii_frame.hpp"
-#include "compat/sleep.hpp"
#include "compat/math-imports.hpp"
#include <opencv2/imgproc.hpp>
-
#include <bluetoothapis.h>
using namespace pt_module;
@@ -35,83 +33,80 @@ WIICamera::WIICamera(const QString& module_name) : s { module_name }
WIICamera::~WIICamera()
{
- // XXX why is this commented out? -sh 20190414
- //stop();
+ stop();
}
QString WIICamera::get_desired_name() const
{
- return desired_name;
+ return QStringLiteral("Wii");
}
QString WIICamera::get_active_name() const
{
- return active_name;
+ return get_desired_name();
}
void WIICamera::show_camera_settings()
{
-
}
WIICamera::result WIICamera::get_info() const
{
- if (cam_info.res_x == 0 || cam_info.res_y == 0)
- return result(false, pt_camera_info());
- return result(true, cam_info);
+ if (cam_info.res_x == 0 || cam_info.res_y == 0)
+ return result(false, pt_camera_info());
+ return result(true, cam_info);
}
WIICamera::result WIICamera::get_frame(pt_frame& frame_)
{
- cv::Mat& frame = frame_.as<WIIFrame>()->mat;
+ cv::Mat& frame = frame_.as<WIIFrame>()->mat;
struct wii_info& wii = frame_.as<WIIFrame>()->wii;
+ const wii_camera_status new_frame = get_frame(frame);
- const wii_camera_status new_frame = get_frame(frame);
//create a fake blank frame
- frame = cv::Mat(cam_info.res_x, cam_info.res_y, CV_8UC3, cv::Scalar(0, 0, 0));
+ frame.create(cam_info.res_x, cam_info.res_y, CV_8UC3);
+ frame.setTo({0});
wii.status = new_frame;
- switch (new_frame)
- {
+ switch (new_frame)
+ {
case wii_cam_data_change:
- get_status(wii);
- get_points(wii);
+ get_status(wii);
+ get_points(wii);
break;
case wii_cam_data_no_change:
return result(false, cam_info);
- default:
- break;
+ default:
+ break;
}
return result(true, cam_info);
}
-bool WIICamera::start(const QString&, int, int, int)
+bool WIICamera::start(const pt_settings&)
{
+ if (m_pDev)
+ return true;
m_pDev = std::make_unique<wiimote>();
m_pDev->ChangedCallback = on_state_change;
m_pDev->CallbackTriggerFlags = (state_change_flags)(CONNECTED |
EXTENSION_CHANGED |
MOTIONPLUS_CHANGED);
- return true;
+ return true;
}
void WIICamera::stop()
{
- desired_name = QString();
- active_name = QString();
- cam_info = pt_camera_info();
- cam_desired = pt_camera_info();
- onExit = true;
-
- if (m_pDev)
- {
- m_pDev->ChangedCallback = nullptr;
- m_pDev->Disconnect();
- m_pDev = nullptr;
- }
-
- Beep(1000, 200);
+ if (!m_pDev)
+ return;
+
+ cam_info = {};
+ cam_desired = {};
+ pitch_ = 0; roll_ = 0;
+
+ m_pDev->ChangedCallback = nullptr;
+ m_pDev->Disconnect();
+ m_pDev = nullptr;
}
#ifdef __MINGW32__
@@ -149,7 +144,6 @@ wii_camera_status WIICamera::pair()
while (ibtidx < max_devices && BluetoothFindNextRadio(&bt_param, hbtlist + ibtidx));
BluetoothFindRadioClose(hbt);
-
int i;
bool error = false;
for (i = 0; i < ibtidx; i++)
diff --git a/tracker-wii/wii_camera.h b/tracker-wii/wii_camera.h
index 3d0ad1aa..3a7993aa 100644
--- a/tracker-wii/wii_camera.h
+++ b/tracker-wii/wii_camera.h
@@ -27,47 +27,39 @@ namespace pt_module {
struct WIICamera final : pt_camera
{
- WIICamera(const QString& module_name);
- ~WIICamera() override;
+ WIICamera(const QString& module_name);
+ ~WIICamera() override;
- bool start(const QString& name, int fps, int res_x, int res_y) override;
- void stop() override;
+ bool start(const pt_settings&) override;
+ void stop() override;
- result get_frame(pt_frame& Frame) override;
- result get_info() const override;
+ result get_frame(pt_frame& Frame) override;
+ result get_info() const override;
- pt_camera_info get_desired() const override { return cam_desired; }
- QString get_desired_name() const override;
- QString get_active_name() const override;
+ pt_camera_info get_desired() const override { return cam_desired; }
+ QString get_desired_name() const override;
+ QString get_active_name() const override;
- void set_fov(f x) override { (void) x; }
- void show_camera_settings() override;
+ void set_fov(f x) override { (void) x; }
+ void show_camera_settings() override;
private:
std::unique_ptr<wiimote> m_pDev;
static void on_state_change(wiimote &remote,
state_change_flags changed,
const wiimote_state &new_state);
- bool onExit = false;
wii_camera_status pair();
wii_camera_status get_frame(cv::Mat& Frame);
bool get_points(struct wii_info& wii);
void get_status(struct wii_info& wii);
- double dt_mean = 0;
+ pt_camera_info cam_info;
+ pt_camera_info cam_desired;
+ int pitch_ = 0, roll_ = 0;
+ pt_settings s;
- Timer t;
-
- pt_camera_info cam_info;
- pt_camera_info cam_desired;
- QString desired_name, active_name;
-
- pt_settings s;
-
- int pitch_ = 0, roll_ = 0;
-
- static constexpr inline double dt_eps = 1./384;
+ static constexpr inline double dt_eps = 1./384;
};
} // ns pt_module
diff --git a/tracker-wii/wii_frame.cpp b/tracker-wii/wii_frame.cpp
index 8f18f4b8..fcde5235 100644
--- a/tracker-wii/wii_frame.cpp
+++ b/tracker-wii/wii_frame.cpp
@@ -17,7 +17,7 @@
using namespace pt_module;
-WIIPreview& WIIPreview::operator=(const pt_frame& frame_)
+void WIIPreview::set_last_frame(const pt_frame& frame_)
{
const struct wii_info& wii = frame_.as_const<WIIFrame>()->wii;
const cv::Mat& frame = frame_.as_const<const WIIFrame>()->mat;
@@ -25,18 +25,13 @@ WIIPreview& WIIPreview::operator=(const pt_frame& frame_)
status = wii.status;
if (frame.channels() != 3)
- {
eval_once(qDebug() << "tracker/pt: camera frame depth: 3 !=" << frame.channels());
- return *this;
- }
const bool need_resize = frame.cols != frame_out.cols || frame.rows != frame_out.rows;
if (need_resize)
cv::resize(frame, frame_copy, cv::Size(frame_out.cols, frame_out.rows), 0, 0, cv::INTER_NEAREST);
else
frame.copyTo(frame_copy);
-
- return *this;
}
WIIPreview::WIIPreview(int w, int h)
diff --git a/tracker-wii/wii_frame.hpp b/tracker-wii/wii_frame.hpp
index 5d6dd199..bbb0c469 100644
--- a/tracker-wii/wii_frame.hpp
+++ b/tracker-wii/wii_frame.hpp
@@ -47,7 +47,7 @@ struct WIIPreview final : pt_preview
{
WIIPreview(int w, int h);
- WIIPreview& operator=(const pt_frame& frame) override;
+ void set_last_frame(const pt_frame& frame) override;
QImage get_bitmap() override;
void draw_head_center(f x, f y) override;
@@ -58,7 +58,7 @@ private:
static void ensure_size(cv::Mat& frame, int w, int h, int type);
cv::Mat frame_copy, frame_out;
- wii_camera_status status;
+ wii_camera_status status = wii_cam_wait_for_connect;
};
} // ns pt_module
diff --git a/tracker-wii/wii_module.cpp b/tracker-wii/wii_module.cpp
index 0358004f..b384078c 100644
--- a/tracker-wii/wii_module.cpp
+++ b/tracker-wii/wii_module.cpp
@@ -15,6 +15,8 @@
#include "pt-api.hpp"
+#include "compat/linkage-macros.hpp"
+
#include <memory>
static const QString module_name = "tracker-wii-pt";
@@ -62,8 +64,10 @@ struct wii_dialog_pt : TrackerDialog_PT
wii_dialog_pt();
};
-struct wii_metadata_pt : Metadata
+struct OTR_GENERIC_EXPORT wii_metadata_pt : Metadata
{
+ Q_OBJECT
+
QString name() { return tr("WiiPointTracker 1.1"); }
QIcon icon() { return QIcon(":/Resources/wii.png"); }
};
diff --git a/tracker-wii/wii_point_extractor.cpp b/tracker-wii/wii_point_extractor.cpp
index 393dc1c9..4f1f92b9 100644
--- a/tracker-wii/wii_point_extractor.cpp
+++ b/tracker-wii/wii_point_extractor.cpp
@@ -27,7 +27,6 @@ using namespace pt_module;
WIIPointExtractor::WIIPointExtractor(const QString& module_name) : s(module_name)
{
-
}
//define a temp draw function
@@ -35,8 +34,8 @@ void WIIPointExtractor::draw_point(cv::Mat& preview_frame, const vec2& p, const
{
static constexpr int len = 9;
- cv::Point p2(iround(p[0] * preview_frame.cols + preview_frame.cols / 2.f),
- iround(-p[1] * preview_frame.cols + preview_frame.rows / 2.f));
+ cv::Point p2(iround(p[0] * preview_frame.cols + preview_frame.cols / 2.f),
+ iround(-p[1] * preview_frame.cols + preview_frame.rows / 2.f));
cv::line(preview_frame,
cv::Point(p2.x - len, p2.y),
@@ -48,18 +47,15 @@ void WIIPointExtractor::draw_point(cv::Mat& preview_frame, const vec2& p, const
cv::Point(p2.x, p2.y + len),
color,
thickness);
-};
+}
-bool WIIPointExtractor::draw_points(cv::Mat& preview_frame, const struct wii_info& wii, std::vector<vec2>& points)
+void WIIPointExtractor::draw_points(cv::Mat& preview_frame, const struct wii_info& wii)
{
constexpr int W = 1024;
constexpr int H = 768;
- points.reserve(4);
- points.clear();
- for (unsigned index = 0; index < 4; index++) // NOLINT(modernize-loop-convert)
+ for (const wii_info_points& dot : wii.Points)
{
- const struct wii_info_points &dot = wii.Points[index];
if (dot.bvis) {
//qDebug() << "wii:" << dot.RawX << "+" << dot.RawY;
//anti-clockwise rotate the 2D point
@@ -70,24 +66,19 @@ bool WIIPointExtractor::draw_points(cv::Mat& preview_frame, const struct wii_inf
//vec2 dt((2.0f*RX - W) / W, -(2.0f*RY - H ) / W);
vec2 dt;
std::tie(dt[0], dt[1]) = to_screen_pos(RX, RY, W, H);
-
- points.push_back(dt);
- draw_point(preview_frame, dt, cv::Scalar(0, 255, 0), clamp(dot.isize, 1, 32));
+ draw_point(preview_frame, dt, cv::Scalar(0, 255, 0), std::clamp(dot.isize, 1, 32));
}
}
- const bool success = points.size() >= PointModel::N_POINTS;
-
- return success;
}
void WIIPointExtractor::draw_bg(cv::Mat& preview_frame, const struct wii_info& wii)
{
//draw battery status
- cv::line(preview_frame,
- cv::Point(0, 0),
- cv::Point(preview_frame.cols*wii.BatteryPercent / 100, 0),
- (wii.bBatteryDrained ? cv::Scalar(255, 0, 0) : cv::Scalar(0, 140, 0)),
- 2);
+ cv::line(preview_frame,
+ cv::Point(0, 0),
+ cv::Point(preview_frame.cols*wii.BatteryPercent / 100, 0),
+ (wii.bBatteryDrained ? cv::Scalar(255, 0, 0) : cv::Scalar(0, 140, 0)),
+ 2);
//draw horizon
int pdelta = iround(preview_frame.rows / 4.f * tan(wii.Pitch * pi / 180.f));
@@ -100,15 +91,46 @@ void WIIPointExtractor::draw_bg(cv::Mat& preview_frame, const struct wii_info& w
1);
}
-void WIIPointExtractor::extract_points(const pt_frame& frame_, pt_preview& preview_frame_, std::vector<vec2>& points)
+void WIIPointExtractor::extract_points(const pt_frame& frame_,
+ pt_preview& preview_frame_,
+ bool preview_visible,
+ std::vector<vec2>& points)
{
const struct wii_info& wii = frame_.as_const<WIIFrame>()->wii;
cv::Mat& preview_frame = *preview_frame_.as<WIIPreview>();
- if (wii.status == wii_cam_data_change)
- {
- draw_bg(preview_frame, wii);
- draw_points(preview_frame, wii, points);
- }
+ map_points(wii, points);
+ if (preview_visible && wii.status == wii_cam_data_change)
+ {
+ draw_bg(preview_frame, wii);
+ draw_points(preview_frame, wii);
+ }
}
+bool WIIPointExtractor::map_points(const struct wii_info& wii, std::vector<vec2>& points)
+{
+ constexpr int W = 1024;
+ constexpr int H = 768;
+ points.reserve(4);
+ points.clear();
+
+ for (unsigned index = 0; index < 4; index++) // NOLINT(modernize-loop-convert)
+ {
+ const struct wii_info_points &dot = wii.Points[index];
+ if (dot.bvis) {
+ //qDebug() << "wii:" << dot.RawX << "+" << dot.RawY;
+ //anti-clockwise rotate the 2D point
+ const float RX = W - dot.ux;
+ const float RY = H - dot.uy;
+ //vec2 dt((dot.RawX - W / 2.0f) / W, -(dot.RawY - H / 2.0f) / W);
+ //vec2 dt((RX - W / 2.0f) / W, -(RY - H / 2.0f) / W);
+ //vec2 dt((2.0f*RX - W) / W, -(2.0f*RY - H ) / W);
+ vec2 dt;
+ std::tie(dt[0], dt[1]) = to_screen_pos(RX, RY, W, H);
+
+ points.push_back(dt);
+ }
+ }
+
+ return points.size() >= PointModel::N_POINTS;
+}
diff --git a/tracker-wii/wii_point_extractor.h b/tracker-wii/wii_point_extractor.h
index 4208f1b2..b8b25b1a 100644
--- a/tracker-wii/wii_point_extractor.h
+++ b/tracker-wii/wii_point_extractor.h
@@ -23,14 +23,18 @@ class WIIPointExtractor final : public pt_point_extractor
public:
// extracts points from frame and draws some processing info into frame, if draw_output is set
// dt: time since last call in seconds
- void extract_points(const pt_frame& frame, pt_preview& preview_frame, std::vector<vec2>& points) override;
+ void extract_points(const pt_frame& frame,
+ pt_preview& preview_frame,
+ bool preview_visible,
+ std::vector<vec2>& points) override;
WIIPointExtractor(const QString& module_name);
private:
pt_settings s;
static void draw_point(cv::Mat& preview_frame, const vec2& p, const cv::Scalar& color, int thickness = 1);
- static bool draw_points(cv::Mat& preview_frame, const struct wii_info& wii, std::vector<vec2>& points);
+ static void draw_points(cv::Mat& preview_frame, const struct wii_info& wii);
static void draw_bg(cv::Mat& preview_frame, const struct wii_info& wii);
+ static bool map_points(const struct wii_info& wii, std::vector<vec2>& points);
};
} // ns impl
diff --git a/tracker-wii/wiiyourself/lang/zh_CN.ts b/tracker-wii/wiiyourself/lang/zh_CN.ts
index 6401616d..e5ca8aa9 100644
--- a/tracker-wii/wiiyourself/lang/zh_CN.ts
+++ b/tracker-wii/wiiyourself/lang/zh_CN.ts
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
</TS>
diff --git a/trackmouse/CMakeLists.txt b/trackmouse/CMakeLists.txt
deleted file mode 100644
index 6240d4b3..00000000
--- a/trackmouse/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-otr_module(executable EXECUTABLE BIN)
-
-set_target_properties(opentrack-executable PROPERTIES
- SUFFIX "${opentrack-binary-suffix}"
- OUTPUT_NAME "trackmouse"
- PREFIX ""
-)
-
-target_link_libraries(${self} opentrack-user-interface opentrack-version)
diff --git a/trackmouse/images/start.png b/trackmouse/images/start.png
deleted file mode 100644
index b8e6f271..00000000
--- a/trackmouse/images/start.png
+++ /dev/null
Binary files differ
diff --git a/trackmouse/images/stop.png b/trackmouse/images/stop.png
deleted file mode 100644
index 0ff13bd5..00000000
--- a/trackmouse/images/stop.png
+++ /dev/null
Binary files differ
diff --git a/trackmouse/main.cpp b/trackmouse/main.cpp
deleted file mode 100644
index 2313e1ac..00000000
--- a/trackmouse/main.cpp
+++ /dev/null
@@ -1,18 +0,0 @@
-#include "gui/init.hpp"
-#include "window.hpp"
-
-#if defined _WIN32
-# include <windows.h>
-#endif
-
-int main(int argc, char** argv)
-{
- return run_application(argc, argv, []() { return new main_window; });
-}
-
-#if defined _MSC_VER
-int CALLBACK WinMain(HINSTANCE, HINSTANCE, LPSTR, int /* nCmdShow */)
-{
- return main(__argc, __argv);
-}
-#endif
diff --git a/trackmouse/trackmouse-res.qrc b/trackmouse/trackmouse-res.qrc
deleted file mode 100644
index f351b3f2..00000000
--- a/trackmouse/trackmouse-res.qrc
+++ /dev/null
@@ -1,6 +0,0 @@
-<RCC>
- <qresource prefix="/images">
- <file>images/start.png</file>
- <file>images/stop.png</file>
- </qresource>
-</RCC>
diff --git a/trackmouse/trackmouse-settings.cpp b/trackmouse/trackmouse-settings.cpp
deleted file mode 100644
index 45ec36a5..00000000
--- a/trackmouse/trackmouse-settings.cpp
+++ /dev/null
@@ -1,132 +0,0 @@
-#include "logic/main-settings.hpp"
-#include "logic/mappings.hpp"
-
-#include "tracker-pt/pt-settings.hpp"
-#include "filter-accela/accela-settings.hpp"
-#include "proto-mouse/mouse-settings.hpp"
-
-#include "options/options.hpp"
-
-#include <QSettings>
-
-using namespace options;
-
-static void force_spline_settings()
-{
- main_settings main;
-
- axis_opts** all_axis_opts = main.all_axis_opts;
- Mappings mappings { all_axis_opts };
-
- for (unsigned k = 0; k < 6; k++)
- {
- Map& map = mappings(k);
- const QString& prefix = all_axis_opts[k]->prefix();
-
- const QString& name1 = map.name;
- const QString& name2 = map.alt_name;
-
- bundle b = make_bundle(prefix);
-
- spline_detail::settings s1(b, name1, Axis(k));
- spline_detail::settings s2(b, name2, Axis(k));
-
- s1.points = QList<QPointF> { { 180, 180 } };
- s2.points = QList<QPointF> { { 180, 180 } };
-
- b->save();
- }
-}
-
-static void force_main_settings()
-{
- main_settings s;
- s.center_at_startup = true;
- s.reltrans_mode = reltrans_disabled;
- s.neck_enable = false;
-
- module_settings m;
-
- m.tracker_dll = "pt";
- m.protocol_dll = "win32-mouse";
- m.filter_dll = "accela";
-
- s.b->save();
- s.b_map->save();
-}
-
-static void force_pt_settings()
-{
- pt_settings s("tracker-pt");
-
- enum { Clip = 0 };
-
- s.active_model_panel = Clip;
- // XXX TODO these are Mini Clip Right sizes
- s.clip_by = 60;
- s.clip_bz = 38.2;
- s.clip_ty = 42.2;
- s.clip_tz = 12.6;
-
- s.cam_fps = 60;
- s.cam_res_x = 640;
- s.cam_res_y = 480;
- s.camera_name = "PS3Eye Camera";
-
- s.min_point_size = 3.7;
- s.max_point_size = 10;
-
- // XXX TODO auto threshold slider position
- s.auto_threshold = true;
- s.threshold_slider = slider_value(82., s.threshold_slider->min(), s.threshold_slider->max());
-
- s.t_MH_x = 0, s.t_MH_y = 0, s.t_MH_z = 0;
- s.blob_color = pt_color_natural;
- s.fov = 56;
- s.dynamic_pose = false;
-
- s.b->save();
-}
-
-static void force_mouse_settings()
-{
-
- mouse_settings s;
-
- s.Mouse_X = Yaw + 1;
- s.Mouse_Y = Pitch + 1;
-
- s.b->save();
-}
-
-static void force_accela_settings()
-{
- // XXX TODO
-}
-
-static void force_shortcut_settings()
-{
- main_settings s;
- s.key_toggle_tracking1.keycode = "Ins";
- s.key_center1.keycode = "PgUp";
- s.key_toggle1.keycode = "PgDown";
-
- for (key_opts* k : { &s.key_toggle_tracking1, &s.key_center1, &s.key_toggle_press1 })
- {
- k->button = -1;
- k->guid = {};
- }
- s.b->save();
-}
-
-void force_trackmouse_settings()
-{
- options::globals::with_settings_object([](QSettings&) {
- force_main_settings();
- force_spline_settings();
- force_pt_settings();
- force_mouse_settings();
- force_accela_settings();
- force_shortcut_settings();
- });
-}
diff --git a/trackmouse/trackmouse.ico b/trackmouse/trackmouse.ico
deleted file mode 100644
index 5cac8da1..00000000
--- a/trackmouse/trackmouse.ico
+++ /dev/null
Binary files differ
diff --git a/trackmouse/trackmouse.rc b/trackmouse/trackmouse.rc
deleted file mode 100644
index 8df1e9b1..00000000
--- a/trackmouse/trackmouse.rc
+++ /dev/null
@@ -1,2 +0,0 @@
-#include <windows.h>
-IDI_ICON1 ICON "trackmouse.ico"
diff --git a/trackmouse/window.cpp b/trackmouse/window.cpp
deleted file mode 100644
index d6c8a8dd..00000000
--- a/trackmouse/window.cpp
+++ /dev/null
@@ -1,388 +0,0 @@
-/* Copyright (c) 2013-2018, 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 "window.hpp"
-#include "options/options.hpp"
-#include "migration/migration.hpp"
-#include "compat/check-visible.hpp"
-#include "compat/sleep.hpp"
-#include "compat/macros.hpp"
-#include "compat/library-path.hpp"
-#include "compat/math.hpp"
-
-#include <algorithm>
-#include <iterator>
-#include <utility>
-
-#include <QMessageBox>
-#include <QDir>
-#include <QFile>
-#include <QString>
-#include <QList>
-#include <QEventLoop>
-#include <QApplication>
-
-extern "C" const char* const opentrack_version;
-
-using namespace options::globals;
-using namespace options;
-
-#if !defined EXIT_SUCCESS
-# define EXIT_SUCCESS 0
-#endif
-
-#if !defined EXIT_FAILURE
-# define EXIT_FAILURE 1
-#endif
-
-/* FreeBSD sysexits(3)
- *
- * The input data was incorrect in some way. This
- * should only be used for user's data and not system
- * files.
- */
-
-#if !defined EX_OSFILE
-# define EX_OSFILE 72
-#endif
-
-void force_trackmouse_settings();
-
-main_window::main_window() : State(OPENTRACK_BASE_PATH + OPENTRACK_LIBRARY_PATH)
-{
- ui.setupUi(this);
-
- update_button_state(false, false);
-
- // ctrl+q exits
- connect(&kbd_quit, SIGNAL(activated()), this, SLOT(exit()));
-
- if (!set_profile())
- {
- die_on_config_not_writable();
- exit(EX_OSFILE);
- return;
- }
-
- // only tie and connect main screen options after migrations are done
- // below is fine, set_profile() is called already
-
- connect(this, &main_window::start_tracker,
- this, [&] { qDebug() << "start tracker"; start_tracker_(); },
- Qt::QueuedConnection);
-
- connect(this, &main_window::stop_tracker,
- this, [&] { qDebug() << "stop tracker"; stop_tracker_(); },
- Qt::QueuedConnection);
-
- connect(this, &main_window::toggle_tracker,
- this, [&] { qDebug() << "toggle tracker"; toggle_tracker_(); },
- Qt::QueuedConnection);
-
- connect(ui.btnStartTracker, SIGNAL(clicked()), this, SLOT(start_tracker_()));
- connect(ui.btnStopTracker, SIGNAL(clicked()), this, SLOT(stop_tracker_()));
-
- {
- tie_setting(mouse.sensitivity_x, ui.sensitivity_slider);
- tie_setting(mouse.sensitivity_x, ui.sensitivity_label,
- [](double x) { return QString::number(x) + QStringLiteral("%"); });
- // one-way only
- tie_setting(mouse.sensitivity_x, this,
- [this](double x) { mouse.sensitivity_y = *mouse.sensitivity_x; });
-
- // no "ok" button, gotta save on timer
- auto save = [this] {
- qDebug() << "trackmouse: saving settings";
- mouse.b->save();
- save_settings_timer.stop();
- };
-
- auto start_save_timer = [this](double) {
- save_settings_timer.start();
- };
-
- save_settings_timer.setInterval(save_settings_interval_ms);
- save_settings_timer.setSingleShot(true);
-
- ui.sensitivity_slider->setTracking(false);
- connect(&save_settings_timer, &QTimer::timeout, this, save, Qt::DirectConnection);
-#if 1
- // this doesn't fire the timer on application load
- connect(ui.sensitivity_slider, &QSlider::valueChanged, this, start_save_timer, Qt::DirectConnection);
-#else
- // but this does so let's not not use it
- tie_setting(mouse.sensitivity_x, this, start_save_timer);
-#endif
- }
-
- force_trackmouse_settings();
-
- register_shortcuts();
- kbd_quit.setEnabled(true);
-
- setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | windowFlags());
- setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
- adjustSize();
-
- setVisible(true);
- show();
-}
-
-void main_window::register_shortcuts()
-{
- global_shortcuts.reload({
- { s.key_toggle_tracking1, [this](bool) { main_window::toggle_tracker(); }, true },
- });
-
- if (work)
- work->reload_shortcuts();
-}
-
-void main_window::die_on_config_not_writable()
-{
- stop_tracker_();
-
- static const QString pad(16, QChar(' '));
-
- QMessageBox::critical(this,
- tr("The Octopus is sad"),
- tr("Check permissions for your .ini directory:\n\n%1\"%2\n\n"
- "Exiting now."
- ).arg(ini_directory(), pad),
- QMessageBox::Close, QMessageBox::NoButton);
-
- exit(EX_OSFILE);
-}
-
-bool main_window::maybe_die_on_config_not_writable(const QString& current)
-{
- const bool writable =
- with_settings_object([&](QSettings& s) {
- return s.isWritable();
- });
-
- if (writable)
- return false;
-
- if (!QFile(ini_combine(current)).open(QFile::ReadWrite))
- {
- die_on_config_not_writable();
- return true;
- }
-
- return false;
-}
-
-main_window::~main_window()
-{
- // stupid ps3 eye has LED issues
- if (work)
- {
- stop_tracker_();
- close();
-
- constexpr int inc = 100, max = 2000;
-
- for (int k = 0; k < max; k += inc)
- {
- QEventLoop ev;
- ev.processEvents();
- portable::sleep(inc);
- }
- }
-
- exit();
-}
-
-void main_window::set_working_directory()
-{
- QDir::setCurrent(OPENTRACK_BASE_PATH);
-}
-
-void main_window::save_modules()
-{
- m.b->save();
-}
-
-std::tuple<main_window::dylib_ptr, int>
-main_window::module_by_name(const QString& name, Modules::dylib_list& list)
-{
- auto it = std::find_if(list.cbegin(), list.cend(), [&name](const dylib_ptr& lib) {
- if (!lib)
- return name.isEmpty();
- else
- return name == lib->module_name;
- });
-
- if (it == list.cend())
- return { nullptr, -1 };
- else
- return { *it, std::distance(list.cbegin(), it) };
-}
-
-main_window::dylib_ptr main_window::current_tracker()
-{
- auto [ptr, idx] = module_by_name(m.tracker_dll, modules.trackers());
- return ptr;
-}
-
-main_window::dylib_ptr main_window::current_protocol()
-{
- auto [ptr, idx] = module_by_name(m.protocol_dll, modules.protocols());
- return ptr;
-}
-
-main_window::dylib_ptr main_window::current_filter()
-{
- auto [ptr, idx] = module_by_name(m.filter_dll, modules.filters());
- return ptr;
-}
-
-void main_window::update_button_state(bool running, bool inertialp)
-{
- bool not_running = !running;
-#if 0
- ui.iconcomboProfile->setEnabled(not_running);
- ui.btnStartTracker->setEnabled(not_running);
- ui.btnStopTracker->setEnabled(running);
- ui.iconcomboProtocol->setEnabled(not_running);
- ui.iconcomboFilter->setEnabled(not_running);
- ui.iconcomboTrackerSource->setEnabled(not_running);
- ui.profile_button->setEnabled(not_running);
-#endif
- ui.video_frame_label->setVisible(not_running || inertialp);
- if(not_running)
- {
- ui.video_frame_label->setPixmap(QPixmap(":/images/tracking-not-started.png"));
- }
- else {
- ui.video_frame_label->setPixmap(QPixmap(":/images/no-feed.png"));
- }
-}
-
-void main_window::start_tracker_()
-{
- if (work)
- return;
-
- work = std::make_shared<Work>(pose, ev, ui.video_frame, current_tracker(), current_protocol(), current_filter());
-
- if (!work->is_ok())
- {
- work = nullptr;
- return;
- }
-
- if (pTrackerDialog)
- pTrackerDialog->register_tracker(work->libs.pTracker.get());
-
- if (pFilterDialog)
- pFilterDialog->register_filter(work->libs.pFilter.get());
-
- if (pProtocolDialog)
- pProtocolDialog->register_protocol(work->libs.pProtocol.get());
-
- // NB check valid since SelectedLibraries ctor called
- // trackers take care of layout state updates
- const bool is_inertial = ui.video_frame->layout() == nullptr;
- update_button_state(true, is_inertial);
-
- ui.btnStopTracker->setFocus();
-}
-
-void main_window::stop_tracker_()
-{
- if (!work)
- return;
-
- with_tracker_teardown sentinel;
-
- if (pTrackerDialog)
- pTrackerDialog->unregister_tracker();
-
- if (pProtocolDialog)
- pProtocolDialog->unregister_protocol();
-
- if (pFilterDialog)
- pFilterDialog->unregister_filter();
-
- work = nullptr;
-
- update_button_state(false, false);
- set_title();
- ui.btnStartTracker->setFocus();
-}
-
-void main_window::set_title(const QString& game_title)
-{
- static const QString version{opentrack_version};
- static const QString sep { tr(" :: ") };
- static const QString pat1{ version + sep + "%1" + sep + "%2" };
- static const QString pat2{ version + sep + "%1" };
-
- const QString current = ini_filename();
-
- if (game_title.isEmpty())
- setWindowTitle(pat2.arg(current));
- else
- setWindowTitle(pat1.arg(current, game_title));
-}
-
-void main_window::exit(int status)
-{
- if (exiting_already)
- return;
- exiting_already = true;
-
- qDebug() << "trackmouse: saving settings on app exit";
- save_settings_timer.stop();
- mouse.b->save();
-
- //close();
- QApplication::setQuitOnLastWindowClosed(true);
- QApplication::exit(status);
-}
-
-bool main_window::set_profile()
-{
- if (maybe_die_on_config_not_writable(OPENTRACK_DEFAULT_CONFIG))
- return false;
-
- set_profile_in_registry();
-
- options::detail::bundler::refresh_all_bundles();
-
- // migrations are for config layout changes and other user-visible
- // incompatibilities in future versions
- run_migrations();
-
- set_title();
-
- return true;
-}
-
-void main_window::closeEvent(QCloseEvent*)
-{
- exit();
-}
-
-void main_window::set_profile_in_registry()
-{
- with_global_settings_object([&](QSettings& s) {
- s.setValue(OPENTRACK_CONFIG_FILENAME_KEY, OPENTRACK_DEFAULT_CONFIG);
- });
-}
-
-void main_window::toggle_tracker_()
-{
- qDebug() << "toggle tracker";
- if (work)
- stop_tracker_();
- else
- start_tracker_();
-}
diff --git a/trackmouse/window.hpp b/trackmouse/window.hpp
deleted file mode 100644
index 2c196852..00000000
--- a/trackmouse/window.hpp
+++ /dev/null
@@ -1,94 +0,0 @@
-#pragma once
-
-/* Copyright (c) 2013-2016, Stanislaw Halik <sthalik@misaki.pl>
-
- * Permission to use, copy, modify, and/or distribute this
- * software for any purpose with or without fee is hereby granted,
- * provided that the above copyright notice and this permission
- * notice appear in all copies.
- */
-
-#pragma once
-
-#include "ui_window.h"
-#include "proto-mouse/mouse-settings.hpp"
-
-#include "api/plugin-support.hpp"
-#include "logic/main-settings.hpp"
-#include "logic/pipeline.hpp"
-#include "logic/shortcuts.h"
-#include "logic/work.hpp"
-#include "logic/state.hpp"
-#include "options/options.hpp"
-
-#include <tuple>
-#include <memory>
-
-#include <QMainWindow>
-#include <QKeySequence>
-#include <QShortcut>
-#include <QPixmap>
-#include <QTimer>
-#include <QString>
-
-class main_window final : public QMainWindow, private State
-{
- Q_OBJECT
-
- Ui::window ui;
-
- QTimer save_settings_timer { this };
-
- Shortcuts global_shortcuts;
- module_settings m;
- mouse_settings mouse;
-
- QShortcut kbd_quit { QKeySequence("Ctrl+Q"), this };
- std::unique_ptr<IFilterDialog> pFilterDialog;
- std::unique_ptr<IProtocolDialog> pProtocolDialog;
- std::unique_ptr<ITrackerDialog> pTrackerDialog;
- bool exiting_already { false };
-
- using dylib_ptr = Modules::dylib_ptr;
- using dylib_list = Modules::dylib_list;
-
- static std::tuple<dylib_ptr, int> module_by_name(const QString& name, Modules::dylib_list& list);
-
- dylib_ptr current_tracker();
- dylib_ptr current_protocol();
- dylib_ptr current_filter();
-
- void update_button_state(bool running, bool inertialp);
-
- void set_title(const QString& game_title = QString());
-
- void set_profile_in_registry();
- void register_shortcuts();
-
- void closeEvent(QCloseEvent *event) override;
-
- bool maybe_die_on_config_not_writable(const QString& current);
- void die_on_config_not_writable();
-
- static constexpr int save_settings_interval_ms = 2500;
-
-private slots:
- void save_modules();
- void exit(int status = EXIT_SUCCESS);
- bool set_profile();
-
- void start_tracker_();
- void stop_tracker_();
- void toggle_tracker_();
-
- static void set_working_directory();
-
-signals:
- void start_tracker();
- void stop_tracker();
- void toggle_tracker();
-
-public:
- main_window();
- ~main_window() override;
-};
diff --git a/trackmouse/window.ui b/trackmouse/window.ui
deleted file mode 100644
index c79ae846..00000000
--- a/trackmouse/window.ui
+++ /dev/null
@@ -1,458 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>window</class>
- <widget class="QMainWindow" name="window">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>755</width>
- <height>240</height>
- </rect>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="maximumSize">
- <size>
- <width>16777215</width>
- <height>240</height>
- </size>
- </property>
- <property name="windowTitle">
- <string>trackmouse prototype</string>
- </property>
- <widget class="QWidget" name="frame">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="maximumSize">
- <size>
- <width>16777215</width>
- <height>240</height>
- </size>
- </property>
- <layout class="QHBoxLayout" name="horizontalLayout_3">
- <property name="spacing">
- <number>6</number>
- </property>
- <property name="leftMargin">
- <number>6</number>
- </property>
- <property name="topMargin">
- <number>0</number>
- </property>
- <property name="rightMargin">
- <number>12</number>
- </property>
- <property name="bottomMargin">
- <number>0</number>
- </property>
- <item>
- <widget class="QFrame" name="video_feed">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>320</width>
- <height>240</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>320</width>
- <height>240</height>
- </size>
- </property>
- <widget class="QFrame" name="video_frame">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>320</width>
- <height>240</height>
- </rect>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>320</width>
- <height>240</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>320</width>
- <height>240</height>
- </size>
- </property>
- <widget class="QLabel" name="video_frame_label">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>320</width>
- <height>240</height>
- </rect>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>320</width>
- <height>240</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>320</width>
- <height>240</height>
- </size>
- </property>
- <property name="font">
- <font>
- <family>Candara</family>
- <pointsize>37</pointsize>
- <weight>50</weight>
- <bold>false</bold>
- <kerning>true</kerning>
- </font>
- </property>
- <property name="text">
- <string/>
- </property>
- <property name="pixmap">
- <pixmap resource="../../gui/opentrack-res.qrc">:/images/tracking-not-started.png</pixmap>
- </property>
- <property name="scaledContents">
- <bool>false</bool>
- </property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
- </property>
- <property name="wordWrap">
- <bool>true</bool>
- </property>
- </widget>
- </widget>
- </widget>
- </item>
- <item>
- <widget class="QWidget" name="widget_3" native="true">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout_2">
- <property name="spacing">
- <number>12</number>
- </property>
- <property name="leftMargin">
- <number>8</number>
- </property>
- <property name="topMargin">
- <number>12</number>
- </property>
- <property name="rightMargin">
- <number>12</number>
- </property>
- <property name="bottomMargin">
- <number>12</number>
- </property>
- <item>
- <widget class="QGroupBox" name="groupBox_3">
- <property name="title">
- <string>Keyboard shortcuts</string>
- </property>
- <layout class="QGridLayout" name="gridLayout">
- <item row="0" column="0">
- <widget class="QLabel" name="label_2">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="font">
- <font>
- <pointsize>12</pointsize>
- <weight>75</weight>
- <bold>true</bold>
- <stylestrategy>PreferAntialias</stylestrategy>
- <kerning>false</kerning>
- </font>
- </property>
- <property name="text">
- <string>start/stop tracking</string>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QLabel" name="label_3">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="font">
- <font>
- <pointsize>12</pointsize>
- <weight>75</weight>
- <bold>true</bold>
- <stylestrategy>PreferAntialias</stylestrategy>
- <kerning>false</kerning>
- </font>
- </property>
- <property name="text">
- <string>Insert</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QLabel" name="label_4">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="font">
- <font>
- <pointsize>12</pointsize>
- <weight>75</weight>
- <bold>true</bold>
- <stylestrategy>PreferAntialias</stylestrategy>
- <kerning>false</kerning>
- </font>
- </property>
- <property name="text">
- <string>center</string>
- </property>
- </widget>
- </item>
- <item row="1" column="1">
- <widget class="QLabel" name="label_5">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="font">
- <font>
- <pointsize>12</pointsize>
- <weight>75</weight>
- <bold>true</bold>
- <stylestrategy>PreferAntialias</stylestrategy>
- <kerning>false</kerning>
- </font>
- </property>
- <property name="text">
- <string>Page Up</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- </widget>
- </item>
- <item row="2" column="0">
- <widget class="QLabel" name="label_6">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="font">
- <font>
- <pointsize>12</pointsize>
- <weight>75</weight>
- <bold>true</bold>
- <stylestrategy>PreferAntialias</stylestrategy>
- <kerning>false</kerning>
- </font>
- </property>
- <property name="text">
- <string>freeze toggle</string>
- </property>
- </widget>
- </item>
- <item row="2" column="1">
- <widget class="QLabel" name="label_7">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="font">
- <font>
- <pointsize>12</pointsize>
- <weight>75</weight>
- <bold>true</bold>
- <stylestrategy>PreferAntialias</stylestrategy>
- <kerning>false</kerning>
- </font>
- </property>
- <property name="text">
- <string>Page Down</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <widget class="QGroupBox" name="groupBox_2">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="title">
- <string>Sensitivity</string>
- </property>
- <layout class="QHBoxLayout">
- <item>
- <widget class="QSlider" name="sensitivity_slider">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>10</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="maximum">
- <number>475</number>
- </property>
- <property name="pageStep">
- <number>1</number>
- </property>
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="tickPosition">
- <enum>QSlider::TicksAbove</enum>
- </property>
- <property name="tickInterval">
- <number>50</number>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QLabel" name="sensitivity_label">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
- <horstretch>2</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>100%</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <widget class="QWidget" name="widget" native="true">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <layout class="QHBoxLayout" name="horizontalLayout">
- <property name="topMargin">
- <number>3</number>
- </property>
- <property name="bottomMargin">
- <number>3</number>
- </property>
- <item>
- <widget class="QCommandLinkButton" name="btnStartTracker">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Start</string>
- </property>
- <property name="icon">
- <iconset resource="trackmouse-res.qrc">
- <normaloff>:/images/images/start.png</normaloff>:/images/images/start.png</iconset>
- </property>
- <property name="iconSize">
- <size>
- <width>43</width>
- <height>20</height>
- </size>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QCommandLinkButton" name="btnStopTracker">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Stop</string>
- </property>
- <property name="icon">
- <iconset resource="trackmouse-res.qrc">
- <normaloff>:/images/images/stop.png</normaloff>:/images/images/stop.png</iconset>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- </layout>
- </widget>
- </widget>
- <resources>
- <include location="trackmouse-res.qrc"/>
- <include location="../../gui/opentrack-res.qrc"/>
- </resources>
- <connections/>
-</ui>
diff --git a/variant/default/_variant.cmake b/variant/default/_variant.cmake
index 7353b0f4..e69de29b 100644
--- a/variant/default/_variant.cmake
+++ b/variant/default/_variant.cmake
@@ -1,32 +0,0 @@
-function(otr_init_variant)
- set_property(GLOBAL PROPERTY opentrack-variant "default")
- set_property(GLOBAL PROPERTY opentrack-ident "opentrack-2.3")
-
- set(subprojects
- "tracker-*"
- "proto-*"
- "filter-*"
- "ext-*"
- "options"
- "api"
- "compat"
- "logic"
- "dinput"
- "gui"
- "main"
- "x-plane-plugin"
- "csv"
- "pose-widget"
- "spline"
- "qxt-mini"
- "macosx"
- "cv"
- "migration"
- "main-window"
- "video"
- "video-*"
- "opentrack"
- )
-
- set_property(GLOBAL PROPERTY opentrack-subprojects "${subprojects}")
-endfunction()
diff --git a/variant/trackmouse/_variant.cmake b/variant/trackmouse/_variant.cmake
deleted file mode 100644
index 1a65f6df..00000000
--- a/variant/trackmouse/_variant.cmake
+++ /dev/null
@@ -1,22 +0,0 @@
-function(otr_init_variant)
- set_property(GLOBAL PROPERTY opentrack-variant "trackmouse")
- set_property(GLOBAL PROPERTY opentrack-ident "trackmouse-prototype")
- set(subprojects
- "tracker-pt"
- "proto-mouse"
- "filter-accela"
- "options"
- "api"
- "compat"
- "logic"
- "dinput"
- "gui"
- "spline"
- "cv"
- "migration"
- "executable"
- "pose-widget"
- "trackmouse"
- )
- set_property(GLOBAL PROPERTY opentrack-subprojects "${subprojects}")
-endfunction()
diff --git a/video-opencv/CMakeLists.txt b/video-opencv/CMakeLists.txt
index c56e65d4..0b2460a4 100644
--- a/video-opencv/CMakeLists.txt
+++ b/video-opencv/CMakeLists.txt
@@ -2,8 +2,11 @@ include(opentrack-opencv)
find_package(OpenCV QUIET)
if(OpenCV_FOUND)
+ foreach(k core videoio imgcodecs imgproc)
+ otr_install_lib("opencv_${k}" "${opentrack-libexec}")
+ endforeach()
otr_module(video-opencv)
- target_link_libraries(${self} opencv_core opencv_videoio opentrack-video)
+ target_link_libraries(${self} opencv_core opencv_imgcodecs opencv_videoio opentrack-video)
if(WIN32)
target_link_libraries(${self} strmiids)
endif()
diff --git a/video-opencv/impl-camera.cpp b/video-opencv/impl-camera.cpp
index 3f2a1b1a..96081399 100644
--- a/video-opencv/impl-camera.cpp
+++ b/video-opencv/impl-camera.cpp
@@ -1,6 +1,7 @@
#include "impl.hpp"
#include "compat/sleep.hpp"
#include "video-property-page.hpp"
+#include <opencv2/core/utils/logger.hpp>
namespace opencv_camera_impl {
@@ -33,7 +34,8 @@ bool cam::is_open()
bool cam::start(info& args)
{
stop();
- cap.emplace(idx + video_capture_backend);
+ cv::utils::logging::setLogLevel(cv::utils::logging::LogLevel::LOG_LEVEL_WARNING);
+ cap.emplace(idx, video_capture_backend);
if (args.width > 0 && args.height > 0)
{
@@ -43,6 +45,9 @@ bool cam::start(info& args)
if (args.fps > 0)
cap->set(cv::CAP_PROP_FPS, args.fps);
+ if (args.use_mjpeg)
+ cap->set(cv::CAP_PROP_FOURCC, cv::VideoWriter::fourcc('M', 'J', 'P', 'G'));
+
if (!cap->isOpened())
goto fail;
diff --git a/video-opencv/impl-metadata.cpp b/video-opencv/impl-metadata.cpp
index ffc51773..7642c017 100644
--- a/video-opencv/impl-metadata.cpp
+++ b/video-opencv/impl-metadata.cpp
@@ -19,11 +19,8 @@ std::vector<QString> metadata::camera_names() const
{
std::vector<std::tuple<QString, int>> names = get_camera_names();
std::vector<QString> ret;
- for (const auto& t : names)
- {
- const auto& [str, idx] = t;
+ for (const auto& [str, idx] : names)
ret.push_back(str);
- }
return ret;
}
diff --git a/video-opencv/impl.hpp b/video-opencv/impl.hpp
index db569823..ed5499b0 100644
--- a/video-opencv/impl.hpp
+++ b/video-opencv/impl.hpp
@@ -8,18 +8,13 @@
#pragma once
#include "video/camera.hpp"
-
#include <optional>
-
-#include <opencv2/core.hpp>
#include <opencv2/videoio.hpp>
namespace opencv_camera_impl {
using namespace video::impl;
-struct cam;
-
struct metadata : camera_
{
metadata();
diff --git a/video-opencv/lang/de_DE.ts b/video-opencv/lang/de_DE.ts
new file mode 100644
index 00000000..1552582e
--- /dev/null
+++ b/video-opencv/lang/de_DE.ts
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+</TS>
diff --git a/video-opencv/lang/nl_NL.ts b/video-opencv/lang/nl_NL.ts
index 6401616d..9e739505 100644
--- a/video-opencv/lang/nl_NL.ts
+++ b/video-opencv/lang/nl_NL.ts
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="nl_NL">
</TS>
diff --git a/video-opencv/lang/ru_RU.ts b/video-opencv/lang/ru_RU.ts
index 6401616d..f62cf2e1 100644
--- a/video-opencv/lang/ru_RU.ts
+++ b/video-opencv/lang/ru_RU.ts
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="ru_RU">
</TS>
diff --git a/video-opencv/lang/zh_CN.ts b/video-opencv/lang/zh_CN.ts
index 6401616d..e5ca8aa9 100644
--- a/video-opencv/lang/zh_CN.ts
+++ b/video-opencv/lang/zh_CN.ts
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
</TS>
diff --git a/video-ps3eye/CMakeLists.txt b/video-ps3eye/CMakeLists.txt
index 2f0bf50b..1f1780f9 100644
--- a/video-ps3eye/CMakeLists.txt
+++ b/video-ps3eye/CMakeLists.txt
@@ -57,6 +57,10 @@ if(TARGET ps3eye-subprocess)
if(EXISTS "${path}")
otr_install_lib("${path}" "${opentrack-libexec}")
endif()
+ set(vcrun "${SDK_LIBUSB}/vcruntime140.dll")
+ if(EXISTS "${vcrun}")
+ otr_install_lib("${vcrun}" "${opentrack-libexec}")
+ endif()
endif()
endif()
diff --git a/video-ps3eye/dialog.ui b/video-ps3eye/dialog.ui
index 1e94ce74..68060eb9 100644
--- a/video-ps3eye/dialog.ui
+++ b/video-ps3eye/dialog.ui
@@ -10,7 +10,7 @@
<x>0</x>
<y>0</y>
<width>439</width>
- <height>211</height>
+ <height>124</height>
</rect>
</property>
<property name="minimumSize">
@@ -31,6 +31,12 @@
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="2">
<widget class="QSpinBox" name="exposure_label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
<property name="readOnly">
<bool>true</bool>
</property>
@@ -64,8 +70,8 @@
<item row="0" column="1">
<widget class="QSlider" name="exposure_slider">
<property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
- <horstretch>1</horstretch>
+ <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
@@ -87,7 +93,7 @@
<item row="1" column="1">
<widget class="QSlider" name="gain_slider">
<property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
diff --git a/filter-kalman/lang/nl_NL.ts b/video-ps3eye/lang/de_DE.ts
index e3c61bbb..f6281358 100644
--- a/filter-kalman/lang/nl_NL.ts
+++ b/video-ps3eye/lang/de_DE.ts
@@ -1,37 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1" language="nl_NL">
+<TS version="2.1" language="de_DE">
<context>
- <name>KalmanUICdialog_kalman</name>
+ <name>Dialog</name>
<message>
- <source>Kalman settings</source>
+ <source>PS3 Eye</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Measurement noise</source>
+ <source>Camera settings</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Rotation</source>
+ <source>Exposure</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>Position</source>
+ <source>Gain</source>
<translation type="unfinished"></translation>
</message>
+</context>
+<context>
+ <name>dialog</name>
<message>
- <source>°</source>
+ <source>Unknown error</source>
<translation type="unfinished"></translation>
</message>
<message>
- <source>-</source>
+ <source>Can&apos;t open camera</source>
<translation type="unfinished"></translation>
</message>
-</context>
-<context>
- <name>kalmanDll</name>
<message>
- <source>Kalman</source>
+ <source>PS3 Eye driver error: %1</source>
<translation type="unfinished"></translation>
</message>
</context>
diff --git a/video-ps3eye/lang/nl_NL.ts b/video-ps3eye/lang/nl_NL.ts
index c98370ec..f3a16fd4 100644
--- a/video-ps3eye/lang/nl_NL.ts
+++ b/video-ps3eye/lang/nl_NL.ts
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="nl_NL">
<context>
<name>Dialog</name>
<message>
@@ -20,4 +20,19 @@
<translation type="unfinished"></translation>
</message>
</context>
+<context>
+ <name>dialog</name>
+ <message>
+ <source>Can&apos;t open camera</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>PS3 Eye driver error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Unknown error</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
</TS>
diff --git a/video-ps3eye/lang/ru_RU.ts b/video-ps3eye/lang/ru_RU.ts
index c98370ec..1edcf50d 100644
--- a/video-ps3eye/lang/ru_RU.ts
+++ b/video-ps3eye/lang/ru_RU.ts
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="ru_RU">
<context>
<name>Dialog</name>
<message>
@@ -20,4 +20,19 @@
<translation type="unfinished"></translation>
</message>
</context>
+<context>
+ <name>dialog</name>
+ <message>
+ <source>Can&apos;t open camera</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>PS3 Eye driver error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Unknown error</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
</TS>
diff --git a/video-ps3eye/lang/stub.ts b/video-ps3eye/lang/stub.ts
index c98370ec..81ffc826 100644
--- a/video-ps3eye/lang/stub.ts
+++ b/video-ps3eye/lang/stub.ts
@@ -20,4 +20,19 @@
<translation type="unfinished"></translation>
</message>
</context>
+<context>
+ <name>dialog</name>
+ <message>
+ <source>Can&apos;t open camera</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>PS3 Eye driver error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Unknown error</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
</TS>
diff --git a/video-ps3eye/lang/zh_CN.ts b/video-ps3eye/lang/zh_CN.ts
index c98370ec..9650c966 100644
--- a/video-ps3eye/lang/zh_CN.ts
+++ b/video-ps3eye/lang/zh_CN.ts
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
<context>
<name>Dialog</name>
<message>
@@ -9,14 +9,29 @@
</message>
<message>
<source>Camera settings</source>
- <translation type="unfinished"></translation>
+ <translation>相机设置</translation>
</message>
<message>
<source>Exposure</source>
- <translation type="unfinished"></translation>
+ <translation>曝光</translation>
</message>
<message>
<source>Gain</source>
+ <translation>增益</translation>
+ </message>
+</context>
+<context>
+ <name>dialog</name>
+ <message>
+ <source>Can&apos;t open camera</source>
+ <translation>无法打开相机</translation>
+ </message>
+ <message>
+ <source>PS3 Eye driver error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Unknown error</source>
<translation type="unfinished"></translation>
</message>
</context>
diff --git a/video-ps3eye/module.cpp b/video-ps3eye/module.cpp
index a7078180..02543082 100644
--- a/video-ps3eye/module.cpp
+++ b/video-ps3eye/module.cpp
@@ -92,7 +92,11 @@ std::unique_ptr<camera> ps3eye_camera_::make_camera(const QString& name)
static bool show_dialog_()
{
- (new dialog)->show();
+ auto& dlg = *new dialog;
+ dlg.setWindowFlag(Qt::MSWindowsFixedSizeDialogHint);
+ dlg.setAttribute(Qt::WA_DeleteOnClose);
+ dlg.adjustSize(); dlg.setFixedSize(dlg.size());
+ dlg.show();
return true;
}
@@ -116,6 +120,8 @@ ps3eye_camera::ps3eye_camera()
wrapper.setWorkingDirectory(library_path);
#ifdef _WIN32
wrapper.setProgram("\"ps3eye-subprocess.exe\"");
+ // workaround apparent Qt 5.15.2 bug -sh 20210817
+ wrapper.setProcessChannelMode(QProcess::ForwardedChannels);
#else
wrapper.setProgram("ps3eye-subprocess");
#endif
@@ -132,6 +138,11 @@ void ps3eye_camera::stop()
if (wrapper.state() != QProcess::NotRunning)
{
+ volatile auto& ptr = *(ps3eye::shm*)shm.ptr();
+ ptr.in.do_exit = 1;
+ std::atomic_thread_fence(std::memory_order_seq_cst);
+ wrapper.waitForFinished(1000);
+
if (wrapper.state() != QProcess::NotRunning)
wrapper.kill();
wrapper.waitForFinished(1000);
@@ -143,6 +154,8 @@ bool ps3eye_camera::start(info& args)
if (!shm.success())
return false;
+ stop();
+
volatile auto& ptr = *(ps3eye::shm*)shm.ptr();
QString error;
@@ -150,7 +163,7 @@ bool ps3eye_camera::start(info& args)
open = false;
fr = {};
- fr.channels = 3;
+ fr.channels = args.num_channels == 1 ? 1 : 3;
fr.channel_size = 1;
if (!args.width || args.width > 320)
@@ -168,12 +181,13 @@ bool ps3eye_camera::start(info& args)
ptr.in.framerate = (uint8_t)std::clamp(args.fps, 30, 187);
ptr.in.gain = (uint8_t)s.gain;
ptr.in.exposure = (uint8_t)s.exposure;
+ ptr.in.channels = args.num_channels == 1 ? 1 : 3;
- sleep_ms = std::clamp(int(std::floor(1000./ptr.in.framerate*2)), 1, 10);
+ sleep_ms = std::clamp(int(std::floor(450./ptr.in.framerate)), 1, 10);
wrapper.start();
- constexpr int sleep_ms = 10, max_sleeps = 5000/sleep_ms;
+ constexpr int sleep_ms = 10, max_sleeps = 2000/sleep_ms;
for (int i = 0; i < max_sleeps; i++)
{
@@ -183,13 +197,13 @@ bool ps3eye_camera::start(info& args)
}
if (ptr.out.error_string[0] == '\0')
- error = "Unknown error";
+ error = QString{};
else
error = QString::fromLatin1((const char*)ptr.out.error_string,
strnlen((const char*)ptr.out.error_string, sizeof(ptr.out.error_string)));
- run_in_thread_async(qApp, [=]() {
- QMessageBox::critical(nullptr, "Can't open camera", "PS3 Eye driver error: " + error, QMessageBox::Close);
+ run_in_thread_async(qApp, [error = std::move(error)] {
+ dialog::show_open_failure_msgbox(error);
});
return false;
@@ -247,16 +261,26 @@ OTR_REGISTER_CAMERA(ps3eye_camera_)
dialog::dialog(QWidget* parent) : QWidget(parent)
{
ui.setupUi(this);
+ t.setInterval(500); t.setSingleShot(true);
tie_setting(s.exposure, ui.exposure_slider);
tie_setting(s.gain, ui.gain_slider);
ui.exposure_label->setValue((int)*s.exposure);
ui.gain_label->setValue((int)*s.gain);
- connect(&s.exposure, value_::value_changed<slider_value>(), this, [this](const slider_value&) { s.set_exposure(); });
- connect(&s.gain, value_::value_changed<slider_value>(), this, [this](const slider_value&) { s.set_gain(); });
+ connect(&s.exposure, value_::value_changed<slider_value>(), this, [this](const slider_value&) { t.stop(); t.start(); });
+ connect(&s.gain, value_::value_changed<slider_value>(), this, [this](const slider_value&) { t.stop(); t.start(); });
connect(ui.exposure_slider, &QSlider::valueChanged, ui.exposure_label, &QSpinBox::setValue);
connect(ui.gain_slider, &QSlider::valueChanged, ui.gain_label, &QSpinBox::setValue);
connect(ui.buttonBox, &QDialogButtonBox::accepted, this, &dialog::do_ok);
connect(ui.buttonBox, &QDialogButtonBox::rejected, this, &dialog::do_cancel);
+ connect(&t, &QTimer::timeout, this, [this] { s.set_exposure(); s.set_gain(); });
+}
+void dialog::show_open_failure_msgbox(const QString& error)
+{
+ const QString& error_ = error.isNull() ? tr("Unknown error") : error;
+ QMessageBox::critical(nullptr,
+ tr("Can't open camera"),
+ tr("PS3 Eye driver error: %1").arg(error_),
+ QMessageBox::Close);
}
// XXX copypasta -sh 20200329
diff --git a/video-ps3eye/module.hpp b/video-ps3eye/module.hpp
index 56ffe5df..f6934d70 100644
--- a/video-ps3eye/module.hpp
+++ b/video-ps3eye/module.hpp
@@ -4,12 +4,13 @@
#include "shm-layout.hpp"
#include "compat/shm.h"
#include "options/options.hpp"
-#include "compat/macros1.h"
+#include "compat/macros.h"
#include "compat/timer.hpp"
#include "ui_dialog.h"
#include <QDialog>
#include <QProcess>
+#include <QTimer>
using namespace options;
@@ -34,6 +35,7 @@ class dialog final : public QWidget
Q_OBJECT
Ui_Dialog ui;
settings s;
+ QTimer t{this};
shm_wrapper shm { "ps3eye-driver-shm", nullptr, sizeof(ps3eye::shm) };
@@ -41,10 +43,11 @@ class dialog final : public QWidget
void do_cancel() { s.b->reload(); close(); deleteLater(); }
protected:
- void closeEvent(QCloseEvent*) override { do_cancel(); }
+ void closeEvent(QCloseEvent*) override { do_cancel(); if (t.isActive()) { s.set_exposure(); s.set_gain(); } }
public:
explicit dialog(QWidget* parent = nullptr);
+ static void show_open_failure_msgbox(const QString& error);
};
struct ps3eye_camera final : video::impl::camera
@@ -54,7 +57,7 @@ struct ps3eye_camera final : video::impl::camera
settings s;
frame fr;
Timer t;
- unsigned char data[640 * 480 * 3] = {};
+ unsigned char data[640 * 480 * ps3eye::num_channels] = {};
int framerate = 30, sleep_ms = 1;
bool open = false;
unsigned timecode = 0;
diff --git a/video-ps3eye/ps3eye-driver b/video-ps3eye/ps3eye-driver
-Subproject ac056aa85dca83be3b1a14df7b20fd07104e052
+Subproject fe4eef71669bc6365a651b3c734ebd2bb5f57f8
diff --git a/video-ps3eye/shm-layout.hpp b/video-ps3eye/shm-layout.hpp
index 577021b9..65b0a4f1 100644
--- a/video-ps3eye/shm-layout.hpp
+++ b/video-ps3eye/shm-layout.hpp
@@ -3,11 +3,13 @@
namespace ps3eye {
+static constexpr unsigned num_channels = 3;
+
struct shm_in {
enum class mode : uint8_t { qvga, vga, };
uint32_t settings_updated;
- uint8_t framerate;
+ uint8_t framerate, channels;
mode resolution;
//uint8_t sharpness, contrast, brightness hue, saturation;
uint8_t gain, exposure, auto_gain, test_pattern;
@@ -23,18 +25,14 @@ struct shm_out
status status_;
char error_string[256];
union {
- uint8_t data_320x240[320][240][3];
- uint8_t data_640x480[640][480][3];
+ uint8_t data_320x240[320][240][num_channels];
+ uint8_t data_640x480[640][480][num_channels];
};
};
-struct shm {
- static constexpr unsigned _cacheline_len = 64;
- static constexpr unsigned _padding_len =
- (_cacheline_len - (sizeof(shm_in) & (_cacheline_len - 1))) & (_cacheline_len - 1);
-
+struct alignas(64) shm {
shm_out out;
- const char* _padding[_padding_len];
+ [[maybe_unused]] const char _padding[128 - sizeof(shm_out) % 128]; // NOLINT
shm_in in;
};
diff --git a/video-ps3eye/wrapper.cxx b/video-ps3eye/wrapper.cxx
index eca8968d..b7f58185 100644
--- a/video-ps3eye/wrapper.cxx
+++ b/video-ps3eye/wrapper.cxx
@@ -52,6 +52,7 @@ int main(int argc, char** argv)
volatile auto& ptr_ = *(ps3eye::shm*)mem_.ptr();
volatile auto& in = ptr_.in;
volatile auto& out = ptr_.out;
+ int num_channels = in.channels;
auto cameras = ps3eye::list_devices();
@@ -61,16 +62,18 @@ int main(int argc, char** argv)
error(out, "no camera found");
auto& camera = cameras[0];
- camera->set_debug(false);
+ camera->set_debug(true);
auto* frame = (uint8_t*)out.data_640x480;
decltype(out.timecode) timecode = 0;
+ auto fmt = num_channels == 1 ? ps3eye::format::Gray : ps3eye::format::BGR;
+
{
int framerate = in.framerate;
if (framerate <= 0)
framerate = 60;
- if (!camera->init(get_mode(in.resolution), framerate))
+ if (!camera->init(get_mode(in.resolution), framerate, fmt))
error(out, "camera init failed: %s", camera->error_string());
update_settings(*camera, in);
@@ -94,7 +97,7 @@ int main(int argc, char** argv)
int framerate = in.framerate;
if (framerate <= 0)
framerate = 60;
- if (!camera->init(get_mode(in.resolution), framerate))
+ if (!camera->init(get_mode(in.resolution), framerate, fmt))
error(out, "camera init failed: %s", camera->error_string());
if (!camera->start())
error(out, "can't start camera: %s", camera->error_string());
diff --git a/video/camera.cpp b/video/camera.cpp
index 42320402..a66d8a59 100644
--- a/video/camera.cpp
+++ b/video/camera.cpp
@@ -4,8 +4,12 @@
#include <utility>
#include <QMutex>
-static std::vector<std::unique_ptr<video::impl::camera_>> metadata;
-static QMutex mtx;
+std::pair<std::vector<std::unique_ptr<video::impl::camera_>>&, QMutex&> get_metadata()
+{
+ static std::vector<std::unique_ptr<video::impl::camera_>> metadata;
+ static QMutex mtx;
+ return { metadata, mtx };
+}
namespace video::impl {
@@ -17,6 +21,7 @@ camera::~camera() = default;
void register_camera(std::unique_ptr<impl::camera_> camera)
{
+ auto [metadata, mtx] = get_metadata();
QMutexLocker l(&mtx);
metadata.push_back(std::move(camera));
}
@@ -27,6 +32,7 @@ namespace video {
bool show_dialog(const QString& camera_name)
{
+ auto [metadata, mtx] = get_metadata();
QMutexLocker l(&mtx);
for (auto& camera : metadata)
@@ -39,6 +45,7 @@ bool show_dialog(const QString& camera_name)
std::unique_ptr<camera_impl> make_camera_(const QString& name)
{
+ auto [metadata, mtx] = get_metadata();
QMutexLocker l(&mtx);
for (auto& camera : metadata)
@@ -54,6 +61,7 @@ std::unique_ptr<camera_impl> make_camera(const QString& name)
if (auto ret = make_camera_(name))
return ret;
+ auto [metadata, mtx] = get_metadata();
for (auto& camera : metadata)
for (const QString& name_ : camera->camera_names())
if (auto ret = camera->make_camera(name_))
@@ -64,6 +72,8 @@ std::unique_ptr<camera_impl> make_camera(const QString& name)
std::vector<QString> camera_names()
{
+ auto [metadata, mtx] = get_metadata();
+
QMutexLocker l(&mtx);
std::vector<QString> names; names.reserve(32);
diff --git a/video/camera.hpp b/video/camera.hpp
index a0fe0adb..6181dbf3 100644
--- a/video/camera.hpp
+++ b/video/camera.hpp
@@ -48,11 +48,14 @@ struct OTR_VIDEO_EXPORT camera
{
struct info final
{
+ enum : unsigned char { channels_gray = 1, channels_bgr = 3 };
// TODO: expose FOV-based focal length for regular webcams
int width = 0, height = 0, fps = 0;
double fx = 0, fy = 0; // focal length
double P_x = 0, P_y = 0; // principal point
double dist_c[8] {}; // distortion coefficients
+ bool use_mjpeg = false;
+ int num_channels = channels_bgr;
};
camera();
@@ -75,12 +78,22 @@ void register_camera(std::unique_ptr<impl::camera_> metadata);
static const char init_ ## ctr = \
(::video::impl::register_camera(std::make_unique<type>()), 0);
-#define OTR_REGISTER_CAMERA2(type, ctr) \
- OTR_REGISTER_CAMERA3(type, ctr)
+#ifdef _MSC_VER
+ // shared library targets without any symbols break cmake build
+# define OTR_REGISTER_CAMERA_IMPL(type) \
+ extern "C" [[maybe_unused]] __declspec(dllexport) \
+ void _opentrack_module_video_ ##type (void) {}
+# define OTR_REGISTER_CAMERA_IMPL2(type) \
+ OTR_REGISTER_CAMERA_IMPL(type)
+#else
+# define OTR_REGISTER_CAMERA_IMPL2(type)
+#endif
+#define OTR_REGISTER_CAMERA2(type, ctr) \
+ OTR_REGISTER_CAMERA3(type, ctr) \
+ OTR_REGISTER_CAMERA_IMPL2(type)
#define OTR_REGISTER_CAMERA(type) \
OTR_REGISTER_CAMERA2(type, __COUNTER__)
-
namespace video
{
using camera_impl = impl::camera;
diff --git a/video/lang/de_DE.ts b/video/lang/de_DE.ts
new file mode 100644
index 00000000..1552582e
--- /dev/null
+++ b/video/lang/de_DE.ts
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+</TS>
diff --git a/video/lang/nl_NL.ts b/video/lang/nl_NL.ts
index 6401616d..9e739505 100644
--- a/video/lang/nl_NL.ts
+++ b/video/lang/nl_NL.ts
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="nl_NL">
</TS>
diff --git a/video/lang/ru_RU.ts b/video/lang/ru_RU.ts
index 6401616d..f62cf2e1 100644
--- a/video/lang/ru_RU.ts
+++ b/video/lang/ru_RU.ts
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="ru_RU">
</TS>
diff --git a/video/lang/zh_CN.ts b/video/lang/zh_CN.ts
index 6401616d..e5ca8aa9 100644
--- a/video/lang/zh_CN.ts
+++ b/video/lang/zh_CN.ts
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
</TS>
diff --git a/video/video-widget.cpp b/video/video-widget.cpp
index 4399b11a..3018a3c4 100644
--- a/video/video-widget.cpp
+++ b/video/video-widget.cpp
@@ -17,9 +17,13 @@ void video_widget::init_image_nolock()
video_widget::video_widget(QWidget* parent) : QWidget(parent)
{
+ if (parent)
+ setFixedSize(parent->size());
+ else
+ setFixedSize(320, 240);
init_image_nolock();
connect(&timer, &QTimer::timeout, this, &video_widget::draw_image, Qt::DirectConnection);
- timer.start(65);
+ timer.start(15);
}
void video_widget::update_image(const QImage& img)
@@ -84,4 +88,3 @@ void video_widget::set_fresh(bool x)
{
fresh_.store(x, std::memory_order_release);
}
-
diff --git a/video/video-widget.hpp b/video/video-widget.hpp
index bb218c69..d70ee443 100644
--- a/video/video-widget.hpp
+++ b/video/video-widget.hpp
@@ -18,7 +18,7 @@
#include <QImage>
#include <QTimer>
-#include <QMutex>
+#include <QRecursiveMutex>
struct OTR_VIDEO_EXPORT video_widget : QWidget
{
@@ -29,12 +29,12 @@ struct OTR_VIDEO_EXPORT video_widget : QWidget
void resizeEvent(QResizeEvent*) override;
void paintEvent(QPaintEvent*) override;
void draw_image();
+ bool fresh() const;
protected:
- mutable QMutex mtx { QMutex::NonRecursive };
+ mutable QRecursiveMutex mtx;
QImage texture;
std::vector<unsigned char> vec;
- bool fresh() const;
void set_fresh(bool x);
void set_image(const unsigned char* src, int width, int height, int stride, QImage::Format fmt);
diff --git a/x-plane-plugin/plugin.c b/x-plane-plugin/plugin.c
index ed72e50e..e43ee0ef 100644
--- a/x-plane-plugin/plugin.c
+++ b/x-plane-plugin/plugin.c
@@ -134,6 +134,8 @@ static int TrackToggleHandler(XPLMCommandRef inCommand,
XPLMCommandPhase inPhase,
void* inRefCon)
{
+ if (inPhase != xplm_CommandBegin) return 0;
+
if (track_disabled)
{
//Enable
@@ -156,6 +158,8 @@ static int TranslationToggleHandler(XPLMCommandRef inCommand,
XPLMCommandPhase inPhase,
void* inRefCon)
{
+ if (inPhase != xplm_CommandBegin) return 0;
+
translation_disabled = !translation_disabled;
if (!translation_disabled)
{