diff options
Diffstat (limited to 'gui/main-window.cpp')
-rw-r--r-- | gui/main-window.cpp | 600 |
1 files changed, 600 insertions, 0 deletions
diff --git a/gui/main-window.cpp b/gui/main-window.cpp new file mode 100644 index 00000000..d3f9e96e --- /dev/null +++ b/gui/main-window.cpp @@ -0,0 +1,600 @@ +/* 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. + */ + +#include "main-window.hpp" +#include "opentrack-logic/tracker.h" +#include "opentrack-compat/options.hpp" +#include "opentrack-library-path.h" +#include "new_file_dialog.h" +#include <QFile> +#include <QFileDialog> +#include <QDesktopServices> +#include <QCoreApplication> + +#ifdef _WIN32 +# include <windows.h> +#endif + +MainWindow::MainWindow() : + State(OPENTRACK_BASE_PATH + OPENTRACK_LIBRARY_PATH), + pose_update_timer(this), + kbd_quit(QKeySequence("Ctrl+Q"), this), + is_refreshing_profiles(false) +{ + ui.setupUi(this); + + setFixedSize(size()); + + updateButtonState(false, false); + + connect(ui.btnEditCurves, SIGNAL(clicked()), this, SLOT(showCurveConfiguration())); + connect(ui.btnShortcuts, SIGNAL(clicked()), this, SLOT(show_options_dialog())); + connect(ui.btnShowEngineControls, SIGNAL(clicked()), this, SLOT(showTrackerSettings())); + connect(ui.btnShowServerControls, SIGNAL(clicked()), this, SLOT(showProtocolSettings())); + connect(ui.btnShowFilterControls, SIGNAL(clicked()), this, SLOT(showFilterSettings())); + + modules.filters().push_front(std::make_shared<dylib>("", dylib::Filter)); + + for (auto x : modules.trackers()) + ui.iconcomboTrackerSource->addItem(x->icon, x->name); + + for (auto x : modules.protocols()) + ui.iconcomboProtocol->addItem(x->icon, x->name); + + for (auto x : modules.filters()) + ui.iconcomboFilter->addItem(x->icon, x->name); + + refresh_config_list(); + connect(&config_list_timer, SIGNAL(timeout()), this, SLOT(refresh_config_list())); + config_list_timer.start(1000 * 3); + + tie_setting(m.tracker_dll, ui.iconcomboTrackerSource); + tie_setting(m.protocol_dll, ui.iconcomboProtocol); + tie_setting(m.filter_dll, ui.iconcomboFilter); + + connect(ui.iconcomboTrackerSource, + &QComboBox::currentTextChanged, + [&](QString) -> void { if (pTrackerDialog) pTrackerDialog = nullptr; save_modules(); }); + + connect(ui.iconcomboProtocol, + &QComboBox::currentTextChanged, + [&](QString) -> void { if (pProtocolDialog) pProtocolDialog = nullptr; save_modules(); }); + + connect(ui.iconcomboFilter, + &QComboBox::currentTextChanged, + [&](QString) -> void { if (pFilterDialog) pFilterDialog = nullptr; save_modules(); }); + + connect(ui.btnStartTracker, SIGNAL(clicked()), this, SLOT(startTracker())); + connect(ui.btnStopTracker, SIGNAL(clicked()), this, SLOT(stopTracker())); + connect(ui.iconcomboProfile, SIGNAL(currentTextChanged(QString)), this, SLOT(profile_selected(QString))); + + connect(&pose_update_timer, SIGNAL(timeout()), this, SLOT(showHeadPose())); + connect(&kbd_quit, SIGNAL(activated()), this, SLOT(exit())); + + profile_menu.addAction("Create new empty config", this, SLOT(make_empty_config())); + profile_menu.addAction("Create new copied config", this, SLOT(make_copied_config())); + profile_menu.addAction("Open configuration directory", this, SLOT(open_config_directory())); + ui.profile_button->setMenu(&profile_menu); + + kbd_quit.setEnabled(true); + + connect(&det_timer, SIGNAL(timeout()), this, SLOT(maybe_start_profile_from_executable())); + det_timer.start(1000); + + ensure_tray(); + + if (!QFile(group::ini_pathname()).exists()) + { + set_profile(OPENTRACK_DEFAULT_CONFIG); + const auto pathname = group::ini_pathname(); + if (!QFile(pathname).exists()) + { + QFile file(pathname); + (void) file.open(QFile::ReadWrite); + } + } + + if (group::ini_directory() == "") + QMessageBox::warning(this, + "Configuration not saved.", + "Can't create configuration directory! Expect major malfunction.", + QMessageBox::Ok, QMessageBox::NoButton); + + connect(this, &MainWindow::emit_start_tracker, + this, [&]() -> void { qDebug() << "start tracker"; startTracker(); }, + Qt::QueuedConnection); + + connect(this, &MainWindow::emit_stop_tracker, + this, [&]() -> void { qDebug() << "stop tracker"; stopTracker(); }, + Qt::QueuedConnection); + + connect(this, &MainWindow::emit_toggle_tracker, + this, [&]() -> void { qDebug() << "toggle tracker"; if (work) stopTracker(); else startTracker(); }, + Qt::QueuedConnection); + + connect(this, &MainWindow::emit_restart_tracker, + this, [&]() -> void { qDebug() << "restart tracker"; stopTracker(); startTracker(); }, + Qt::QueuedConnection); + + register_shortcuts(); + + ui.btnStartTracker->setFocus(); +} + +void MainWindow::register_shortcuts() +{ + using t_key = Shortcuts::t_key; + using t_keys = Shortcuts::t_keys; + + t_keys keys + { + t_key(s.key_start_tracking, [&](bool) -> void { emit_start_tracker(); }, true), + t_key(s.key_stop_tracking, [&](bool) -> void { emit_stop_tracker(); }, true), + t_key(s.key_toggle_tracking, [&](bool) -> void { emit_toggle_tracker(); }, true), + t_key(s.key_restart_tracking, [&](bool) -> void { emit_restart_tracker(); }, true), + }; + + global_shortcuts.reload(keys); + + if (work) + work->reload_shortcuts(); +} + +void MainWindow::warn_on_config_not_writable() +{ + QString current_file = group::ini_pathname(); + QFile f(current_file); + f.open(QFile::ReadWrite); + + if (!f.isOpen()) + { + QMessageBox::warning(this, "Something went wrong", "Check permissions and ownership for your .ini file!", QMessageBox::Ok, QMessageBox::NoButton); + } +} + +bool MainWindow::get_new_config_name_from_dialog(QString& ret) +{ + new_file_dialog dlg; + dlg.exec(); + return dlg.is_ok(ret); +} + +MainWindow::~MainWindow() +{ + if (tray) + tray->hide(); + stopTracker(); + save_modules(); +} + +void MainWindow::set_working_directory() +{ + QDir::setCurrent(OPENTRACK_BASE_PATH); +} + +void MainWindow::save_modules() +{ + m.b->save(); +} + +void MainWindow::load_mappings() +{ + pose.load_mappings(); + if (mapping_widget) + mapping_widget->reload(); +} + +void MainWindow::load_settings() +{ + m.b->reload(); + s.b->reload(); + load_mappings(); +} + +void MainWindow::make_empty_config() +{ + QString name; + const QString dir = group::ini_directory(); + if (dir != "" && get_new_config_name_from_dialog(name)) + { + QFile filename(dir + "/" + name); + (void) filename.open(QFile::ReadWrite); + refresh_config_list(); + ui.iconcomboProfile->setCurrentText(name); + } +} + +void MainWindow::make_copied_config() +{ + const QString dir = group::ini_directory(); + const QString cur = group::ini_pathname(); + QString name; + if (cur != "" && dir != "" && get_new_config_name_from_dialog(name)) + { + const QString new_name = dir + "/" + name; + (void) QFile::remove(new_name); + (void) QFile::copy(cur, new_name); + refresh_config_list(); + ui.iconcomboProfile->setCurrentText(name); + } +} + +void MainWindow::open_config_directory() +{ + const QString path = group::ini_directory(); + if (path != "") + { + QDesktopServices::openUrl("file:///" + QDir::toNativeSeparators(path)); + } +} + +extern "C" const char* opentrack_version; + +void MainWindow::refresh_config_list() +{ + if (work) + return; + + if (group::ini_list().size() == 0) + { + QFile filename(group::ini_directory() + "/" OPENTRACK_DEFAULT_CONFIG); + (void) filename.open(QFile::ReadWrite); + } + + QStringList ini_list = group::ini_list(); + set_title(); + QString current = group::ini_filename(); + is_refreshing_profiles = true; + ui.iconcomboProfile->clear(); + for (auto x : ini_list) + ui.iconcomboProfile->addItem(QIcon(":/images/settings16.png"), x); + is_refreshing_profiles = false; + ui.iconcomboProfile->setCurrentText(current); + warn_on_config_not_writable(); +} + +void MainWindow::updateButtonState(bool running, bool inertialp) +{ + bool not_running = !running; + 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); + 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 MainWindow::reload_options() +{ + if (work) + work->reload_shortcuts(); + ensure_tray(); +} + + +void MainWindow::startTracker() +{ + if (work) + return; + + // tracker dtor needs run first + work = nullptr; + + libs = SelectedLibraries(ui.video_frame, current_tracker(), current_protocol(), current_filter()); + + { + double p[6] = {0,0,0, 0,0,0}; + display_pose(p, p); + } + + if (!libs.correct) + { + QMessageBox::warning(this, "Library load error", + "One of libraries failed to load. Check installation.", + QMessageBox::Ok, + QMessageBox::NoButton); + libs = SelectedLibraries(); + return; + } + + save_modules(); + + work = std::make_shared<Work>(pose, libs, winId()); + + reload_options(); + + if (pTrackerDialog) + pTrackerDialog->register_tracker(libs.pTracker.get()); + + if (pFilterDialog) + pFilterDialog->register_filter(libs.pFilter.get()); + + if (pProtocolDialog) + pProtocolDialog->register_protocol(libs.pProtocol.get()); + + pose_update_timer.start(50); + + // NB check valid since SelectedLibraries ctor called + // trackers take care of layout state updates + const bool is_inertial = ui.video_frame->layout() == nullptr; + updateButtonState(true, is_inertial); + + // Update the state of the options window directly. + // Might be better to emit signals and allow the options window + // to connect its slots to them (?) + if (options_widget) + options_widget->update_widgets_states(true); + + ui.btnStopTracker->setFocus(); +} + +void MainWindow::stopTracker() +{ + if (!work) + return; + + //ui.game_name->setText("Not connected"); + + pose_update_timer.stop(); + ui.pose_display->rotateBy(0, 0, 0, 0, 0, 0); + + if (pTrackerDialog) + pTrackerDialog->unregister_tracker(); + + if (pProtocolDialog) + pProtocolDialog->unregister_protocol(); + + if (pFilterDialog) + pFilterDialog->unregister_filter(); + + save_modules(); + + work = nullptr; + libs = SelectedLibraries(); + + { + double p[6] = {0,0,0, 0,0,0}; + display_pose(p, p); + } + updateButtonState(false, false); + + if (options_widget) + options_widget->update_widgets_states(false); + + set_title(); + + ui.btnStartTracker->setFocus(); +} + +void MainWindow::display_pose(const double *mapped, const double *raw) +{ + ui.pose_display->rotateBy(mapped[Yaw], mapped[Pitch], -mapped[Roll], + mapped[TX], mapped[TY], mapped[TZ]); + + if (mapping_widget) + mapping_widget->update(); + + double mapped_[6], raw_[6]; + + for (int i = 0; i < 6; i++) + { + mapped_[i] = (int) mapped[i]; + raw_[i] = (int) raw[i]; + } + + ui.raw_x->display(raw_[TX]); + ui.raw_y->display(raw_[TY]); + ui.raw_z->display(raw_[TZ]); + ui.raw_yaw->display(raw_[Yaw]); + ui.raw_pitch->display(raw_[Pitch]); + ui.raw_roll->display(raw_[Roll]); + + ui.pose_x->display(mapped_[TX]); + ui.pose_y->display(mapped_[TY]); + ui.pose_z->display(mapped_[TZ]); + ui.pose_yaw->display(mapped_[Yaw]); + ui.pose_pitch->display(mapped_[Pitch]); + ui.pose_roll->display(mapped_[Roll]); + + QString game_title; + if (libs.pProtocol) + game_title = libs.pProtocol->game_name(); + set_title(game_title); +} + +void MainWindow::set_title(const QString& game_title_) +{ + QString game_title; + if (game_title_ != "") + game_title = " :: " + game_title_; + QString current = group::ini_filename(); + setWindowTitle(opentrack_version + QStringLiteral(" :: ") + current + game_title); +} + +void MainWindow::showHeadPose() +{ + if (!ui.video_frame->isEnabled()) + return; + + double mapped[6], raw[6]; + + work->tracker->get_raw_and_mapped_poses(mapped, raw); + + display_pose(mapped, raw); +} + +template<typename t> +bool mk_dialog(mem<dylib> lib, mem<t>& orig) +{ + if (orig && orig->isVisible()) + { + orig->show(); + orig->raise(); + return false; + } + + if (lib && lib->Dialog) + { + auto dialog = mem<t>(reinterpret_cast<t*>(lib->Dialog())); + dialog->setWindowFlags(Qt::Dialog); + dialog->setFixedSize(dialog->size()); + + orig = dialog; + dialog->show(); + + QObject::connect(dialog.get(), &plugin_api::detail::BaseDialog::closing, [&]() -> void { orig = nullptr; }); + + return true; + } + + return false; +} + +void MainWindow::showTrackerSettings() +{ + if (mk_dialog(current_tracker(), pTrackerDialog) && libs.pTracker) + pTrackerDialog->register_tracker(libs.pTracker.get()); +} + +void MainWindow::showProtocolSettings() +{ + if (mk_dialog(current_protocol(), pProtocolDialog) && libs.pProtocol) + pProtocolDialog->register_protocol(libs.pProtocol.get()); +} + +void MainWindow::showFilterSettings() +{ + if (mk_dialog(current_filter(), pFilterDialog) && libs.pFilter) + pFilterDialog->register_filter(libs.pFilter.get()); +} + +template<typename t, typename... Args> +bool mk_window(mem<t>* place, Args&&... params) +{ + if (*place && (*place)->isVisible()) + { + (*place)->show(); + (*place)->raise(); + return false; + } + else + { + *place = std::make_shared<t>(std::forward<Args>(params)...); + (*place)->setWindowFlags(Qt::Dialog); + (*place)->show(); + return true; + } +} + +void MainWindow::show_options_dialog() +{ + if (mk_window(&options_widget, [&](bool flag) -> void { set_keys_enabled(!flag); })) + { + connect(options_widget.get(), &OptionsDialog::saving, this, &MainWindow::reload_options); + options_widget->update_widgets_states(work != nullptr); + } +} + +void MainWindow::showCurveConfiguration() +{ + mk_window(&mapping_widget, pose); +} + +void MainWindow::exit() +{ + QCoreApplication::exit(0); +} + +void MainWindow::profile_selected(const QString& name) +{ + if (name == "" || is_refreshing_profiles) + return; + + const auto old_name = group::ini_filename(); + const auto new_name = name; + + if (old_name != new_name) + { + save_modules(); + set_profile(new_name); + set_title(); + load_settings(); + } +} + +void MainWindow::ensure_tray() +{ + if (tray) + tray->hide(); + tray = nullptr; + if (s.tray_enabled) + { + tray = std::make_shared<QSystemTrayIcon>(this); + tray->setIcon(QIcon(":/images/facetracknoir.png")); + tray->show(); + connect(tray.get(), SIGNAL(activated(QSystemTrayIcon::ActivationReason)), + this, SLOT(restore_from_tray(QSystemTrayIcon::ActivationReason))); + } +} + +void MainWindow::restore_from_tray(QSystemTrayIcon::ActivationReason) +{ + show(); + setWindowState( (windowState() & ~Qt::WindowMinimized) | Qt::WindowActive); + raise(); // for MacOS + activateWindow(); // for Windows +} + +void MainWindow::maybe_start_profile_from_executable() +{ + if (!work) + { + QString prof; + if (det.config_to_start(prof)) + { + ui.iconcomboProfile->setCurrentText(prof); + startTracker(); + } + } + else + { + if (det.should_stop()) + stopTracker(); + } +} + +void MainWindow::set_keys_enabled(bool flag) +{ + if (!flag) + { + if (work) + work->sc->reload({}); + global_shortcuts.reload({}); + } + else + { + register_shortcuts(); + } + qDebug() << "keybindings set to" << flag; +} + +void MainWindow::set_profile(const QString &profile) +{ + QSettings settings(OPENTRACK_ORG); + settings.setValue(OPENTRACK_CONFIG_FILENAME_KEY, profile); + warn_on_config_not_writable(); +} |