summaryrefslogtreecommitdiffhomepage
path: root/opentrack/main-window.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'opentrack/main-window.cpp')
-rw-r--r--opentrack/main-window.cpp920
1 files changed, 920 insertions, 0 deletions
diff --git a/opentrack/main-window.cpp b/opentrack/main-window.cpp
new file mode 100644
index 00000000..b90aa3bd
--- /dev/null
+++ b/opentrack/main-window.cpp
@@ -0,0 +1,920 @@
+/* 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 "logic/pipeline.hpp"
+#include "options/options.hpp"
+#include "new_file_dialog.h"
+#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 "compat/sysexits.hpp"
+
+#include <algorithm>
+#include <utility>
+
+#include <QMessageBox>
+#include <QDesktopServices>
+#include <QDir>
+
+extern "C" const char* const opentrack_version;
+
+using namespace options::globals;
+using namespace options;
+
+main_window::main_window() : State(OPENTRACK_BASE_PATH + OPENTRACK_LIBRARY_PATH)
+{
+ ui.setupUi(this);
+
+#if !defined _WIN32 && !defined __APPLE__
+ annoy_if_root();
+#endif
+
+ init_profiles();
+ init_buttons();
+ init_tray_menu();
+ init_dylibs();
+ init_shortcuts();
+
+ setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | windowFlags());
+ setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ adjustSize();
+
+ if (!start_in_tray())
+ {
+ setVisible(true);
+ show();
+ }
+ else
+ setVisible(false);
+
+ connect(&pose_update_timer, &QTimer::timeout,
+ this, &main_window::show_pose, Qt::DirectConnection);
+ connect(&det_timer, &QTimer::timeout,
+ this, &main_window::maybe_start_profile_from_executable);
+ det_timer.start(1000);
+}
+
+void main_window::init_shortcuts()
+{
+ register_shortcuts();
+
+ // ctrl+q exits
+ connect(&kbd_quit, &QShortcut::activated, this, [this]() { main_window::exit(EXIT_SUCCESS); }, Qt::DirectConnection);
+ kbd_quit.setEnabled(true);
+}
+
+void main_window::init_dylibs()
+{
+ using dylib_ptr = Modules::dylib_ptr;
+ using dylib_list = Modules::dylib_list;
+
+ modules.filters().insert(modules.filters().begin(),
+ std::make_shared<dylib>("", dylib_type::Filter));
+
+ for (dylib_ptr& x : modules.trackers())
+ ui.iconcomboTrackerSource->addItem(x->icon, x->name, x->module_name);
+
+ for (dylib_ptr& x : modules.protocols())
+ ui.iconcomboProtocol->addItem(x->icon, x->name, x->module_name);
+
+ for (dylib_ptr& x : modules.filters())
+ ui.iconcomboFilter->addItem(x->icon, x->name, x->module_name);
+
+ connect(ui.iconcomboTrackerSource, &QComboBox::currentTextChanged,
+ this, [&](const QString&) { pTrackerDialog = nullptr; });
+
+ connect(ui.iconcomboProtocol, &QComboBox::currentTextChanged,
+ this, [&](const QString&) { pProtocolDialog = nullptr; });
+
+ connect(ui.iconcomboFilter, &QComboBox::currentTextChanged,
+ this, [&](const QString&) { pFilterDialog = nullptr; });
+
+ connect(&m.tracker_dll, value_::value_changed<QString>(),
+ this, &main_window::save_modules,
+ Qt::DirectConnection);
+
+ connect(&m.protocol_dll, value_::value_changed<QString>(),
+ this, &main_window::save_modules,
+ Qt::DirectConnection);
+
+ connect(&m.filter_dll, value_::value_changed<QString>(),
+ this, &main_window::save_modules,
+ Qt::DirectConnection);
+
+ {
+ struct list {
+ dylib_list& libs;
+ QComboBox* input;
+ value<QString>& place;
+ };
+
+ list types[] {
+ { modules.trackers(), ui.iconcomboTrackerSource, m.tracker_dll },
+ { modules.protocols(), ui.iconcomboProtocol, m.protocol_dll },
+ { modules.filters(), ui.iconcomboFilter, m.filter_dll },
+ };
+
+ for (list& type : types)
+ {
+ list t = type;
+ tie_setting(t.place, t.input,
+ [t](const QString& name) {
+ auto [ptr, idx] = module_by_name(name, t.libs);
+ return idx;
+ },
+ [t](int, const QVariant& userdata) {
+ auto [ptr, idx] = module_by_name(userdata.toString(), t.libs);
+ if (ptr)
+ return ptr->module_name;
+ else
+ return QString();
+ });
+ }
+ }
+}
+
+void main_window::init_profiles()
+{
+ refresh_profile_list();
+ // implicitly created by `ini_directory()'
+ if (ini_directory().isEmpty() || !QDir(ini_directory()).isReadable())
+ die_on_profile_not_writable();
+
+ set_profile(ini_filename());
+
+ // profile menu
+ profile_menu.addAction(tr("Create new empty config"), this, &main_window::create_empty_profile);
+ profile_menu.addAction(tr("Create new copied config"), this, &main_window::create_copied_profile);
+ profile_menu.addAction(tr("Open configuration directory"), this, &main_window::open_profile_directory);
+ ui.profile_button->setMenu(&profile_menu);
+
+ connect(&profile_list_timer, &QTimer::timeout, this, &main_window::refresh_profile_list);
+ profile_list_timer.start(1000 * 5);
+
+ connect(ui.iconcomboProfile, &QComboBox::currentTextChanged,
+ this, [this](const QString& x) { main_window::set_profile(x); });
+}
+
+void main_window::init_tray_menu()
+{
+ tray_menu.clear();
+
+ QString display_name(opentrack_version);
+ if (display_name.startsWith("opentrack-"))
+ display_name = tr("opentrack") + " " + display_name.mid(sizeof("opentrack-") - 1);
+ if (display_name.endsWith("-DEBUG"))
+ display_name.replace(display_name.size() - int(sizeof("DEBUG")), display_name.size(), tr(" (debug)"));
+
+ menu_action_header.setEnabled(false);
+ menu_action_header.setText(display_name);
+ menu_action_header.setIcon(QIcon(":/images/opentrack.png"));
+ tray_menu.addAction(&menu_action_header);
+
+ 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); });
+ tray_menu.addAction(&menu_action_show);
+
+ tray_menu.addSeparator();
+
+ menu_action_tracker.setText(tr("Tracker settings"));
+ menu_action_tracker.setIcon(QIcon(":/images/tools.png"));
+ QObject::connect(&menu_action_tracker, &QAction::triggered, this, &main_window::show_tracker_settings);
+ tray_menu.addAction(&menu_action_tracker);
+
+ menu_action_filter.setText(tr("Filter settings"));
+ menu_action_filter.setIcon(QIcon(":/images/filter-16.png"));
+ QObject::connect(&menu_action_filter, &QAction::triggered, this, &main_window::show_filter_settings);
+ tray_menu.addAction(&menu_action_filter);
+
+ menu_action_proto.setText(tr("Protocol settings"));
+ menu_action_proto.setIcon(QIcon(":/images/settings16.png"));
+ QObject::connect(&menu_action_proto, &QAction::triggered, this, &main_window::show_proto_settings);
+ tray_menu.addAction(&menu_action_proto);
+
+ tray_menu.addSeparator();
+
+ menu_action_mappings.setIcon(QIcon(":/images/curves.png"));
+ menu_action_mappings.setText(tr("Mappings"));
+ QObject::connect(&menu_action_mappings, &QAction::triggered, this, &main_window::show_mapping_window);
+ tray_menu.addAction(&menu_action_mappings);
+
+ 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);
+ tray_menu.addAction(&menu_action_options);
+
+ tray_menu.addSeparator();
+
+ menu_action_exit.setText(tr("Exit"));
+ QObject::connect(&menu_action_exit, &QAction::triggered, this, &main_window::exit);
+ tray_menu.addAction(&menu_action_exit);
+
+ connect(&s.tray_enabled, value_::value_changed<bool>(),
+ this, &main_window::ensure_tray);
+
+ ensure_tray();
+}
+
+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.btnShowEngineControls, &QPushButton::clicked, this, &main_window::show_tracker_settings);
+ connect(ui.btnShowServerControls, &QPushButton::clicked, this, &main_window::show_proto_settings);
+ connect(ui.btnShowFilterControls, &QPushButton::clicked, this, &main_window::show_filter_settings);
+ connect(ui.btnStartTracker, &QPushButton::clicked, this, &main_window::start_tracker_);
+ connect(ui.btnStopTracker, &QPushButton::clicked, this, &main_window::stop_tracker_);
+}
+
+void main_window::register_shortcuts()
+{
+ global_shortcuts.reload({
+ { s.key_start_tracking1, [this](bool) { start_tracker(); }, true },
+ { s.key_start_tracking2, [this](bool) { start_tracker(); }, true },
+
+ { s.key_stop_tracking1, [this](bool) { stop_tracker(); }, true },
+ { s.key_stop_tracking2, [this](bool) { stop_tracker(); }, true },
+
+ { s.key_toggle_tracking1, [this](bool) { toggle_tracker(); }, true },
+ { s.key_toggle_tracking2, [this](bool) { toggle_tracker(); }, true },
+
+ { s.key_restart_tracking1, [this](bool) { restart_tracker(); }, true },
+ { s.key_restart_tracking2, [this](bool) { restart_tracker(); }, true },
+ });
+
+ if (work)
+ work->reload_shortcuts();
+}
+
+void main_window::die_on_profile_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\nExiting now.").arg(ini_directory(), pad),
+ QMessageBox::Close, QMessageBox::NoButton);
+
+ exit(EX_OSFILE);
+}
+
+bool main_window::profile_name_from_dialog(QString& ret)
+{
+ new_file_dialog dlg;
+ dlg.exec();
+ return dlg.is_ok(ret);
+}
+
+main_window::~main_window()
+{
+ // stupid ps3 eye has LED issues
+ if (work && ui.video_frame->layout())
+ {
+ hide();
+ stop_tracker_();
+ close();
+
+ constexpr int inc = 25, max = 1000;
+
+ for (int k = 0; k < max; k += inc)
+ {
+ QEventLoop ev;
+ ev.processEvents();
+ portable::sleep(inc);
+ }
+ }
+
+ exit();
+}
+
+void main_window::save_modules()
+{
+ m.b->save();
+}
+
+void main_window::create_empty_profile()
+{
+ QString name;
+ if (profile_name_from_dialog(name))
+ {
+ QFile(ini_combine(name)).open(QFile::ReadWrite);
+ refresh_profile_list();
+
+ if (profile_list.contains(name))
+ {
+ QSignalBlocker q(ui.iconcomboProfile);
+
+ set_profile(name, false);
+ mark_profile_as_not_needing_migration();
+ }
+ }
+}
+
+void main_window::create_copied_profile()
+{
+ const QString cur = ini_pathname();
+ QString name;
+ if (!cur.isEmpty() && profile_name_from_dialog(name))
+ {
+ const QString new_name = ini_combine(name);
+ (void) QFile::remove(new_name);
+ QFile::copy(cur, new_name);
+
+ refresh_profile_list();
+
+ if (profile_list.contains(name))
+ {
+ QSignalBlocker q(ui.iconcomboProfile);
+
+ set_profile(name, false);
+ mark_profile_as_not_needing_migration();
+ }
+ }
+
+}
+
+void main_window::open_profile_directory()
+{
+ QDesktopServices::openUrl("file:///" + QDir::toNativeSeparators(ini_directory()));
+}
+
+void main_window::refresh_profile_list()
+{
+ if (work)
+ return;
+
+ QStringList list = ini_list();
+ QString current = ini_filename();
+
+ if (list == profile_list)
+ return;
+
+ if (!list.contains(current))
+ current = OPENTRACK_DEFAULT_PROFILE;
+
+ profile_list = list;
+
+ static const QIcon icon(":/images/settings16.png");
+
+ QSignalBlocker l(ui.iconcomboProfile);
+
+ ui.iconcomboProfile->clear();
+ ui.iconcomboProfile->addItems(list);
+
+ for (int i = 0; i < list.size(); i++)
+ ui.iconcomboProfile->setItemIcon(i, icon);
+
+ ui.iconcomboProfile->setCurrentText(current);
+}
+
+
+
+void main_window::update_button_state(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 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;
+ }
+
+ {
+ double p[6] = {0,0,0, 0,0,0};
+ show_pose_(p, p);
+ }
+
+ 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());
+
+ 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;
+ update_button_state(true, is_inertial);
+
+ ui.btnStopTracker->setFocus();
+}
+
+void main_window::stop_tracker_()
+{
+ if (!work)
+ return;
+
+ force_is_visible(true);
+ with_tracker_teardown sentinel;
+
+ pose_update_timer.stop();
+ ui.pose_display->present(0,0,0, 0,0,0);
+
+ if (pTrackerDialog)
+ pTrackerDialog->unregister_tracker();
+
+ if (pProtocolDialog)
+ pProtocolDialog->unregister_protocol();
+
+ if (pFilterDialog)
+ pFilterDialog->unregister_filter();
+
+ work = nullptr;
+
+ {
+ double p[6] {};
+ show_pose_(p, p);
+ }
+
+ update_button_state(false, false);
+ set_title();
+ ui.btnStartTracker->setFocus();
+}
+
+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]);
+
+ QLCDNumber* raw_[] = {
+ ui.raw_x, ui.raw_y, ui.raw_z,
+ ui.raw_yaw, ui.raw_pitch, ui.raw_roll,
+ };
+
+ QLCDNumber* mapped_[] = {
+ ui.pose_x, ui.pose_y, ui.pose_z,
+ ui.pose_yaw, ui.pose_pitch, ui.pose_roll,
+ };
+
+ for (int k = 0; k < 6; k++)
+ {
+ raw_[k]->display(iround(raw[k]));
+ mapped_[k]->display(iround(mapped[k]));
+ }
+
+ QString game_title;
+ if (work && work->libs.pProtocol)
+ game_title = work->libs.pProtocol->game_name();
+ set_title(game_title);
+}
+
+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::show_pose()
+{
+ set_is_visible(*this);
+
+ if (mapping_widget)
+ mapping_widget->refresh_tab();
+
+ if (!check_is_visible())
+ return;
+
+ double mapped[6], raw[6];
+
+ work->pipeline_.raw_and_mapped_pose(mapped, raw);
+
+ show_pose_(mapped, raw);
+}
+
+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, typename... Args>
+static bool mk_window(std::unique_ptr<t>& place, Args&&... params)
+{
+ return mk_window_common(place, [&] {
+ return std::make_unique<t>(params...);
+ });
+}
+
+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{};
+ });
+}
+
+void main_window::show_tracker_settings()
+{
+ 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; });
+}
+
+void main_window::show_proto_settings()
+{
+ 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; });
+}
+
+void main_window::show_filter_settings()
+{
+ 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; });
+}
+
+void main_window::show_options_dialog()
+{
+ if (mk_window(options_widget, [&](bool flag) { set_keys_enabled(!flag); }))
+ {
+ // 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);
+ }
+}
+
+void main_window::show_mapping_window()
+{
+ mk_window(mapping_widget, pose);
+}
+
+void main_window::exit(int status)
+{
+ // don't use std::call_once here, leads to freeze in Microsoft's CRT
+ // this function never needs reentrancy anyway
+
+ // this is probably harmless, but better safe than sorry
+ if (exiting_already)
+ return;
+ exiting_already = true;
+
+ qDebug() << "opentrack: exiting";
+
+ if (tray)
+ tray->hide();
+ tray = nullptr;
+
+ //close();
+ QApplication::setQuitOnLastWindowClosed(true);
+ QApplication::exit(status);
+}
+
+void main_window::set_profile(const QString& new_name_, bool migrate)
+{
+ QSignalBlocker b(ui.iconcomboProfile);
+
+ QString new_name = new_name_;
+
+ if (!profile_list.contains(new_name))
+ {
+ new_name = OPENTRACK_DEFAULT_PROFILE;
+ if (!profile_list.contains(new_name))
+ migrate = false;
+ }
+
+ const bool status = new_name != ini_filename();
+
+ if (status)
+ set_profile_in_registry(new_name);
+
+ using bundler = options::detail::bundler;
+
+ bundler::reload_no_notify();
+
+ if (migrate)
+ // migrations are for config layout changes and other user-visible
+ // incompatibilities in future versions
+ run_migrations();
+ else
+ mark_profile_as_not_needing_migration();
+
+ bundler::notify();
+
+ set_title();
+
+ if (status)
+ ui.iconcomboProfile->setCurrentText(new_name);
+}
+
+void main_window::ensure_tray()
+{
+ if (!QSystemTrayIcon::isSystemTrayAvailable())
+ {
+ QApplication::setQuitOnLastWindowClosed(true);
+ return;
+ }
+
+ if (s.tray_enabled)
+ {
+ if (!tray)
+ {
+ tray = std::make_unique<QSystemTrayIcon>(this);
+ tray->setIcon(QIcon(":/images/opentrack.png"));
+ tray->setContextMenu(&tray_menu);
+ tray->show();
+
+ connect(tray.get(),
+ &QSystemTrayIcon::activated,
+ this,
+ &main_window::toggle_restore_from_tray);
+ }
+
+ QApplication::setQuitOnLastWindowClosed(false);
+ }
+ else
+ {
+ if (!isVisible())
+ {
+ show();
+ setVisible(true);
+
+ raise(); // for OSX
+ activateWindow(); // for Windows
+ }
+
+ if (tray)
+ tray->hide();
+ tray = nullptr;
+
+ QApplication::setQuitOnLastWindowClosed(true);
+ }
+}
+
+void main_window::toggle_restore_from_tray(QSystemTrayIcon::ActivationReason e)
+{
+ switch (e)
+ {
+ // if we enable double click also then it causes
+ // toggle back to the original state
+ //case QSystemTrayIcon::DoubleClick:
+ case QSystemTrayIcon::Trigger: // single click
+ break;
+ default:
+ return;
+ }
+
+ ensure_tray();
+
+ const bool is_minimized = isHidden() || !tray_enabled();
+
+ menu_action_show.setText(!isHidden() ? tr("Show the Octopus") : tr("Hide the Octopus"));
+
+ setVisible(is_minimized);
+ setHidden(!is_minimized);
+
+ setWindowState(is_minimized ? windowState() & ~Qt::WindowMinimized : Qt::WindowNoState);
+
+ if (is_minimized)
+ {
+ raise(); // for OSX
+ activateWindow(); // for Windows
+ }
+ else
+ {
+ lower();
+ clearFocus();
+ }
+}
+
+bool main_window::maybe_hide_to_tray(QEvent* e)
+{
+ if (e->type() == QEvent::WindowStateChange &&
+ (windowState() & Qt::WindowMinimized) &&
+ tray_enabled())
+ {
+ e->accept();
+ ensure_tray();
+ hide();
+
+ return true;
+ }
+
+ return false;
+}
+
+void main_window::closeEvent(QCloseEvent*)
+{
+ exit();
+}
+
+void main_window::maybe_start_profile_from_executable()
+{
+ if (!work)
+ {
+ QString profile;
+ if (det.profile_to_start(profile) && profile_list.contains(profile))
+ {
+ set_profile(profile);
+ start_tracker_();
+ }
+ }
+ else
+ {
+ if (det.should_stop())
+ stop_tracker_();
+ }
+}
+
+void main_window::set_keys_enabled(bool flag)
+{
+ if (!flag)
+ {
+ if (work)
+ work->sc.reload({});
+ global_shortcuts.reload({});
+ }
+ else
+ register_shortcuts();
+}
+
+void main_window::changeEvent(QEvent* e)
+{
+ if (!maybe_hide_to_tray(e))
+ e->ignore();
+}
+
+bool main_window::event(QEvent* event)
+{
+ using t = QEvent::Type;
+
+ if (work)
+ {
+ switch (event->type())
+ {
+ case t::Hide:
+ case t::WindowActivate:
+ case t::WindowDeactivate:
+ case t::WindowStateChange:
+ case t::FocusIn:
+ set_is_visible(*this, true);
+ break;
+ default:
+ break;
+ }
+ }
+ return QMainWindow::event(event);
+}
+
+bool main_window::tray_enabled()
+{
+ return s.tray_enabled && QSystemTrayIcon::isSystemTrayAvailable();
+}
+
+bool main_window::start_in_tray()
+{
+ return tray_enabled() && s.tray_start;
+}
+
+void main_window::set_profile_in_registry(const QString &profile)
+{
+ with_global_settings_object([&](QSettings& s) {
+ s.setValue(OPENTRACK_PROFILE_FILENAME_KEY, profile);
+ });
+}
+
+void main_window::restart_tracker_()
+{
+ qDebug() << "restart tracker";
+
+ stop_tracker_();
+ start_tracker_();
+}
+
+void main_window::toggle_tracker_()
+{
+ qDebug() << "toggle tracker";
+
+ if (work)
+ stop_tracker_();
+ else
+ start_tracker_();
+}
+
+#if !defined _WIN32
+# include <unistd.h>
+void main_window::annoy_if_root()
+{
+ if (geteuid() == 0)
+ {
+ struct lst {
+ QString caption;
+ QString msg;
+ int sleep_ms;
+ };
+
+ const lst list[] = {
+ {
+ tr("Running as root is bad"),
+ tr("Do not run as root. Set correct device node permissions."),
+ 1000,
+ },
+ {
+ tr("Running as root is bad, seriously"),
+ tr("Do not run as root. I'll keep whining at every startup."),
+ 3000,
+ },
+ {
+ tr("Be annoyed, comprehensively."),
+ tr("Don't run as root to remove these annoying messages."),
+ 0
+ }
+ };
+
+ for (const auto& x : list)
+ {
+ QMessageBox::critical(this, x.caption, x.msg, QMessageBox::Ok);
+ portable::sleep(x.sleep_ms);
+ }
+ }
+}
+#endif