diff options
-rw-r--r-- | facetracknoir/keyboard.ui | 19 | ||||
-rw-r--r-- | facetracknoir/process_detector.cpp | 243 | ||||
-rw-r--r-- | facetracknoir/process_detector.h | 93 | ||||
-rw-r--r-- | facetracknoir/process_widget.ui | 109 | ||||
-rw-r--r-- | facetracknoir/shortcut-dialog.cpp | 2 | ||||
-rw-r--r-- | facetracknoir/ui.cpp | 41 | ||||
-rw-r--r-- | facetracknoir/ui.h | 9 |
7 files changed, 504 insertions, 12 deletions
diff --git a/facetracknoir/keyboard.ui b/facetracknoir/keyboard.ui index cdfc0657..4ec3ce49 100644 --- a/facetracknoir/keyboard.ui +++ b/facetracknoir/keyboard.ui @@ -7,7 +7,7 @@ <x>0</x> <y>0</y> <width>461</width> - <height>181</height> + <height>413</height> </rect> </property> <property name="sizePolicy"> @@ -289,6 +289,16 @@ </widget> </item> <item> + <widget class="process_detector" name="game_detector"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item> <widget class="QDialogButtonBox" name="buttonBox"> <property name="standardButtons"> <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> @@ -297,6 +307,13 @@ </item> </layout> </widget> + <customwidgets> + <customwidget> + <class>process_detector</class> + <extends>QWidget</extends> + <header>facetracknoir/process_detector.h</header> + </customwidget> + </customwidgets> <resources/> <connections/> <slots> diff --git a/facetracknoir/process_detector.cpp b/facetracknoir/process_detector.cpp new file mode 100644 index 00000000..3ac6329b --- /dev/null +++ b/facetracknoir/process_detector.cpp @@ -0,0 +1,243 @@ +#include "process_detector.h" +#include "facetracknoir/ui.h" +#include <QList> +#include <QFileDialog> +#include <QComboBox> +#include <QString> +#include <QHash> +#include <QPushButton> + +void settings::set_game_list(const QString &game_list) +{ + QSettings settings(group::org); + settings.setValue("executable-list", game_list); +} + +QString settings::get_game_list() +{ + QSettings settings(group::org); + return settings.value("executable-list").toString(); +} + +bool settings::is_enabled() +{ + QSettings settings(group::org); + return settings.value("executable-detector-enabled", false).toBool(); +} + +void settings::set_is_enabled(bool enabled) +{ + QSettings settings(group::org); + settings.setValue("executable-detector-enabled", enabled); +} + +QHash<QString, QString> settings::split_process_names() +{ + QHash<QString, QString> ret; + QString str = get_game_list(); + QStringList pairs = str.split('|'); + for (auto pair : pairs) + { + QList<QString> tmp = pair.split(':'); + if (tmp.count() != 2) + continue; + ret[tmp[0]] = tmp[1]; + } + return ret; +} + +void BrowseButton::browse() +{ + QFileDialog dialog(this); + dialog.setFileMode(QFileDialog::ExistingFile); + QString dir_path = QFileInfo(group::ini_pathname()).absolutePath(); + QString filename = dialog.getOpenFileName( + this, + tr("Set executable name"), + dir_path, + tr("Executable (*.exe);;All Files (*)")); + MainWindow::set_working_directory(); + filename = QFileInfo(filename).fileName(); + twi->setText(filename); +} + +process_detector::add_row(QString exe_name, QString profile) +{ + int i = ui.tableWidget->rowCount(); + ui.tableWidget->insertRow(i); + + QComboBox* cb = new QComboBox(); + cb->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Maximum); + cb->addItems(group::ini_list()); + ui.tableWidget->setCellWidget(i, 1, cb); + + QTableWidgetItem* twi = new QTableWidgetItem(exe_name); + ui.tableWidget->setItem(i, 0, twi); + + { + BrowseButton* b = new BrowseButton(twi); + b->setText("..."); + QObject::connect(b, SIGNAL(pressed()), b, SLOT(browse())); + ui.tableWidget->setCellWidget(i, 2, b); + } + + cb->setCurrentText(profile); + + return i; +} + +void process_detector::add_items() +{ + auto names = s.split_process_names(); + ui.tableWidget->clearContents(); + auto keys = names.keys(); + qSort(keys); + for (auto n : keys) + add_row(n, names[n]); +} + +process_detector::process_detector(QWidget* parent) : QWidget(parent) +{ + ui.setupUi(this); + connect(ui.add, SIGNAL(pressed()), this, SLOT(add())); + connect(ui.remove, SIGNAL(pressed()), this, SLOT(remove())); + + add_items(); + + QResizeEvent e(ui.tableWidget->size(), ui.tableWidget->size()); + ui.tableWidget->resizeEvent(&e); + + settings s; + + ui.enabled->setChecked(s.is_enabled()); +} + +void process_detector::save() +{ + QString str; + + for (int i = 0; i < ui.tableWidget->rowCount(); i++) + { + auto exe = ui.tableWidget->item(i, 0)->text(); + auto profile = reinterpret_cast<QComboBox*>(ui.tableWidget->cellWidget(i, 1))->currentText(); + str += "|" + exe + ":" + profile; + } + + s.set_game_list(str); + s.set_is_enabled(ui.enabled->isChecked()); +} + +void process_detector::revert() +{ +} + +void process_detector::add() +{ + add_row(); +} + +void process_detector::remove() +{ + int r = ui.tableWidget->currentRow(); + if (r != -1) + ui.tableWidget->removeRow(r); +} + +#ifdef _WIN32 + +#include <windows.h> +#include <TlHelp32.h> + +bool process_detector_worker::should_stop() +{ + if (last_exe_name == "") + return false; + + settings s; + + if (!s.is_enabled()) + { + last_exe_name = ""; + return false; + } + + HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (h == INVALID_HANDLE_VALUE) + return false; + + PROCESSENTRY32 e; + + if (Process32First(h, &e) == TRUE) + { + QString exe_name(e.szExeFile); + if (exe_name == last_exe_name) + { + CloseHandle(h); + return false; + } + } + + while (Process32Next(h, &e) == TRUE) + { + QString exe_name(e.szExeFile); + if (exe_name == last_exe_name) + { + CloseHandle(h); + return false; + } + } + + last_exe_name = ""; + CloseHandle(h); + + return true; +} + +bool process_detector_worker::config_to_start(QString& str) +{ + settings s; + if (!s.is_enabled()) + { + last_exe_name = ""; + return false; + } + + auto filenames = s.split_process_names(); + + HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (h == INVALID_HANDLE_VALUE) + return false; + + PROCESSENTRY32 e; + + if (Process32First(h, &e) == TRUE) + { + QString exe_name(e.szExeFile); + if (filenames.contains(exe_name)) + { + str = filenames[exe_name]; + last_exe_name = str; + CloseHandle(h); + return true; + } + } + + while (Process32Next(h, &e) == TRUE) + { + QString exe_name(e.szExeFile); + if (filenames.contains(exe_name)) + { + str = filenames[exe_name]; + last_exe_name = str; + CloseHandle(h); + return true; + } + } + + CloseHandle(h); + + return false; +} + + +#endif diff --git a/facetracknoir/process_detector.h b/facetracknoir/process_detector.h new file mode 100644 index 00000000..2333ecc6 --- /dev/null +++ b/facetracknoir/process_detector.h @@ -0,0 +1,93 @@ +#pragma once + +#include <QObject> +#include <QWidget> +#include <QTableWidget> +#include <QResizeEvent> + +#include "opentrack/options.hpp" +using namespace options; + +class FancyTable : public QTableWidget +{ + Q_OBJECT +public: + void resizeEvent(QResizeEvent* e) override + { + QTableView::resizeEvent(e); + int w = width(); + setColumnWidth(2, 32); + w -= 48; + setColumnWidth(0, w / 2); + setColumnWidth(1, w / 2); + } +public: + FancyTable(QWidget* parent = nullptr) : QTableWidget(parent) {} +}; + +struct settings +{ + QHash<QString, QString> split_process_names(); + QString get_game_list(); + void set_game_list(const QString& game_list); + bool is_enabled(); + void set_is_enabled(bool enabled); +}; + +#include "ui_process_widget.h" + +class process_detector : public QWidget +{ + Q_OBJECT + + Ui_Dialog ui; + settings s; + + int add_row(QString exe_name = "...", QString profile = ""); + void add_items(); +public: + process_detector(QWidget* parent = nullptr); +public slots: + void save(); + void revert(); +private slots: + void add(); + void remove(); +}; + +class BrowseButton : public QPushButton +{ + Q_OBJECT + QTableWidgetItem* twi; +public: + BrowseButton(QTableWidgetItem* twi) : twi(twi) + {} +public slots: + void browse(); +}; + +#ifdef _WIN32 + +class process_detector_worker : QObject +{ + Q_OBJECT + settings s; + QString last_exe_name; +public: + bool config_to_start(QString& s); + bool should_stop(); +}; + +#else + +class process_detector_worker : QObject +{ + Q_OBJECT +public: + bool config_to_start(QString&) + { + return false; + } +}; + +#endif diff --git a/facetracknoir/process_widget.ui b/facetracknoir/process_widget.ui new file mode 100644 index 00000000..ff0ace71 --- /dev/null +++ b/facetracknoir/process_widget.ui @@ -0,0 +1,109 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Dialog</class> + <widget class="QWidget" name="Dialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>327</width> + <height>346</height> + </rect> + </property> + <property name="windowTitle"> + <string>Game detector</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="FancyTable" name="tableWidget"> + <column> + <property name="text"> + <string>Executable</string> + </property> + </column> + <column> + <property name="text"> + <string>Profile</string> + </property> + </column> + <column> + <property name="text"> + <string/> + </property> + </column> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string/> + </property> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QPushButton" name="add"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>14</pointsize> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text"> + <string>+</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="remove"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>14</pointsize> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text"> + <string>-</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QCheckBox" name="enabled"> + <property name="text"> + <string>Start profiles from game executable names in this list</string> + </property> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>FancyTable</class> + <extends>QTableWidget</extends> + <header>facetracknoir/process_detector.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/facetracknoir/shortcut-dialog.cpp b/facetracknoir/shortcut-dialog.cpp index c157078e..b4c236c6 100644 --- a/facetracknoir/shortcut-dialog.cpp +++ b/facetracknoir/shortcut-dialog.cpp @@ -36,6 +36,7 @@ KeyboardShortcutDialog::KeyboardShortcutDialog() void KeyboardShortcutDialog::doOK() { s.b->save(); s.s_main.b->save(); + ui.game_detector->save(); this->close(); emit reload(); } @@ -43,5 +44,6 @@ void KeyboardShortcutDialog::doOK() { void KeyboardShortcutDialog::doCancel() { s.b->reload(); s.s_main.b->reload(); + ui.game_detector->revert(); close(); } diff --git a/facetracknoir/ui.cpp b/facetracknoir/ui.cpp index 4c6c694c..fb28c3c6 100644 --- a/facetracknoir/ui.cpp +++ b/facetracknoir/ui.cpp @@ -79,6 +79,9 @@ MainWindow::MainWindow() : connect(&pose_update_timer, SIGNAL(timeout()), this, SLOT(showHeadPose())); connect(&kbd_quit, SIGNAL(activated()), this, SLOT(exit())); kbd_quit.setEnabled(true); + + connect(&det_timer, SIGNAL(timeout()), this, SLOT(maybe_start_profile_from_executable())); + det_timer.start(3000); ensure_tray(); } @@ -179,16 +182,13 @@ extern "C" volatile const char* opentrack_version; void MainWindow::fill_profile_combobox() { - QString currentFile = group::ini_pathname(); - qDebug() << "Config file now" << currentFile; - QFileInfo pathInfo ( currentFile ); - setWindowTitle(QString( const_cast<const char*>(opentrack_version) + QStringLiteral(" :: ")) + pathInfo.fileName()); - QDir settingsDir( pathInfo.dir() ); - auto iniFileList = settingsDir.entryList( QStringList { "*.ini" } , QDir::Files, QDir::Name ); + QStringList ini_list = group::ini_list(); + QString current = QFileInfo(group::ini_pathname()).fileName(); + setWindowTitle(QString( const_cast<const char*>(opentrack_version) + QStringLiteral(" :: ")) + current); ui.iconcomboProfile->clear(); - for (auto x : iniFileList) + for (auto x : ini_list) ui.iconcomboProfile->addItem(QIcon(":/images/settings16.png"), x); - ui.iconcomboProfile->setCurrentText(pathInfo.fileName()); + ui.iconcomboProfile->setCurrentText(current); } void MainWindow::updateButtonState(bool running, bool inertialp) @@ -486,3 +486,28 @@ void MainWindow::changeEvent(QEvent* e) } QMainWindow::changeEvent(e); } + +void MainWindow::maybe_start_profile_from_executable() +{ + if (!work) + { + QString prof; + if (det.config_to_start(prof)) + { + QString profile = QFileInfo(group::ini_pathname()).absolutePath() + "/" + prof; + set_profile(profile); + startTracker(); + } + } + else + { + if (det.should_stop()) + stopTracker(); + } +} + +void MainWindow::set_profile(const QString &profile) +{ + QSettings settings(group::org); + settings.setValue(group::filename_key, MainWindow::remove_app_path(profile)); +} diff --git a/facetracknoir/ui.h b/facetracknoir/ui.h index aea4991d..d0ea4e75 100644 --- a/facetracknoir/ui.h +++ b/facetracknoir/ui.h @@ -55,6 +55,7 @@ #include "opentrack/state.hpp" #include "curve-config.h" #include "shortcut-dialog.hpp" +#include "process_detector.h" using namespace options; @@ -65,6 +66,7 @@ class MainWindow : public QMainWindow, private State Ui::OpentrackUI ui; mem<QSystemTrayIcon> tray; QTimer pose_update_timer; + QTimer det_timer; mem<KeyboardShortcutDialog> shortcuts_widget; mem<MapWidget> mapping_widget; QShortcut kbd_quit; @@ -72,6 +74,7 @@ class MainWindow : public QMainWindow, private State mem<IFilterDialog> pFilterDialog; mem<IProtocolDialog> pProtocolDialog; mem<ITrackerDialog> pTrackerDialog; + process_detector_worker det; mem<dylib> current_tracker() { @@ -94,7 +97,6 @@ class MainWindow : public QMainWindow, private State void fill_profile_combobox(); void display_pose(const double* mapped, const double* raw); void ensure_tray(); - void set_working_directory(); public slots: void shortcutRecentered(); void shortcutToggled(); @@ -115,15 +117,16 @@ private slots: void showHeadPose(); void restore_from_tray(QSystemTrayIcon::ActivationReason); - + void maybe_start_profile_from_executable(); public slots: void startTracker(); void stopTracker(); - public: MainWindow(); ~MainWindow(); void save_mappings(); void load_mappings(); static QString remove_app_path(const QString full_path); + static void set_working_directory(); + static void set_profile(const QString& profile); }; |