diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml new file mode 100644 index 0000000..72827af --- /dev/null +++ b/.github/workflows/build-and-release.yml @@ -0,0 +1,157 @@ +name: Build and Release + +on: + push: + tags: + - 'v*' + +jobs: + build-windows: + runs-on: windows-latest + + steps: + - uses: actions/checkout@v4 + + - name: Install Qt + uses: jurplel/install-qt-action@v4 + with: + version: "6.7.2" + add-tools-to-path: true + + - name: Setup MSVC + uses: ilammy/msvc-dev-cmd@v1 + + - name: Build + run: | + mkdir build + cd build + qmake ../ + nmake + + - name: Remove source and object files + shell: pwsh + run: | + $buildDir = "build/release" + if (Test-Path $buildDir) { + Get-ChildItem -Path $buildDir -Include *.cpp, *.h, *.obj, *.res -Recurse | Remove-Item -Force + } else { + Write-Host "Directory not found: $buildDir" + } + + - name: Deploy Qt + shell: pwsh + run: | + cd build + $windeployqtPath = "D:\a\HeadsetControl-GUI\Qt\6.7.2\msvc2019_64\bin\windeployqt6.exe" + if (Test-Path $windeployqtPath) { + & $windeployqtPath ` + --exclude-plugins qsvgicon,qsvg,qico,qjpeg,qgif,qnetworklistmanager,qtuiotouchplugin ` + --no-opengl-sw ` + --no-system-dxc-compiler ` + --no-compiler-runtime ` + --no-translations ` + --no-system-d3d-compiler ` + D:\a\HeadsetControl-GUI\HeadsetControl-GUI\build\release\HeadsetControl-GUI.exe + } else { + Write-Error "windeploygui not found at the expected path!" + exit 1 + } + - name: Download ZIP from other repo + shell: pwsh + run: | + Invoke-WebRequest -Uri "https://github.com/Sapd/HeadsetControl/releases/latest/download/headsetcontrol-windows.zip" -OutFile headsetcontrol-windows.zip + Expand-Archive -Path headsetcontrol-windows.zip -DestinationPath build/release/ + + - name: Zip binaries folder + run: | + $zipFile = "HeadsetControl-GUI_windows_64.zip" + $folder = "build/release/" + Compress-Archive -Path $folder -DestinationPath $zipFile + shell: pwsh + + - name: Upload Windows artifact + uses: actions/upload-artifact@v4 + with: + name: HeadsetControl-GUI_windows_64 + path: HeadsetControl-GUI_windows_64.zip + + build-linux: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Install Qt + uses: jurplel/install-qt-action@v4 + with: + version: "6.7.2" + host: "linux" + add-tools-to-path: true + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y build-essential libgl1-mesa-dev + + - name: Build with qmake + run: | + mkdir build + cd build + qmake ../HeadsetControl-GUI.pro CONFIG+=release + make -j$(nproc) + + - name: Zip binaries folder + run: | + zip build/HeadsetControl-GUI_linux_64.zip build/HeadsetControl-GUI + + - name: Upload Artifacts + uses: actions/upload-artifact@v4 + with: + name: HeadsetControl-GUI_linux_64 + path: build/HeadsetControl-GUI_linux_64.zip + + create-release: + needs: [build-linux, build-windows] + runs-on: ubuntu-latest + + steps: + - name: Download Linux artifact + uses: actions/download-artifact@v4 + with: + name: HeadsetControl-GUI_linux_64 + + - name: Download Windows artifact + uses: actions/download-artifact@v4 + with: + name: HeadsetControl-GUI_windows_64 + + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: Release v${{ github.ref }} + draft: false + prerelease: false + + - name: Upload Linux Release Asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./HeadsetControl-GUI_linux_64.zip + asset_name: HeadsetControl-GUI_linux_64.zip + asset_content_type: application/octet-stream + + - name: Upload Windows Release Asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./HeadsetControl-GUI_windows_64.zip + asset_name: HeadsetControl-GUI_windows_64.zip + asset_content_type: application/octet-stream diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 0000000..26b281c --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,58 @@ +name: Build + +on: + push: + branches: [main] + paths-ignore: + - '.src/**' + - 'HeadsetControl-GUI.pro' + pull_request: + branches: [main] + +jobs: + build-windows: + runs-on: windows-latest + + steps: + - uses: actions/checkout@v4 + + - name: Install Qt + uses: jurplel/install-qt-action@v4 + with: + version: "6.7.2" + add-tools-to-path: true + + - name: Setup MSVC + uses: ilammy/msvc-dev-cmd@v1 + + - name: Build + run: | + mkdir build + cd build + qmake ../ + nmake + + build-linux: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Install Qt + uses: jurplel/install-qt-action@v4 + with: + version: "6.7.2" + host: "linux" + add-tools-to-path: true + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y build-essential libgl1-mesa-dev + + - name: Build with qmake + run: | + mkdir build + cd build + qmake ../HeadsetControl-GUI.pro CONFIG+=release + make -j$(nproc) diff --git a/.gitignore b/.gitignore index 259148f..bf96c2c 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,9 @@ *.exe *.out *.app + +# Folders +build/* + +# User files +HeadsetControl-GUI.pro.user diff --git a/HeadsetControl-GUI.pro b/HeadsetControl-GUI.pro index 3c39a7e..d4a5a40 100644 --- a/HeadsetControl-GUI.pro +++ b/HeadsetControl-GUI.pro @@ -1,30 +1,52 @@ -QT += core gui +QT += core gui network +greaterThan(QT_MAJOR_VERSION, 5): QT += widgets -greaterThan(QT_MAJOR_VERSION, 4): QT += widgets +CONFIG += c++17 -CONFIG += c++11 - -# You can make your code fail to compile if it uses deprecated APIs. -# In order to do so, uncomment the following line. -#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 +INCLUDEPATH += \ + src/DataTypes \ + src/UI \ + src/Utils SOURCES += \ - main.cpp \ - mainwindow.cpp + src/UI/settingswindow.cpp \ + src/main.cpp \ + src/DataTypes/device.cpp \ + src/DataTypes/settings.cpp \ + src/UI/dialoginfo.cpp \ + src/UI/loaddevicewindow.cpp \ + src/UI/mainwindow.cpp \ + src/Utils/utils.cpp HEADERS += \ - mainwindow.h + src/DataTypes/device.h \ + src/DataTypes/settings.h \ + src/UI/dialoginfo.h \ + src/UI/loaddevicewindow.h \ + src/UI/mainwindow.h \ + src/UI/settingswindow.h \ + src/Utils/utils.h FORMS += \ - mainwindow.ui + src/UI/dialoginfo.ui \ + src/UI/loaddevicewindow.ui \ + src/UI/mainwindow.ui \ + src/UI/settingswindow.ui TRANSLATIONS += \ - HeadsetControl-GUI_en_US.ts + src/Resources/tr/HeadsetControl_GUI_en_US.ts \ + src/Resources/tr/HeadsetControl_GUI_it_IT.ts + +RESOURCES += \ + src/Resources/icons.qrc \ + src/Resources/translations.qrc + +RC_FILE = src/Resources/appicon.rc + +DISTFILES += \ + .gitignore # Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin else: unix:!android: target.path = /opt/$${TARGET}/bin !isEmpty(target.path): INSTALLS += target - -RESOURCES += \ - icons.qrc diff --git a/HeadsetControl-GUI_en_US.ts b/HeadsetControl-GUI_en_US.ts deleted file mode 100644 index edd0d34..0000000 --- a/HeadsetControl-GUI_en_US.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/README.md b/README.md index 6c54379..c9d7dac 100644 --- a/README.md +++ b/README.md @@ -1,62 +1,61 @@ -# HeadsetControl-GUI -This is a GUI for [Sapds great HeadsetControl](https://github.com/Sapd/HeadsetControl/). It's just a frontend to graphically interact with the original HeadsetControl and has no functionality by itself. +A simply remake of [HeadsetControl-GUI](https://github.com/LeoKlaus/HeadsetControl-GUI) by @LeoKlaus +# HeadsetControl-GUI [![Github All Releases](https://img.shields.io/github/downloads/nicola02nb/headsetcontrol-gui/total.svg)]() [![license](https://img.shields.io/github/license/nicola02nb/HeadsetControl-GUI)]() +This is a GUI for [Sapds great HeadsetControl](https://github.com/Sapd/HeadsetControl/).
+It's just a frontend to graphically interact with the original HeadsetControl and has no functionality by itself.
-I have to give a huge thank you to Sapd for doing all the heavy lifting and developing the command line tool HeadsetControl without which this project wouldn't be possible. -**Disclaimer**: -This program is in no way affiliated with Sapd or HeadsetControl. -All issues regarding the functionality of HeadsetControl (like compatiblity with devices) are beyond the scope of this project. +## Platforms + +OS | Compiled | Tested +:------------ | :-------------| :------------- +Windows | ✅ | ✅ +Linux | ✅ | ❌ +MacOS | ❌ | ❌ + +If you are on Linux or Mac and try to build the app and test it, I'd be happy to hear if it did or didn't work. -## Installation (Windows only for now) -Download the [latest release](https://github.com/LeoKlaus/HeadsetControl-GUI/releases/latest/) of HeadsetControl-GUI from the [releases section](https://github.com/LeoKlaus/HeadsetControl-GUI/releases) of this page. -Download the corresponding version of [Sapds HeadsetControl from their GitHub page](https://github.com/Sapd/HeadsetControl/releases/). +## Installation (Windows) +1. Download the [latest release](https://github.com/nicola02nb/HeadsetControl-GUI/releases/latest/) of HeadsetControl-GUI from the [releases section](https://github.com/nicola02nb/HeadsetControl-GUI/releases) of this page. +2. Extract HeadsetControl-GUI to any folder. -Extract HeadsetControl-GUI to any folder of your choice and drop HeadsetControl into the same folder. The finished folder should look something like this: -![Screenshot of the folder structure](https://i.imgur.com/bbymxL6.jpg "Screenshot of the folder structure") +![image](https://github.com/user-attachments/assets/0145ca37-6e59-4170-ba26-804e8856dbc8) -You HAVE to download a version of the [original headsetcontrol](https://github.com/Sapd/HeadsetControl/releases/) and put it in the same folder. -The executable of headsetcontrol has to be called "HeadsetControl". -Only if both these requirements are met, the GUI can work. +### Usage +Start HeadsetControl-GUI by double-clicking "HeadsetControl-GUI.exe", and if your headset is supported and everything was set up correctly, you will be greeted by the following screen HeadsetControl-GUI has.. -## Usage -Start HeadsetControl-GUI by double-clicking "HeadsetControl-GUI.exe". -If your headset is supported and everything was set up correctly, you will be greeted by the only screen HeadsetControl-GUI has. +If you don't find some features in you ui, probably it's not supported by your headset or it has not been implemented by [HeadsetControl](https://github.com/Sapd/HeadsetControl/). -![Screenshot of the GUI with a Corsair Void Pro Wireless](https://i.imgur.com/xALkNjr.jpg) +![image](https://github.com/nicola02nb/HeadsetControl-GUI/assets/61830443/ce6a9628-4705-4a79-a262-8c43db2c92b0) Here you can adjust all settings supported by your headset. -In my experience, these changes persist even after rebooting the system or turning the headset off. -If you have a wired headset and are finished changing settings, you can close the GUI and call it day. Yay! +Changes may or may not persist even after rebooting the system or turning the headset off(It depends on how headsets stores their own settings). If you have a wireless headset with support for battery levels, you can also minimize HeadsetControl-GUI to the system tray. -![HeadsetControl-GUI in the system tray](https://i.imgur.com/83Apn66.jpg) +![image](https://github.com/nicola02nb/HeadsetControl-GUI/assets/61830443/ea327c0a-e39a-4035-aa99-bc6325724571) -That way, you will be able to see the battery status at a glance and get a reminder when the batteries of your headset run low (below 30%). +That way, you will be able to see the battery status at a glance and get a reminder when the batteries of your headset run low (below 15%). Hovering over the tray icon will show you the current battery percentage. You can also right-click the tray icon to bring up a context menu with quick access to the light control. You can also open or completely close the GUI through the context menu. -![The tray icon context menu](https://i.imgur.com/2IWhbfa.jpg) +![image](https://github.com/nicola02nb/HeadsetControl-GUI/assets/61830443/0213a37c-806c-44d5-b8d7-5cc6b5d69407) -## Performance +### Performance While the concept of calling another app for every single interaction has some inherit overhead, HeadsetControl-GUI is very light on ressources. -Being open in the background, HeadsetControl-GUI consists of a single process that uses virtually no CPU time and less than 8MB of system memory. - -![Screenshot of the background task in task manager](https://i.imgur.com/3PaxKF6.jpg) +Being open in the background, HeadsetControl-GUI consists of a single process that uses virtually no CPU time and about 8-10MB of system memory. +![image](https://github.com/user-attachments/assets/3171e62d-8a0c-49b6-88bd-e5b03393c7fe) ## Building from source -To build HeadsetControl-GUI from source, you have to have a proper QT-ready development environment. -I developed, built and tested the program with Qt 6.0.3, though there's no apparent reason why it wouldn't work with older or newer versions of Qt. -Clone the source code, import the project into Qt creator or your favourite IDE and build it. - -## Support for other platforms -I haven't used any platform-specific code, so generally, you should be able to build and run this app on Linux or MacOS just fine. -I haven't taken the time to build and test on neither Linux nor MacOS (yet), so I can't make any definitive claims on what is supported and what isn't. -If you are on Linux or Mac and try to build the app, I'd be happy to hear if it did or didn't work. +To build HeadsetControl-GUI from source, you have to have a proper QT-ready development environment.
+I developed, built and tested the program with Qt 6.7.0 and [Qt Creator](https://www.qt.io/product/development-tools) as IDE.
+Clone the source code, import the project into [Qt Creator](https://www.qt.io/product/development-tools) or your favourite IDE and build it. ## Additional information -This was written in a day and I'm aware the code is pretty ugly. I plan to fix this and add some additional functionality like persistent settings later down the road. +This software comes with no warranty whatsoever.
+It's not properly tested for memory leakage and may or may not work with configurations other than those I've tested. -This software comes with no warranty whatsoever. It's not properly tested for memory leakage and may or may not work with configurations other than those I've tested. +**Disclaimer**: +This program is in no way affiliated with Sapd or HeadsetControl. +All issues regarding the functionality of HeadsetControl (like compatiblity with devices) are beyond the scope of this project. diff --git a/main.cpp b/main.cpp deleted file mode 100644 index fd2c98e..0000000 --- a/main.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "mainwindow.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -int main(int argc, char *argv[]) -{ - QApplication a(argc, argv); - MainWindow w; - w.resize(10, 10); - w.show(); - - return a.exec(); -} diff --git a/mainwindow.cpp b/mainwindow.cpp deleted file mode 100644 index 63c8394..0000000 --- a/mainwindow.cpp +++ /dev/null @@ -1,279 +0,0 @@ -#include "mainwindow.h" -#include "ui_mainwindow.h" -#include -#include -#include -#include - -MainWindow::MainWindow(QWidget *parent) - : QMainWindow(parent) - , ui(new Ui::MainWindow) -{ - ui->setupUi(this); - - tray->setIcon(QIcon(":/icons/headphones-inv.png")); - tray->show(); - tray->setToolTip("HeadsetControl"); - - QMenu *menu = new QMenu(nullptr); - menu->addAction("Show", this, SLOT(show())); - menu->addAction("Turn Lights On", this, SLOT(on_onButton_clicked())); - menu->addAction("Turn Lights Off", this, SLOT(on_offButton_clicked())); - menu->addAction("Exit", this, SLOT(close())); - - tray->setContextMenu(menu); - - connect(tray, SIGNAL(DoubleClick), this, SLOT(show())); - - tray->connect(tray, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, - SLOT(RestoreWindowTrigger(QSystemTrayIcon::ActivationReason))); - - QProcess *proc = new QProcess(); - proc->start("headsetcontrol", QStringList() << QString("-c?")); - - proc->waitForFinished(); - QByteArray strdata = proc->readAllStandardOutput(); - QString supportedParams = strdata; - //supportedParams = "sbnlimvr"; //Uncomment this to enable all "modules" - - if (supportedParams == "") { - ui->notSupportedFrame->setHidden(false); - ui->sidetoneFrame->setHidden(true); - ui->batteryFrame->setHidden(true); - ui->lightFrame->setHidden(true); - ui->inactivityFrame->setHidden(true); - ui->voicepromptFrame->setHidden(true); - ui->rotateFrame->setHidden(true); - } - else { - ui->notSupportedFrame->setHidden(true); - - if (supportedParams.contains("s")){ - ui->sidetoneFrame->setHidden(false); - qDebug() << "Sidetone supported"; - } - else ui->sidetoneFrame->setHidden(true); - - if (supportedParams.contains("b")){ - ui->batteryFrame->setHidden(false); - - QTimer *timer = new QTimer(this); - connect(timer, SIGNAL(timeout()), this, SLOT(setBatteryStatus())); - timer->start(300000); - this->setBatteryStatus(); - qDebug() << "Battery percentage supported"; - } - else ui->batteryFrame->setHidden(true); - - if (supportedParams.contains("l")){ - ui->lightFrame->setHidden(false); - qDebug() << "Light control supported"; - } - else ui->lightFrame->setHidden(true); - if (supportedParams.contains("i")){ - ui->inactivityFrame->setHidden(false); - qDebug() << "Inactivity timer supported"; - } - else ui->inactivityFrame->setHidden(true); - if (supportedParams.contains("v")){ - ui->voicepromptFrame->setHidden(false); - qDebug() << "Voice prompt control supported"; - } - else ui->voicepromptFrame->setHidden(true); - if (supportedParams.contains("r")){ - ui->rotateFrame->setHidden(false); - qDebug() << "Rotate to mute supported"; - } - else ui->rotateFrame->setHidden(true); - } -} - -MainWindow::~MainWindow() -{ - delete ui; -} - -void MainWindow::on_onButton_clicked() -{ - QProcess *proc = new QProcess(); - proc->start("headsetcontrol", QStringList() - << QString("-n 1") - << QString("-cl 1") - ); - proc->waitForFinished(); - //qDebug() << proc->readAllStandardError(); -} - -void MainWindow::on_offButton_clicked() -{ - QProcess *proc = new QProcess(); - proc->start("headsetcontrol", QStringList() - << QString("-n 0") - << QString("-cl 0") - ); - proc->waitForFinished(); - //qDebug() << proc->readAllStandardError(); -} - -void MainWindow::on_voiceOnButton_clicked() -{ - QProcess *proc = new QProcess(); - proc->start("headsetcontrol", QStringList() - << QString("-n 1") - << QString("-cv 1") - ); - proc->waitForFinished(); - //qDebug() << proc->readAllStandardError(); -} - -void MainWindow::on_voiceOffButton_clicked() -{ - QProcess *proc = new QProcess(); - proc->start("headsetcontrol", QStringList() - << QString("-n 0") - << QString("-cv 0") - ); - proc->waitForFinished(); - //qDebug() << proc->readAllStandardError(); -} - -void MainWindow::on_sideToneApply_clicked() -{ - QProcess *proc = new QProcess(); - proc->start("headsetcontrol", QStringList() - << QString("-n 1") - << QString("-s" + QString::number(ui->sidetoneSlider->sliderPosition())) - ); - proc->waitForFinished(); - //qDebug() << proc->readAllStandardError(); -} - -void MainWindow::on_sideToneOff_clicked() -{ - QProcess *proc = new QProcess(); - proc->start("headsetcontrol", QStringList() - << QString("-n 0") - << QString("-s 0") - ); - proc->waitForFinished(); - ui->sidetoneSlider->setValue(0); - //qDebug() << proc->readAllStandardError(); -} - -void MainWindow::on_inactivityOffButton_clicked() -{ - QProcess *proc = new QProcess(); - proc->start("headsetcontrol", QStringList() - << QString("-n 0") - << QString("-i 0") - ); - proc->waitForFinished(); - ui->sidetoneSlider->setValue(0); - //qDebug() << proc->readAllStandardError(); -} - -void MainWindow::on_inactivityApplyButton_clicked() -{ - QProcess *proc = new QProcess(); - proc->start("headsetcontrol", QStringList() - << QString("-n 1") - << QString("-i" + QString::number(ui->inactivitySlider->sliderPosition())) - ); - proc->waitForFinished(); - //qDebug() << proc->readAllStandardError(); -} - -void MainWindow::on_rotateOn_clicked() -{ - QProcess *proc = new QProcess(); - proc->start("headsetcontrol", QStringList() - << QString("-n 1") - << QString("-r 1") - ); - proc->waitForFinished(); - //qDebug() << proc->readAllStandardError(); -} - -void MainWindow::on_rotateOff_clicked() -{ - QProcess *proc = new QProcess(); - proc->start("headsetcontrol", QStringList() - << QString("-n 0") - << QString("-r 0") - ); - proc->waitForFinished(); - //qDebug() << proc->readAllStandardError(); -} - -void MainWindow::setBatteryStatus() -{ - QProcess *proc = new QProcess(); - proc->start("headsetcontrol", QStringList() - << QString("-cb") - ); - proc->waitForFinished(); - QString batteryStatus = proc->readAllStandardOutput(); - //qDebug() << proc->readAllStandardError(); - - if (batteryStatus == "-2"){ - ui->batteryPercentage->setText("Headset Off"); - tray->setToolTip("HeadsetControl \r\nHeadset Off"); - } - else if (batteryStatus == "-1") { - ui->batteryPercentage->setText("Headset Charging"); - tray->setToolTip("HeadsetControl \r\nBattery Charging"); - tray->setIcon(QIcon(":/icons/battery-charging-inv.png")); - } - else { - ui->batteryPercentage->setText(batteryStatus); - tray->setToolTip("HeadsetControl \r\nBattery: " + batteryStatus + "%"); - if (batteryStatus.toInt() >= 70){ - tray->setIcon(QIcon(":/icons/battery-level-full-inv.png")); - notified = false; - } - else if (batteryStatus.toInt() >= 30) { - tray->setIcon(QIcon(":/icons/battery-medium-inv.png")); - notified = false; - } - else { - tray->setIcon(QIcon(":/icons/battery-low-inv.png")); - if (!notified){ - tray->showMessage("Battery Alert!", "The battery of your headset is running low", QIcon(":/icons/battery-low-inv.png")); - notified = true; - } - } - } -} - -void MainWindow::changeEvent(QEvent* e) -{ - switch (e->type()) - { - case QEvent::LanguageChange: - this->ui->retranslateUi(this); - break; - case QEvent::WindowStateChange: - { - if (this->windowState() & Qt::WindowMinimized) - { - QTimer::singleShot(0, this, SLOT(hide())); - } - - break; - } - default: - break; - } - - QMainWindow::changeEvent(e); -} - -void MainWindow::RestoreWindowTrigger(QSystemTrayIcon::ActivationReason RW) -{ - if(RW == QSystemTrayIcon::DoubleClick) - { - show(); - activateWindow(); - raise(); - } -} diff --git a/mainwindow.h b/mainwindow.h deleted file mode 100644 index 8570f53..0000000 --- a/mainwindow.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef MAINWINDOW_H -#define MAINWINDOW_H - -#include -#include - -QT_BEGIN_NAMESPACE -namespace Ui { class MainWindow; } -QT_END_NAMESPACE - -class MainWindow : public QMainWindow -{ - Q_OBJECT - -public: - MainWindow(QWidget *parent = nullptr); - ~MainWindow(); - - bool notified = false; - QSystemTrayIcon *tray = new QSystemTrayIcon(this); - -private slots: - void changeEvent(QEvent *e); - - void RestoreWindowTrigger(QSystemTrayIcon::ActivationReason RW); - - void on_onButton_clicked(); - - void on_offButton_clicked(); - - void on_voiceOnButton_clicked(); - - void on_voiceOffButton_clicked(); - - void on_sideToneApply_clicked(); - - void on_sideToneOff_clicked(); - - void on_inactivityOffButton_clicked(); - - void on_inactivityApplyButton_clicked(); - - void on_rotateOn_clicked(); - - void on_rotateOff_clicked(); - - void setBatteryStatus(); - -private: - Ui::MainWindow *ui; -}; -#endif // MAINWINDOW_H diff --git a/mainwindow.ui b/mainwindow.ui deleted file mode 100644 index 2224440..0000000 --- a/mainwindow.ui +++ /dev/null @@ -1,689 +0,0 @@ - - - MainWindow - - - - 0 - 0 - 510 - 647 - - - - - 0 - 0 - - - - HeadsetControl - GUI - - - - :/icons/headphones.png:/icons/headphones.png - - - background-color: rgb(44, 44, 44); -color: rgb(208, 208, 208); -font: 87 10pt "Segoe UI Black"; - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - QLabel { - min-width: 120px; - max-width: 120px; -} - -QPushButton { - border-width: 1px; - border-color: rgb(200, 200, 200); - border-style: inset; - border-radius: 5px; -} -.QFrame { - border-radius: 5px; - border-width: 1px; - border-style: solid; - border-color: rgb(200, 200, 200); -} - - - - - - - 0 - 0 - - - - QLabel { -min-width: 400px; -max-width: 400px; -} - - - - - - - 400 - 0 - - - - - 400 - 16777215 - - - - HeadsetControl couldn't find any compatible headsets. :( - - - Qt::AlignCenter - - - - - - - - - - true - - - - 0 - 0 - - - - - - - - 6 - - - 9 - - - 9 - - - - - - 0 - 0 - - - - - 120 - 0 - - - - - 120 - 16777215 - - - - Battery: - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - - - - - true - - - - 0 - 0 - - - - - 200 - 16777215 - - - - QLabel { -min-width: 200px; -max-width: 200px; -} - - - No compatible Device found! - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - 0 - 0 - - - - - - - - 120 - 0 - - - - - 120 - 16777215 - - - - Lights: - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - - - - - - 120 - 0 - - - - RGB ON - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 120 - 0 - - - - RGB OFF - - - - - - - - - - - 0 - 0 - - - - - - - - 120 - 0 - - - - Sidetone Level: - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - - - - - QLayout::SetDefaultConstraint - - - - - false - - - Drag to adjust Sidetone Level - - - 128 - - - 16 - - - Qt::Horizontal - - - QSlider::TicksBelow - - - 16 - - - - - - - - - Quiet - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Qt::LeftToRight - - - Loud - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - 120 - 0 - - - - Off - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 120 - 0 - - - - Apply - - - - - - - - - - - - - - - 0 - 0 - - - - - - - - - - 120 - 10 - - - - - 120 - 16777215 - - - - Voice Prompts: - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - - - - - - - - 120 - 0 - - - - Voice On - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 120 - 0 - - - - Voice Off - - - - - - - - - - - - - - - 0 - 0 - - - - - - - - 120 - 0 - - - - Inactivity Timer: - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - - - - - - - 1 - - - 90 - - - Qt::Horizontal - - - QSlider::TicksBelow - - - 10 - - - - - - - - - 1 Minute - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - 90 Minutes - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - 120 - 0 - - - - Off - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 120 - 0 - - - - Apply - - - - - - - - - - - - - - - 0 - 0 - - - - - - - Rotate to mute: - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - - - - - Rotate-to-Mute On - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Rotate-to-Mute Off - - - - - - - - - - - - - - diff --git a/src/DataTypes/device.cpp b/src/DataTypes/device.cpp new file mode 100644 index 0000000..68449f8 --- /dev/null +++ b/src/DataTypes/device.cpp @@ -0,0 +1,305 @@ +#include "device.h" +#include +#include +#include +#include +#include + +Battery::Battery(){ + +} + +Battery::Battery(QString stat, int lev){ + status=stat; + level=lev; +} + +Equalizer::Equalizer(){ + +} + +Equalizer::Equalizer(int bands, int baseline, double step, int min, int max){ + bands_number=bands; + band_baseline=baseline; + band_min=min; + band_step=step; + band_max=max; +} + +Device::Device(){ + +} + +Device::Device(const QJsonObject& jsonObj, QString jsonData){ + connected=jsonObj["status"].toString()=="success"; + + device=jsonObj["device"].toString(); + vendor=jsonObj["vendor"].toString(); + product=jsonObj["product"].toString(); + id_vendor=jsonObj["id_vendor"].toString(); + id_product=jsonObj["id_product"].toString(); + + QJsonArray caps=jsonObj["capabilities"].toArray(); + for (const QJsonValue &value : caps) { + capabilities.insert(value.toString()); + } + if (capabilities.contains("CAP_BATTERY_STATUS")){ + QJsonObject jEq=jsonObj["battery"].toObject(); + battery=Battery(jEq["status"].toString(), jEq["level"].toInt()); + } + if (capabilities.contains("CAP_CHATMIX_STATUS")){ + chatmix=jsonObj["chatmix"].toInt(); + } + + if (capabilities.contains("CAP_EQUALIZER_PRESET")){ + if (jsonObj.contains("equalizer_presets") && jsonObj["equalizer_presets"].isObject()) { + QJsonObject equalizerPresets = jsonObj["equalizer_presets"].toObject(); + + // Parse the original JSON string to find the order of keys + QRegularExpression re("\"(\\w+)\":\\s*\\["); + QRegularExpressionMatchIterator i = re.globalMatch(jsonData); + while (i.hasNext()) { + QRegularExpressionMatch match = i.next(); + QString presetName = match.captured(1); + if (equalizerPresets.contains(presetName)) { + EqualizerPreset preset; + preset.name = presetName; + + QJsonArray valuesArray = equalizerPresets[presetName].toArray(); + for (const QJsonValue& value : valuesArray) { + preset.values.append(value.toDouble()); + } + + presets_list.append(preset); + } + } + } + } + if (capabilities.contains("CAP_EQUALIZER")){ + QJsonObject jEq=jsonObj["equalizer"].toObject(); + if(!jEq.isEmpty()){ + equalizer=Equalizer(jEq["bands"].toInt(), jEq["baseline"].toInt(), jEq["step"].toDouble(), jEq["min"].toInt(), jEq["max"].toInt()); + equalizer_curve=QVector(equalizer.bands_number, equalizer.band_baseline); + } + } +} + +//Helper functions +bool Device::operator!=(const Device &d) const { + return this->id_vendor!=d.id_vendor || this->id_product!=d.id_product; +} + +bool Device::operator==(const Device &d) const { + return this->id_vendor==d.id_vendor && this->id_product==d.id_product; +} + +bool Device::operator==(const Device* d) const { + return this->id_vendor==d->id_vendor && this->id_product==d->id_product; +} + +void Device::updateDevice(const Device* new_device){ + this->battery=new_device->battery; + this->chatmix=new_device->chatmix; +} + +void Device::updateDevice(const QList& new_device_list){ + for (int i = 0; i < new_device_list.length(); ++i) { + if(this!=new_device_list.at(i)){ + this->battery=new_device_list.at(i)->battery; + this->chatmix=new_device_list.at(i)->chatmix; + break; + } + } +} + +QJsonObject Device::toJson() const { + QJsonObject json; + json["device"] = device; + json["vendor"] = vendor; + json["product"] = product; + json["id_vendor"] = id_vendor; + json["id_product"] = id_product; + + json["lights"] = lights; + json["sidetone"] = sidetone; + json["voice_prompts"] = voice_prompts; + json["inactive_time"] = inactive_time; + json["equalizer_preset"] = equalizer_preset; + json["equalizer_curve"] = QJsonArray::fromVariantList(QVariantList(equalizer_curve.begin(), equalizer_curve.end())); + json["volume_limiter"] = volume_limiter; + json["rotate_to_mute"] = rotate_to_mute; + json["mic_mute_led_brightness"] = mic_mute_led_brightness; + json["mic_volume"] = mic_volume; + json["bt_when_powered_on"] = bt_when_powered_on; + json["bt_call_volume"] = bt_call_volume; + + return json; +} + +Device Device::fromJson(const QJsonObject& json) { + Device device; + device.device = json["device"].toString(); + device.vendor = json["vendor"].toString(); + device.product = json["product"].toString(); + device.id_vendor = json["id_vendor"].toString(); + device.id_product = json["id_product"].toString(); + + device.lights = json["lights"].toInt(); + device.sidetone = json["sidetone"].toInt(); + device.voice_prompts = json["voice_prompts"].toInt(); + device.inactive_time = json["inactive_time"].toInt(); + device.equalizer_preset = json["equalizer_preset"].toInt(); + + QJsonArray curveArray = json["equalizer_curve"].toArray(); + for (const auto& value : curveArray) { + device.equalizer_curve.append(value.toInt()); + } + + device.volume_limiter = json["volume_limiter"].toInt(); + device.rotate_to_mute = json["rotate_to_mute"].toInt(); + device.mic_mute_led_brightness = json["mic_mute_led_brightness"].toInt(); + device.mic_volume = json["mic_volume"].toInt(); + device.bt_when_powered_on = json["bt_when_powered_on"].toInt(); + device.bt_call_volume = json["bt_call_volume"].toInt(); + + return device; +} + +//HC rleated functions +QString sendCommand(const QStringList& args_list){ + QProcess *proc = new QProcess(); + QStringList args = QStringList() << QString("--output") << QString("JSON"); + //args << QString("--test-device"); //Uncomment this to enable all "modules" + args << args_list; + + proc->start("headsetcontrol", args); + proc->waitForFinished(); + QString output=proc->readAllStandardOutput(); + //qDebug() << args; + //qDebug() << output; + return output; +} + +Action sendAction(const QStringList& args_list){ + QString output=sendCommand(args_list); + QJsonDocument jsonDoc = QJsonDocument::fromJson(output.toUtf8()); + QJsonObject jsonInfo = jsonDoc.object(); + QJsonArray actions = jsonInfo["actions"].toArray(); + Action action; + if(!actions.isEmpty()){ + QJsonObject jaction = actions[0].toObject(); + + action.device=jaction["device"].toString(); + action.capability=jaction["capability"].toString(); + action.status=jaction["status"].toString(); + action.error_message=jaction["error_message"].toString(); + } + + return action; +} + +QVersionNumber getHCVersion(){ + QStringList args=QStringList() << QString("--output") << QString("JSON"); + QJsonDocument jsonDoc = QJsonDocument::fromJson(sendCommand(args).toUtf8()); + QJsonObject jsonInfo=jsonDoc.object(); + return QVersionNumber::fromString(jsonInfo["version"].toString()); +} + +QList getDevices(){ + QList devices; + + return devices; +} + +QList mergeDevices(QList connectedDevices, const QList& savedDevices) { + for (Device* savedDevice : savedDevices) + { + bool deviceFound = false; + for (Device* connectedDevice : connectedDevices) + { + if (connectedDevice->id_vendor==savedDevice->id_vendor && connectedDevice->id_product==savedDevice->id_product) + { + // Update the connected device with saved device's information + connectedDevice->lights = savedDevice->lights; + connectedDevice->sidetone = savedDevice->sidetone; + connectedDevice->voice_prompts = savedDevice->voice_prompts; + connectedDevice->inactive_time = savedDevice->inactive_time; + + connectedDevice->equalizer_preset = savedDevice->equalizer_preset; + connectedDevice->equalizer_curve = savedDevice->equalizer_curve; + connectedDevice->volume_limiter = savedDevice->volume_limiter; + + connectedDevice->rotate_to_mute = savedDevice->rotate_to_mute; + connectedDevice->mic_mute_led_brightness = savedDevice->mic_mute_led_brightness; + connectedDevice->mic_volume = savedDevice->mic_volume; + + connectedDevice->bt_when_powered_on = savedDevice->bt_when_powered_on; + connectedDevice->bt_call_volume = savedDevice->bt_call_volume; + + deviceFound = true; + break; + } + } + + if (!deviceFound) + { + // If the device wasn't found in saved devices, add it + connectedDevices.append(new Device(*savedDevice)); + } + } + return connectedDevices; +} + +QList getSavedDevices(const QString& file_name){ + return deserializeDevices(file_name); +} + +QList getConnectedDevices(){ + QStringList args=QStringList() << QString("--output") << QString("JSON"); + QString output = sendCommand(args); + QJsonDocument jsonDoc = QJsonDocument::fromJson(output.toUtf8()); + QJsonObject jsonInfo = jsonDoc.object(); + + int device_number = jsonInfo["device_count"].toInt(); + QList devices; + QJsonArray jsonDevices = jsonInfo["devices"].toArray(); + if(!jsonDoc.isNull()){ + for (int i = 0; i < device_number; ++i) { + devices.append(new Device(jsonDevices[i].toObject(), output)); + } + } + + return devices; +} + +void serializeDevices(const QList& devices, const QString& filename) { + QJsonArray jsonArray; + for (const auto* device : devices) { + jsonArray.append(device->toJson()); + } + + QJsonDocument doc(jsonArray); + QFile file(filename); + if (file.open(QIODevice::WriteOnly)) { + file.write(doc.toJson()); + file.close(); + } +} + +QList deserializeDevices(const QString& filename) { + QList devices; + QFile file(filename); + if (file.open(QIODevice::ReadOnly)) { + QByteArray data = file.readAll(); + QJsonDocument doc = QJsonDocument::fromJson(data); + QJsonArray jsonArray = doc.array(); + + for (const auto& value : jsonArray) { + Device* device = new Device(Device::fromJson(value.toObject())); + devices.append(device); + } + + file.close(); + } + return devices; +} diff --git a/src/DataTypes/device.h b/src/DataTypes/device.h new file mode 100644 index 0000000..bb00a61 --- /dev/null +++ b/src/DataTypes/device.h @@ -0,0 +1,104 @@ +#ifndef DEVICE_H +#define DEVICE_H + +#include +#include +#include + +class Action{ +public: + QString capability; + QString device; + QString status; + QString error_message; +}; + +class Battery{ +public: + Battery(); + Battery(QString stat, int lev); + QString status = "BATTERY_UNAVAILABLE"; + int level = 0; +}; + +class EqualizerPreset{ +public: + QString name; + QList values; +}; + +class Equalizer{ +public: + Equalizer(); + Equalizer(int bands, int baseline, double step, int min, int max); + + int bands_number = 0; + int band_baseline = 0; + double band_step = 0; + int band_min = 0; + int band_max = 0; +}; + +class Device +{ +public: + Device(); + Device(const QJsonObject& jsonObj, QString jsonData); + + //Status + bool connected = false; + + //Basic info + QString device; + QString vendor; + QString product; + QString id_vendor; + QString id_product; + QSet capabilities; + + //Info to get from json and display + Battery battery; + int chatmix = 65; + QList presets_list; + Equalizer equalizer; + bool notification_sound=false; + + //Info to set with gui and to save + int lights=-1; + int sidetone=-1; + int voice_prompts=-1; + int inactive_time=-1; + int equalizer_preset=-1; + QList equalizer_curve; + int volume_limiter=-1; + int rotate_to_mute=-1; + int mic_mute_led_brightness=-1; + int mic_volume=-1; + int bt_when_powered_on=-1; + int bt_call_volume=-1; + + bool operator!=(const Device& d) const; + bool operator==(const Device &d) const; + bool operator==(const Device* d) const; + + void updateDevice(const Device* new_device); + void updateDevice(const QList& new_device_list); + + QJsonObject toJson() const; + static Device fromJson(const QJsonObject& json); +}; + +QString sendCommand(const QStringList& args_list); +Action sendAction(const QStringList& args_list); + +QVersionNumber getHCVersion(); + +QList getDevices(); +QList mergeDevices(QList connectedDevices, const QList& savedDevices); +QList getSavedDevices(const QString& file_name); +QList getConnectedDevices(); + +void serializeDevices(const QList& devices, const QString& filename); +QList deserializeDevices(const QString& filename); + +#endif // DEVICE_H diff --git a/src/DataTypes/settings.cpp b/src/DataTypes/settings.cpp new file mode 100644 index 0000000..871af4e --- /dev/null +++ b/src/DataTypes/settings.cpp @@ -0,0 +1,52 @@ +#include "settings.h" +#include +#include +#include + +Settings::Settings() {} + +Settings loadSettingsFromFile(const QString& filename){ + Settings s; + + QFile file(filename); + + if (!file.open(QIODevice::ReadOnly)) { + qWarning("Couldn't open save file."); + return s; + } + + QByteArray saveData = file.readAll(); + file.close(); + + QJsonDocument doc(QJsonDocument::fromJson(saveData)); + QJsonObject json = doc.object(); + + if (json.contains("runOnStartup")) { + s.runOnstartup = json["runOnStartup"].toBool(); + } + if (json.contains("batteryLowThreshold")) { + s.batteryLowThreshold = json["batteryLowThreshold"].toInt(); + } + if (json.contains("msecUpdateIntervalTime")) { + s.msecUpdateIntervalTime = json["msecUpdateIntervalTime"].toInt(); + } + + return s; +} + +void saveSettingstoFile(const Settings& settings, const QString& filename){ + QJsonObject json; + json["runOnStartup"] = settings.runOnstartup; + json["batteryLowThreshold"] = settings.batteryLowThreshold; + json["msecUpdateIntervalTime"] = settings.msecUpdateIntervalTime; + + QJsonDocument doc(json); + QFile file(filename); + + if (!file.open(QIODevice::WriteOnly)) { + qWarning("Couldn't open save file."); + } + + file.write(doc.toJson()); + file.close(); +} diff --git a/src/DataTypes/settings.h b/src/DataTypes/settings.h new file mode 100644 index 0000000..5554512 --- /dev/null +++ b/src/DataTypes/settings.h @@ -0,0 +1,19 @@ +#ifndef SETTINGS_H +#define SETTINGS_H + +#include + +class Settings +{ +public: + Settings(); + + bool runOnstartup=false; + int batteryLowThreshold=15; + int msecUpdateIntervalTime=30000; +}; + +Settings loadSettingsFromFile(const QString& filename); +void saveSettingstoFile(const Settings& settings, const QString& filename); + +#endif // SETTINGS_H diff --git a/src/Resources/appicon.rc b/src/Resources/appicon.rc new file mode 100644 index 0000000..d2559c1 --- /dev/null +++ b/src/Resources/appicon.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "headphones-exe.ico" diff --git a/src/Resources/headphones-exe.ico b/src/Resources/headphones-exe.ico new file mode 100644 index 0000000..1b7b622 Binary files /dev/null and b/src/Resources/headphones-exe.ico differ diff --git a/icons.qrc b/src/Resources/icons.qrc similarity index 100% rename from icons.qrc rename to src/Resources/icons.qrc diff --git a/icons/battery-charging-inv.png b/src/Resources/icons/battery-charging-inv.png similarity index 100% rename from icons/battery-charging-inv.png rename to src/Resources/icons/battery-charging-inv.png diff --git a/icons/battery-charging.png b/src/Resources/icons/battery-charging.png similarity index 100% rename from icons/battery-charging.png rename to src/Resources/icons/battery-charging.png diff --git a/icons/battery-level-full-inv.png b/src/Resources/icons/battery-level-full-inv.png similarity index 100% rename from icons/battery-level-full-inv.png rename to src/Resources/icons/battery-level-full-inv.png diff --git a/icons/battery-level-full.png b/src/Resources/icons/battery-level-full.png similarity index 100% rename from icons/battery-level-full.png rename to src/Resources/icons/battery-level-full.png diff --git a/icons/battery-low-inv.png b/src/Resources/icons/battery-low-inv.png similarity index 100% rename from icons/battery-low-inv.png rename to src/Resources/icons/battery-low-inv.png diff --git a/icons/battery-low.png b/src/Resources/icons/battery-low.png similarity index 100% rename from icons/battery-low.png rename to src/Resources/icons/battery-low.png diff --git a/icons/battery-medium-inv.png b/src/Resources/icons/battery-medium-inv.png similarity index 100% rename from icons/battery-medium-inv.png rename to src/Resources/icons/battery-medium-inv.png diff --git a/icons/battery-medium.png b/src/Resources/icons/battery-medium.png similarity index 100% rename from icons/battery-medium.png rename to src/Resources/icons/battery-medium.png diff --git a/icons/headphones-inv.png b/src/Resources/icons/headphones-inv.png similarity index 100% rename from icons/headphones-inv.png rename to src/Resources/icons/headphones-inv.png diff --git a/icons/headphones.png b/src/Resources/icons/headphones.png similarity index 100% rename from icons/headphones.png rename to src/Resources/icons/headphones.png diff --git a/src/Resources/tr/HeadsetControl_GUI_en_US.qm b/src/Resources/tr/HeadsetControl_GUI_en_US.qm new file mode 100644 index 0000000..da379f7 Binary files /dev/null and b/src/Resources/tr/HeadsetControl_GUI_en_US.qm differ diff --git a/src/Resources/tr/HeadsetControl_GUI_en_US.ts b/src/Resources/tr/HeadsetControl_GUI_en_US.ts new file mode 100644 index 0000000..87a833a --- /dev/null +++ b/src/Resources/tr/HeadsetControl_GUI_en_US.ts @@ -0,0 +1,453 @@ + + + + + MainWindow + + + HeadsetControl - GUI + + + + + HeadsetControl couldn't find any compatible or working headsets. :( + + + + + <html><head/><body><p>Device:<br/>Vendor:<br/>Model:</p></body></html> + + + + + No info of the device + + + + + Battery: + + + + + No compatible Device found! + + + + + Other + + + + + Lights: + + + + + RGB OFF + + + + + RGB ON + + + + + Sidetone Level: + + + + + Drag to adjust Sidetone Level + + + + + Quiet (Off) + + + + + + Loud + + + + + Voice Prompts: + + + + + Voice Off + + + + + Voice On + + + + + Notification Sound: + + + + + Test 0 + + + + + Test 1 + + + + + Inactivity Timer: + + + + + 0 Minute (Off) + + + + + 90 Minutes + + + + + Chatmix: + + + + + None + + + + + Equalizer + + + + + Equalizer preset: + + + + + Equalizer: + + + + + Apply Equalizer + + + + + Volume Limiter: + + + + + Limiter Off + + + + + Limiter On + + + + + Microphone + + + + + Rotate to mute: + + + + + Off + + + + + On + + + + + Muted led brightness: + + + + + Low (Off) + + + + + High + + + + + Microphone volume: + + + + + Quiet + + + + + Bluetooth + + + + + Bluetooth when powered on: + + + + + Bluetooth Off + + + + + Bluetooth On + + + + + Bluetooth call volume: + + + + + BT and PC + + + + + PC -12dB + + + + + BT only + + + + + File + + + + + Help + + + + + Check Updates + + + + + About + + + + + + Credits + + + + + Load Device + + + + + Settings + + + + + Hide/Show + + + + + Turn Lights On + + + + + Turn Lights Off + + + + + Exit + + + + + Missing headsetcontrol + + + + + Missing headsetcontrol<br>Download <a href='https://github.com/Sapd/HeadsetControl/releases/latest'>headsetcontrol</a> in the opened folder. + + + + + Headset Off + + + + + HeadsetControl +Headset Off + + + + + % - Charging + + + + + HeadsetControl +Battery Charging + + + + + % - Descharging + + + + + HeadsetControl +Battery: + + + + + Battery Alert! + + + + + The battery of your headset is running low + + + + + No battery info + + + + + Game + + + + + Chat + + + + + Check for updates + + + + + + up-to date v + + + + + + Newer version + + + + + About this program + + + + + <a href='https://github.com/nicola02nb/HeadsetControl-GUI'>This</a> is a forked version of <a href='https://github.com/LeoKlaus/HeadsetControl-GUI'>HeadsetControl-GUI</a>.<br>Made by <a href='https://github.com/nicola02nb/HeadsetControl-GUI'>nicola02nb</a><br>Version: + + + + + Big shout-out to:<br> - Sapd for <a href='https://github.com/Sapd/HeadsetControl'>HeadsetCoontrol</a><br> - LeoKlaus for <a href='https://github.com/LeoKlaus/HeadsetControl-GUI'>HeadsetControl-GUI</a> + + + + + dialogInfo + + + Dialog + + + + + loaddevicewindow + + + Select device to load + + + + + Select device: + + + + + settingswindow + + + Settings + + + + + Run on Startup: + + + + + Battery low threshold: + Battery low treshold: + + + + <html><head/><body><p>Update Info interval time (seconds):<br/>Default: 30,0 seconds<br/>DON'T PUT TOO LOW VALUES</p></body></html> + + + + diff --git a/src/Resources/tr/HeadsetControl_GUI_it_IT.qm b/src/Resources/tr/HeadsetControl_GUI_it_IT.qm new file mode 100644 index 0000000..a9cf69f Binary files /dev/null and b/src/Resources/tr/HeadsetControl_GUI_it_IT.qm differ diff --git a/src/Resources/tr/HeadsetControl_GUI_it_IT.ts b/src/Resources/tr/HeadsetControl_GUI_it_IT.ts new file mode 100644 index 0000000..40c9ce5 --- /dev/null +++ b/src/Resources/tr/HeadsetControl_GUI_it_IT.ts @@ -0,0 +1,456 @@ + + + + + MainWindow + + + HeadsetControl - GUI + + + + + HeadsetControl couldn't find any compatible or working headsets. :( + HeadsetControl non è riuscito a trovare delle cuffie funizionanti o compatibili. :( + + + + <html><head/><body><p>Device:<br/>Vendor:<br/>Model:</p></body></html> + <html><head/><body><p>Dispositivo:<br/>Distributore:<br/>Modello:</p></body></html> + + + + No info of the device + Nessuna informazione sul dipositivo + + + + Battery: + Batteria: + + + + No compatible Device found! + Nessun dispositivo compatibile è stato trovato! + + + + Other + Altro + + + + Lights: + Luci: + + + + RGB OFF + RGB OFF + + + + RGB ON + RGB ON + + + + Sidetone Level: + Tono Laterale: + + + + Drag to adjust Sidetone Level + Trascina per regolare il Tono Laterale + + + + Quiet (Off) + Silenzioso (Spento) + + + + + Loud + Forte + + + + Voice Prompts: + Istruzioni Vocali: + + + + Voice Off + Voce Accesa + + + + Voice On + Voce Spenta + + + + Notification Sound: + Suono di Notifica: + + + + Test 0 + Prova 0 + + + + Test 1 + Prova 1 + + + + Inactivity Timer: + Tempo di Inattività: + + + + 0 Minute (Off) + 0 Minuti (Spento) + + + + 90 Minutes + 90 Minuti + + + + Chatmix: + Chatmix: + + + + None + Nessun valore + + + + Equalizer + Equalizzatore + + + + Equalizer preset: + Preset Equalizzatore: + + + + Equalizer: + Equalizzatore: + + + + Apply Equalizer + Applica Equalizzatore + + + + Volume Limiter: + Limitatore Volume: + + + + Limiter Off + Limitatore Spento + + + + Limiter On + Limitatore Acceso + + + + Microphone + Microfono + + + + Rotate to mute: + Ruota per mutare: + + + + Off + Spento + + + + On + Acceso + + + + Muted led brightness: + Luminosità microfono mutato: + + + + Low (Off) + Basso (Spento) + + + + High + Alto + + + + Microphone volume: + Volume microfono: + + + + Quiet + Basso + + + + Bluetooth + Bluetooth + + + + Bluetooth when powered on: + Bluetooth quando accese: + + + + Bluetooth Off + Bluetooth Spento + + + + Bluetooth On + Bluetooth Acceso + + + + Bluetooth call volume: + Bluetoot volume chiamata: + + + + BT and PC + BT e PC + + + + PC -12dB + PC -12dB + + + + BT only + Solo BT + + + + File + File + + + + Help + Aiuto + + + + Check Updates + Controlla Aggiornamenti + + + + About + About + + + + + Credits + Crediti + + + + Load Device + Carica Dispositivo + + + + Settings + Impostazioni + + + + Hide/Show + Nascondi/Mostra + + + + Turn Lights On + Accendi le Luci + + + + Turn Lights Off + Spegni le Luci + + + + Exit + Esci + + + + Missing headsetcontrol + Manca headsetcontrol.exe + + + + Missing headsetcontrol<br>Download <a href='https://github.com/Sapd/HeadsetControl/releases/latest'>headsetcontrol</a> in the opened folder. + Manca headsetcontrol.exe<br>Scarica <a href='https://github.com/Sapd/HeadsetControl/releases/latest'>headsetcontrol</a> nella cartella aperta. + + + + Headset Off + Cuffie Spente + + + + HeadsetControl +Headset Off + HeadsetControl +Cuffie Spente + + + + % - Charging + % - In Carica + + + + HeadsetControl +Battery Charging + HeadsetControl +Batteria in Carica + + + + % - Descharging + % - Batteria in scarica + + + + HeadsetControl +Battery: + HeadsetControl +Batteria: + + + + Battery Alert! + Attenzione Batteria! + + + + The battery of your headset is running low + La batteria delle tue cuffie è scarica + + + + No battery info + No informazioni sulla batteria + + + + Game + Gioco + + + + Chat + Chat + + + + Check for updates + Controlla Aggirnamenti + + + + + up-to date v + aggiornato + + + + + Newer version + Nuova versione + + + + About this program + + + + + <a href='https://github.com/nicola02nb/HeadsetControl-GUI'>This</a> is a forked version of <a href='https://github.com/LeoKlaus/HeadsetControl-GUI'>HeadsetControl-GUI</a>.<br>Made by <a href='https://github.com/nicola02nb/HeadsetControl-GUI'>nicola02nb</a><br>Version: + <a href='https://github.com/nicola02nb/HeadsetControl-GUI'>Questa</a> è il fork della versione <a href='https://github.com/LeoKlaus/HeadsetControl-GUI'>HeadsetControl-GUI</a>.<br>Fatta da <a href='https://github.com/nicola02nb/HeadsetControl-GUI'>nicola02nb</a><br>Version: + + + + Big shout-out to:<br> - Sapd for <a href='https://github.com/Sapd/HeadsetControl'>HeadsetCoontrol</a><br> - LeoKlaus for <a href='https://github.com/LeoKlaus/HeadsetControl-GUI'>HeadsetControl-GUI</a> + Un grande ringraziamento va a:<br> - Sapd per <a href='https://github.com/Sapd/HeadsetControl'>HeadsetCoontrol</a><br> - LeoKlaus per <a href='https://github.com/LeoKlaus/HeadsetControl-GUI'>HeadsetControl-GUI</a> + + + + dialogInfo + + + Dialog + + + + + loaddevicewindow + + + Select device to load + Seleziona dispositivo da caricare + + + + Select device: + Seleziona dispositivo: + + + + settingswindow + + + Settings + Impostazioni + + + + Run on Startup: + Esecuzione all'avvio: + + + + Battery low threshold: + Soglia batteria scarica: + + + + <html><head/><body><p>Update Info interval time (seconds):<br/>Default: 30,0 seconds<br/>DON'T PUT TOO LOW VALUES</p></body></html> + <html><head/><body><p>Intervallo di aggiornamento info (secondi):<br/>Predefinito: 30,0 secondi<br/>NON IMPOSTARE VALORI TROPPO BASSI</p></body></html> + + + diff --git a/src/Resources/translations.qrc b/src/Resources/translations.qrc new file mode 100644 index 0000000..c246505 --- /dev/null +++ b/src/Resources/translations.qrc @@ -0,0 +1,6 @@ + + + tr/HeadsetControl_GUI_en_US.qm + tr/HeadsetControl_GUI_it_IT.qm + + diff --git a/src/UI/dialoginfo.cpp b/src/UI/dialoginfo.cpp new file mode 100644 index 0000000..4a44966 --- /dev/null +++ b/src/UI/dialoginfo.cpp @@ -0,0 +1,25 @@ +#include "dialoginfo.h" +#include "ui_dialoginfo.h" + +DialogInfo::DialogInfo(QWidget *parent) + : QDialog(parent) + , ui(new Ui::dialogInfo) +{ + setModal(true); + ui->setupUi(this); +} + +DialogInfo::~DialogInfo() +{ + delete ui; +} + +void DialogInfo::setTitle(const QString& title) +{ + this->setWindowTitle(title); +} + +void DialogInfo::setLabel(const QString& text) +{ + ui->label->setText(text); +} diff --git a/src/UI/dialoginfo.h b/src/UI/dialoginfo.h new file mode 100644 index 0000000..10c4209 --- /dev/null +++ b/src/UI/dialoginfo.h @@ -0,0 +1,25 @@ +#ifndef DIALOGINFO_H +#define DIALOGINFO_H + +#include + +namespace Ui { +class dialogInfo; +} + +class DialogInfo : public QDialog +{ + Q_OBJECT + +public: + explicit DialogInfo(QWidget *parent = nullptr); + ~DialogInfo(); + + void setTitle(const QString& title); + void setLabel(const QString& text); + +private: + Ui::dialogInfo *ui; +}; + +#endif // DIALOGINFO_H diff --git a/src/UI/dialoginfo.ui b/src/UI/dialoginfo.ui new file mode 100644 index 0000000..c3db3f9 --- /dev/null +++ b/src/UI/dialoginfo.ui @@ -0,0 +1,143 @@ + + + dialogInfo + + + + 0 + 0 + 305 + 86 + + + + + 0 + 0 + + + + Dialog + + + + :/icons/headphones-inv.png:/icons/headphones-inv.png + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + Qt::TextBrowserInteraction + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + buttonBox + accepted() + dialogInfo + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + dialogInfo + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/UI/loaddevicewindow.cpp b/src/UI/loaddevicewindow.cpp new file mode 100644 index 0000000..e949b41 --- /dev/null +++ b/src/UI/loaddevicewindow.cpp @@ -0,0 +1,21 @@ +#include "loaddevicewindow.h" +#include "ui_loaddevicewindow.h" + +LoaddeviceWindow::LoaddeviceWindow(const QStringList& devices, QWidget *parent) + : QDialog(parent) + , ui(new Ui::loaddevicewindow) +{ + setModal(true); + ui->setupUi(this); + + ui->devicelistComboBox->addItems(devices); +} + +int LoaddeviceWindow::getDeviceIndex(){ + return ui->devicelistComboBox->currentIndex(); +} + +LoaddeviceWindow::~LoaddeviceWindow() +{ + delete ui; +} diff --git a/src/UI/loaddevicewindow.h b/src/UI/loaddevicewindow.h new file mode 100644 index 0000000..1dbcc21 --- /dev/null +++ b/src/UI/loaddevicewindow.h @@ -0,0 +1,24 @@ +#ifndef LOADDEVICEWINDOW_H +#define LOADDEVICEWINDOW_H + +#include + +namespace Ui { +class loaddevicewindow; +} + +class LoaddeviceWindow : public QDialog +{ + Q_OBJECT + +public: + explicit LoaddeviceWindow(const QStringList& devices, QWidget *parent = nullptr); + ~LoaddeviceWindow(); + + int getDeviceIndex(); + +private: + Ui::loaddevicewindow *ui; +}; + +#endif // LOADDEVICEWINDOW_H diff --git a/src/UI/loaddevicewindow.ui b/src/UI/loaddevicewindow.ui new file mode 100644 index 0000000..cd34f27 --- /dev/null +++ b/src/UI/loaddevicewindow.ui @@ -0,0 +1,111 @@ + + + loaddevicewindow + + + + 0 + 0 + 174 + 112 + + + + + 0 + 0 + + + + Select device to load + + + + :/icons/headphones-inv.png:/icons/headphones-inv.png + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + Select device: + + + + + + + + + + 99 + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + loaddevicewindow + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + loaddevicewindow + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/UI/mainwindow.cpp b/src/UI/mainwindow.cpp new file mode 100644 index 0000000..2234ffb --- /dev/null +++ b/src/UI/mainwindow.cpp @@ -0,0 +1,838 @@ +#include "mainwindow.h" +#include "ui_mainwindow.h" +#include "device.h" +#include "dialoginfo.h" +#include "settingswindow.h" +#include "loaddevicewindow.h" + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + this->bindEvents(); + + settings=loadSettingsFromFile(PROGRAM_SETTINGS_FILENAME); + darkMode = isOsDarkMode(); + + if(darkMode){ + this->setWindowIcon(QIcon(":/icons/headphones-inv.png")); + trayIconPath = ":/icons/headphones-inv.png"; + } + else{ + this->setWindowIcon(QIcon(":/icons/headphones.png")); + trayIconPath = ":/icons/headphones.png"; + } + + tray->setIcon(QIcon(trayIconPath)); + tray->show(); + tray->setToolTip("HeadsetControl"); + + menu = new QMenu(nullptr); + menu->addAction(tr("Hide/Show"), this, &MainWindow::toggleWindow); + ledOn = menu->addAction(tr("Turn Lights On"), this, &MainWindow::onlightButton_clicked); + ledOff = menu->addAction(tr("Turn Lights Off"), this, &MainWindow::offlightButton_clicked); + menu->addAction(tr("Exit"), this, &QApplication::quit); + + tray->setContextMenu(menu); + tray->connect(tray, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason))); + + this->disableFrames(); + + QString exe = "headsetcontrol"; +#ifdef Q_OS_WIN + exe = exe+".exe"; +#endif + + if(!fileExists(exe)){ + openFileExplorer("."); + DialogInfo* dialog=new DialogInfo(this); + dialog->setTitle(tr("Missing headsetcontrol")); + dialog->setLabel(tr("Missing headsetcontrol
" + "Download headsetcontrol in the opened folder.")); + dialog->exec(); + } + + this->loadDevices(); + if(deviceList.length() && n_connected>0){ + this->loadDevice(); + } + + this->setMaximumHeight(this->minimumHeight()); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + +void MainWindow::bindEvents(){ + // Tool Bar + connect(ui->actionSettings, &QAction::triggered, this, &MainWindow::editProgramSetting); + connect(ui->actionLoad_Device, &QAction::triggered, this, &MainWindow::selectDevice); + connect(ui->actionCheck_Updates, &QAction::triggered, this, &MainWindow::checkForUpdates); + + connect(ui->actionAbout, &QAction::triggered, this, &MainWindow::showAbout); + connect(ui->actionCredits, &QAction::triggered, this, &MainWindow::showCredits); + + // Other Section + connect(ui->onlightButton, &QPushButton::clicked, this, &MainWindow::onlightButton_clicked); + connect(ui->offlightButton, &QPushButton::clicked, this, &MainWindow::offlightButton_clicked); + connect(ui->sidetoneSlider, &QSlider::sliderReleased, this, &MainWindow::sidetoneSlider_sliderReleased); + connect(ui->voiceOnButton, &QPushButton::clicked, this, &MainWindow::voiceOnButton_clicked); + connect(ui->voiceOffButton, &QPushButton::clicked, this, &MainWindow::voiceOffButton_clicked); + connect(ui->notification0Button, &QPushButton::clicked, this, &MainWindow::notification0Button_clicked); + connect(ui->notification1Button, &QPushButton::clicked, this, &MainWindow::notification1Button_clicked); + connect(ui->inactivitySlider, &QSlider::sliderReleased, this, &MainWindow::inactivitySlider_sliderReleased); + + // Equalizer Section + connect(ui->equalizerPresetcomboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &MainWindow::equalizerPresetcomboBox_currentIndexChanged); + connect(ui->applyEqualizer, &QPushButton::clicked, this, &MainWindow::applyEqualizer_clicked); + connect(ui->volumelimiterOffButton, &QPushButton::clicked, this, &MainWindow::volumelimiterOffButton_clicked); + connect(ui->volumelimiterOnButton, &QPushButton::clicked, this, &MainWindow::volumelimiterOnButton_clicked); + + // Microphone Section + connect(ui->muteledbrightnessSlider, &QSlider::sliderReleased, this, &MainWindow::muteledbrightnessSlider_sliderReleased); + connect(ui->micvolumeSlider, &QSlider::sliderReleased, this, &MainWindow::micvolumeSlider_sliderReleased); + connect(ui->rotateOn, &QPushButton::clicked, this, &MainWindow::rotateOn_clicked); + connect(ui->rotateOff, &QPushButton::clicked, this, &MainWindow::rotateOff_clicked); + + // Bluetooth Section + connect(ui->btwhenonOffButton, &QPushButton::clicked, this, &MainWindow::btwhenonOffButton_clicked); + connect(ui->btwhenonOnButton, &QPushButton::clicked, this, &MainWindow::btwhenonOnButton_clicked); + connect(ui->btbothRadioButton, &QRadioButton::clicked, this, &MainWindow::btbothRadioButton_clicked); + connect(ui->btpcdbRadioButton, &QRadioButton::clicked, this, &MainWindow::btpcdbRadioButton_clicked); + connect(ui->btonlyRadioButton, &QRadioButton::clicked, this, &MainWindow::btonlyRadioButton_clicked); +} + +void MainWindow::changeEvent(QEvent* e) +{ + switch (e->type()){ + case QEvent::PaletteChange: + darkMode = isOsDarkMode(); + updateIcons(); + break; + case QEvent::WindowStateChange: + if (this->windowState()==Qt::WindowMinimized){ + this->hide(); + } + default: + break; + } + + QMainWindow::changeEvent(e); +} + +void MainWindow::trayIconActivated(QSystemTrayIcon::ActivationReason reason) +{ + if(reason == QSystemTrayIcon::ActivationReason::Trigger) + { + toggleWindow(); + } +} + +void MainWindow::toggleWindow(){ + if(this->isHidden()){ + this->show(); + if(firstShow){ + checkForUpdates(firstShow); + firstShow = false; + } + } else{ + this->hide(); + } +} + +bool MainWindow::isOsDarkMode(){ + // Check if the application is using a dark palette + QPalette palette = QApplication::palette(); + QColor textColor = palette.color(QPalette::WindowText); + QColor backgroundColor = palette.color(QPalette::Window); + + // If text is brighter than background, it's likely a dark theme + return textColor.lightness() > backgroundColor.lightness(); +} + +void MainWindow::updateIcons(){ + QString inv = ""; + if(darkMode){ + inv = "-inv"; + trayIconPath.replace(".png", "-inv.png"); + } + else{ + trayIconPath.replace("-inv.png", ".png"); + } + + this->setWindowIcon(QIcon(":/icons/headphones"+inv+".png")); + tray->setIcon(QIcon(trayIconPath)); +} + +void MainWindow::disableFrames(){ + ledOn->setEnabled(false); + ledOff->setEnabled(false); + + ui->notSupportedFrame->setHidden(false); + + ui->deviceinfoFrame->setHidden(true); + ui->batteryFrame->setHidden(true); + + ui->tabWidget->hide(); + ui->tabWidget->setTabEnabled(3, false); + ui->tabWidget->setTabEnabled(2, false); + ui->tabWidget->setTabEnabled(1, false); + ui->tabWidget->setTabEnabled(0, false); + + ui->lightFrame->setHidden(true); + ui->voicepromptFrame->setHidden(true); + ui->notificationFrame->setHidden(true); + ui->sidetoneFrame->setHidden(true); + ui->inactivityFrame->setHidden(true); + ui->chatmixFrame->setHidden(true); + ui->volumelimiterFrame->setHidden(true); + + ui->equalizerpresetFrame->setHidden(true); + ui->equalizerFrame->setHidden(true); + ui->applyEqualizer->setEnabled(false); + + ui->rotatetomuteFrame->setHidden(true); + ui->muteledbrightnessFrame->setHidden(true); + ui->micvolumeFrame->setHidden(true); + + ui->btwhenonFrame->setHidden(true); + ui->btcallvolumeFrame->setHidden(true); +} + +void MainWindow::loadDevices(){ + QList c=getConnectedDevices(), s=deserializeDevices(FILE_DEVICES_SETTINGS); + n_connected=c.length(); n_saved=s.length(); + deviceList = mergeDevices(c, s); +} + +void MainWindow::loadDevice(int deviceIndex){ + disableFrames(); + + if(deviceIndex<0){ + selectedDevice=nullptr; + return; + } + + selectedDevice=deviceList.value(deviceIndex); + QSet& capabilities=selectedDevice->capabilities; + + if(timerGUI!=nullptr){ + timerGUI->stop(); + timerGUI=nullptr; + } + timerGUI = new QTimer(this); + connect(timerGUI, SIGNAL(timeout()), this, SLOT(updateDevice())); + connect(timerGUI, SIGNAL(timeout()), this, SLOT(saveDevicesSettings())); + connect(timerGUI, SIGNAL(timeout()), this, SLOT(updateGUI())); + timerGUI->start(settings.msecUpdateIntervalTime); + + ui->notSupportedFrame->setHidden(true); + + //Info section + ui->deviceinfovalueLabel->setText(selectedDevice->device+"\n"+selectedDevice->vendor+"\n"+selectedDevice->product); + ui->deviceinfoFrame->setHidden(false); + if (capabilities.contains("CAP_BATTERY_STATUS")){ + ui->batteryFrame->setHidden(false); + this->setBatteryStatus(); + qDebug() << "Battery percentage supported"; + } + + ui->tabWidget->show(); + //Other Section + if (capabilities.contains("CAP_LIGHTS")){ + ui->lightFrame->setHidden(false); + ui->tabWidget->setTabEnabled(0, true); + ledOn->setEnabled(true); + ledOff->setEnabled(true); + qDebug() << "Light control supported"; + } + if (capabilities.contains("CAP_SIDETONE")){ + ui->sidetoneFrame->setHidden(false); + ui->tabWidget->setTabEnabled(0, true); + qDebug() << "Sidetone supported"; + } + if (capabilities.contains("CAP_VOICE_PROMPTS")){ + ui->voicepromptFrame->setHidden(false); + ui->tabWidget->setTabEnabled(0, true); + qDebug() << "Voice prompt supported"; + } + if (capabilities.contains("CAP_NOTIFICATION_SOUND")){ + ui->notificationFrame->setHidden(false); + ui->tabWidget->setTabEnabled(0, true); + qDebug() << "Notification sound supported"; + } + if (capabilities.contains("CAP_INACTIVE_TIME")){ + ui->inactivityFrame->setHidden(false); + ui->tabWidget->setTabEnabled(0, true); + qDebug() << "Inactivity timer supported"; + } + if (capabilities.contains("CAP_CHATMIX_STATUS")){ + ui->chatmixFrame->setHidden(false); + ui->tabWidget->setTabEnabled(0, true); + this->setChatmixStatus(); + qDebug() << "Chatmix supported"; + } + //Eualizer Section + if (capabilities.contains("CAP_EQUALIZER_PRESET") && !selectedDevice->presets_list.empty()){ + ui->equalizerpresetFrame->setHidden(false); + ui->tabWidget->setTabEnabled(1, true); + qDebug() << "Eqaulizer preset supported"; + } + if (capabilities.contains("CAP_EQUALIZER") && selectedDevice->equalizer.bands_number>0){ + ui->equalizerFrame->setHidden(false); + ui->tabWidget->setTabEnabled(1, true); + qDebug() << "Equalizer supported"; + } + if (capabilities.contains("CAP_VOLUME_LIMITER")){ + ui->volumelimiterFrame->setHidden(false); + ui->tabWidget->setTabEnabled(1, true); + qDebug() << "Volume limiter preset supported"; + } + //Microphone Section + if (capabilities.contains("CAP_ROTATE_TO_MUTE")){ + ui->rotatetomuteFrame->setHidden(false); + ui->tabWidget->setTabEnabled(2, true); + qDebug() << "Rotate to mute supported"; + } + if (capabilities.contains("CAP_MICROPHONE_MUTE_LED_BRIGHTNESS")){ + ui->muteledbrightnessFrame->setHidden(false); + ui->tabWidget->setTabEnabled(2, true); + qDebug() << "Muted led brightness supported"; + } + if (capabilities.contains("CAP_MICROPHONE_VOLUME")){ + ui->micvolumeFrame->setHidden(false); + ui->tabWidget->setTabEnabled(2, true); + qDebug() << "Microphone volume supported"; + } + //Bluetooth Section + if (capabilities.contains("CAP_BT_WHEN_POWERED_ON")){ + ui->btwhenonFrame->setHidden(false); + ui->tabWidget->setTabEnabled(3, true); + qDebug() << "Bluetooth when powered on volume supported"; + } + if (capabilities.contains("CAP_BT_CALL_VOLUME")){ + ui->btcallvolumeFrame->setHidden(false); + ui->tabWidget->setTabEnabled(3, true); + qDebug() << "Bluetooth call volume volume supported"; + } + + loadGUIValues(); + this->setMaximumHeight(this->minimumHeight()); +} + +void MainWindow::loadGUIValues(){ + if(selectedDevice->lights>=0){ + ui->onlightButton->setChecked(selectedDevice->lights); + ui->offlightButton->setChecked(!selectedDevice->lights); + } + if(selectedDevice->sidetone>=0){ + ui->sidetoneSlider->setSliderPosition(selectedDevice->sidetone); + } + if(selectedDevice->voice_prompts>=0){ + ui->voiceOnButton->setChecked(selectedDevice->voice_prompts); + ui->voiceOffButton->setChecked(!selectedDevice->voice_prompts); + } + if(selectedDevice->inactive_time>=0){ + ui->inactivitySlider->setSliderPosition(selectedDevice->inactive_time); + } + + ui->equalizerPresetcomboBox->clear(); + ui->equalizerPresetcomboBox->addItem("-"); + ui->equalizerPresetcomboBox->setCurrentIndex(0); + for (int i = 0; i < selectedDevice->presets_list.size(); ++i) { + ui->equalizerPresetcomboBox->addItem(selectedDevice->presets_list.at(i).name); + } + if(selectedDevice->equalizer_preset>=0){ + ui->equalizerPresetcomboBox->setCurrentIndex(selectedDevice->equalizer_preset); + } + + QHBoxLayout *equalizerLayout = ui->equalizerLayout; + clearLayout(equalizerLayout); + if(selectedDevice->equalizer.bands_number>0){ + int i; + for (i = 0; i < selectedDevice->equalizer.bands_number; ++i) { + QLabel *l = new QLabel(QString::number(i)); + l->setAlignment(Qt::AlignHCenter); + + QSlider *s = new QSlider(Qt::Vertical); + s->setMaximum(selectedDevice->equalizer.band_max/selectedDevice->equalizer.band_step); + s->setMinimum(selectedDevice->equalizer.band_min/selectedDevice->equalizer.band_step); + s->setSingleStep(1); + s->setTickInterval(1/selectedDevice->equalizer.band_step); + s->setTickPosition(QSlider::TicksBothSides); + if(selectedDevice->equalizer_curve.size()==selectedDevice->equalizer.bands_number){ + s->setValue(selectedDevice->equalizer_curve.value(i)); + } else{ + s->setValue(selectedDevice->equalizer.band_baseline); + } + + QVBoxLayout *lb = new QVBoxLayout(); + lb->addWidget(l); + lb->addWidget(s); + + slidersEq.append(s); + equalizerLayout->addLayout(lb); + } + ui->applyEqualizer->setEnabled(true); + } + + if(selectedDevice->volume_limiter>=0){ + ui->volumelimiterOnButton->setChecked(selectedDevice->volume_limiter); + ui->volumelimiterOffButton->setChecked(!selectedDevice->volume_limiter); + } + + if(selectedDevice->rotate_to_mute>=0){ + ui->rotateOn->setChecked(selectedDevice->rotate_to_mute); + ui->rotateOff->setChecked(!selectedDevice->rotate_to_mute); + } + if(selectedDevice->mic_mute_led_brightness>=0){ + ui->muteledbrightnessSlider->setSliderPosition(selectedDevice->mic_mute_led_brightness); + } + if(selectedDevice->mic_volume>=0){ + ui->micvolumeSlider->setSliderPosition(selectedDevice->mic_volume); + } + + if(selectedDevice->bt_call_volume>=0){ + switch (selectedDevice->bt_call_volume) { + case 0: + ui->btbothRadioButton->setChecked(true); + break; + case 1: + ui->btpcdbRadioButton->setChecked(true); + break; + case 2: + ui->btonlyRadioButton->setChecked(true); + break; + default: + break; + } + } + if(selectedDevice->bt_when_powered_on>=0){ + ui->btwhenonOnButton->setChecked(selectedDevice->bt_when_powered_on); + ui->btwhenonOffButton->setChecked(!selectedDevice->bt_when_powered_on); + } +} + +void MainWindow::saveDevicesSettings(){ + if(!savedDevices){ + serializeDevices(deviceList, FILE_DEVICES_SETTINGS); + } +} + +void MainWindow::updateDevice(){ + if(selectedDevice!=nullptr){ + QList newDl=getConnectedDevices(); + selectedDevice->updateDevice(newDl); + } +} + +void MainWindow::updateGUI(){ + setBatteryStatus(); + setChatmixStatus(); +} + +//Info Section Events +void MainWindow::setBatteryStatus() +{ + QString status = selectedDevice->battery.status; + int batteryLevel = selectedDevice->battery.level; + QString level=QString::number(batteryLevel); + + if(batteryLevel>=0){ + ui->batteryProgressBar->show(); + ui->batteryProgressBar->setValue(batteryLevel); + } + else{ + ui->batteryProgressBar->hide(); + } + + if (status == "BATTERY_UNAVAILABLE"){ + ui->batteryPercentage->setText(tr("Headset Off")); + tray->setToolTip(tr("HeadsetControl \r\nHeadset Off")); + trayIconPath =":/icons/headphones-inv.png"; + } + else if (status == "BATTERY_CHARGING") { + ui->batteryPercentage->setText(level+tr("% - Charging")); + tray->setToolTip(tr("HeadsetControl \r\nBattery Charging")); + trayIconPath = ":/icons/battery-charging-inv.png"; + } + else if(status == "BATTERY_AVAILABLE"){ + ui->batteryPercentage->setText(level+tr("% - Descharging")); + tray->setToolTip(tr("HeadsetControl \r\nBattery: ") + level + "%"); + if (level.toInt() > 75){ + trayIconPath = ":/icons/battery-level-full-inv.png"; + notified = false; + } + else if (level.toInt() > settings.batteryLowThreshold) { + trayIconPath = ":/icons/battery-medium-inv.png"; + notified = false; + } + else { + trayIconPath = ":/icons/battery-low-inv.png"; + if (!notified){ + tray->showMessage(tr("Battery Alert!"), tr("The battery of your headset is running low"), QIcon(":/icons/battery-low-inv.png")); + notified = true; + } + } + } else{ + ui->batteryPercentage->setText(tr("No battery info")); + tray->setToolTip("HeadsetControl"); + trayIconPath = ":/icons/headphones-inv.png"; + } + + if(!darkMode){ + trayIconPath.replace("-inv", ""); + } + tray->setIcon(QIcon(trayIconPath)); +} + +//Other Section Events +void MainWindow::onlightButton_clicked() +{ + QStringList args=QStringList() << QString("--light") << QString("1"); + Action s=sendAction(args); + if(s.status=="success"){ + selectedDevice->lights=1; + savedDevices=false; + } +} + +void MainWindow::offlightButton_clicked() +{ + QStringList args=QStringList() << QString("--light") << QString("0"); + Action s=sendAction(args); + if(s.status=="success"){ + selectedDevice->lights=0; + savedDevices=false; + } +} + +void MainWindow::sidetoneSlider_sliderReleased(){ + QStringList args=QStringList() << QString("--sidetone") << QString::number(ui->sidetoneSlider->sliderPosition()); + Action s=sendAction(args); + if(s.status=="success"){ + selectedDevice->sidetone=ui->sidetoneSlider->value(); + savedDevices=false; + } +} + +void MainWindow::voiceOnButton_clicked() +{ + QStringList args=QStringList() << QString("--voice-prompt") << QString("1"); + Action s=sendAction(args); + if(s.status=="success"){ + selectedDevice->voice_prompts=1; + savedDevices=false; + } +} + +void MainWindow::voiceOffButton_clicked() +{ + QStringList args=QStringList() << QString("--voice-prompt") << QString("0"); + Action s=sendAction(args); + if(s.status=="success"){ + selectedDevice->voice_prompts=0; + savedDevices=false; + } +} + +void MainWindow::notification0Button_clicked() +{ + QStringList args=QStringList() << QString("--notificate") << QString("0"); + Action s=sendAction(args); + if(s.status!="success"){ + + } +} + +void MainWindow::notification1Button_clicked() +{ + QStringList args=QStringList() << QString("--notificate") << QString("1"); + Action s=sendAction(args); + if(s.status!="success"){ + + } +} + +void MainWindow::rotateOn_clicked() +{ + QStringList args=QStringList() << QString("--rotate-to-mute") << QString("1"); + Action s=sendAction(args); + if(s.status=="success"){ + selectedDevice->rotate_to_mute=1; + savedDevices=false; + } +} + +void MainWindow::rotateOff_clicked() +{ + QStringList args=QStringList() << QString("--rotate-to-mute") << QString("0"); + Action s=sendAction(args); + if(s.status=="success"){ + selectedDevice->rotate_to_mute=0; + savedDevices=false; + } +} + +void MainWindow::inactivitySlider_sliderReleased(){ + QStringList args=QStringList() << QString("--inactive-time") << QString::number(ui->inactivitySlider->sliderPosition()); + Action s=sendAction(args); + if(s.status=="success"){ + selectedDevice->inactive_time=ui->inactivitySlider->value(); + savedDevices=false; + } +} + +void MainWindow::setChatmixStatus(){ + int chatmix = selectedDevice->chatmix; + QString chatmixValue = QString::number(chatmix); + QString chatmixStatus; + if(chatmix<65)chatmixStatus=tr("Game"); + else if(chatmix>65)chatmixStatus=tr("Chat"); + ui->chatmixvalueLabel->setText(chatmixValue); + ui->chatmixstatusLabel->setText(chatmixStatus); +} + +//Equalizer Section Events +void MainWindow::equalizerPresetcomboBox_currentIndexChanged(){ + int preset=ui->equalizerPresetcomboBox->currentIndex(); + if(preset==0){ + //setSliders(selectedDevice->equalizer.band_baseline); + } else if(preset>=1 && preset<=selectedDevice->presets_list.length()){ + this->setSliders(selectedDevice->presets_list.value(preset-1).values); + QStringList args=QStringList() << QString("--equalizer-preset") << QString::number(preset-1); + Action s=sendAction(args); + if(s.status=="success"){ + selectedDevice->equalizer_preset=ui->equalizerPresetcomboBox->currentIndex(); + savedDevices=false; + } + } +} + +void MainWindow::applyEqualizer_clicked(){ + ui->equalizerPresetcomboBox->setCurrentIndex(0); + QString eq_string=""; + QList values; + for (QSlider* slider : slidersEq) { + eq_string+= QString::number(slider->value())+","; + values.append(slider->value()/selectedDevice->equalizer.band_step); + } + eq_string.removeLast(); + QStringList args=QStringList() << QString("--equalizer") << eq_string; + Action s=sendAction(args); + if(s.status=="success"){ + selectedDevice->equalizer_curve=values; + selectedDevice->equalizer_preset=-1; + savedDevices=false; + } +} + +void MainWindow::setSliders(int value){ + for (QSlider* slider : slidersEq) { + slider->setValue(value/selectedDevice->equalizer.band_step); + } +} + +void MainWindow::setSliders(QList values){ + int i=0; + if(values.length()==selectedDevice->equalizer.bands_number){ + for (QSlider* slider : slidersEq) { + slider->setValue(values[i++]/selectedDevice->equalizer.band_step); + } + } + else{ + qDebug() << "ERROR: Bad Equalizer Preset"; + } +} + +void MainWindow::clearLayout(QLayout* layout){ + if (!layout) { + return; + } + + QLayoutItem* item; + while ((item = layout->takeAt(0))) { + if (item->layout()) { + clearLayout(item->layout()); // Delete the layout if it exists + } + if (item->widget()) { + delete item->widget(); // Delete the widget + } + delete item; // Delete the layout item + } +} + +void MainWindow::volumelimiterOffButton_clicked(){ + QStringList args=QStringList() << QString("--volume-limiter") << QString("0"); + Action s=sendAction(args); + if(s.status=="success"){ + selectedDevice->volume_limiter=0; + savedDevices=false; + } +} + +void MainWindow::volumelimiterOnButton_clicked(){ + QStringList args=QStringList() << QString("--volume-limiter") << QString("1"); + Action s=sendAction(args); + if(s.status=="success"){ + selectedDevice->volume_limiter=1; + savedDevices=false; + } +} + +//Microphone Section Events +void MainWindow::muteledbrightnessSlider_sliderReleased(){ + QStringList args=QStringList() << QString("--microphone-mute-led-brightness") << QString::number(ui->muteledbrightnessSlider->sliderPosition()); + Action s=sendAction(args); + if(s.status=="success"){ + selectedDevice->mic_mute_led_brightness=ui->muteledbrightnessSlider->value(); + savedDevices=false; + } +} + +void MainWindow::micvolumeSlider_sliderReleased(){ + QStringList args=QStringList() << QString("--microphone-volume") << QString::number(ui->micvolumeSlider->sliderPosition()); + Action s=sendAction(args); + if(s.status=="success"){ + selectedDevice->mic_volume=ui->micvolumeSlider->value(); + savedDevices=false; + } +} + +//Bluetooth Section Events +void MainWindow::btwhenonOffButton_clicked(){ + QStringList args=QStringList() << QString("--bt-when-powered-on") << QString("0"); + Action s=sendAction(args); + if(s.status=="success"){ + selectedDevice->bt_when_powered_on=0; + savedDevices=false; + } +} + +void MainWindow::btwhenonOnButton_clicked(){ + QStringList args=QStringList() << QString("--bt-when-powered-on") << QString("1"); + Action s=sendAction(args); + if(s.status=="success"){ + selectedDevice->bt_when_powered_on=1; + savedDevices=false; + } +} + +void MainWindow::btbothRadioButton_clicked(){ + QStringList args=QStringList() << QString("--bt-call-volume") << QString("0"); + Action s=sendAction(args); + if(s.status=="success"){ + selectedDevice->bt_call_volume=0; + savedDevices=false; + } +} + +void MainWindow::btpcdbRadioButton_clicked(){ + QStringList args=QStringList() << QString("--bt-call-volume") << QString("1"); + Action s=sendAction(args); + if(s.status=="success"){ + selectedDevice->bt_call_volume=1; + savedDevices=false; + } +} + +void MainWindow::btonlyRadioButton_clicked(){ + QStringList args=QStringList() << QString("--bt-call-volume") << QString("2"); + Action s=sendAction(args); + if(s.status=="success"){ + selectedDevice->bt_call_volume=2; + savedDevices=false; + } +} + +//Tool Bar Events +void MainWindow::editProgramSetting(){ + SettingsWindow* settingsW=new SettingsWindow(settings, this); + if (settingsW->exec() == QDialog::Accepted) { + settings=settingsW->getSettings(); + saveSettingstoFile(settings, PROGRAM_SETTINGS_FILENAME); + timerGUI->setInterval(settings.msecUpdateIntervalTime); + } +} + +void MainWindow::selectDevice(){ + this->loadDevices(); + + QStringList devices=QStringList(); + for (Device* device : deviceList){ + if(device->connected){ + devices<device; + } + } + + LoaddeviceWindow* loadDevWindow=new LoaddeviceWindow(devices, this); + if (loadDevWindow->exec() == QDialog::Accepted) { + int index = loadDevWindow->getDeviceIndex(); + this->disableFrames(); + if (index>=0) { + if(index==0){ + ui->tabWidget->setDisabled(false); + } + else { + ui->tabWidget->setDisabled(true); + } + this->loadDevice(index); + } + } +} + +void MainWindow::checkForUpdates(bool firstStart){ + bool needsUpdate = false; + DialogInfo* dialogWindow = new DialogInfo(this); + dialogWindow->setTitle(tr("Check for updates")); + + const QVersionNumber& local_hc=getHCVersion(); + const QVersionNumber local_gui=QVersionNumber::fromString(qApp->applicationVersion()); + QString v1 = getLatestGitHubReleaseVersion("Sapd","HeadsetControl"); + QString v2 = getLatestGitHubReleaseVersion("nicola02nb","HeadsetControl-GUI"); + QVersionNumber remote_hc =QVersionNumber::fromString(v1); + QVersionNumber remote_gui =QVersionNumber::fromString(v2); + QString s1 = tr("up-to date v")+local_hc.toString(); + QString s2 = tr("up-to date v")+local_gui.toString(); + if(!(v1=="") && remote_hc>local_hc){ + s1=tr("Newer version")+" -> "+remote_hc.toString()+""; + needsUpdate = true; + } + if(!(v2=="") && remote_gui>local_gui){ + s2=tr("Newer version")+" -> "+remote_gui.toString()+""; + needsUpdate = true; + } + + if((needsUpdate && firstStart) || !firstStart){ + QString text = "HeadesetControl: "+s1+"
HeadesetControl-GUI: "+s2; + dialogWindow->setLabel(text); + + dialogWindow->show(); + } + +} + +void MainWindow::showAbout(){ + DialogInfo* dialogWindow=new DialogInfo(this); + dialogWindow->setTitle(tr("About this program")); + QString text = tr("This is a forked version of HeadsetControl-GUI." + "
Made by nicola02nb" + "
Version: ")+qApp->applicationVersion(); + dialogWindow->setLabel(text); + + dialogWindow->show(); +} + +void MainWindow::showCredits(){ + DialogInfo* dialogWindow=new DialogInfo(this); + dialogWindow->setTitle(tr("Credits")); + QString text = tr("Big shout-out to:" + "
- Sapd for HeadsetCoontrol" + "
- LeoKlaus for HeadsetControl-GUI"); + dialogWindow->setLabel(text); + + dialogWindow->show(); +} diff --git a/src/UI/mainwindow.h b/src/UI/mainwindow.h new file mode 100644 index 0000000..323bfc5 --- /dev/null +++ b/src/UI/mainwindow.h @@ -0,0 +1,129 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include "utils.h" +#include "device.h" +#include "settings.h" +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE +namespace Ui { + class MainWindow; +} +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + bool firstShow = true; + bool notified = false; + bool savedDevices = true; + QSystemTrayIcon* tray = new QSystemTrayIcon(this); + +private: + const QString FILE_DEVICES_SETTINGS = "devices.json"; + const QString PROGRAM_SETTINGS_FILENAME = "settings.json"; + + int n_connected = 0, n_saved = 0; + + Settings settings; + bool darkMode; + QString trayIconPath; + + QMenu *menu; + + QAction* ledOn; + QAction* ledOff; + + Device* selectedDevice; + QList deviceList; + QList slidersEq; + + QTimer* timerGUI = nullptr; + +private slots: + void bindEvents(); + void changeEvent(QEvent *e); + void trayIconActivated(QSystemTrayIcon::ActivationReason reason); + + void toggleWindow(); + + bool isOsDarkMode(); + void updateIcons(); + + void disableFrames(); + + void loadDevices(); + void loadDevice(int deviceIndex=0); + void loadGUIValues(); + + void saveDevicesSettings(); + + void updateDevice(); + void updateGUI(); + + void setBatteryStatus(); + + //Other Section Events + void onlightButton_clicked(); + void offlightButton_clicked(); + + void sidetoneSlider_sliderReleased(); + + void voiceOnButton_clicked(); + void voiceOffButton_clicked(); + + void notification0Button_clicked(); + void notification1Button_clicked(); + + void inactivitySlider_sliderReleased(); + + void rotateOn_clicked(); + void rotateOff_clicked(); + + void setChatmixStatus(); + + //Equalizer Section Events + void equalizerPresetcomboBox_currentIndexChanged(); + + void applyEqualizer_clicked(); + void setSliders(int value); + void setSliders(QList values); + void clearLayout(QLayout* layout); + + void volumelimiterOffButton_clicked(); + void volumelimiterOnButton_clicked(); + + //Microphone Section Events + void muteledbrightnessSlider_sliderReleased(); + void micvolumeSlider_sliderReleased(); + + //Bluetooth Section Events + void btwhenonOffButton_clicked(); + void btwhenonOnButton_clicked(); + + void btbothRadioButton_clicked(); + void btpcdbRadioButton_clicked(); + void btonlyRadioButton_clicked(); + + //Tool Bar Events + void editProgramSetting(); + void checkForUpdates(bool firstStart = false); + void selectDevice(); + void showAbout(); + void showCredits(); + +private: + Ui::MainWindow* ui; +}; +#endif // MAINWINDOW_H diff --git a/src/UI/mainwindow.ui b/src/UI/mainwindow.ui new file mode 100644 index 0000000..f2731d4 --- /dev/null +++ b/src/UI/mainwindow.ui @@ -0,0 +1,1745 @@ + + + MainWindow + + + + 0 + 0 + 488 + 690 + + + + + 0 + 0 + + + + + 11 + + + + HeadsetControl - GUI + + + + :/icons/headphones-inv.png:/icons/headphones-inv.png + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + + + + + + + 0 + 0 + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + HeadsetControl couldn't find any compatible or working headsets. :( + + + Qt::AlignCenter + + + + + + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + + 120 + 0 + + + + + 11 + true + + + + <html><head/><body><p>Device:<br/>Vendor:<br/>Model:</p></body></html> + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 300 + 16777215 + + + + + + + No info of the device + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 40 + 20 + + + + + + + + + + + true + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 6 + + + 9 + + + 9 + + + + + + 0 + 0 + + + + + 120 + 0 + + + + + 120 + 16777215 + + + + + 11 + true + + + + Battery: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + + true + + + + 0 + 0 + + + + + 200 + 16777215 + + + + false + + + + + + No compatible Device found! + + + false + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + false + + + + + + + + 0 + 0 + + + + 0 + + + false + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + true + + + + 0 + 0 + + + + QTabWidget::North + + + 0 + + + false + + + false + + + false + + + + + 0 + 0 + + + + Other + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + + 120 + 0 + + + + + 120 + 16777215 + + + + + 11 + true + + + + Lights: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + 120 + 0 + + + + RGB OFF + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 120 + 0 + + + + RGB ON + + + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + + 120 + 0 + + + + + 11 + true + + + + Sidetone Level: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + QLayout::SetDefaultConstraint + + + + + false + + + Drag to adjust Sidetone Level + + + 128 + + + 16 + + + Qt::Horizontal + + + QSlider::TicksBelow + + + 16 + + + + + + + + + Quiet (Off) + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::LeftToRight + + + Loud + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + + 120 + 10 + + + + + 120 + 16777215 + + + + + 11 + true + + + + Voice Prompts: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + + + 120 + 0 + + + + Voice Off + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 120 + 0 + + + + Voice On + + + + + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + + 120 + 0 + + + + + 11 + true + + + + Notification Sound: + + + true + + + + + + + + + Test 0 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Test 1 + + + + + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + + 120 + 0 + + + + + 11 + true + + + + Inactivity Timer: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + + 1 + + + 90 + + + Qt::Horizontal + + + QSlider::TicksBelow + + + 10 + + + + + + + + + 0 Minute (Off) + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + 90 Minutes + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + + 120 + 0 + + + + + 11 + true + + + + Chatmix: + + + + + + + + 0 + 0 + + + + None + + + + + + + + 30 + 0 + + + + + + + + + + + Qt::Horizontal + + + + 217 + 20 + + + + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + + 0 + 0 + + + + Equalizer + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 120 + 0 + + + + + 11 + true + + + + Equalizer preset: + + + + + + + + 150 + 0 + + + + -1 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 120 + 0 + + + + + 11 + true + + + + Equalizer: + + + + + + + + + + + 120 + 30 + + + + Apply Equalizer + + + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + + 120 + 0 + + + + + 11 + true + + + + Volume Limiter: + + + + + + + + + Limiter Off + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Limiter On + + + + + + + + + + + + + + 0 + 0 + + + + Microphone + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + + 120 + 0 + + + + + 11 + true + + + + Rotate to mute: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + Off + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + On + + + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + + 120 + 0 + + + + + 11 + true + + + + false + + + Muted led brightness: + + + false + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + true + + + + + + + + + 3 + + + 1 + + + Qt::Horizontal + + + QSlider::TicksBelow + + + + + + + + + Low (Off) + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + High + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + + 120 + 0 + + + + + 11 + true + + + + Microphone volume: + + + true + + + + + + + + + 128 + + + 16 + + + Qt::Horizontal + + + QSlider::TicksBelow + + + 16 + + + + + + + + + Quiet + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Loud + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + 0 + 0 + + + + Bluetooth + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + + 120 + 0 + + + + + 11 + true + + + + Bluetooth when powered on: + + + true + + + + + + + + + Bluetooth Off + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Bluetooth On + + + + + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + + 120 + 0 + + + + + 120 + 16777215 + + + + + 11 + true + + + + Bluetooth call volume: + + + true + + + + + + + + + + + BT and PC + + + + + + + PC -12dB + + + + + + + BT only + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + 0 + 0 + 488 + 26 + + + + + File + + + + + + + + + Help + + + + + + + + + + + + + Check Updates + + + + + + + + About + + + + + + + + Credits + + + + + true + + + + + + Load Device + + + + + + + + Settings + + + + + offlightButton + onlightButton + sidetoneSlider + voiceOffButton + inactivitySlider + equalizerPresetcomboBox + applyEqualizer + rotateOff + rotateOn + muteledbrightnessSlider + micvolumeSlider + + + + + + diff --git a/src/UI/settingswindow.cpp b/src/UI/settingswindow.cpp new file mode 100644 index 0000000..5fa8b2a --- /dev/null +++ b/src/UI/settingswindow.cpp @@ -0,0 +1,37 @@ +#include "settingswindow.h" +#include "ui_settingswindow.h" +#include "utils.h" + + +SettingsWindow::SettingsWindow(const Settings& programSettings, QWidget *parent) + : QDialog(parent) + , ui(new Ui::settingswindow) +{ + setModal(true); + ui->setupUi(this); + + connect(ui->runonstartupCheckBox, &QCheckBox::clicked, this, &SettingsWindow::setRunOnStartup); + + ui->runonstartupCheckBox->setChecked(programSettings.runOnstartup); + ui->batterylowtresholdSpinBox->setValue(programSettings.batteryLowThreshold); + ui->updateintervaltimeDoubleSpinBox->setValue((double)programSettings.msecUpdateIntervalTime/1000); +} + +Settings SettingsWindow::getSettings(){ + Settings settings; + settings.runOnstartup=ui->runonstartupCheckBox->isChecked(); + settings.batteryLowThreshold=ui->batterylowtresholdSpinBox->value(); + settings.msecUpdateIntervalTime=ui->updateintervaltimeDoubleSpinBox->value()*1000; + + return settings; +} + +void SettingsWindow::setRunOnStartup(){ + bool enabled = setOSRunOnStartup(ui->runonstartupCheckBox->isChecked()); + ui->runonstartupCheckBox->setChecked(enabled); +} + +SettingsWindow::~SettingsWindow() +{ + delete ui; +} diff --git a/src/UI/settingswindow.h b/src/UI/settingswindow.h new file mode 100644 index 0000000..577c275 --- /dev/null +++ b/src/UI/settingswindow.h @@ -0,0 +1,27 @@ +#ifndef SETTINGSWINDOW_H +#define SETTINGSWINDOW_H + +#include "settings.h" +#include + +namespace Ui { +class settingswindow; +} + +class SettingsWindow : public QDialog +{ + Q_OBJECT + +public: + explicit SettingsWindow(const Settings& programSettings, QWidget *parent = nullptr); + ~SettingsWindow(); + + Settings getSettings(); + +private: + Ui::settingswindow *ui; + + void setRunOnStartup(); +}; + +#endif // SETTINGSWINDOW_H diff --git a/src/UI/settingswindow.ui b/src/UI/settingswindow.ui new file mode 100644 index 0000000..20572e6 --- /dev/null +++ b/src/UI/settingswindow.ui @@ -0,0 +1,217 @@ + + + settingswindow + + + + 0 + 0 + 352 + 206 + + + + Settings + + + + :/icons/headphones-inv.png:/icons/headphones-inv.png + + + + + + true + + + + 0 + 0 + + + + text-align: left; + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Run on Startup: + + + + + + + Qt::RightToLeft + + + text-align: end + + + + + + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + Battery low threshold: + + + + + + + + 120 + 0 + + + + + 120 + 16777215 + + + + 100 + + + 15 + + + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + <html><head/><body><p>Update Info interval time (seconds):<br/>Default: 30,0 seconds<br/>DON'T PUT TOO LOW VALUES</p></body></html> + + + + + + + + 120 + 0 + + + + + 120 + 16777215 + + + + 1 + + + 1000.000000000000000 + + + 30.000000000000000 + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + settingswindow + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + settingswindow + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/Utils/utils.cpp b/src/Utils/utils.cpp new file mode 100644 index 0000000..90dbe4c --- /dev/null +++ b/src/Utils/utils.cpp @@ -0,0 +1,140 @@ +#include "utils.h" +#include +#include +#include +#include +#include +#include +#include + +#include + +QString getLatestGitHubReleaseVersion(const QString& owner, const QString& repo) +{ + QEventLoop loop; + QNetworkAccessManager manager; + QNetworkRequest request(QUrl(QString("https://api.github.com/repos/%1/%2/releases/latest").arg(owner, repo))); + request.setHeader(QNetworkRequest::UserAgentHeader, "Mozilla/5.0"); + + QNetworkReply *reply = manager.get(request); + QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); + loop.exec(); + + if (reply->error() == QNetworkReply::NoError) { + QJsonDocument doc = QJsonDocument::fromJson(reply->readAll()); + QJsonObject jsonObj = doc.object(); + QString latestVersion = jsonObj.value("tag_name").toString(); + reply->deleteLater(); + return latestVersion.removeFirst(); + } else { + qDebug() << "Error:" << reply->errorString(); + reply->deleteLater(); + return QString(); + } +} + +bool fileExists(const QString& filepath) +{ + QFileInfo checkFile(filepath); + return checkFile.exists(); +} + +bool downloadAndUnzipGithubRepo(const QString& user, const QString& repo, const QString& savePath) +{ + // Step 1: Download the zip file + QString url = QString("https://github.com/%1/%2/archive/refs/heads/headsetcontrol-windows.zip").arg(user, repo); + QString zipFilePath = savePath + "/headsetcontrol-windows.zip"; + + url= "https://github.com/Sapd/HeadsetControl/releases/download/3.0.0/headsetcontrol-windows.zip"; + QNetworkAccessManager manager; + QEventLoop loop; + QNetworkReply *reply = manager.get(QNetworkRequest(QUrl(url))); + QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); + loop.exec(); + + if (reply->error() != QNetworkReply::NoError) { + qDebug() << "Download Error:" << reply->errorString(); + reply->deleteLater(); + return false; + } + + QFile file(zipFilePath); + if (!file.open(QIODevice::WriteOnly)) { + qDebug() << "Could not open file for writing:" << file.errorString(); + reply->deleteLater(); + return false; + } + + file.write(reply->readAll()); + file.close(); + reply->deleteLater(); + + // Step 2: Unzip the file + QDir().mkpath(savePath); + QProcess unzip; + unzip.setWorkingDirectory(savePath); + unzip.start("unzip", QStringList() << "-o" << zipFilePath << "-d" << savePath); + if (!unzip.waitForFinished()) { + qDebug() << "Unzip Error:" << unzip.errorString(); + return false; + } + + // Optional: Remove the zip file after extraction + QFile::remove(zipFilePath); + + return true; +} + +bool openFileExplorer(const QString& path) +{ + QDir dir(path); + if (!dir.exists()) + { + qDebug() << "Path does not exist:" << path; + return false; + } + + QUrl url = QUrl::fromLocalFile(dir.absolutePath()); + return QDesktopServices::openUrl(url); +} + +bool setOSRunOnStartup(bool enable){ + QString appName = QCoreApplication::applicationName(); + QString appDir = QCoreApplication::applicationDirPath(); + QString appPath = QCoreApplication::applicationFilePath(); + +#ifdef Q_OS_WIN + QString startupPath = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation) + QDir::separator() + "Startup"; + QString linkPath = startupPath + "\\" + appName + ".lnk"; + if(enable){ + QFile::remove(linkPath); + return QFile::link(appPath, linkPath); + } + QFile::remove(linkPath); + return false; + +#elif defined(Q_OS_LINUX) + QString autostartPath = QDir::homePath() + "/.config/autostart/"; + QString desktopFilePath = autostartPath + appName + ".desktop"; + + if(enable){ + QFile::remove(desktopFilePath); + QFile desktopFile(desktopFilePath); + if (desktopFile.open(QIODevice::WriteOnly | QIODevice::Text)) { + QTextStream out(&desktopFile); + out << "[Desktop Entry]\n"; + out << "Path=" + appDir + "\n"; + out << "Type=Application\n"; + out << "Exec=" << appPath << "\n"; + out << "Name=" << appName << "\n"; + out << "Comment=Auto-starts " << appName << " on boot\n"; + desktopFile.close(); + return true; + } + } + QFile::remove(desktopFilePath); + + return false; +#endif +} + diff --git a/src/Utils/utils.h b/src/Utils/utils.h new file mode 100644 index 0000000..89c53b9 --- /dev/null +++ b/src/Utils/utils.h @@ -0,0 +1,16 @@ +#ifndef UTILS_H +#define UTILS_H + +#include + +QString getLatestGitHubReleaseVersion(const QString& owner, const QString& repo); + +bool downloadAndUnzipGithubRepo(const QString& user, const QString& repo, const QString& savePath); + +bool fileExists(const QString& filepath); + +bool openFileExplorer(const QString& path); + +bool setOSRunOnStartup(bool enable); + +#endif // UTILS_H diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..21b23ec --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,21 @@ +#include "mainwindow.h" + +#include +#include + +const QString GUI_VERSION = "0.15.0"; + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + app.setApplicationVersion(GUI_VERSION); + QLocale locale = QLocale::system(); + QString languageCode = locale.name(); + QTranslator translator; + if (translator.load(":/translations/tr/HeadsetControl_GUI_"+languageCode+".qm")) { + app.installTranslator(&translator); + } + MainWindow window; + + return app.exec(); +}