From 08a9a76aaee2ff6a406ac3bda70faa30fb07b9d9 Mon Sep 17 00:00:00 2001 From: Hane Date: Sat, 11 Feb 2023 03:12:45 +0100 Subject: [PATCH 01/78] no copies objetos payaso --- src/back/backlasses.cpp | 26 ++++++++++++++------------ src/back/backlasses.h | 4 ++-- src/cont/contclasses.cpp | 4 ++-- src/cont/contclasses.h | 2 +- src/qtestmain.cpp | 2 +- 5 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/back/backlasses.cpp b/src/back/backlasses.cpp index 61e3562..6545136 100644 --- a/src/back/backlasses.cpp +++ b/src/back/backlasses.cpp @@ -25,12 +25,13 @@ void Endpoint::setVolume(float volume) { if(FAILED(endpointVolume->SetMasterVolumeLevelScalar(volume, NULL))) { log_debugcpp("si"); }; } -//Endpoint::~Endpoint(){ -// free(friendlyName); -// properties->Release(); -// endpointVolume->Release(); -// endpoint->Release(); -//} +Endpoint::~Endpoint(){ + log_debugcpp("cum"); + free(friendlyName); + properties->Release(); + endpointVolume->Release(); + endpoint->Release(); +} void Overseer::initCOMLibrary(){ @@ -90,12 +91,13 @@ std::vector Overseer::getPlaybackEndpoints() { return playbackDevices; } -//Overseer::~Overseer(){ - // deviceEnumerator->Release(); - // for(unsigned long long i = 0; i < playbackDevices.size(); i++){ - //delete(playbackDevices.at(i)); - //} - //} +Overseer::~Overseer(){ + log_debugcpp("cum"); + deviceEnumerator->Release(); + for(unsigned long long i = 0; i < playbackDevices.size(); i++){ + delete(playbackDevices.at(i)); + } +} //int Overseer::getCaptureEndpoints(std::vector *captureEndpoints); diff --git a/src/back/backlasses.h b/src/back/backlasses.h index 90bc88b..dc4287a 100644 --- a/src/back/backlasses.h +++ b/src/back/backlasses.h @@ -25,7 +25,7 @@ class Endpoint { void setVolume(float volume); float getVolume(); LPWSTR getName(); - //~Endpoint(); + ~Endpoint(); private: IMMDevice* endpoint; @@ -46,7 +46,7 @@ class Overseer { //int getDefaultCaptureEndpoint(Endpoint** defaultEndpoint); //int getCaptureEndpoints(std::vector *captureEndpoints); //IMMDeviceEnumerator** setOrigin(); - //~Overseer(); + ~Overseer(); private: unsigned int numPlaybackEndpoints; diff --git a/src/cont/contclasses.cpp b/src/cont/contclasses.cpp index 30b2688..8b5b752 100644 --- a/src/cont/contclasses.cpp +++ b/src/cont/contclasses.cpp @@ -19,8 +19,8 @@ float EndpointHandler::getVolume(){ return ept->getVolume(); } -Overseer OverseerHandler::getOverseer(){ - return os; +Overseer* OverseerHandler::getOverseer(){ + return &os; } OverseerHandler::OverseerHandler(QObject *parent) : QObject(parent) { diff --git a/src/cont/contclasses.h b/src/cont/contclasses.h index 647ca1d..178a756 100644 --- a/src/cont/contclasses.h +++ b/src/cont/contclasses.h @@ -34,7 +34,7 @@ public: OverseerHandler(QObject *parent = nullptr); void setEndpointHandlers(std::vector *ephs); std::vector* getEndpointHandlers(); - static Overseer getOverseer(); + static Overseer* getOverseer(); private: static Overseer os; diff --git a/src/qtestmain.cpp b/src/qtestmain.cpp index 610f0b4..1305a69 100644 --- a/src/qtestmain.cpp +++ b/src/qtestmain.cpp @@ -24,7 +24,7 @@ QApplication* createApplication(int &argc, char *argv[]) int main (int argc, char* argv[]) { //QApplication::setStyle("windowsvista"); //INIT CONT - std::vector epts = OverseerHandler::getOverseer().getPlaybackEndpoints(); + std::vector epts = OverseerHandler::getOverseer()->getPlaybackEndpoints(); std::vector* ephs = new std::vector; for(unsigned int i = 0; i < epts.size(); i++){ EndpointHandler *eph = new EndpointHandler(epts.at(i)); From 9c6563a394aebb399c6c674d4bd3e92076560d2e Mon Sep 17 00:00:00 2001 From: Hane Date: Wed, 15 Feb 2023 10:17:59 +0100 Subject: [PATCH 02/78] debug.h y endpointwidget --- qtest.pro | 2 +- src/back/backlasses.cpp | 14 ++++++++ src/back/backlasses.h | 1 + src/cont/contclasses.h | 1 - src/debug.h | 10 ++++++ src/global.h | 8 +++-- src/qt/qtclasses.cpp | 79 +++++++++++++++++++++++++++++++---------- src/qt/qtclasses.h | 33 ++++++++++++++--- src/qtestmain.cpp | 3 -- 9 files changed, 121 insertions(+), 30 deletions(-) create mode 100644 src/debug.h diff --git a/qtest.pro b/qtest.pro index 7f7dbb8..abfbb93 100644 --- a/qtest.pro +++ b/qtest.pro @@ -4,5 +4,5 @@ INCLUDEPATH += "$$PWD\src" "$$PWD\src\qt" "$$PWD\src\back" "$$PWD\src\cont" DESTPATH += "$$PWD\src" "$$PWD\src\qt" "$$PWD\src\back" "$$PWD\src\cont" VPATH += "$$PWD\src" "$$PWD\src\qt" "$$PWD\src\back" "$$PWD\src\cont" SOURCES += qtestmain.cpp qtclasses.cpp backlasses.cpp contclasses.cpp -HEADERS += qtclasses.h backlasses.h contclasses.h global.h +HEADERS += qtclasses.h backlasses.h contclasses.h global.h debug.h #DESTDIR += "build" diff --git a/src/back/backlasses.cpp b/src/back/backlasses.cpp index 6545136..933520f 100644 --- a/src/back/backlasses.cpp +++ b/src/back/backlasses.cpp @@ -21,6 +21,20 @@ float Endpoint::getVolume(){ return volume; } +/* + * float Endpoint::getLeftChannelVolume(){ + * float volume; + * if(FAILED(endpointVolume->GetMasterVolumeLevelScalar(&volume))) { log_debugcpp("si");} + * return volume; + * } + * + * float Endpoint::getRightChannelVolume(){ + * float volume; + * if(FAILED(endpointVolume->GetMasterVolumeLevelScalar(&volume))) { log_debugcpp("si");} + * return volume; + * } + */ + void Endpoint::setVolume(float volume) { if(FAILED(endpointVolume->SetMasterVolumeLevelScalar(volume, NULL))) { log_debugcpp("si"); }; } diff --git a/src/back/backlasses.h b/src/back/backlasses.h index dc4287a..3a27869 100644 --- a/src/back/backlasses.h +++ b/src/back/backlasses.h @@ -1,6 +1,7 @@ #pragma once #define WIN32_LEAN_AND_MEAN +#include "debug.h" #include "global.h" #include #include diff --git a/src/cont/contclasses.h b/src/cont/contclasses.h index 178a756..6a3d28e 100644 --- a/src/cont/contclasses.h +++ b/src/cont/contclasses.h @@ -1,6 +1,5 @@ #pragma once #include -#include "global.h" #include "backlasses.h" diff --git a/src/debug.h b/src/debug.h new file mode 100644 index 0000000..f91232b --- /dev/null +++ b/src/debug.h @@ -0,0 +1,10 @@ +#pragma once + +#if defined (QT_DEBUG) || defined (DEBUG) || defined (_DEBUG) + +#define log_debugcpp(str) do { \ + std::cout << "[DEBUG]" << "(" << __FILE__ << ":" << __LINE__ << "): " << str << std::endl; \ + } while (0) +#else +#define log_debugcpp(str) +#endif diff --git a/src/global.h b/src/global.h index c44b7e5..fff36de 100644 --- a/src/global.h +++ b/src/global.h @@ -1,4 +1,6 @@ #pragma once -#define log_debugcpp(str) do { \ - std::cout << "[DEBUG]" << "(" << __FILE__ << ":" << __LINE__ << "): " << str << std::endl; \ - } while (0) + +//INIT BACK +class OverseerHandler; +extern OverseerHandler *osh; + diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 38ae3f2..ba25884 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -1,5 +1,50 @@ #include "qtclasses.h" + +EndpointWidget::EndpointWidget(EndpointHandler* eph, QWidget *parent) : QWidget(parent){ + layout = new QGridLayout(); + this->setLayout(layout); + log_debugcpp("olaW"); + if (parent == nullptr) { log_debugcpp("owo?"); } + + mainLabel = new QLabel(eph->getName()); + leftChannelLabel = new QLabel("88"); + rightChannelLabel = new QLabel("77"); + mainSlider = new QSlider(Qt::Horizontal); + leftChannelSlider = new QSlider(Qt::Horizontal); + rightChannelSlider = new QSlider(Qt::Horizontal); + + mainSlider->setFocusPolicy(Qt::StrongFocus); + mainSlider->setTickPosition(QSlider::TicksBothSides); + + mainSlider->setTickInterval(5); + mainSlider->setSingleStep(1); + mainSlider->setRange(0,100); + leftChannelSlider->setTickInterval(5); + leftChannelSlider->setSingleStep(1); + leftChannelSlider->setRange(0,100); + rightChannelSlider->setTickInterval(5); + rightChannelSlider->setSingleStep(1); + rightChannelSlider->setRange(0,100); + + float volume = eph->getVolume() * 100; + mainSlider->setValue((int)volume); + log_debugcpp("ENDPOINT SET WITH VOLUME " << volume); + + layout->addWidget(mainLabel, 0, 0); + layout->addWidget(mainSlider, 0, 1); + layout->addWidget(leftChannelSlider, 1, 0); + layout->addWidget(leftChannelLabel, 2, 0); + layout->addWidget(rightChannelSlider, 1, 1); + layout->addWidget(rightChannelLabel, 2, 1); + layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), 3, 0); + layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), 3, 1); + + connect(mainSlider, &QSlider::valueChanged, eph, &EndpointHandler::setValue); + log_debugcpp("ENDPOINT_WIDGETED"); +} + + MainWindow::MainWindow(std::vector *ephs, QWidget *parent) : QMainWindow(parent) { // setWindowState(Qt::WindowFullScreen); // setCentralWidget(centralWidget); @@ -12,27 +57,25 @@ MainWindow::MainWindow(std::vector *ephs, QWidget *parent) : Q setWindowTitle("slidea resbala nu c"); - setEndpointHandlers(ephs); - for (unsigned int i = 0; i < this->ephs->size(); i++){ - QLabel *pintas = new QLabel(ephs->at(i)->getName()); - QSlider *teSlider = new QSlider(Qt::Horizontal); - teSlider->setFocusPolicy(Qt::StrongFocus); - teSlider->setTickPosition(QSlider::TicksBothSides); - teSlider->setTickInterval(5); - teSlider->setSingleStep(1); - teSlider->setRange(0,100); - float volume = ephs->at(i)->getVolume() * 100; - teSlider->setValue((int)volume); - log_debugcpp("ENDPOINT SET WITH VOLUME " << volume); - layout->addWidget(pintas, i, 0); - layout->addWidget(teSlider, i, 1); - connect(teSlider, &QSlider::valueChanged, ephs->at(i), &EndpointHandler::setValue); + /*s + * setEndpointHandlers(ephs); + */ + unsigned int i = 0; + for (; i < ephs->size(); i++) { + log_debugcpp("EPWidget creation"); + EndpointWidget *epw = new EndpointWidget(ephs->at(i), widget); + ews.push_back(epw); + layout->addWidget(epw, i, 0); } + layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), i, 0); } -void MainWindow::setEndpointHandlers(std::vector *ephs){ - this->ephs = ephs; -} +/* + * void MainWindow::setEndpointHandlers(std::vector *ephs){ + * this->ephs = ephs; + */ + + /* * void MainWindow::setPlotButton() { diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index 6ef82b0..6294a4f 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -8,23 +8,48 @@ #include #include #include -//#include "global.h" #include "contclasses.h" //#include //#include +class EndpointWidget : public QWidget { + Q_OBJECT + +public: + EndpointWidget(EndpointHandler* eph, QWidget *parent = nullptr); + //void populateEndpointWidget(EndpointHandler *eph); + //void setEndpointHandlers(std::vector *ephs); + +private: + QLabel *mainLabel = nullptr, *leftChannelLabel = nullptr, *rightChannelLabel = nullptr; + QSlider *mainSlider = nullptr; + QSlider *leftChannelSlider = nullptr; + QSlider *rightChannelSlider = nullptr; + QGridLayout *layout = nullptr; + //std::vector *ephs; + //std::vector *sliders; + + //public slots: + // void setEndpointHandlers(std::vector *ephs); + + //signals: + //void valueChanged(int value); + +}; + + class MainWindow : public QMainWindow { Q_OBJECT //QWidget *centralWidget; public: MainWindow(std::vector *ephs, QWidget *parent = nullptr); - void setEndpointHandlers(std::vector *ephs); + //void setEndpointHandlers(std::vector *ephs); private: - std::vector *ephs; - std::vector *sliders; + //std::vector *ephs; + std::vector ews; QWidget *widget; QGridLayout *layout; //QLabel *pintas; diff --git a/src/qtestmain.cpp b/src/qtestmain.cpp index 1305a69..883d7e7 100644 --- a/src/qtestmain.cpp +++ b/src/qtestmain.cpp @@ -9,10 +9,7 @@ #include #include "qtclasses.h" -//TODO david #include "backlasses.h" - -//INIT BACK OverseerHandler *osh = new OverseerHandler(); QApplication* createApplication(int &argc, char *argv[]) From 6213f9250d8aff0e9ade5c7518986f033466278c Mon Sep 17 00:00:00 2001 From: Hane Date: Wed, 15 Feb 2023 12:40:43 +0100 Subject: [PATCH 03/78] barritas de canal que funcionan weeeee --- src/back/backlasses.cpp | 22 ++++++++++++++++------ src/back/backlasses.h | 6 ++++-- src/cont/contclasses.cpp | 14 +++++++++----- src/cont/contclasses.h | 6 +++--- src/global.h | 5 +++++ src/qt/qtclasses.cpp | 16 ++++++++++++++-- src/qt/qtclasses.h | 1 + 7 files changed, 52 insertions(+), 18 deletions(-) diff --git a/src/back/backlasses.cpp b/src/back/backlasses.cpp index 933520f..da50727 100644 --- a/src/back/backlasses.cpp +++ b/src/back/backlasses.cpp @@ -15,28 +15,38 @@ LPWSTR Endpoint::getName(){ return friendlyName; } -float Endpoint::getVolume(){ +float Endpoint::getVolume(int channel){ float volume; - if(FAILED(endpointVolume->GetMasterVolumeLevelScalar(&volume))) { log_debugcpp("si");} + if (channel == ENDPOINT_MASTER_VOLUME) { + if(FAILED(endpointVolume->GetMasterVolumeLevelScalar(&volume))) { log_debugcpp("si");} + } else { + if(FAILED(endpointVolume->GetChannelVolumeLevelScalar(channel, &volume))) { log_debugcpp("si");} + } return volume; } + /* * float Endpoint::getLeftChannelVolume(){ * float volume; - * if(FAILED(endpointVolume->GetMasterVolumeLevelScalar(&volume))) { log_debugcpp("si");} + * if(FAILED(endpointVolume-> GetChannelVolumeLevelScalar(0, &volume)) { log_debugcpp("si"); } ); * return volume; * } * * float Endpoint::getRightChannelVolume(){ * float volume; - * if(FAILED(endpointVolume->GetMasterVolumeLevelScalar(&volume))) { log_debugcpp("si");} + * if(FAILED(endpointVolume-> GetChannelVolumeLevelScalar(1, &volume)) { log_debugcpp("si");} * return volume; * } */ -void Endpoint::setVolume(float volume) { - if(FAILED(endpointVolume->SetMasterVolumeLevelScalar(volume, NULL))) { log_debugcpp("si"); }; + +void Endpoint::setVolume(int channel, float volume) { + if (channel == ENDPOINT_MASTER_VOLUME) { + if(FAILED(endpointVolume->SetMasterVolumeLevelScalar(volume, NULL))) { log_debugcpp("si"); }; + } else { + if(FAILED(endpointVolume->SetChannelVolumeLevelScalar(channel, volume, NULL))) { log_debugcpp("si"); }; + } } Endpoint::~Endpoint(){ diff --git a/src/back/backlasses.h b/src/back/backlasses.h index 3a27869..6b36fb6 100644 --- a/src/back/backlasses.h +++ b/src/back/backlasses.h @@ -23,8 +23,10 @@ class Endpoint { public: Endpoint(IMMDevice* endpoint); - void setVolume(float volume); - float getVolume(); + void setVolume(int channel, float volume); + /* float getLeftChannelVolume(); */ + /* float getRightChannelVolume(); */ + float getVolume(int channel); LPWSTR getName(); ~Endpoint(); diff --git a/src/cont/contclasses.cpp b/src/cont/contclasses.cpp index 8b5b752..e8ba796 100644 --- a/src/cont/contclasses.cpp +++ b/src/cont/contclasses.cpp @@ -6,17 +6,21 @@ EndpointHandler::EndpointHandler(Endpoint *ept, QObject *parent) : QObject(paren this->ept = ept; eptName = QString::fromStdWString(ept->getName()); } - -void EndpointHandler::setValue(int value){ - ept->setVolume((float)value / 100); +/* + * -1 for master volume + */ +void EndpointHandler::setValue(int channel, int value){ + if (channel == ENDPOINT_MASTER_VOLUME) + ept->setVolume(channel, (float)value / 100); + else ept->setVolume(channel, (float)value / 100); } QString EndpointHandler::getName(){ return eptName; } -float EndpointHandler::getVolume(){ - return ept->getVolume(); +float EndpointHandler::getVolume(int channel){ + return ept->getVolume(channel); } Overseer* OverseerHandler::getOverseer(){ diff --git a/src/cont/contclasses.h b/src/cont/contclasses.h index 6a3d28e..c496e0c 100644 --- a/src/cont/contclasses.h +++ b/src/cont/contclasses.h @@ -9,15 +9,15 @@ class EndpointHandler : public QObject { public: EndpointHandler(Endpoint *ept, QObject *parent = nullptr); QString getName(); - float getVolume(); - + float getVolume(int channel); + private: Endpoint *ept; QString eptName; //QSlider *slidy; public slots: - void setValue(int value); + void setValue(int channel, int value); //signals: diff --git a/src/global.h b/src/global.h index fff36de..db86521 100644 --- a/src/global.h +++ b/src/global.h @@ -1,6 +1,11 @@ #pragma once +//TODO enum capullo +#define ENDPOINT_MASTER_VOLUME -1 +#define ENDPOINT_LEFT_CHANNEL_VOLUME 0 +#define ENDPOINT_RIGHT_CHANNEL_VOLUME 1 //INIT BACK + class OverseerHandler; extern OverseerHandler *osh; diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index ba25884..43361ae 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -2,6 +2,7 @@ EndpointWidget::EndpointWidget(EndpointHandler* eph, QWidget *parent) : QWidget(parent){ + this->eph = eph; layout = new QGridLayout(); this->setLayout(layout); log_debugcpp("olaW"); @@ -27,8 +28,14 @@ EndpointWidget::EndpointWidget(EndpointHandler* eph, QWidget *parent) : QWidget( rightChannelSlider->setSingleStep(1); rightChannelSlider->setRange(0,100); - float volume = eph->getVolume() * 100; + float volume = eph->getVolume(ENDPOINT_MASTER_VOLUME) * 100; mainSlider->setValue((int)volume); + volume = eph->getVolume(ENDPOINT_LEFT_CHANNEL_VOLUME) * 100; + leftChannelSlider->setValue((int)volume); + leftChannelLabel->setText(QString::number(volume)); + volume = eph->getVolume(ENDPOINT_RIGHT_CHANNEL_VOLUME) * 100; + rightChannelSlider->setValue((int)volume); + rightChannelLabel->setText(QString::number(volume)); log_debugcpp("ENDPOINT SET WITH VOLUME " << volume); layout->addWidget(mainLabel, 0, 0); @@ -40,7 +47,12 @@ EndpointWidget::EndpointWidget(EndpointHandler* eph, QWidget *parent) : QWidget( layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), 3, 0); layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), 3, 1); - connect(mainSlider, &QSlider::valueChanged, eph, &EndpointHandler::setValue); + /* + * connect(mainSlider, &QSlider::valueChanged, eph, &EndpointHandler::setValue); + */ + connect(mainSlider, &QSlider::valueChanged, [this](int newValue){this->eph->setValue(ENDPOINT_MASTER_VOLUME, newValue); }); + connect(leftChannelSlider, &QSlider::valueChanged, [this](int newValue){ this->eph->setValue(ENDPOINT_LEFT_CHANNEL_VOLUME, newValue); this->leftChannelLabel->setText(QString::number(newValue)); }); + connect(rightChannelSlider, &QSlider::valueChanged, [this](int newValue){ this->eph->setValue(ENDPOINT_RIGHT_CHANNEL_VOLUME, newValue); this->rightChannelLabel->setText(QString::number(newValue)); }); log_debugcpp("ENDPOINT_WIDGETED"); } diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index 6294a4f..1f7beee 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -22,6 +22,7 @@ public: //void setEndpointHandlers(std::vector *ephs); private: + EndpointHandler* eph; QLabel *mainLabel = nullptr, *leftChannelLabel = nullptr, *rightChannelLabel = nullptr; QSlider *mainSlider = nullptr; QSlider *leftChannelSlider = nullptr; From 308a0486b6c16d8a0b647713bdaec85aad8b277f Mon Sep 17 00:00:00 2001 From: Hane Date: Thu, 16 Feb 2023 20:34:54 +0100 Subject: [PATCH 04/78] mute city boton momento --- src/back/backlasses.cpp | 18 ++++++++++++++++++ src/back/backlasses.h | 4 +++- src/cont/contclasses.cpp | 10 ++++++++++ src/cont/contclasses.h | 4 ++-- src/global.h | 4 ++++ src/qt/qtclasses.cpp | 12 +++++++++--- src/qt/qtclasses.h | 5 ++++- 7 files changed, 50 insertions(+), 7 deletions(-) diff --git a/src/back/backlasses.cpp b/src/back/backlasses.cpp index da50727..acd4f3a 100644 --- a/src/back/backlasses.cpp +++ b/src/back/backlasses.cpp @@ -26,6 +26,15 @@ float Endpoint::getVolume(int channel){ } +bool Endpoint::getMute(){ + BOOL mut; + if(FAILED(endpointVolume->GetMute(&mut))) { log_debugcpp("si"); } + log_debugcpp("back BOOL is " << mut); + bool mute = (bool)mut; + log_debugcpp("translate to bool " << mute); + return mute; +} + /* * float Endpoint::getLeftChannelVolume(){ * float volume; @@ -49,6 +58,15 @@ void Endpoint::setVolume(int channel, float volume) { } } +void Endpoint::setMute() { + log_debugcpp("bool mute arrives as " << mut); + BOOL mut; + if(FAILED(endpointVolume->GetMute(&mut))) { log_debugcpp("si"); } + log_debugcpp("translate to BOOL as " << mute); + if(FAILED(endpointVolume->SetMute((mut == false ? 1 : 0), NULL))) { log_debugcpp("si"); }; +} + + Endpoint::~Endpoint(){ log_debugcpp("cum"); free(friendlyName); diff --git a/src/back/backlasses.h b/src/back/backlasses.h index 6b36fb6..ebfe0e5 100644 --- a/src/back/backlasses.h +++ b/src/back/backlasses.h @@ -26,7 +26,9 @@ class Endpoint { void setVolume(int channel, float volume); /* float getLeftChannelVolume(); */ /* float getRightChannelVolume(); */ - float getVolume(int channel); + float getVolume(int channel); + void setMute(); + bool getMute(); LPWSTR getName(); ~Endpoint(); diff --git a/src/cont/contclasses.cpp b/src/cont/contclasses.cpp index e8ba796..bd05f88 100644 --- a/src/cont/contclasses.cpp +++ b/src/cont/contclasses.cpp @@ -15,6 +15,12 @@ void EndpointHandler::setValue(int channel, int value){ else ept->setVolume(channel, (float)value / 100); } +void EndpointHandler::setMute(){ + //Qt momento, de ahi el param? + log_debugcpp("kinda handling the muting tbh"); + ept->setMute(); +} + QString EndpointHandler::getName(){ return eptName; } @@ -23,6 +29,10 @@ float EndpointHandler::getVolume(int channel){ return ept->getVolume(channel); } +bool EndpointHandler::getMute(){ + return ept->getMute(); +} + Overseer* OverseerHandler::getOverseer(){ return &os; } diff --git a/src/cont/contclasses.h b/src/cont/contclasses.h index c496e0c..c562b73 100644 --- a/src/cont/contclasses.h +++ b/src/cont/contclasses.h @@ -10,6 +10,7 @@ public: EndpointHandler(Endpoint *ept, QObject *parent = nullptr); QString getName(); float getVolume(int channel); + bool getMute(); private: Endpoint *ept; @@ -18,8 +19,7 @@ private: public slots: void setValue(int channel, int value); - - + void setMute(); //signals: diff --git a/src/global.h b/src/global.h index db86521..96d177b 100644 --- a/src/global.h +++ b/src/global.h @@ -4,8 +4,12 @@ #define ENDPOINT_MASTER_VOLUME -1 #define ENDPOINT_LEFT_CHANNEL_VOLUME 0 #define ENDPOINT_RIGHT_CHANNEL_VOLUME 1 + +#define STRING_MUTE "Mute" +#define STRING_UNMUTE "Unmute" //INIT BACK + class OverseerHandler; extern OverseerHandler *osh; diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 43361ae..0cac13d 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -8,6 +8,7 @@ EndpointWidget::EndpointWidget(EndpointHandler* eph, QWidget *parent) : QWidget( log_debugcpp("olaW"); if (parent == nullptr) { log_debugcpp("owo?"); } + muteButton = new QPushButton(); mainLabel = new QLabel(eph->getName()); leftChannelLabel = new QLabel("88"); rightChannelLabel = new QLabel("77"); @@ -15,9 +16,9 @@ EndpointWidget::EndpointWidget(EndpointHandler* eph, QWidget *parent) : QWidget( leftChannelSlider = new QSlider(Qt::Horizontal); rightChannelSlider = new QSlider(Qt::Horizontal); + muteButton->setStyleSheet("background-color: #A3C1DA; color: red"); mainSlider->setFocusPolicy(Qt::StrongFocus); mainSlider->setTickPosition(QSlider::TicksBothSides); - mainSlider->setTickInterval(5); mainSlider->setSingleStep(1); mainSlider->setRange(0,100); @@ -27,7 +28,8 @@ EndpointWidget::EndpointWidget(EndpointHandler* eph, QWidget *parent) : QWidget( rightChannelSlider->setTickInterval(5); rightChannelSlider->setSingleStep(1); rightChannelSlider->setRange(0,100); - + + muteButton->setText(eph->getMute() ? STRING_UNMUTE : STRING_MUTE); float volume = eph->getVolume(ENDPOINT_MASTER_VOLUME) * 100; mainSlider->setValue((int)volume); volume = eph->getVolume(ENDPOINT_LEFT_CHANNEL_VOLUME) * 100; @@ -38,7 +40,10 @@ EndpointWidget::EndpointWidget(EndpointHandler* eph, QWidget *parent) : QWidget( rightChannelLabel->setText(QString::number(volume)); log_debugcpp("ENDPOINT SET WITH VOLUME " << volume); - layout->addWidget(mainLabel, 0, 0); + mainMuteLayout = new QGridLayout(); + layout->addLayout(mainMuteLayout, 0, 0); + mainMuteLayout->addWidget(mainLabel, 0, 0); + mainMuteLayout->addWidget(muteButton, 0, 1); layout->addWidget(mainSlider, 0, 1); layout->addWidget(leftChannelSlider, 1, 0); layout->addWidget(leftChannelLabel, 2, 0); @@ -53,6 +58,7 @@ EndpointWidget::EndpointWidget(EndpointHandler* eph, QWidget *parent) : QWidget( connect(mainSlider, &QSlider::valueChanged, [this](int newValue){this->eph->setValue(ENDPOINT_MASTER_VOLUME, newValue); }); connect(leftChannelSlider, &QSlider::valueChanged, [this](int newValue){ this->eph->setValue(ENDPOINT_LEFT_CHANNEL_VOLUME, newValue); this->leftChannelLabel->setText(QString::number(newValue)); }); connect(rightChannelSlider, &QSlider::valueChanged, [this](int newValue){ this->eph->setValue(ENDPOINT_RIGHT_CHANNEL_VOLUME, newValue); this->rightChannelLabel->setText(QString::number(newValue)); }); + connect(muteButton, &QPushButton::clicked, [this](bool clicked){ log_debugcpp("cliqui" << clicked << "cloqui"); this->eph->setMute(); this->muteButton->setText(this->eph->getMute() ? STRING_UNMUTE : STRING_MUTE); }); log_debugcpp("ENDPOINT_WIDGETED"); } diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index 1f7beee..a02bff8 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -8,6 +8,7 @@ #include #include #include +#include #include "contclasses.h" //#include //#include @@ -20,14 +21,16 @@ public: EndpointWidget(EndpointHandler* eph, QWidget *parent = nullptr); //void populateEndpointWidget(EndpointHandler *eph); //void setEndpointHandlers(std::vector *ephs); - + private: EndpointHandler* eph; + QPushButton *muteButton = nullptr; QLabel *mainLabel = nullptr, *leftChannelLabel = nullptr, *rightChannelLabel = nullptr; QSlider *mainSlider = nullptr; QSlider *leftChannelSlider = nullptr; QSlider *rightChannelSlider = nullptr; QGridLayout *layout = nullptr; + QGridLayout *mainMuteLayout = nullptr; //std::vector *ephs; //std::vector *sliders; From 40bee906101942d862d7a0b89677283562ce15e3 Mon Sep 17 00:00:00 2001 From: Hane Date: Wed, 7 Feb 2024 17:20:59 +0100 Subject: [PATCH 05/78] poll merge squash --- assets.qrc | 5 + assets/SoundVolumeView.exe | Bin 0 -> 203592 bytes assets/notificationAreaIcon.png | Bin 0 -> 159931 bytes bueno.bat | 4 +- qtest.pro | 17 +- src/back/backfuncs.h | 32 ++ src/back/backlasses.cpp | 700 +++++++++++++++++++++++++++++--- src/back/backlasses.h | 139 +++++-- src/back/backsessionclasses.cpp | 282 +++++++++++++ src/back/backsessionclasses.h | 57 +++ src/back/ipolicyconfig.h | 192 +++++++++ src/back/msinclude.h | 26 ++ src/cont/contclasses.cpp | 335 +++++++++++++-- src/cont/contclasses.h | 150 +++++-- src/cont/contsessionclasses.cpp | 79 ++++ src/cont/contsessionclasses.h | 42 ++ src/debug.h | 41 +- src/global.h | 74 +++- src/qt/qtclasses.cpp | 638 +++++++++++++++++++++++++---- src/qt/qtclasses.h | 174 +++++++- src/qtestmain.cpp | 53 ++- 21 files changed, 2766 insertions(+), 274 deletions(-) create mode 100644 assets.qrc create mode 100644 assets/SoundVolumeView.exe create mode 100644 assets/notificationAreaIcon.png create mode 100644 src/back/backfuncs.h create mode 100644 src/back/backsessionclasses.cpp create mode 100644 src/back/backsessionclasses.h create mode 100644 src/back/ipolicyconfig.h create mode 100644 src/back/msinclude.h create mode 100644 src/cont/contsessionclasses.cpp create mode 100644 src/cont/contsessionclasses.h diff --git a/assets.qrc b/assets.qrc new file mode 100644 index 0000000..8b63932 --- /dev/null +++ b/assets.qrc @@ -0,0 +1,5 @@ + + + assets/notificationAreaIcon.png + + diff --git a/assets/SoundVolumeView.exe b/assets/SoundVolumeView.exe new file mode 100644 index 0000000000000000000000000000000000000000..4c7a7d2fe0e11b39fec51ffb8152ecd41e19e9c9 GIT binary patch literal 203592 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

~>seLx`ep|j5*LqYWqW(|Ths?p)KLc^ zIM*pz{sKCG7R8XRu*X6LVgBb45Ev(5w;Zk;1hG;~%vPWIqvY6N+}ldQ1YxeO;j;EA zfXxDcl=k76e-1$+s*x{8+FRWs6^gWXo96-rfQpkKu15g;ON$^Z!Yb+d7}|;<>7&`c zChVc+gu1O!X~~>5{DmbX zwwxI9FInZ(B|}&@%F~bv6hk4p6u?9JbKP)K@#duKU&3YHAHtWzIp#1rvs7f69E7jh zp=a&JpiR0~$?0j)k<9RDCHo4*r64=|8)YirPAN5EJ^?O1iS>om<0oMhF4GXHHbUA! ztw%bsmE9*oqZe+yVyKxDnnW&K)hK7=-*vuS`=O>1aiB-OH(blp#@3Tbp8 zV@D9@J!Z!*6v{{%GaOVXUqgaSluEob)Q0k*PCX*;ldiTW8N-gSTYu|o`hgx(6M@x_ zkltKq_2!qthRl*{ffUa^*8@Jot0YZPNlDjO=`i$1u809cPiLx~di$!k6knyr)SAf3 z%d^XTVzFHJQIi#)kkl6|AHq6zdLePUIs9SS6!Lgg-4I2_R@I9ddT3i(OuFd)WSQ{^ zZ4lgQ3^0$P63YW!={JhJ@71uxorHB+;m_da7KeBJyxo57x#dV|^L0=`A^sL|Tzf17 z$j-j@+;TaRcM~`kg89wT0eIvwN0KD9waqIw`Sb7gH22-^GxWe zD=l-~wli)Yf^eF78I}AF(~3**xUq=PPuFUhfU)V>3(j-#$l-r(V8QfXtUT|?44wu2 zAIkru_pd=w)$U=w;+H!w_FdMfk z?>mr*? z+DUV}f`%CpUhU4LV*Dw~_`i><1c{x*`rf5owU>k)yR`h3I9fW)<7i`7KsyB|XnexY z0#n46cR;(aD_GUO%def>uDuj$8LP21) ze^k3&u?2V&fV*_5PE9REEPmEpu_=Sg+8Q+u{dy=jv7%IN=MIbJgckegG-mufp!MHQU zhxQH*hZa^*hLClJShf5ALW0_CHdf1(rBS}oK#EsM4X*($UG*t1?hNYl?OvJvvhYrL zl1dxKtPCP`$DzHc;o%D3BCP2#vg5>m4hDB}4>KhZB_#nQOA-Ia29SVb{G)#IkNP_6 zhpdFJ;aj=rR0mOGUBZRp@AHppiw8=Q506^*)?R-+|rgbK=`RXJ+uY{C2SuDD?uS@L; z7O8tIA@o+ErC5{@54%GsVwoUAd6nMJl-|uyx*%|3+1;axq%}b16M*U%9LkkwRxW;ZLcjbRzAGv zr^XF(FvC@9=S9d<^GqbjO!>eaD!)olpRA)3H4lqACwLJ7+IKcs0lU_|kLbY?7tl%( z<*(vt2{hc+nQ~k3D{4}=<09oM`HowEAuJR1M%ru{`wyrZWQ#v=&sR@Eb-%cKb-TJtO7G+rJpI< z$ZV#VCsW~G?T=nv4%%YyD{Zz1?_Ro{ErZ+vV|4N6;v`r0m5WBT6@gS<)m>Yy$8XNW zp_E-yJXMiU+8=G*{e%1C zjC+_!$k)%RN1Vep@1huF)uStL2$wPY%J(uPb^o@eLQyY+Wzix#&HRIUW5tT=M2xUi z)TURV59VG_p4DpRQM+!GJK{CRW|*@C(zwKND8SDpFS;T>7~Pg3z*^%S${6pEiDGra zGf{dnQ9?)Mr!M3&_B#qy1e3ax-QJ%(6dmmS8n<3mBa6?@a$`q84Ph`oY_8Tcs+yB) za^8O=9o3rNH7^omP+|tC5oym%Y&Hh_#h3H(s7>B3R%M)wmlto~?_^X#7-{T8ccfo5 zkr8dnh%U?UzFlKBZjKGEJ%e0!2@%M__>hT0kDubgtW)8m&8+Nmhe+7~dLmE4A|$5c zwr!YX)+cC1vSa5Yobd}YVuOW|eZt7PFf#LyEBe#gjlE-H$JqMxs%0|P@gaUC14J|b zGc7~DX>JS=VLp{%K1!<00J&z!;(w7h_X-afZ0jN~ft@9=9&?bu#+{isZ&YEKn?)j7 zW(zoTzC>h=xCAOhP$ykG#OBAZ_PFHNFPB=84)%RUZksXPfN*uJEs<4(;9nLBs^ezJ zA!wzdUB;WOqKUm7b6$M9Qqj&O-Reh#BGy}f-p~+_RvKd9m}zqA)OFgksq7Do6!wHr ztxJ6*UBjqX4LJgveO~z@S_&MYjHNePGFC#f&=c@?;L-`mkT8^Ceq>^Ecu*iVyzDRu z^i4R^10#eL+Su2eZsrwV?|ZmsV#5mnR)*@{@JULkP&&k?g^qi9;E>%Dyz=pWB9Le-) z4|HHY(&665n%(tuJULN7!2|<(r#%!QKhPjt<18W`|-^^Z8%EJHN#A6spx73#(W zQmtcJo3$XoVjdxgQ8_`gsz(g9WD519=B2W`pyvQ_lMXQN1wkFfC|Z@nfB9h&A)ach zC(gj~4Zty`u|S4koouyrnb$MWNXWxz4jG7>K*>J^(cf|mRISE_)g4%%)(EG@6t8;s`$%q0|6j?E4nn?TiW`Jax>4smS$5B}P#SM5RtG%upv#@&WF_cHH} zd6OHHwHL;7&+&HFW*d7){X7c74nG1!M|A|FV?9FsVIH~6vZ_PRA}{wA;kEdX3Fc{F zH|iU`YK5OD)?DS;vBai*w5qrC6JAVlT?)rO(FSPR!g0d?@f$MO&X;iu7(W<41dPvO zmCeK_J{hZgzQgy5L}0}#Uo>A}H>}!UdL z$gj#8KaWgn+^#h?0iqUOW1~5jjr9{DPt~qr*HF z`KC3&s!S-_->d4i445_UZUOPhVp_tFA zNv8IwZ;mQPKUx9;rt38+|2-U(^0SKzblZRBZN2Kc9Fa?V8@H=wHq{Ob83W{aI>GQ- zgIJN`ZjbUiClhjNwi=UjL&nJFe3G-?-2Tq}?3D)je zHYtP{o3Gp@GkB2R_C)B|KDq!lfM zFs@QPYeO(L<}K>QvVAKjv8Hju+{t~{D;CIH`3Zz7`P~A-!DWR;VlueM8nd#SP4xJ5 zBEqMti14ZT(mG7-i;Za(8p_;bx%5c@n490PQ#{!CXDV6hsFTQC-t>-@(*0Q8RpfR! zD+Pv0)Zx5X-k_C`Nb|8H(P{3ihYVGDCFJNar;|r~qO({2TULI7mH$1L4EN3dx0U}R z8I=D}-q4OhEKA%K`sT{DIr3;wZ?PktJd;WKE^IO=^L1U{MJz-j9;ANVP1+^hth`frpL!r zB;G*Te;Zxc5k8w$XX{@zFO7U6`G^K(4G_Bm9g^rk0mTHkR8Vm=%c)(;pF z%hUY0kClE5Q;aQAWIoF!wj1Z!aMDPus$pu2rZOV3@pu=^DUL6PnD$ycF-b_roq6&a`jycnM&K5RKy2Q^VkviXyS{=2t4y43tKAD|9ss3I! zAJX{mc}X|^TCI$-71dtNmC@#hl0aaD4w-jwkIXBc^*#!QbK)=Gd>AxANp}82gZjd6 zG3OFq2c{^UMFTB*tbx%!*tOV zza{l7*)KYttlP>+Vusu2iPHM{jNSYWqU@#Y4ByQDpEzX1f<^VEInGsyuY-gNW%zE# z#+T4ZpJm=C2YJ%fpF(1iR*L%;Pc!ky!V+TTV-*)VB&C8k@hwnq0G`1@zKEXrc6Vfk zk9(P0O~4UdJL#&TJwg0Ut;PHWF6yK!C>VhKY!R<>Qv!X4-4E-G(zu7Xam=BvI0;{3 z-NNHUq5H`9l26hAKU_`nbz7DW5_7G|VKfJ?}B*5DR8$K01{&ALDh zSN9D<7EzjDZ&O5uHLy{2rbqcvimIc>0pk2@kj#nqxeG@`Od(r+!ZvRD^$9Ws^fxf# zNmqli>g6W&qTaLkxf3mX2SM=Y;^CX3o94Ayi3&MClqEZ-7#VQ+3(F1x9scj?S zJetXxG~2CyRIj;CM&~aziU!Jh+kxAe-${nRYDL~dN@n&zV8piYBK6ye<`tNXjjNCGz~gW>)FWYB8rc{m)m zqAeY;A|Su58_cE8$FUuTCMmZmaCQRz*%r3C4wK2wqNx6_i&84gCHG#Lp9ugVI$6~* z;x1gvAc4hsx+)1+(zTWn1Wj3XrosyjYIdUR->-mNA3P+b^8ODDW7d(x=sZj|_3gKE z9w7%(CSBtj1k!ah7*gPKa*3rR;59ZpM1hyjgFO)Oz17N#>KRfWIZM6c5*~YZr~zo2%Tl6HrT1JsMH4(O#qIQY{`4`FBZIERlLxK zUq~tes7&hHVN=d~;7(wUl|v&dxynj*s^l{j>0A#zs4%G$KAI%R!R)USpC_|R2CX`F z#uR=i+L~YKs}TN+|Bv&5LTe_!kBYj>4aQpO9pkOpAV(N>njhUPO7jx0Aqsy!2*mm~6_Xd$C})rQt?9;d!TS;Q0U z6E?Z!C^{jN`oxwhdgnw+d%Lk$nYDV7+bs9xJ#wWVnJ$UPr?J9=Rrk@Oln=wt5z|q zfSsFyRcGu-qW5*6mdwW@?wWg!(8;3LLeFgGkR`NC=SG-RGy^r^NT(!ylV5q3R5|5J zv+)l_DtAZ*#kgH#c9cu-+G+`C`DkIu!Hg^P`7;IYc`tsRee( zFK6!gBIQ9_eG|mS-OY(bqd)RCgT1u=$m;6Y81cnrbJ+At%Yw1?D`aPaA&II@!vE&$ zo1q)m(Una)+uySSh9A3k0CI`|ck@`ea*+kn<-=+ zj8x@of6Jzn_PRflYh?y2z_x5ZfZGyR$(0{91=aPX>-*0;jIBt~IHh+mcVQxrNna>l z5VxBoselCQaI-qWYhQ7YIxVYi)j^q|Y#`~{fVC1Z&dXuc{IW4JC#_10*~?I2@ut>v zYv&_FtoBVwPU<=geiYTfA*4Omp)U?*wb~;KeB5uv=nJ`0ryJdmN-T)qs)UypsDxyAsJ%Nlz`RN_q9;;(oq4f(|EmCzR)uyR{j1a|PpJwZ z@m5*v)?;V5vGWq5V8Uau46{=8n3xUD-@(5?sUc+JL7#TqadAe zxt-BwW#p$be0Ii@D&u9jzb&2TvGW$Hywzr*LU@u~7y*f%k<@4v16bwbf60IA3%e7> zrqbJTYTc!`&8LgvX22IhWJ3u>(SG_t@ zow}BfFs&l7drwL8j2tB7{sS?d4meBQV`Ek1_uSpB_Q?2EC}zaYu;dJ*xfRi@d+pO%D6}} z;(6h2Sztd0MDdc*tyjr*ktz`6Y*_r1se0R2{bWBpYaiRFrsxKmlr>a|P<-qo{A?c^ z8@YS^%`yV!&g&)jF=^5~<73?JMpyIGPd)Y2MD4k0+{d=(GY+hdk4+KRu4?9dW10y~ zEd=(oL#E{Uv1xnVky7wmgW9Jxy=}Vj-|NOAsZ?u8&0yrS}%p>vAl$gV-QFF z8t<9+ypFo*T@fA)LsS^R$nOh{Fbvgz9PSIP@X8Go6*c_{^Ol!b_Ae9MDKzGnsip4) zfvu@aW3{&2SX+Be`t#k|6GQy$QfZDHIG8Zo&mAG-V?!2jMpc5!f-Gj<`&A7ekm6Mh z*FgNWfmqc{8JZa~E*1r?Ypt*9{^PYu<`7R&zu%D~r_0tyJk@^IQvPsG&jI6)TsHCz zZ|AM4dlrsv-P4uG4H{7D4Sr0H-|=Qlh|51ztVKGm&UoLB)Zc4*;c8`H%0=d<$wyNi zbPpLv)_*A%b8_icxxsO9sK5Rss-eUboATQk6f@@6k*#YjTXJv7^6%Mf%$MszWyzOx0<;XeM8;weljOfeD`QTRm$E8#(DwNS0PJ#i9_1(EVqJI`R zRvq@OiM#7(EEPx;r^rwvCa*@@@iKq}Z!xp?LG2{RTVAe|T4OH@04K(!0&991YU(MK zEp>I$dhtW-Zrsk{;@ToKrxMHGCoud_tc*(igIkwOTU3fN+VeTRw- z5(&oT&JQ<3vv8&N?Z_0}_@}JVRzH~1>Ns}N^(QL!w%4A{xqGIHy0t=X#j&pP!FzTm z`POX};VjfjEBN^9jL19e@E6!BhD{r}|0U_Vz{a#lFhwHU0Zcvqyq@!3{}zN;EzJEd z%3=K4ah&YkOJ&mS;Z#Oe()BaG0P>FXFZ~&wk*s4(9du##=7?`KNut2IChI$Y1l@+ zj2xGB;nm*L#at+lPsk}ddX@2>zy1@s{SZ?mvv0S;`R3MUyH)g=m5E&QeZIw>WFw6T z(!t{PTxb5o8Gb!h26bnN%m{v20{qcH0|UV-xe^cVy1hjILK5^y76rMvk`62+k0HMFSt~?MyL;|4I}Y92kJ$ zL0CXKrk}N8i`}iRD6+%MI6rW&WCIz9rLG+N3>QMcxGFc`UE+Cy%*YVp7;)`|Cv!T; z+n}y*3ZIv5K@2OI^r+wb)nx`(Sf-{l7J?0kEoSR`mdAsoa|%!N=4oPA-A+Sdfp4J^ zJUDJ#?zr1~|M1Nr*De(CI!Z{S)Taz#>BF+LYKs<2Ei$qg+Ut_{3J5J4S9@)EEsY|XD zv+81Ma;}iNm_FV=DZCcm0M9BvgW#8rf0vz|l;zxWzcQXjb?JAXIW$?kJt=Y78mj7S zanYbGLQ^?-dj`2`-t-CsPIK_4aDVf0+E0WIRq=jt6)>OJwy)*>Reay#48k^&-*>|u zkgqe#QfW8KRtaX(&o%giB#gP22*TfnSK|#mIm3;eglV6A+@!bizMPmfoB_S>KeKg@ z3IKyud#w{0itFEvl^wwKyZ;}?-aIhs;{N{+gw=q=jlwD_YSd_{ctr6aMr>9#veBUN zMn$E@B37#?*)}R5aT6iFS;V9D!fLBltru4MA%I8l0F)zm<5}y~cN!1807c#3@o&V&14p%`GzpG*gxtq z(Il0KI5l(kDhD%jtQ@tU!#<5ZQaoeN+e|rpZYdh0TvUL^jg>tvDj7{pM+5osd*$^u zgd6`z2V!NH)f2X#g?%yoAP-^LN`C?Gq*OH47bJGB_O`LN&q2PG(ew=(QoWVZH~-80 zjpbA7Whlh>;cBz4v-m);f^(Tm#63T8D}~k#!z0$fyGe^YN6ikt)3?%$>lxxZ_X$pvMt=Vl0t>Jdev;iF7lsIL7~2sgp{x6RLNDfK{PdZp{~ zVLY}&mnW9h4@eBpj(+nJ0nXnafN!Bt*Yzd5M^raf)&2h-{->~(qB50XkTd8YA1|>%*tFMmVAw0{<&DAZ4vI;i5W^JK3F%Gba0aAmH(@5SW~*bL@d@CKm5Pa9_AfW zPqAWb$dQk!jPi9;?`TS2ag(gI7(0>WVRCe00Iezr&P zQ)s4CqYv-BZmnf5uqDZ~c6ZTVUY~ibXN8bW&U0DEKV{79T+A->B^MPjQJ}ML)t$$H z-L`R&HtF0AJZ0h@cE-ZFnY(uAKXrzL1L%D+DNnA4fv8jW2ZXFII5eTe8 zZTZn!xD6lZMM!w!?D^)NL*@1ty>r*!c-d{mLJT(U2(Kq_1joL2>zH@_Fhb+rEsA94 zX$xKflQ*;Y4Ke=gV#d@S?7dLh%b_mJWZ>T8flSl+Z^@*wYwn2iSxRjHI z?4D5Ion8f8g00R=~bYtB(^?JwTn0geK+R`UY z4w#?{A#hwvMquVv*}QQBT~7ZmWHa6TWgRbP?}ofB)Mj3vLZq12cP5r?GjZ{`d36!) zSfR<>58dEFvZssw^EQbNVyUcddqm3P6ZsRHi%h=Emi&vHm;<*MGi-}7zuaQX@UAg5 z))wXVgK2T^cnlb^4Q47`>A5Qa4)e_>l$NR+S_$y}B(8xg23QLKvAH-9N9|EZ7&SI^ zl2!fq%1obRRYAI$-9uJ)?DUq384#)u&IArR%KRQh9S)SA5pqUHys``?D9``#G-9(H zn-0PWjwWB!#%RPVi`sv3d#&`OrmtB4A}=5&|07_{azipoUCE8HARN7~p- z>-4!s5tzou>6DE-b%WUdY}yM?)PvRYPkZ&pdUBtC6lSx3%uO@|O-^e#9N@BpB;ljTWC+U ze=yChsvX*oy`-BN1Mfh$b_IGdb|55#-0wtfCeNmGP8`Z+a&_ls(mI7K)keb4BW)lg zHH2*QC&9;NQ1%a#rY5PlArYW!n|~~^nM2r|ZAtJ_Y_E75z`ut1>W*Ix=CdY=N~I#d zq3meORC?v_Ok32A$po*l;8PTwuO_BKgEQmY=Dz@Q%`0`6P72T2hf;OA#0c5^u1+l| zS=)7pkd;Hs7^N4IQ&$Y84ZP#QSXGB(UvrN$qz1`uA6v8SRNQu)oJz(WsuV8x{Vk`b-<%f4y z>8XG92^wL`#qPe9R0byh@U2zgT?z`oA%!WiFn(QSAGxU74W6B6hurjrnUnxaHVt^EW!Bpe08G7Rm6aY? zM}WVb`o%(hw}79D)FVZVwtPr${QZeZoqe7npH+9Y^lQ?qtNz=^J4`q!2)?+a#$RS( zXhl&}HnkQ3|6yWm)Z1W)hgEwEMPno|;%+n={(S+}3jaa0UBIIEX+i4LgH^3+(nB6U zjkTC>O~Pq|^P5djm`kxX@=18kLbY7;uXev-!JIoTRsBew;gZySBi|?^Jt>%{oEqyYi`Ia!S~ zFi*D-E-iw}%z-UfxCzurviqYzZdwXjLC7f4vpT+JGWh#ZBGiZnpCL`U;5g#*Zn~a7 z3Sl=Nrw*JuS!)%ve0b0q$O*R$Sh;M3Wj6N6zSdJm==bb?|GianV_+9)PH%!D&ycPRxlI@N2u!)q$>V;B;o$AIThU?=aE`S@AF!0hJ?3zW z-&?|49IM;!tZbUNcgnL^0#IzG2vuYp;R_ZT>8qAy1LyaeRP6}X;I3=My70n+j-w|g zzwcyH|L-!U+inDn&+@GgDJYk3wfg*+>e80e+h$_ZixVLwlM@SiwsTh{3pm%+jHhv~ z21;?RGr^kx}A9~e*f7pk-{ zA6xS=9hR!|<~~{%pRDe@*P5M^E$bVYR=*Yf(XNxZ*eU zh^v>4CB@f>w9CENKhoj*zm0OV=`CFv{1EOW$I>< zY|DIbOX;;VbScx2hGHxN9Z=o3re0I_W%ZQWeO2vO{+rawWwObnmma1jU>+OWdcKpZ z)8#)U3b;eocrT&!aFQUWC5Tl1J>)F^NjD;IHoaU(#lvi=nNnw1GLZ+^Kcvoiejn00 zf|AMo-7(~TFDLg({}Pw`xE{HmBW>5*3oRrP({?FS;yr{RP{Ib*@l~+;1##~SRK@(k zsyey6D1Ti%`FWqjXWT|Oz?KC1@J#ww*5FMd)+%Q!DkK7P5;_R0;gy(qlp6lBs!9#9B71PBx`h1o$GR2~L#S6O|q2f%} z-Kedsqc5Q%b>z_&7tra1)BKaI5qAxI4bfqJ>1IZk;@)P8) zMs)#pSE`%KoMrU*bM_I;$$Zthea}}_g*(n^TUcu>Rf3!@BV&!8dO>GUN zf6{o}k#&P>CMOL;h&RdZ7_o6SvvW+gV*g!cHDX-tCo8&%=Gu#CTZ`V)WTOQC6x@B` z(5;wdT4P>J-#Dl;&FQQ$Hvj&Z)THkX?!B(K8$WPd>R zw@U$K5uTsq*&5F&;GAD;V)%U0N~M?;w-_%wXa9Z497PyOd0l{=65u+-_{V4mYZK{O zRsK#^5-TXJm)I32bDyVMpCl_29(MNWRf^kkpZ=-J#0QG)%`~!2jV#M)e3% zgnPTPHakX>o6u$Y^|*xo$0s-CComoQ>+uTm$Gy!y<>$V!<|UGQp^oPz=i-Sq z-=l8`Tlh@u_f-ten<0d@xOlP+ow^Cy-z%yA;35`?#Kg+7qv{5_HvnedN60pdSVg$q zc`e+6Gp4&K+~`AFqvabD$HdC68dq1rKh1DIB~H|MEpJ6#*qw#YhUYE;GYZ|+ zQ9fJvOh+c3$X>iJ63P)TM!nC{k-w=P8FIW2Po+fSLNu-$f43ZTx_fInRraJEb_d5w z;l{a;py-nWi}+t8>Rtu5OP?t6p_R2Bi$vh`t9t{4eFRJ=Th2MH5&iInac#6JphhC;6!Vk=3=;A4X+B{;+ajK3Pg#{ zAA~nnzP{SqboKDDwV#g7N0P#^XuKB5#q$uOvFz$d!WmPTbDAiYy}^d*@9;$Uh!?Sr zq#>TvF_>p`kW98xf&aIAVAWq_>&ct2+@{+{NA^#vpW)fNFdyA{*kqj;WR*36N(%^@ z*YR=AS+?#w6q~$3Vox(pV44XCuXAveEx`yT`Qs<3Lvw13W+17dEz%)8?PjSFaO3rz zj_nz%K3Z++LiD2}AB%fJQ26 z3-8@&)m`HkThj45qC(W6b1LjtKX0RC1}3}9VOTlxz<`V=*mr|iRiriLwJWXESgf05INCW>$lDFj(!{8 z+-rHvUa|?^t1^xd_Az6-gqmJwP`j58OM!*sbxxkZjNZHz7Eq*AFrT4JZr&QXssvMu$*@rFGA?a85I%<-e`zCfwKJz4lnpan8 zV`vqP_!I_bbHC2GUv|cwOC!{_9g-JJV5~z^CT83BvE4iArG3&OWN*DGBwi7#_dHA+ zI=L3C{fBahFNH%nn1Z7Aa-F}tOcR*NYHH7!do$AbPCj1)ADRk1!or#9d*^<-bO@}~ z&Y}jxKj6feT0+)3lX4wHUA89$rkyV|OHXvfD@bPjp;T(|1;mT@JQ$COZ2dkKW&0*k z9~b!9NTnb3_f=4`x?+@M|ALPy7ypL($u1+VW)3$e{M(bArOFKNmyIYu3S-f&< zd0V3{C9P>87HBAmb_C<8# z`|(;7nRxlUEuR0cc%^%H|7;@9>&R-_cTNH^$ zH2(`KQqfmGr~)BJnw7bHQTt%Bc9%bA_@U;)E14$EHwcB6HwYr&be?Bw{@#Ciz;R!m z+QHQDi_O<661%pkIVE_!=mW`T`$%hp%b$OedfwE}yleM^a(UD1y*VrJz&%!O@|I%d zW?;FWI8dl~`7ez(f;(MDEn1e=AR3&-!$+)Qrq&1TUhCfkAiNipvFuuvlsG87_>+N` z5AQAWaosbYM&=Gw*5Sc&>tW2}wP}AS$)ODo!oEN@Y-_W^FF#ZS0YcgQsH*GKOmU#^ z$~T$)tIjhl<13}r<-g2HKNsy^3@?n0&6~JChD1j7&-=p(j1Z&reV?iSVJi9l2&!WH zI^}SEopy-0;_xg4^mzzB!l~~WmUu1P9J1%yfwQuL5}4yd*0#SxEL&!-9hEJ7fQC-5 z1?SMw)Om=t`}JeJ>OG45&sB+c{VWwGiBTC@PYW(gNErnJi!B!(RQ|Y@zG`({LmDWS| zOzgA8>bPn$A+N4qc<~nlc2{(Fi>K`Riq|K&byBwr2rqQkP2F~P zk0o;}U)O zd+)l&4WHG(Gy`ojt@%9~xhh6VpQl!NUyRlHzF`52Ntsc4 zK_AY)gkm{j7RqtTH-br0hdvd5rB&r9CBup_L@^N;eH>ZGEA}jy{y`6j;fP#*vMUvZ zV|3(RP?BQ*mk(Op*V^$bac*3pi#tsF_3m+BxiUXIrynmEL37Ju zSyVgU_MYo_Lv*`SFPj~kg!5XnQFzNfCSfvX0mJ{#fqfLgj_Efdjlm78 zty6TnIVWBANjWyV_Y)qkU4zW}$m+bhiDHhF)5dr#XU!7#8*m2y#n#H0_P-3uN`EvI zl}Wy@QLo@qW6|dpEf)i8qVB`-gJcWU_BJ$Xv*{ zh=bXWczq6~V_ORYyKeWq&ag$Le(+Y}{I1svv|PdIX|d-LyF4@sKb4SY1ioxptxf60eMp=*7$cF}S^5J>&qqO*WQ+H)c9@LCD_s8? z@|hA^l8*H0QMXIkew3yntU#c@-nzJi5*$+{R)+1+GLmG@TiJ2-a<2iH!ll3EKlvvi z{zJk#WVKJ;fGT<{HHM9Yc*R&*1(wNIRfKO@uC2L$%NP=4%wb!Was+~c&9@5jlIuA< zal;q)W(iP>-G#7GG$dPmEOlw0=#y1cLGj#mrD!UV4R$cZDmbiX1vgm**1+hKFOk=l zCAE(1mZ?wAOy}fgl6rmgX*#HCUA{_hnPpveOpgxjm78^j>XVExx4s*CX8J-wZWNE@$BWzuLr?N*^ zN9HylZVg!0wX1*YnJGUvlh<3O%X(&7qoBpcmHV029fecU= zLO;c~uk!y%NHqWRhM(4xZMsp}XP};(^&Cs34%;t-?5I}zkzeIZ1($!3KDdH#r=NIX^e$v=^P&)-wbXrw=dq)zDPPrKi?IOUYP;TfX$C)%0k zJgI{>J`mD`z98HglV61|o;tFo<3@u2KAQXn?{Sa!xR_3w}XGw=`N*wcjE(P!v6 z>i+?;jI#xw0!=Z+e;-MlE%>|eO}o+y-GN&cYS!1q6h8qE?fkWz)RFOwE%>&v`K|s? z8tH}3BMMvar(43pI8I;-zS2TM2jDF4Hw%FZi4Wx?8QPbB?7^pgXwxj_E6=ifWiFBQyL>qH^X!O&d4T5bv%t@KwFHjC(vSd3209f$MNzj(*auO zFMK3NhU(5VK^azNUJ8H~Zu+Hz*5UsMpmjVF6rgqZ0>IJN8WK5++^G{ne%pOnXvGQg zuP)AomKPdLV7&H42d&-oA!yx3{VzVwM+RE+*h>Pf{Ye0{etwERaCRk&3JzM;2#*}J z<~nF?B#DF8SNNt)^Fp8FmWAHY*QFHy4?IBY2|3-NbqS61LJNojS`S*n!rO5IXx(BV zp&2+pOYQJNGx^Xj;u zoEnDDWL27l?tbUJ2rx{*4~}#d@b`Y-KOjQE4jQ|dGF8vHF2w{2F6qb$+oyx_%kdDc z+20)6`96)0?GWwqcXC2K-?oE^l{GvMw-Xea2rcekq%C=YWG*g+o!6}uB;5C65C%Vo zczO6BbN&>rnlOVG+^g;S@zw$>J18_AIkU4gBS)qnU>!j6Edj9X6a0~k**l$Yk#`II z{at4Tl5Xd;ql4QfK_jk+=bIuT)pRf}T$mLtTk;}~ogaM=#2ydRqbBO36AZv@peiU+KB+Tc0U24mPe*qtV%iq#NJ+ecx01y97*~ zfYq2bN5&kFW1*!aQ{?Y4#i$E%X_SFG2Kd|YjYPGihV!Dsy_eOPXCFnhS3jgr;urB$ z%}`UcTJ3-LA60O@rW!re^xA<5m5%&ze?d*E8j{F#zSn?$B1zquoSjW~>L>mTWc;m% zYFdVl;W*poGRG!vmQUYBA=mFrZgryA|40E+##4y9sBL#xlb?-DURjX$4KM4ycCIdo za1`}Q-N0|6;i5j_1D0Odw+{W4zL+Bgj$8w+@?QNWyd7(RG#Hc5kF;~JEv@aUO85D{ z#)=L`U|3$CLP4Rbp>;MfhKgvz6vzr(D)Iy|1ljdXOv0Uv_T&lu=-Lv(VL`O{so^}3 z&;mkV=nOrAP!2#2{QYz{Od5wyQ{$iRYym8kgn3DJIaQmM(K~A=t;TixnK+yy5X(08NvFsKh0C6` z=2%i_Q4c!a59Y1(lRIsbpt(eK9NSbn)ps5jxf-f$Z7{2vr%_=%`8NgRCI6;I;>?#r zQv>47bj(O29mo6_{DxlVI}n8{rO~okN*?X7r~IE$0NyrqVVr}iaR1dmQ}UO6u2_}) zTY-ufQdaPPZ^^NMk0L@Bp5}0|LZ>hwgWfDyndH*`<^yPr^y$lD_ADu`Aejc1vFrO zx#P}!_LmQ)?A!U%@xDT=jyL^_ikOSY9d?qw;E%K{xa#aM<`a|bhZ{e#?zp1jshNG1 z-=zOsAmP3ox>^q9);NvzA51%&?pCK}N<9_yUTF6O+)<8MF?O*sibCw7-eZeg?2_JN z2fNtUdygIBVmJ04JJiMY5012Sz(lJwz1`@}{ws4uV;#4#zMuZPvDSBHtZUHTV5~=W zAM3LQG1dq0*KMp%FU*d0T~$1F%K(Nk&2d;MWzgi2(mpCLIm`o9$m_mnhDUD$L_X5DA1Nb7=`_ z{tilcJ=(rKoW!MwTZ<5i?cDh?*5}kc%!YPl}xOh~A#)Z^HXFmb?pS zArTz&`-g1{E|it>R34}iGtTc-qXC%0WS_c1a4Poa|Cg?zJb)Fe-=o8FDvA-do*J&On5xvR}H{I`gC3-3oZF6o> zjO$ru1yIl23UF5q$vT3b8KKVf?C)XgS)v-yF0khNzorf0Vee;XKDe>M|L!MS9#GH; zAFzMp$+Wm{-A?}Dq@*9?frJJhYWgZ~`OprHlB4t&Lg}OVUzR^?#4SAgP}UDfsc1&k zDUObdGDF8JqTyjv){Bs5-s{x`j86XAwsTVW`AQ7nV6@n;hG%0e7c#sgwKo-yziwm_1A`O_wQ!8a6+Gj;nb#3D^2Ry=7^XMQV#@#HZey z6={42l<8nP@~u40p|LmX+ZfE|`uYqTDLKGeiVP}-4t)13h8IN;|85$J9BH zG9!OB-9`fPkDKIl(FR7aain#i3*Vo)FEJsxXe#IdNp24}QUjOHS7 z1uz2X$RI>wRo)*}fcRuOod@Tym7Z}1B2a>1!Sy{<+V{6QDUd9~f-H<;Ughw2W6kGV zwkSX|NVX}fO!O3|Bj4?6wP?gMwG8K_lN+U)5FP$5wULfWTUn!&jV6%m8FCSjj+}-B zZOisc?47w{#5V2-EF&(8c@_Ocu=4ytFAF|*1Se!_L?sU_IOXJxYlUqlJ>i}eSmL;9 z!~|!xze8N79;(TQoE6cywP9eU4~J6nYwM$k@>dQx<@HM z4#^54e^d~{^5&#M>-G+PjHBWSFE(w%p5qUSiMXGvrxI&*+z?1o#qd`GK%>WCW#~ zsb+=q-X)&_sH4eyEPpT-AeTghHUjBL3~^1z*2%qhRBgdVN^=#s=jtA3b)aQGBDhOA z0l^Z_kn4rJZHKt4A)Df`?hjadHt(n)G?CMh3WOSqVUlUJwwCEgnz-O@&A#qlO^5g2 zr9buaRxfSeJ-?l8r}rIvWi5aCpn~CrG1c&44^c7LWlV>~iZK=4#!j3}#>Z4ys=7 z)u$u7x~8D<%=?;FIiB?-u-O0L38v@sAQ75h7qJ_dQjxoeAjopIl?$+~%njrzLppVO z8$J{8kb1N-1+M4=o=*k!CiAPC#ce=-SMs~6syP{Fhlo>y6IdY@^}%~Hj&xF%HI`^D z_M0`Yi)hWkdCjFy?xr?f4c)xIl?*RIFxfF4&@ZUZr~K*khz9HEjV~#vht*H zMmgi~s+==$an5wkSvZt49)|*kxpGQzu)Dz6P9~gU>Tq;^N-_0#Rm>H*I9EF7DjbTL zibFARS4@p-&RhZuTyt*5`^B?X&aHU+QQmF1GbreGT%0>_C}BvKaIlnsj5-6Chb}N{c!zM5< zMc~ts2y-iz8a-6}_-hoY69ZqEh2Ot2q-oY@AE}dzZR(xR&QhnIjkvZihthCS?RR{p zjtd(ZdYRc75Wh8^pN>R{W7oHiA~EFpDu;>FYdlvS_YcYtzL{m$bYut#xgnm|4xw&m z=m2GR$;F<%HFZr;80w;$9C_tGOkHS~D7ZKF2?7q?3y3_AM+@Zal5eS(HF7AuTCZN+ z+pkaF)5YXd*A!yUKBObpkpoU%$w$0+Tk^wjYQMhF%5o~d>>ICMoY%ik4cj;>3qQ7+y-W>Jvi@8F zd`7|;+d?~8yBw(79FZ#tRE9azhpfNb32w*8V7~XJ*H+bhZ7E5}3wNbuX)N)JE>SUD z-@WxLZPF1R=<(W*&&`5>@r7*$h_(JlzY@u@qpGS}d&#BByF<4= zs=SB9wN{mHn0_GUKsb6E&3by=k1=obChz2L4J~xbe>H6?_7@;xBh`?q;TscZuvjHD zRGp44x|W8;n;VL3EUYT_>*?Tj?Mk<4U-%p{QMx>ZQFIeL9oc9N_9oB_5s21wkO6yE6tth7;FcX5ij!OVA*^v&UI+EL4Dap11SG5aQ1O;p+M*8RjkZjx`JauYB#-h ztgP0|5!l4tj4vz_zkI%3OWBX5Hq|t5xd=~n$ppG++hL$K8DyDILtVJ%AB`J_R*9=% zd>JUV(N)P8hLk@lt^cWaL*6DXzc!A4-ty|^zg6JzKU}5VH452n)y?xRH-hj5+fA42yDLnWf!Frv)hEIB)-)C(3swtSunfaRF z!Th!O)&6;`bsYuBFc(^cYYn5cxRjCbTR}^;dB)ecslx9{b=;q(iQ=)_nrCjJqy9EP zhqX}*%zm^N?~i!3Wcr72pu0q`f9W}&=VIZ<=2T8}^VS5xuhgeve7fNikd}ay%yV33H=7l=n zBH<7m|BfT^cR8(&kSHDbk3lSRiXCn7!6{bMSo1S$=?~}lOkNFs$5G1P<_{O-a8Lfq z0p-A5^o?X*NYe29e2r0#Fei}ZWcM~4IhL|95;4b6gOMNJ^`t||Q9s_JBE0o=+hv5? zMp!mjpf&co%TVB*H~sjj76E63)y`d`Dbd!dm;p?kEyB`n+xC(u3G z#X>^;aL_$cJG{^~d?Z78{G)sHPO5rjpXS1i@Tm!`p6n{Vr`lUBxVQxb8I|#nRfavL zk#TWgKD{|~$X{tvQm4*YeV1JL7jyRn6qq68KVg$BBYUgJZ0a4a!|kp~_I7>l{B8h6j%?tbVnPY zg7eoAhkLI>eO?wvJ_xbVFXbU3-+}Dh z?ez)=6A~*ctGh5WAwt^|GW9bj)kcl-Ip%t}X}Bf_1>AWZv*UEA^n#&6vBEoNgZ`6f z6j-W^&Gv-(Rm67(O@AUBX#R7vN`iF#iLT!` zD+e^c*~&n(fdqhNIZiHUcI@9BG)qb0K(hp&K(i3HEOei~1e&RM0L?Tx-9a;wMtY&? zL;;#9may=AoPcuu+CoCd;yBQpf-f1W;=dC#6?|ntGZH_bX&;6IXlewSaR!O+vbY=5<1Pg60Aap={Pn-$^wWUI3{4gEtsxJS(-$PqSEMX3ZZ6 z^B?`LCulAq!hxn5UlueQugw9?OR#3FDrb=Z(Cmkk3!150cL&Wrq;Q}q#wXAW#VrdB z(3e2-?;EMgi+m`jJ815tkzVK{q5#c0OIY|KP5_!^77}_4$AM-MzGUc8{yRZ)A72^J z+=(C19RCX(Kr=z0nPi|jiu*r0ZT|UJbb{tx!u&zRcb_${65&AeIKC`s##4TlpdHAz5YV)e0MOik zlM9-EBVOrB&^%H&&@|x_Xc}7eAnRZf_hwgDirVY7I24b9)2Lv6mTWo+Hvgvnd0b zzYx+DGW=)hZzn=K+pczSo1I-Tj zvY=U-%mK~KI;Hgr_a*_L*#Og*3z}n?Q(Zx`o)iu=|HUWJe2QBZdQD#f%|mzq&BJoK zgXRJn>4hF43ef!35*FTs6M$x}g@mraaiB@!ONQ$C?*z@Id}Tm$0e(O;vIGZSV1|}M zWQMbF4M4jS-*fIfc&PzsPa+LK-)8`nM@Uxy{XC~P0L{H70MPw|Re#}J0Op^C)(rh# zKn~5FVJvGI0NqWP|K|Fh0J?<;2cQJLEP#qBKRb86;Wh#QY9s*wIu$1uK=&az>k6O~ zNZ|lfg--yg#4QVr(3b!-01pk_MoxDCEu)cMXdqDlsE;Kq{5q8d(3f~AVI7VG(1-Yv zp*Q*O1kf73G5}hJ9{}C5Ck_Cb8vy9m41lH+-xELuml%K=h%^BGkO9!SgmeYa*@X0* zL0`@Y0JPO0)nE7}1L`mS(f~BbN^SFJvbbdclomI481da_&;}wLfL_Cw1<=)$p9RoK z?B@Z{M3Q6Grv^72fC?B^h z^c6h10cZst0Q91q?f`0}kzVK}q5#k{mauR>P5_`sEF|wLY+${srY{=hY7!xB)hg1b}TkPA=GFqZ@NwC?I8o z1gDWA72AegPz^o_3950+LPzLJLW1q^0JiPrbibgqnnr>fL;ol(3$+G*rr58rOMvWDw+)W^v8wRySc$bvpdodZlJp2Cw*&z5s6#J{Z*zlL?&WtA8i|US* z_aOc4slP$7@|UpgO%gP!3V(Beva$bfZfT~w2Z{@hNUyCzE}OfUyAnKKM0fTW~G0@c2sx| zpET0Ti>Xa3{SONXoZ|uue@!dC36HnP4la{CK9_lkblV?4LE^UQgAhANzfO8o3jQ6z zD&*xs2#OELNxxrb`kw}NrXLlgzl`)8A7zT!hv1GvJ5Eq(Z_*^EHkEw7_?%9=e28o~ ztTE*?GjG=(_0}Uh)lp1v?&ak`(%NMKt zC1~2qBrJk@D=1Lcy14T)lUZ+hKt(#-@06Kwb4b%kT5!Askhv1Wi21Mn2P$PMQ^`@) zY(E?!+y!4xLU`YAL<4qz{TM{NPh3B3;)hBK-^Dj`&@TFMB<6UrNu4NPl8~3Gtgl z0*K$aIQEoPRtWTnq&Dji-)5-aIFh*QnJ3{B^*bK7EOey4MEwTgp`C-}bfjG5ooxiBbOlI=m@otju(iwN?8^Su%rbr>TXA?-9mCy zvIsSoA*i_&LnTdT$le95L4q$~Ds;Ajx)${#XF8H#9J%t!6q;?pfu-IZ2o|c8>Ldp3 zqg3ROAsQE#hy$*4WG|9nY)K^68UdBGXD>=)sW|l2S(P}C^K7795ztJ|E$9#ds8>>~ zfB11sF=V=9zO~Q{X+=e|0nG?YlaBoN8!BpeUhU_F(QdRp`Iosi-fzbpwa*ODSN=zK zgCg7r=rFK~DM%(tar=$-)i?NRw6FZcR*LJmVi8s3mA{o3!1-FSM8h2ulbb{CQy}O# zUAAE@mid&|5l@Z&KDnvyANu3c{gRFm$;|^?JdnW3dvfzOiS1Qbe?DJ!!~kCS`5S5@|B^8bZc(V50(a2pg32HO(0Yvp1cq4iG>1H6vk0flZ;8+%0sGN-Q zEG_8CrP6$R2@AsyT$WquY~B-r^?07Dp0;zx(h;AhBXil(j`Hb6U4L&a2LY=p5zc!U z_fa1IPjC#_PdkcO&cM=? zrA_UYRi%oRE><>c233Pbr9cQ~sv512< z+Wi_Pb42I4%MHJ9SrC=s{$?=6~$pt#?UToWF`8deGR5);5CcB zYqc*{c&R`3CkhlGGJ3%}*$Q+L-rL^sBmS-9)$sb=s98~|nm+y+1o5dEDed*IALEm@ zoCT}ULtpzeU^{nPDBdDzF|b5_2+a>S9Sn+0Oy0eYS1f4JthaG>gj7Hp|H*%;l{c5n zfckOWmt{eR(6hDKgx)K3;^G7H{700G(d#!cSxii1G%d~~@V6yW;Ge+%y(Pa@)m)FN zN9Q$DvZ&SsJs?&iee7|5wbdHA(>6-8EsH-=VV;*6wi#AUZDk^nUoTLagC3H6P9h_ll=(nn9vi^TFvzAv|lxc3js`2=5%iwXb?#wV;<% zok}W{zRa{;Hp*3%GSfcn8s&<2}9eBb-|DRuHs*Zb0 z1bL}VTyG`~{}1U8cq_kkgR-`!tV%ClFxI0tbR)X--+d{t8m_X2Cv#3giQ;Z0PeoQ2 z2_z(ujpUPxsB$nyU5bLTQXpEAvf!N z#Q@#TiIp6zzau8{ceMXsa#V7aRJVh7FSj$6nI7}Xodv3_udIylb4_DZ}7b)grm zVb$TvXN^g9T&w^nmc}ouH%QsWh3`dBRIwgz?BJDB0;E z;rLEnY;pdjyvs2x;6SYH)u{$4r$iTN!I$~OGthuw^1$djJWbuCc>i|Ou+g#|9DxTk z+RlPmv`=L^uOFbs2XohJ?>>EhdPmXs+GRN#o3AN2J2w9!HaY5?E5OI9B~$Vy=Uq;N zbS_+k?w|=mb|J18{-2F^%nR*CBnF+H>Q9+mvYw7ffWjZ(!((N?%$_?&%Uh>@iEZzu zcQ_S(1mYNOI)v&_nV5BPuphXKrJnu3tV>vePo_oi*$-1n6yO%yp3L?Gw-My8MIG6x zOk|IQZy?<52QI~DGOQb6w|A!W?0(?$cH0k3Cjt9`6L5IzGP@hN3lWSICg@Q$QQ@zv{-p)q&#&LpO1%j_LI|Hi1iGO^gI;R-J$fH*t<82z~n7075^p@1TxP8B_o9w+uo9 z$Sw#?KgUp4JGPC#C;9-QyFutn!X1QG<1+}|#t_z1dKN;*`39kPNdSZ%!O4ZtPh81! zDlNg8{R1R$p!q942^s&4TNawFFG+!G@ccBhG6=r^PQK4)6O!sBoP_{1$F zbP$e%PdUD1sFeRs*!+U8jF9nP+o)Gs$RUX98Q*{h1wf^uc_aN06FE6*LxN$oS@ii>Oas#FZ2{q!08{Bu<#z708V#XNT?ad z!RaP^$Ow7#k9YBF=!+!TGXmmO5(Ds%>e%Y-nuUW`#(7pRqkL$n7J-dJ)sPQM`qZ~^;*!jSkQ;;g!qG~3Gd z&*$2v7Mc%8Cl+AFQZaKT{|v-K&JfJ(|7a0IUk7T>dXqI&d^Q)>MF?0CuzTTTP2Bkr zpEsGLj_Hbf?c((4$9Y+{?vvNux-c1Zr3JG~F2|Ey7nT#79CgeTI@NVum`kCC!TH4X z#NgD)MsBtul6B$f+;!o@%Z0@U^}a6r5el4L7bZ^(uz4m@8mP`>ZOzJQ5qNSlrJ#-L z7o3@;It>K*EwA;UI#&?xu=#X+*>#~WrDw7E^^XjjCz1ecJ{%{CN#Tql-Ar&jSQic< zD!VQW=fe@7z46()P%Mwug^tS%pFfk+o$lOEBeDBM6!`q1B`kauC&1^m77|*76Kg(X zmKSx^>~{ z?7C1!m;m)Z)`d0a2N3#1$|$^R#+drIy=oBpda{Gi#Bo^&{fi)f2co;J3vUwcAY|_( zXOtZ$P>!lY<(f+Z2O7PXsCD65+_F%ezO*hJhX*{W zmeUj|vGxd7xv9w*MN3zLg%U6@ON0JY_HVV~LnPNTjv zIPLQr0P)q!2B%}mt(o*Riw<;2>-XUV`IitaIOVJh2NLe!RERITF0`D-N5&wY2L(go zI+O&!>2p}Q+?h0rdDdlJ@JZm-g%9y*U3ee2EVN2rS{LrY1Dx)a(|uhymnM3l`-lQg zw_CzO4<~?=_()2)49CIgDtyV%Wd1wXg$wwyb%FIjHu8<+YRj(Ox*1WLT_R#9;w;eF z!4n$>e_JkCFkVFhEZX;ofL8n0EOfg8k=#z|JcaY9%x)mFr*|S1p4S-5Y<-iBVZ0f| zKTqOB?Hxn@3v2Nu?=kse@}5#{5R&%{vwcF!o6m0BKf9I`Y(Gb#S1R{x%b^WFcYZVV zNOqv*&Q4*t$IL8ZTcb@yI?}>6)54YeO&%_i%iSLCYnb2wAw~#zWn`&c?I}q`m8U+U z;f0&nxJ-}Btb{|)R43{>kLU(d!VywI)}Of27Y`SIa}t>J}V=3o0jJ|a|q z+EKyH+r4QQA~E*W?pChLf%LjEeDt#6evC;{9It&{S0=8(6oACqN6kZ&)R1nQmpGeU zqJ#lR2+2%Fp{lagaW|ux-RE4ec^4+dPZYF@x9>^kanh}-6f{9+vNRWyYNCu4IBRZ@e&%aL%Rxk%?j$*qafMYZJx!R z)+*Iq|J2M&EiLcNbZem5)SWduNV`30>G#}uiA=K0*v=q@|IJ+vS7vrtvOmpBu;J?h zf<`4ilR?SjFdPBav(gr;B(4=-FTLiW4-4`yn>GM!zHcCchYJhsXOQ8 zo|(iZZW(Hg1&dEuGYkvP0?`To+huHQ4O zqb*CUPQEaB7Cxt=eMpVWT6|Yk{$1T^TWHpjRh@01S(t?LuN|RIxR$C@FERgv=5*oO z-ayL1NL)Iv?jW2?=GEKSas~d_lWnNtb=kj@V+u}StuWoCcN9HAX#K;a$ zV)R5+JoHiTRZMYJ{GZFXuxG~0w~+A&m+|p=;U*1O07wIgH>-t^EMS^vm$<=lQ1k!3 zQpumt6qj7Z#dQwG*p~w!%`3yOR4a|dK z9mk!g3I0f{(MkTir080}p+`&L!D}X|L`cP?7AQR83yui*szdSo!irQ)1rj3@`6w(M zxe@9I4mm@RZ#`|Js;)pVbtsFx8Jq(K*Ud{vZ0HD#j3aRGqr`voC8b>H-%UmmT{|za zN7pGZx!7L?g$l@uUvrVAd-EwLGK_$?WNVt2h;+*~)c>O+Y%}I12H33g>mISZ$$5!w zg2{893u&B}=sb8n-i7^kUIHna`7l_~|>;nU((iq@}zyJxK%9CeL3NB&%^ndGo4Md$)eTyuXlTM+fp8p7<9r zeFL#_&AU z39E@N-Wq|m|8hgT-`|x)xA=P`c_c{E-;zk(qDPYJ zf+U|lXe}8(FL78mU=H*TzTMSy?Y(l{!YA7$*@f?ubq3|5L9hUs(vh7UCRGI;x$#pg z|0U?gT${-OA>ar@BvI5LR`%%gOeK!3HKXcw^at!_B!SCtb=${l9m(LV=$l=&30WsG zb!lb!d*R04!ryQ~mjRxKfH9t$+fVCXtZd(K;|WA*7LSC$LZmdwN9wOae{px`bn_L~ z`X_>x?)Qnc^t7I6UEmM9&5cC;VoMXVrOXhm3jvR~@fbZ1$@cdU%;=N*(dTf}P3k%8 z(t1ddk<2)Y*jhfSaeZ80YAu{`{Ai-hn@fr{9;w36aiUM$i&v7CS2^%u$aWJ_@eXAS z?)zTCsvU`o!eH`l(rlNw{4G0f>O!fOBOl2onDge(!oVnSNf}&%h1nhB{69$gn=eq( zmPz`RD+7}LBJB>aYD*;D?kDGv^yzQ2#aW}KjhUAi8DL$k?7D?Vo>8ib`p*kT*z_r66XowCkOa-F(*=>`Xqn&^I(=UgbfkVVki!3zF?j@ zbipTbIKYu8sAjA;Yd*s~)|;V;=-2M8NtKE8$G>hEQ|&luhglCOp|&&+MI6ZI$gUV1 z66O||8SH`<@5*?Sf^)6cj4DpwF}jFzL+y|j@0N+1OP_N;5ic&Ext3~c)Tic|%t2`WzOSff*DIX+slJQ) zKu&6AF??oq^JAO%nz($XPS?`Yi!^3qQ=^NjQYRNApApR?)Ks!K9FOc|8$~-DpFkN9 zyuJj*Jy;<=WkdB2GU&9q&42h528G>cjX^K_YM!htls`+aY?nHs6(zWtlcP|e142h_ z#lXSRxKlOxj^=l)j@ORAvVUUhiF-g*v7!VODfQdiMLpX5_dbKXq(X<8H~>V@Bed*x zOU?GN$|&~7FQpS?s_8%#DRK&f&+1&nF!_yFs(aWn(SV?FgOfey%ACD-4%3jly7Ph| zlDS8$8q$y+gS37}4U!fH#z_q>^xq>WSQSQAvQr+a6`{?)4$)$=<=fF5o+i+kNLJ7! zn&>4*;^{nrXA^AtOzRDgJDVe@&0x6wwajcyh@w%A&BFhTyU* zSIKsE$H-Ku1I03=QCGMKi~^UQ0U0|=yriy4gR>MS3?S{ubmSi5mM2x7(uzXpEzypF zXgbe-_bBbeFxSJd|)J z68d4hBs9T2<@;**$cTjM*O*ACKS>b@y-m8sgBXjfcy*Y>IJx(fUnPkX-mS(bq0o!C zWub-ol2B+49@=@mobJNAV`-!pnoATyp(aaM_FEBCmZwn^J{Ofd~F9ssdX7J`+?~ryt#r~7W$38CQmzuQ_Sq?A>_eETbS`*RkTq>dUXs%K zfTAOOkIVyW(vhj^=|6S*9KDNonlRuwlz8F6?2<%T8Mqor z1~g7TTKG&yY7rv2fyt;p{v4kD@y|{^s~N{?pQ8brd8H)YQ&%cuHxFArA6MJU72jne{mpdptL$M8(<|( zeGtvSc(rJaY-O?obL6O*gd@_)&Hbn6r>@!fUzF5bU zQ7IdWk!bh<{WxH=39WSREBD@XVoWqyU-WaLVr*Icki-$aut)yxi-cq_E6|)phe1`& zUnWM!8M;!qfBHT&v*GH3e2RCkcP1`RUS0HUg7xIjj^|w&=m^$@ywgW^SgR7^T}Eb^UlB9-&XZSx!awQ(R^h@9?r?Ug>{(0!r7#Ld@o; zhtRKh^R>n5-ql4%u>41#{Nh<{AI0}I{KVZu{MRdS*#$$wO-=Y{yB34Mn8_TZO~HDQ z#9LiE27#-yr3G44EFJ+4n!&_fU$m^HWEHbjfk0jB$p#l(M6fQoRL9t9R|r%Uiz}#1 z`gSNNpcxA7DW)V0cjY5$qqSeq+HjMT3tNT3wsZr9{>Oky;OU=QVv_XGMYYdNb{H@ zV~tl2soN{=RcdE8pB{UsQ_<=)&$x|Y&wB#wz*@fsaR=?ZBXMS6`Zw94O>myapzXD+ zZYxSJsoO@EzqT=NDC#Yj?mojZEcx^*fS8xqohA#M0XX!y0+^<_bY#+UL4Tfm!?}x2 z+EsQEB~v=%9e>Q-BLvGw7sbNQ9J{^L{+%|e*D#ApP~pZl_=eVk#6AdA1w|b$B{!i6 zPiUVGAMI}Q7InOr+|(!Ww%6bg#Uxr^N5lIrjkf3l6XWxBJiPCUufyjQe)PSr6`f)J zHuG=i>57s6$|{Y0PtnWm2vC@5*L0p~g?6vhV*OUKMI)S77uQ~_4zy}+q5nEQ+ZOsS z)J)!^5K~nB<~bWISd&M}p#Z6>ZOuqnOv##)O4hM+*An)~DZzC;V@ua=@ihFPlkzHw zt-eoOR*A7*ZxBrV1R&vGeu;RRy0fs9CX2*uS8FuuZ7{sK8l>1Yp6A)SW`<|J`M0}%{dN@j1frL?fkAy1I%uzxHTW9r8?yI%WbYM{+Dh7%K>)AH zYai=}2brfh+}Nfm_QUXSqo`8y zhpWO(>&T1pvL;V;xd{})P0uS#qzL<6FDUx@Rtga5dDVR?wPyJ`|If@OGi~1HKhz|z zsdtO(DK+90Gvd@qjZ&x0G=g+Fc+fuH)jf>DtIDF#%HtG}T=W6O2jVy#7hU@tr1MOI z;GB&vRxTCgTh~%H=cAF87rnDFy0Igs73S6G(x3XF6@{I}Sbn>*){>PrslWh}`j8g_ zW>wJ2VPeyY>20J`r(ENT-f2(twTT>`+;>L62*1&i$FO!+25Seeg!BvS?!orQ^n#NpfpK zxar6~Huz-q*sJVB(e1l1a@FMx8o+RqUffWh-y~-71>~?D$BEAdo*H?wDA%q%-ve#q5F7R(UV_1v`l24>#UL`i}Rw>#(U$xar2u%!`N7 z5l6_dFG?xxD2wG)iTt2RV_WwIGo`#1rYqbuj>zPU2WT$$hk87$c7`63Q+KzgAOI=E zBZQQaXsMn3J0mbvx*>?6e&u?1scQa_27YDZVP^c`KA0^ZSb$-xy~@?jLXh^J-Cwkq z#{`H?`h&uHcpFcjaHFPq(l4mnGr4I%xaqH!+nO8Hn2vhcSC`l;;BpJy`GGv&#UIK==E`jw)7j=~Q zuib#)VUVAgEBQn6FSd~Q$=SwElWA8nd1m+y(sfw*`ozH-Du=MJ+7NQ>!h79+WinZJ zA-JNBb1NIscyjN@DrggStQb1x8$>Z+b~GCNAaDU^v3!T-E5ar5bJW8If=% z)CB!a)?Xdi6Y8^E>>IW+h41W+4rMbd6}?%cHqWi*A+jFHPXVJK&VPm?Q=rEfOO>kEd>%ujZvfJN-xLAJlYdeo&V3n;(4G{_7Rr zd=X+FUS|pIsbt>wu~g-L(g|sP(1=6xgF^sj^MevaH9shE5=qnAT48^aujLJs`4@-O z{9uixIYnujAJp~&M|+Fr2es*FesCQBE!wO!H`KYL38cnR{NjF|MG?f z{1^X!yuAs0l+_jgorPdf;uDwAShqo=jp7nTTjGdjWTF#IH14=#!H7#Og#=?o1Se6Z zPX^IaYg-qp*4k=|t+oPgASgi;P^+R;#aitX$M}OA$_}&P|5OP&o8-dKY|?SIoimRo>OhVRs04_QaFKdJc5nN@pdV1;7+1(yixKQ z0-L^0@RnTHNmvL;JL1xFwC627XF9+0m<98CKd;I3oFvJc({moQuU&sXk~zZ;l$4CI zu)j(dDkUqy?rdanx@6AbOzrQPs_ggNWxw~>uXs1Oi=N+u@6K!UhL;Gkosk(lO(?L` zZMuTnKiZI05_T?_&}Ndd-@d3Y*rJh6={a4-0CT2-QgH9Q4fx-B z`+&9X1J=5090@{&lMcwzYg-VHIvGjBEhW9%dTcp|T>VAx-oAv&p$4INhc9I(`N#$kHFRTihf7_h17Z zeXm5Rk(||3hKA^@JeU_3_3m#Kf|VxRW%;gO9uf&JG6!$-j`=7;QoFw{4<#=Mf8TVA813j9hZt+LVdCu*J z!#C$xTjVO+>NR1oG8hCVTz2GjXyg#7kH;1SJE*+OX@$YgN**qS>r(5L!BuaHHEk}F zsF^PhyeVuwy~aw(_!1mgx1K>=mXdM8_{#^By%H#`+qp1~d-*4etl8a&JJYoIb zgVA06Zh5pNU%^_EQUaM9rtX5_JYJD_h;|4nU z^-UV+m`xgJZ@?$~=~}p>or14v{%`0b{!}H}45F8-WX=hr&k51*I7C~oR(HPo3&Sec zog4ZfDeZq{@Q2rgq}>}G_t_Rb*ZM?lJeCWcyyH=^$b5Izr3$`Y+6$m~L(K9|UeK@CzGMSWS(Z+tB7aI9p=9e6A^7^|{SdrYNkcYw2$pRY z!6W}W1d;UTY!1JL{qXya!|yk%9De`0Gi>P%g$h^}9M&JlEjGdNqQ8j-A%_<=`X2^w z?XCsu;a6e~^Ex8HaR)1zqgq$6D^tJuTN)od%Y62WP)QF;6z=82|J6)HJD5ZG)n&Rn&7tWlrCoAmXOGe4wwu zt1NKWd|;;n$64U+`M`_KayX{veggCb5`{dp|_nI|GRch%{!b4c~L92?U%Iv6kKOB z{*9XafqqTiCKNJdg@1Jze>aCQo6HR3$)EHiYdy))aS%+w5r%wMumf3o#aDuuN`o1B zmStJZ)ZV(SPl94(_uMSxNO*}L_~KhCJ+c)OFXT@H_n`cR#1ij&1@E4#DQyc2bhZbA zGeOu#pou_Nu$wy|2+MS$t1Uetej^iks)7!9kRt z@H=#|Rx)!c>l1^YtaRJ0AuFN060j_I z3m4ct*$adF&HJg>>vUzXt<@(ttOVCvg0A2~0Oj));dvIcT1k=JSh^N*4{-^kVTF@` z7#DqfSm!0)PSnh^0sgdj?8C4c$kclrP29EDjwW++G(ji0T-T8AdJe!dr|)8Wdb+qr zLS==F`GJEl`FGU6XP_%H#1IYPW%htThw0BL$I9TQTS7XV7BcASkNVJ|IJig2sfA9Q z%U81)ro7(ZM1~uju0R(><>L#*lu-!V=>Jofoz*7z;jj!{_mik>V1Ri)IKcq-CGa2r zDu(Gdm%*RoMpa$YxY}S`kNizBeB}!&3Q9KtbMb!yGb9Hl1yV(=L+ffDsjPyzT6ff2 zrQXWwwZzWw{Vj&FZR?p#svUaXOZqM>Q8T`xb;o3;ZI|FDXNfy8$Y8{OIe4X+O2_^Q zsS)f{=Bt)XkQrYr{w*_*$jE%x_0jE zQ(O%|{>Mb`|E+TsCo_+jgFQ}!zgrlTaGS{1ZJ`Q5jL<*BZq`WMpY&n>y-bv@5qzy-Qn^bB0Gh02KI z4vbY4fsgM#y2ma}Q} zZ^|N^EXyC!#&X0jekYVQUgvb?=>CH?)$b>8E<^94u`fifsZ2sy$YEyzfT(v58JqVmbG!q$!cFi0mQ)?%HD2dotTXjv z{~s`qhqQB-DfP{i<`X zXVqy~>Z)@wSXh%R4y!XK=vSS|O5U$YUSHO)I%ipeu3(r|CzKk+@yvb$gW53a6S0$n zVHO}$5OXyx{@-hgY7(sa^M9=A!mX{QH+H$2P6Z3Asa%*O2(`~6-!6s0jY{6Hrk~i< zJue7@_EZve27xH#Gbo`{?N4Pgpe8c|RyQ?5|oORnI(1KgaTa(mL;I zmSLNDJq^vvj#o^6#u=e#j_S5;%{#M9#%cg<-;CR5QWBH?(Tpbox3UM<_l_L#-d=j< z#=Z3BG9%Q_mpDS5enUtoHaHleZehn+A3|NE%q(DeLM1IhR}dAT%@Rs|=YJ&B zm>`WkNQ64*h5w0A(4wuF^8N#@#xO|ax??Y`PT!bTmku^s{q!kEtD!SDqSg4f`_XEI zlJ}$4?Pej!(`pw>&=q`wV)*|etb1h?`~H5Z$hiut&CPbf6~z^ z3!_^HJU8U4#6SAbswmh-$@|f2qHGh4T&o6A3mm40qag+N0VdqA#8fO%^SDhn_rnWA zv#LbRgbJMW$h4A7$3ez?Tv&DfRTcx(kD z-H&s|g3pr|Wu38Lem`SD3*PUcVPJV|x>8SmvRiY2-~w?bTRNwr05*V?o>CZoXw>So z@A}vZuYC@wm7vWM;Qt{KciJb^%{E0Fz*mtiKhkU;zXUi64AF1E4WtO zBqs!mWsjZpZ}QE~=R3Y{K5j2wADsPf@{P;q+kv-)&fT|OJ1pxK0lgW2hc|Q;q^Zko zJI72j)Y8k6MzYvpU#_bYZNz2PKO})ujBkX20c92S`dSTD& zSn9Q7(6hv9GGYq*bamEf=OeW<@RQC;O>xv;1OyK8<}A!%#GT|@6hA?^3)dHG zkqs*&ITmF2ycUFm13OSP;XRiByylpw z;u6vpOc$4 z+$?(!Wp@qC78$pS=5_9dl_E6GV;Jk}X|YGKasJT9lx=13=CvW`?-_Fb-hb=I`Rj@{ z=KND1&vQOZj{1ACekRDh;wekq72F2U{(OF47}MT2WYm}$s3+s|qNKwc`0ENO z9!Gf(EhR3oP!oy-L;0nZC)plmSP~1tn=r0pSZ=Q|hQ-)TRNR~M6nvIsZPmLoQF+S5 z;&}1Oc=3XGY~h3tV$ZbpWKK44tuEt9Cq*vvwnnNPBcDEP_+wt#_>oO6{9lyz67n%C!tNMkA+-ekr(> zed;|s8b&?k!YRtAsFRllze}f+@0!Vl6+!96vXOmQ*YOW;Iu^%Vopg3aVQ^%Y0}fJS zE2GAi{i^j2Js?-DEyHU4pm|8n!)>E#%?h98@b+~wk%;~Mga+) zEh@0Hax#zlg=?b8g>Ob1wpu`hFiCFJm7M$NQ9pY7#aWNP)G%~m{bxaa@yg`FFVK^p zNG=SHO!5_b!J9F`rUxJPaFfT1;Qg0{5@LaMy6eQwY_=#bq9VgqbHE&%E)z9vNpJcH z2`$H|ah)NgQ}Q!qw>+rOmE7VSe0!QU-L#g*{WrRQD>`J#W>_rT5=Lc4mDqJbM5V&0 zkEU9#lFX=qJy?A6;jiSwx9B;S@GHag3-jScJx3EhF%180KD=0$3FX6Y$cGQ;*@5s& z!t__T@Y2kv(z?v0m3TN>-Rp|~=_ix%p(_%xH@z85%IUH#by6Bv1c%I4$6gWKbGv)~ zEqq3QnD6kM!5f^&Y50uvc1I9|SrP1Vi?I<0xTAEf2(FmpHo;1=NX0y>7SH^ojwn1Y zF2DS9*&3Gf5AL_0Xzu;HGdBE4y=3Sb+J)NOI$P>xz-TO=UhO{{*WW1Nq;zU4p!(vQgkPqJTtNW#NQUgG}0vMY^8vylUdiN`)^9mIYN3j2{`0te(y9LF0;q}_Ggj9ZFf|)nnb1U`y&&@GpVd1T1=oDBfphT| zd!1SV!S(q&*cjEF|ITMlHZ!!|Rk=p->5{v*!R`I6tH_)=W`N|i!bOTdLw0467Umtr0 zFY?vowunuM5iBJsdy1wN17QDBvAL@@80-<&dB*2f=c{fCYTwzAO|=au@ve0i!+g>m z!=z(eQm^wJs>MERQDQc)4Lso?8sIL|c~OJz3O}V3>x@sQh0_YbA9UXb_a3MphrybR zv#Ip#$}0Y&!J2<5MX0yO7EhiOpSu-cP%h8>)WvMJJ%TeQ3#22T^Zyo6_hrJXwQ(Ux zxmsruKLdY_LcF?_!MYE63k=?YWVQ(__?yugH6Er()Qn3`K3=)3F>d$_kw9}Eo{b!a z$j3u_Hi`1lufuz;t&(hANcyg=y!O>Fc`y_-yPxANpM2OeuqrTQAFHbwvr9__zuR-f z7p+x5r_`>ix2Q(Xfw>Ud*IdIqn%I_-`TR26EU=ooEZNAhFq7(k7&F-3&1rXu#h9AN z*wVd$Y!q=n5BmSK&$S`5br+HF9+4(j+3lZ1_j1W%D_W0Fjw$&&_jEpm%o@qhK@I#I zd{Sci*ver^@3Dpf)ybMpJ48UIdT>@v9?pF~z|Y;Ygo*TCw*iAvMa3&P8slCT-^9;c zk>!fdMs7wv5Up*#XhI;ItG(u9!jSuYsIroLHy72L`^+#m_2~>#pAZThm-mc5UycJs z=d)Zkuj4JW)NJHz?78_0Y_UAotUvs)w^`|eqkbd`{wnWv#^;r)!CpWWj|R8npPtB^RhXzbul%Y3iS&7- z_mb&D*n+G}5*Ou8@~P(B6nYh#qw-^sZcI%2yMFFLaX-*)dL66%k6I4vSlYVFJn=v> z_FOB>S8NyfWXm(h7bas1wPWqLQZ}fuUtb3!9wtu0kHKHqyP4MZ%TRu8GG)7DHXR4& zuH2h0FiAai=$dKomeUPv3-Cu?JA`M0EVhAbkyg}zlTj81<;`+JX(6OJ_G-&OE($AZ z9ppX4PQ#P1Metedb(}`*{C6$Wa`>L`$NvZ{cbXO`B3pD-6l&fP#JQ%PUufyssK0MM z%-um1*@=MeBBC)poeCGI`Jdl`PrJ)!gGaFlxRrzi!R5lV!C}N?BL`ngPCxSS*98TSPF1m?aRWlpxR6N)szlj` z+=2J}l9pW(8SRVW3K!C<#FZ4HmAfe%1_GQHfMXyGPx3Dd^B?B&?`(OAw7j5Us@0Rh zkz@~+?-;2>=_;9~)p$+A&MB2LVikEUYIJ3vLFTKxxN5vWrQN$lm2$Epf{$e_{& zH_Z781}y+91&k-)2m zd5hPyaHfDNB6bqOyhnqjEv~J^yS*LeAQ+!leuCElQ$nyEEp}0_xu_3Z)YTUCTNm|&i(2QRF14r`F6uWfYQ2j(!=fg-s7@F4zKc5AqDHx> zAG)Z&yQpCnHNr)m=Au@(sHjEl;i6(L>aQ-U%%Zk%QA$U>mb$1fkj2@^d((^>B`)eQ z7xhnzdQMU4lE3qTYMjy|wYVyO4hB3h$cX;1^@OsIXNl;KD3cvel{4!{rU|GfFjK?0 zbjf#Uf3F>^gpZ3yGLO#34&nYdQVOI#u!_jlN=w zOx7>k5=`wt0il*I`57R*x~J1+AD3FCIHkj#|DGE079lXw3RkDS0Tb;1HgX}X(nAhq zg&hwj+>KrQLGHXY%pK3GH#jeJdCD!+hNCifbTlhg{sL`M9%?blP4U z*)kVbL!4h01JOQJ`UKt1svcjxLh&Qw4wWs0;|v6zYk||D}qZn{IX+#zqZZJ6^c^ZK8CMg z$#|v5>=gV!yBeHfW?AqTf-^^NmApBb+5VWGk^GXRts-j@Yyf4~sJE>HV7nMjE*SEWTxeof@Q);_YUk4~iXKISffM!~BSL3!w! zMnB-Lmtm`eec4N(G0N*m!U{FJKF>JfwX>rc&pwV_pI443!#+2KTw)3?-29R_txT*3 zoJPL|NPdo+gNKbrPJ}6SoWnAbExgZC6+O&*L1tpsYyUHENo|QCleUD8r%4Fty;nvC z#oz~2)wE5Ghc<+rqY(d83dYz(lDKV3#aoLBVtihO%Gbr3zaleRYb$()6$ZSsm6i9XM!(SphZ{Py2&WU&RKa)2 zS-g%zwn}3US~b0E_C=@e#+Ly)Udw39@hH4wjxxCxw=0cPQ6SCnI9+vx$hsSCpFX%=;#o9moy2@*$gNXJWK1aPhcgm zs)e-*3k!|`8*0B(4kIH;GYC-Ajw`5^1sr;n<2rGYFQ$uba%zJcqZ-%<3(e|dT@w!Q zuh~Vf#S4{;u2e(ufId zXEpWc27j5I*2Vc0&zedR&%Th;{%f^c9tDNta>K#6&m9_rbQa8(*~m{RA(`5ff{#nO zdpdTdTFKOyWI<}oDE>LO)jzJvdp$tZud0lzFnQtqc--j@z*CY zt2wL4lb1?aY)D^7n_oC8b8Hchrr-tj5;{{mEv~MkQ%kkMWp`5dw(PSy#*Y-ki4E>1 zOSin>WxkzGC=W4C*!8jJjIBIGmX8Ff6EImdoKtJ>cU30CXG8dG3ZJKh&r#u1948p+ zN3_Eg;tVW)t6KLDe#f2xXO%g`ke~pHE+|*Rr}7ECcC2<*wcPzvk`rdDQXdbFK!t)s zgTd(64gPu2#@OFlcIYoFWF+Mrt`~3xaAK$R(nQT(Su_F2p*{ZEzTSE$%(P=hg}>2n)>Av}uIwT|AUBbdyAx=mJ&xdK%xUg`r5)Q+B;B`cq z>=VHGL8P~-90<~V^Y??vtefv|cF*(N(RV=C`^2Npn(KF~1*a=?5_`YmnU>0u2EV*9 z_Scr3g|K3TsM?s}U^bkzJf3}CTt&>h+1FwZ0hh$WlBq`!*999Hu*WY7d5=`0RhJGk zzKlR5m=dVenLL96N(~F#P6aB^tk^GGK}r9O2LHI)#@I7n`-?P@e=<`G?%bidjM>(< zd{-_8H2)BQs6j9zJtj#dV*}cbVXvJOZ5tD8%5laVoqKe@Rw+f=@stQL|^umVR-v zcvZLOgmTb9A~mVnE0oA~jGI~NLm8E-fgfIJod|Oubr)5uU#$v9j^|wjPZwt+&oli? za$H>|(xlMz$kJ@&v3%GmF04Eo`CUG2lnaYyBRA#4!gN*H$TbQ}kE}?VP-F4mUsw|# zHDw=eCEg((s~x3%)LrbO7TxNLle)ikY0Wk*gK<+(^yHU^Ex^dLP;;I>Ivkgz_Ol%& z^0p(v32jGXa9`ilcEn*`P99$LywWC%pVmTQ>_Sz0W0O*@%zic{X7)}BSHv?X>KG~P zbC?{s2>kN#QyM!yZQCJs`S`20Nsf6cnHgN0^p6>x%OLd&o`$o7%5J%qdo_eYf7?U61D8LGevvM-=!fa$L1u1YC*FAD3EK|a2cX~_S z8pUl)Cy>bQcItP2#z@-xZCIIZ%#oQ4A{CG7mVx-Aqv0p4W^4|-w6cNwOoWT+{VfN? zGpDYHF|v{UT1c5Wm3c41W_Lc4{_zzYG=eV$(If2Fqv}Fiiy;bqgI%S(_Aw&K9D!T|^X?X6vzx&<3mn=Q{ zv+jjo4Dg>=80=oWa_GB5@2gxuys%IDM`KPnzG8kil}h?kWnq!Zc8>wvB}8w2V6p4* zVdXGdFx`NQ!Y~=F3sm5g^7xJyH*~Z92it(KH z-0DKW2zvcfU;b$o(lpz2)F$R?UwOi?QDKA*xXD9qsca(*6VX&XxLlIVW6|rz^GSlUQ7;HhVz|A1ETs)Ocl3~R#}7GooXC`o zq}7TY^hZ>A77M`U_)&E*@y~VV))b7cVvJ<=@XB(!k;CAFK6AXVj z)MUTgB&^&Gm1igXYwx2s>!8%y%=TL)ygAUC50>vZ1&qNxOKj->4*al0^Ljb3=hUU7s#e@I3|2vOv?j8#QV3kLRv&ZWb+8D-5D4@$5G6 z8MZt6DG4Z7#7p?M8GhcJvzZVS-*!L9CgF}iZ9V4P@$uqC+_4PV*AM`IDGynVrMT9V zz_v5}qc3g99L@Co_|a~L6iU`Xc+fk^dhU2x<RN(-6sgAS(Vc*5qbqC@{<*2{f|~6+}#JFp81i*Al=BZ zo|tT8j%)*xcZA$*AraEh{wj=1l3OcBW*UC)cy8gF13v5a-{rVS|49j{AZ!g@u(y)= zah=6Clr=}*@y1j31GO$bkYE_E%JIhW68p9u=32aB{s7Vkzh^T`&pFI&yZ9<-ISAm3 zdC}b}3wXlt)kE7&#&T-@mO~f*eCQL;tuJ;V34cM));k z-f5-WVjh19-&x6RqCj}v&{ulvUpI7RJhiAWo_eBC*jV>VFmPAFq5cyICh1X=-C&N| zP>>k<7^MK0hr!Z7YrMpR@xr5VxyLHQ;_Yjl8 zS6UCT^CP((jbkK>TKCp)r`Z-}?~|iTiuR+lY~)BPm%6fw6W5oFtP-oRFrfR>yoZ?h z(VsRQT)W?EL1F5rheXi@f*aOLuRaeB`Poc(Lb=noi>)}C$t5BrxZKk!V*^~g$>B9Yye!bCxDGZlM}u1 zd_>0z?++M9w;Ny1q2keobZtf49Zt>^5-v-9QPSvt)EJ(f?R983Y-*l>VyHNX55Imq zoQYjn^cl-}tNc{4_TTswf`H*@CAaL*mn&;wrKlnDD-gyBCsA|RcyGot0BWG~*4A-^ z_>PI#Whg`SV>r}K%FxlHkq%OZ&;gDr<#b}}lnkDu1hFX_ISQkNKST6t@HvpXj??Ow zPNCVZohh(2oH;8MLOhmCEkG{3GWlI#bv)m4RKh=Muz&3K{!!&auf3W43IEvA_|Ptp zlvSmGj#V8Wy0Dl5w%&(ZG*nkfR?9bxcA#ACj26CLXPhCwf$?fB^pa4o%|_-~LxuRB z))qGdMKmvr@ShG?`A!yGjoysDe94 zDvJ`%iP=mONAjae+>dfPp909m*HQW^C|KaW9^^~H`4lOzH+TP%Z0P1Km8GZ8tu81k z2nL@=_|P>S-1Wtnvz|5OmWz|f$w8WDLNWC`C-=iQnR%oKL3~k7T8j~{!?~PE_ zB(Hk@2CxsNoe9Tfkj*`;2e%slWf>13v%XTdyEJLXU4h|~&>X-<+}Olj1dq!n7N?>2 zks--m^;(5bTP);lJ*QWu%&o1Z?AW88_cXg~!B!z8OD9R)APPW@Y8&>HNBz7Y zDn5Kh?ZA|g6dem{y`L@#E<3{%z=|ZJf{?Megx0h0GZG4!TPjr}f5|u4rbC;2l!oe9 z8uu55&DSWR%8t~>UhXsRiO>B?yml(Zij|}Bxmy+heOa)C5Y<<gUfP%pVL`&bZZ?VJ2UimrhTth`4t!N*6P}#f4)sX!(s%p_ao;^9* z{X5`V(UQ$pG%vvv@_hqBV=PLXZhgREc|3uAu>3xS83PK3Cr-%+O*K(I~x+xB3D>eC);>t(}r?U5E0dfVe_ z@cD-7k0pwGQ63B1BV|H0*B&ngFP^H(_Gu4}_1m3#xI+yeQmi${S>y|wgW+l!BGfg< zqRlqPhvfSPeEw=t8#Tw{1olDlb&W7$4p~@+2?w~`9HqoIVCb47k7l`&Y_2)_qKf3k zJCmA=Uy|ftYqmT5f?YTNv7J)yA>ZoD>Aajf{4KCxd>orwVNcmQFa4dRggBQruovkD zrSa=Ei9s?-h9lgAIBWK~1&ZOArk+7g)TNJ$ZZ<1YVyy&5qu)hdzJj1k6O`2A;fu(C zRc|w8Rr?9%iG9l~!!VpHGZ~hN@F_LV1pf?XSnD}~+Ixs=*bM19c1i$Xl)(GTa!6S5 z*HC=NYDpI2o6}o3uqcwXAm#iEG#+VRfy8nsfmtxchl=Jj&v+a0SDdT{HYqH=grPX! z9Z|?|FTWZkjs|}r5q)StQqEhMNa2mSh+BIjN<$RrWfaK3sB&}X4{wYIz30{bcpS4a z9w-o3n0+boryFv`9pH-7Iyoo3F*agXT#-!2SYLnxlBrk$u80!3EnD#@cQ?0$>rjle z{SdP`fIGT-wHX7SOQv^9rq6&+@Mz1FB`ARXcVmECvpcu))SOoeRD9T4N^EV$-C%)4bPP@RLPPIOrmA@kc`?3=9zxdVH5llTw}=yNMM1mB?40;Q)kR{|Y}R&A33 zvUapqr<&9?OnV<4JPuHlh4BonT1~l+2crKi3>q`o;IyG)9uOSLS!vc~0T*$)UmBxyDhbNT*}%ePgSj|&YE6+q9f zMCk@uK2=E>alxC;2fC65eAKEs!}Y_`lg*9U;e!9hdD54V6Fe#JA}eB5h;%lx(1IgJ z%$Kf7hRf;*r6xOa| zPqvma2}*rAcJiD45<%6@Lwb(*=e%>z+2rhQ>ga!*h;>altbWX@xc|AkjRNkGrh!pGTaingL2&|V$Gc7a z^6ZuY?W^7DmO*yg z)>B12bA)wmzop`+?V3PvCpV)joXS8DP1^E|%okpJt%#qxqLeB5q3%_8OYF;QZW~hS zcv8qL5P0pMQDM4Ea_I78>dg(w)aRw%^>10&^5EGh5z%uNFAMW2g57$f_f+)ZME|Kb zI!no6?)?|_Xw_p#y=6ENGRU#?&iPJqc;U8-PIU~O^EF0|1(KK3) zo`iFEi>fzCeRFXvXWfcyV|hnMRPB;X#> zb0q&3_%Eu@e~*~QVXK0miZg1g${uxn;m3lc%orbXTy?%xTD zTB32kxIVRLfZtX8gg0jeea)M*?2@J1_|FYn$tCF%*7$bjcJXt=UPwoFSiK42IqgX{ zGSdS~YG@utf^6g;V33QJuU|O6PkV1FeFn*e#$L1QiZ^i#I0)jrgxVgIJ;vY~W3{~G zA;RS`6H&NTA*0!8y5uGz%qbwl3u#++2^KwIyRMjgAUeub^bd*azDW$jxH>@-T$zHK zQNYFrKR8x8GafvIh!24upSM#%k(!TQTv$*rRhP0wxu=+kP;61FdZ6~P3s!aT9kM5V z=jSWcd%SeDYUeT)4Eadi$VFx&6UndG@#Or7pckic*q^I0sF35~5q6IpcttD4u z0`y-+crV3y-4-l(U&*F&IIMYLP%2Ar6+sKk!Uo$;0IT3Y0Ly6~Ewisro?NLbn7<+e zMQc_WKT2fAlwylD`|aUeS|giTMEm)>r$Q=Ob&ueRXbVje+Vgz{?a{iM3#ub1F6%sX z8hmtcF2$x64eU7|1Mz~?`uk0w9i(hqE8E^=%S7}|Zf8%hY~QwQB|ZBp(U!!uV&FBu zc||w+i-QtNF#zo-ksfd~!!a&?1^5uAmK1d#Aradef6fOKNGhYNFh)PWvfLq4o%EH#tL``#P z+&liOcIt#r1+DKTnI8eDyhKGE-$wv7+} z_yLUWEyJXZMJYY_5Sc~3!*lt#A=+h4^vycLJ|YK)r5?a1m<{F<=S*Zs7`T(tNa`;O zw(c80D2)I7XxcRPmE|c3!`>#$EH_5B(EITcydNh<>;2s99I~!2hALNZ#q?oGKVB+x z)=ENn9%92Dx?hzaM{mc_!h3b_HE^xo;myq@>K)S-3yksqwD;DA5NRy5P=H$U9ArTe z#Ced&R{pr>i}`zMOohNGFUBD2NAScIQ8hUi(@}g=HgY~T8Erv9PLsu5c$V}>mTIB# z6kU4OL~PVb!0s0;DK_AyB0n3YMgEy)#Z@Zq#CwA$1<(6*?T5}lWAGM?JqL!)J3 z#7G1RLSQ(41<~NMI*=7`)`$|7m8uQ@)N`bWS6!u!MZj=>WI2yo+@4myM-YR(_Ow3K zE`)x23L%-u#n`&%;Yhy0TtWjf7`<2m16V}*}ZmuiaNj^eSmV7m*dsFVx#U*OJa z!|uK2Ff90m1t=4Cp$s1~gJLUySA-R>m3_0#g*{p8flvWs4) zz0p6VpFUFC-BxBz5v8m0-CJ8L5l)4sN7 zaF2`IC|`r+>->g%ux>E^8}ecG3{J7Q+tq}ibmPXwGK>cYg-K{$l3c&JBwL3`tm(@- zHlGgsf4!=6i;|n90Iiu zobFY9vCbd-tW?Ch4l-7)2+mPl0!Z&zz7BlG zUEDkS3-5r>f3!twiC49-XOIG(0OAs_%8IUXMXQ6Q7LV$S-%LRb@k0n1mQjag6%fix zE+Ij%D(;XdIlmW)lN=IV!NZF5&R#;HXDnRjjam}SAtYBaKy@EX6{!KsAm}g{_WWFl z*z~mRplI4Zz$LAwEbK})PmIU(>GLEggI-bZy0VtN&Bs}2-SU48c|aY4CSgOjAvAy?41M`|>qR0iWqv3@Zijix@e{nqj-PM@m_ zn}Q!4MOt~?6=^gdUciD&mxP?mn3C_a>=O|jXY$rmjRjmI&Ts`7CSFIkh=5#f8z6Zd zo-BCM5fb9JSZ1L*T#$4C2UU=JziwI!Znw#DvAe8Lb*x1`E}yDI%d+K3pVegfMeKuy zpzN#2lWnRHSiM@jVBKOLI@XQ-vL;DwvdK|YTE#um+K2^h{9`QPGOGqX8P)KklZE@N z$_X|Etq9HsBUzM36WUPuJyoXwq=VE5wS&v^qUAXSnlc-yRuv$~Xi=aA(HI>3Yb|YB zQcQR{`stCY)v-D834`9O%1PxyeFdt@C@?DsbUU@em6>q%I?MviV2f?gavrg|$pLCM zVXcwixf+MBqhwc5GhE;9a=bBDmUm>^P=f|PR!bQTTq4PGhwa2+{Eh}6-6_AQ^VqF? zmhDzP3v#Vc$pY5EX~Y*TRla2LR>9MU3m58Ox|y}aaOR;HP^Bk`Z$dqR4&nClu*M*J ze|;JSH$5>bo+fiqocquHK#LXr=USf}wwlv@WVU*m)B!DoLtaPJT1X;|=^`{@-;0%7 zrO5Thix#vZ*bSmERb}?=wf~ifSPv(}v|bon*Lti!T_^LWXO`;s`f+qfe`>YfuN%*M z{g{+KVvEqecW1_o>@Wio{6aqMXi^mXO`=u znPQGh&m7FVKh+AmPEs)L8|d_P5`+I;IfGTsj;@?t1bQo#GcY}Kdp<<>Bz~Mt?lqcg zg3x5wr0zV5?rcjTPClWU&Aq<@rn!S&XPp_Ha;+^y&@Ex@e7Ah~Fg_Lxdmo07QYSsR zflebXSOoz39a_X_{>L`+GmigSY{`nU1JvXeVtrpq%5eH((${zFMNVm?zl;mc2Yv&R zXLxhGW@yW|Foi3x^qv#2wZ0I??G}Lr!-Ls@4sEMFPnqf$)#P+3;+fTyf$7*4`Vz zy3fHH(4783Y3i~4K%!7cQfHM71PK@D}j*t0E{LH+>Xp$OC~WQVSL z^Iw}Z(xn8-C`mqqeIb6@I)qPNOVDr!^=t&y3lcLxRSF~}=71RPmH?UXr~_fa;C__o4aXw*vf(KP`9}VA_h81(NOXEO-D`SL;f>Cm`OXMsXqE6 z;aGMz4~Etq8`5_weIk8l(miT<MPrh+aviJw$qq`|o+z-pxzW zclm_LaG^0=?BdRBWOl7hTN4yltzyNKI3XKz2%Vb&pcHjh1vcynU?y_)6_iz;^qu!< zJsWLy3QghX+um{KOiCUbp8F3eE)>7E*nO2#0q0$FrrD(&mc4mIHOuZ=L>mx{>|SS^ zX%H5fTHK_ApU%;~;Ztq=c@&~k=g-q95NT5nQ6@@)1HVIP>Ng6&`BBZsLkc3H8u`UM zbYVCr)ut+zUCKk86Ees97%rt)lmK4TLAM%PIm$ARBcRZnBQ-%EMW#Wt1M1pn>v}_M zjn9M~C|9u80v&Bqjm4=s#!15?2EC<~&3<@&+t#5K!O?aZnEZB(5XG;>$T$JZb-q7K z-5U+#a4LixHR*n*T|jS&?-JmAje8UAnGfqt=Mm;=?%G9z z5p>V%029r4Q$1|8{CjXNK)HpQt(O}$&*j0Lylqj`Ur`%Tp;iN=7E+wAz(%^Z*5Tr(#l;_xD_iGx z*ZFG`H6!M(6b4vk_WuKK4e7RfTK7u&&n_wXC7;Cm-XuiN!;z8hn2v}_U2oKVP4 zJ@QVr9Fr@D%axZb<8oOJbXhvXETv|!BM&?kP~%_)Yk^{vTKdt;1jsGS^-8z1Gx!4l z)UtHVH#mCiLcj;F9; zi0?MeaGae^;=T47but_b@3&_VpZ2x2^ub(Kq`j@f8~knj9qL%QWXzHzj%Rh_0QFpq z)Am76Aenl*VJ0_u#W? zhM@Nw#;j`K#Ix9IQz|$ZbiirN`&SntVDQYmKCa452z7k9X_pibkLF=)jfjT&iLApP2OG!z3i-(HHg zhbk9zff)MC8GKY{Ba4p$6wUx!hx!M0&{qfw7Tm=q5~P3ZMOL}6f-^J(a}QL@!3D9` zy!I0imKKagcY7|PyT(EzH#I2b^5ASM%j-B8gk5>3Y+Bw$DzAz11ddH!_f>WZf1Xo$ z@|fu!8twd5xifA#kUy*=f8ZLbo2so?vu*Cv;8a^Y3@|9{9f&jHeURVyqH zsOOCXg65w|9%1V23NEtn)-sMjaMj;?e=0{ebd>|lK+VRIQPv)#`rnEvZL z(Bj&|@I~;}O}2Ju%=$o97in)YE2w^UUPC$ZI9w(( zx!`TxPL(mpkC_R9$xkxVSy@YDnEt7t9T*@rlBv)pv!JmzZgv(l%OJRkkTXqe6^sB3 ze#1IO) zntw~MuXXc1G*`6blb@6_+0Ww%c9qA1zoh3K`I&|`pmH4wAw^53&0(LiLS5|b(^6S2 z3ptJw%-OzZ;82WnwYnlfv(DSEJoZvcHAm023{8E(IWHHm44L$|sLz~SL1&MrCst&e z+L&ZaRik}en09ZZFs*b&wrPCqDX-%|ay)3+u*z4Ym+XSHp z_pJ{a{CB(=Us2}8{AttRqWC3S%B_SkCI~mMKy$e9%}%BY!TI|2iK=*T5LDn zXOa_)2eeKE+r~ak7w?O04#jns7>c-F^`|xnHHFj`LMwCvT^=Dy5-xqp#@yBYrXy8} z*C91nyfckw9|RWgnIjiGBQg!RySK>?C~0=c90@WTanfjq-$wl8m?Otg#w8o!{mY)? z8~h?#xetd>qHF88&DV1??i~T1W0z5bTtCTkierfENlf+e<w0awO`6-uw98T0%&@)o>5wAn50X{4F*7nSsgC`Y(;OH#Yb6rV#m=?~tQj;k z14L*2<3(ofU@~m}L5KqfNE@CdXQT!YI2GvFvTatXt#Q#{rWqF+1aKR+%$|e=Lgkod zui8*0mf@Wz^Jr?*B!6uiQ&Hx}vo90xDFkVAYIXo5Q2!{Inf@-Xd6WOly%asRvZ>&4 zvmG!zoMiqi!6b{h%MKAi+ILod&twR$F2Px0rfOMPmp0d2E4zYfajYv<1uc7M{dqIls_jzx zpqXqM)2f4iRFV^cGFrF@E)%4=0{z(=oDXn_0&xu>l708X_>$k|s+K*(v0564&nqBK z>j+E`YGpi;?e+vx>bMxCDBxb#%#5uKyjxCS*@UlPU*GMq5~&%p`V8v*^&8d;*d7F= z)M%VgA*?933Hd4G-MFTA+2b^>JyJoI z;>Vf@(zpgEDO1fBtu?ugTiDhdt_HaX$jxk%irOLD*GF0C5bK6Xg0p^%{;0uFWlWx_ z(O5{&g_M1V!0TK6b<4yO&QJ}Q8 zrn)e_l^sNAde9 zpS#Bw?(rp$;M_e_$+9JS-}^68Yswzh`;Bw6*?K=C+SSLFV>kPZgicc)kN$Y!1(vOe zhcxBJ^q-ZoF8ycGhQ}x#x`dnA7u=nVJoH`C(?>Zld%4%%uqBCV{wy7Y`zO2^-{Bi^ zb?BkZ1jIg=yh}Xx`<#OEujECa%CK5r{AW{8Fsy#sz%tqW&dQ)5 zHb^*PS!Uc7E~81FGp8N{;(2o_Mm)>VNd|%L>IH4DnMf7MW#;U(I7@sLO)sd!1(i}* z521Loqf*0@h)n>hd|$&-46Ii(%ev6GzrmZ~eCo&~NVQUxYXRpow}=W>t(DY+GNbrv ztFBoIBEK0cgc@ojv)dn+wnQiXWNQ`nk_|!I@8l3zLtSvMd$95!xt9;1x`lkYZTPZw zVEFPe?O;)YWG@8~2WD^xt~7j>^9&|LRUR@%L|X}bJ*7$MIo+Oeh7x^dS$tS6rryq8 zOq4Aw0YX8_VnK*?3p&Mehep0?)uN)8o_ZBMStM|wWRZqG0Mx=V*8mXR&_PpKZE?rlb_j4oC3~VLlMbM3$V6v6;3)`w{zG&|&~9I1 zfupb`bXtQtw6O37j|l)YtTF;Ev&NfC1>MWextCpYFT3YnhU8xM%)RWDdqG}_<^ma% z@1d^w)TD@to(*8zuKGgo>MNWmjS*O~< zI4zaq#P+l}ToPF@T5PSTacYtM!gdt5g^V&zaK+rlC{aslua?UI=9j_%c>Vb~fTIKu z+lDb?g#mQD!)qe8I^i9&x-Ry*7)X(m_KW+RXn=v8Q;e|;4MNVe??Oa8dt#La0hesT z-|}@C<*?A8sOr|XvMngwrF0EP)eCJkX?@0IYVZr1MDW$A!?lrG_o_+hBPK1p&NZQ) zBOwzmfph_+`#Hpma*tL|n@xzspq`|o*1sL?YE4TKWvHwx%B;;rEG-jA(L474}7}Zzuu5qbNR=N!NV*B*nGs9x1k~$mt ztC@b?c`NzUePrc+VQLkZjoeM@Ua2-}AtJ^9(|?p|!PNh6rP^W0!_7&x0WbbFV&Pc6buHiNdTK1KsFqO0Gs~Lq*~K|E7epW zSZq$J-S_dPQf=`L;=2DJ)vlShsZ^T{lk`@2bnew@bGa|nM^=kN-O%fNp2HShlrANc zCe6x-H0|8fl|{+Whp#+bz0thSN@7C%extvJE4wg`wQj)$EgeWDIng%hXFPC4@A1iL zPi&7a6zXI}}L+7&FhAETmsz{y~tp&y!p6 z)gemgUy+mZD6#rSnmb9OLc4kk)oZV)yi`|hXu-vyT&oL^azfGtjS3}XEdX`E(ND|K zj)K~dOKRh>C%wdH%sa7h3T4LVXQhT+!FN3NFE%cTmyGdkQLIoZFHNMy*?vqGc9!Bd z7?(vDuaj$pOra7z!)`RLdn)u%&e2wN!BzdKdfOSf)^JrnXVccO@qy72!q_RW|FJc$ z)y~oWtPFTn^v$ zX8cNo;?Bu78Ry~{=Wg*1D0%*LGZ40=zLuepoY*m2_p0~nP@(AW{>vPnO>bv5dP?PZ za*f*l7Si$Fvvun0!d8|ywjmkb<=%mjYk)PWoTQO9-g3!g`5lyp-3VmaeFZnJQ5fYB z4Ug@^Ugw2I9(*}^9d%`ei<(72D?z!XXlK_qua=xKJ&J#ySG5l7TWcNm^A|W@XFu7` zzisyOzfg7D%glZyUZ4c|z52Y#d~N~7W)md1s{1<8S%;Ca;Q`}wE`5}$VK3shR^zove_N9NaIjak zJjCTBdKs%={i%?*-Eo3JzcXcUGOhF1;K%#hQ^8*+$$Z3V@q@Kc$mT~pCJvCF0A4h> z2*5isCdxTawk&sE;%{wLodvlSp%IZ@CCee?nk>!VkgQy_JA>nY{+>Eq;kprsTVXBbPS&({yR zauqw-@o#vVTH~3`I?1Yyy$5w!?~SzU5Id&R#*{YN#JrB$CXIIj6b7j>#-}^yJO|lY zB)97$h>OBRmNN?HVrz)9yAB>AF*4MgiJl5h4f{BOIMuF~Ew&@uY95AXSGG;uqUWa~ zre&$l_3|Ym>^NDOL!Z80+Tw9BKAP*>wW-nNpim?aBAOS*90wJtGtE9Mo2$(@@7iA) zzYo-EY_-*=TDEjf(Tuu!QlHBdS(U3$Rp{GRrBJF#f}_ArG9b!$aL-9j>|*@W`E8uu zDHhVn`px0E0^nSAA%-d+TA`_=UNuLgj@~sFgq?hJuU(q;GSxlo-7<_zKQUP*CLY9! zT}%%5tdp5)a=mI7n$LydIJA=TQjNq$)^Vh6xx~7y)>%o|q-F~>l4o(-?qlh~vvyg_ zu+)aa){=z3Mb99)9`BZr^QwRSU|=S7$}*I&%q5Tu%lSB7*Catx2C2GTU{@5XhW4S=~Wc!(=qC9Kk`cF1D_vk})Vsghnz| z4{ag}`|(MlVJ}j_Q9VWSJt#zcgldV}g{=xFmB-e3iPtz4R%>WlAINp>ex0nB@7%gT zcuvFkI_uY~@K?`_WV6>kwO#dNJ_=3}QLGl2Fin-Pn$*($FNDgyHK`42QcF#|cCC0f zjJeAclZMz^EIJyuI^QUbO$o1D&X}3~?hh+^xCq4Z2kfWzK17ag<|}_6IgzGy1&m~+ z6#1ipUb_r#TtQsex*aj6XaOFfeMoTrGNv$>5>eE8g$Pz_LsGBbr_80Sx}4KD6T6`n z1k<0+W?5OJi~~z%H!;F(Ns^MAc++~{T1oDw`eq6~BLka~7Fa2ajABBh2?bPJko;R% z;jB-+$Fk$MtUHK{%!MG|KoWqX<&-tP|D_FeUKIzKakHoH^@Q#E=>Xx`VmR~RYG^7z zpax@YD!5x7AZja)pm-2_OvrU=tA|er+X^>32Ah6ubu~%hoDTU}rLsk>r-vq9TYUy#}`+(epJU6O=8*HP48iyq`!e|Tfhxp9B+EfcY2%p5Mv#+ z1j?of3eZ?e@>41gp?!Ax@}^$iFFU zt{qv`UeIWiD+huUHHZZ;Xpp1H>yuyV8BLqCp2Yz%@Po^26fnw9b@>l={VOEM@J)$M zZAa1M)*Ks`@F3y6T>}3pl+csP)hC$tdWZFi$pTACvG3Ck(bI;af+95L|Za*-OY5XhRpS|^zgT7$Xqv* zx6n~3o;@jQxp7arUB_s%sCj#*F&ilf;uV-$M^e&w@!w**Pw-&{0|M)3kFy3WMbZZc zQeg)yoKUd8oNAQ2BS)ZvNbqAhFuP^W2IPWNfHq2X_0Irp{*Fc%bmM&e<=0M6{8=Xm z#zVcLEjQ|KC-qu`3{9nxW?UROl|p|H9W*(KgLVqX_;7Cyt@9Li0qu}{!)xE2N^;^p z!~Env261B|-M(mUpHLXffyO@)*_fy<#=W;%N^d?^ExnXRQTithe`a*a%uT}BS_E_z zVgqK_TZumnpFWza)LoG)L>(XjGz^Uc3GiOsk%@LChO=Y7K_p!A8 z8`7&K4Q@mCA06gzzpc|yu{Eu6GHo9*f$iMj5zdS$OEaiH2&{L&{==vSE+!zcJQyQ8 zi&fe*DwHPZ>u$E$^bDz5JGt=VV8dmhli!)4lix{BDRfJhi1D)blB!49i#+-``R$N1 z4#Vp#e=}q<%@eQsD@=I5lw-hAJ)kfaHM1Aud`%b~BUbb$EnZ-i*Hs_xaZ zT5?kl)z!9bhUc0vZq+mr36jE>| zmNDZ=^MrRD0O(7uIi#EmqsXx2n&J7k!^1c6UR%IT-6nh`6m&lSM`~ZTjZWq1IK_FL z7}cDa!v&C@v-sJIpHd;&$ShV<@ER<6JL5q;Ie; zI&Go4+}tQX=@X}F&-PTk-XIymy97;YAujPjVe1=aAI<4h<89wYExpkIX=2O!p|io; z+ox?6o2|S9UXsSS79)_JK>Jqpp`ilT3Y3*qR|BFmX{W2OCOee?J+oM9A1wiTJuC?kZO|+1cT4;I3OL!lKCs_4_WQj3F1Fvl*zY~|`wRQ+wBITA zJKlc3XTPV|@6q-<+AH)tqJ8 zdtEdh$0B3iXp6GFe|k3Zw=$&Av=LW+^>w!K%$CTo)hs=)RI1E~iwNtUq4zN*Kj)pG z)AT4_GHrNM9johG|B!za_?#UeY)ExYXQs0BwDFpDf?S2z2v$LODe% zEVO6OR9j2GMB6G+&@Yi48I+9_Q^n+xlJ&oaKK$|euiUF&_WHIkX~|tFWDTlf*%JG_ z!9Ig0z%ez#zY?HY9WVP6-`U75B%$d`AUa{60z9~mh=jlEX=Kxi=_)dy9J(i>Kgu1z z-yu#Di&~TFNdz|+!T)v}1NA#0m?=A1pbiIAgJ1G9V(L@F3tG3d!{56_=tc|?K&x(! zr-tv*I=s2Ld8YpBQwxeLoD;bUn%U@3+B{;9f|jD5cU;0D`2;;XSRS%RCvDXm`av$^ zHq9fV1()}UdFRu1+u>5!!ksJ+;nW8fJppi7=*M1josrQ%ccNM>V zvhbRtZu3k^FKY8bGHk+RF1sWFD+!*NGj{5m7Q2cpNM=c zamwGGBNDauH8;m7_{g01WwAW`pX9k<(lqRw588@I<8PY2P7$=;1bF*SP4TO}-iXsb zfwZOH4^FJ&gi~JQXhmYFsf{TAlK4&2{d1@pxwsJhomk~hB*{LUrkm&!s}!0O_Q`~6 z=7bEHaMhfULJ4ZEqUeQ8l=SdOuYur|gt8B&q4Hk?=hR+RUgMcPG1IiB> zSYdUAPh%wA0)>|p9#yzq;TnY+KaB^=dUgsQhRgHI3R|o6jtT=5ex~p%g|ii=DLk&~ zZ&A2V;S_~em0SuGK2zutCfk2SVLOFB3O`jCqcBlnvci=LH!9qx@VLUO3hya=rqDqz z+pVjxjl!-9KUMgd!Y>q#RXAVa8iiXF9#nWkVS&PD3SE^v+9>RI7nfx(pSF1`wHzMq&%uBbXC|~p}Rsah5ZzUD>Nz$Q0*kE z^Av??3bPfSQ+Pw+BZW4Rl8@>N8!L2I=&f*|!We~P6wX$-Qel?Dg9^_n%vV^b(BU)L zZUcoa6t+|7t#E+C&lC<-I9B0&h3ggWRG6nQU*Thgc0**lwH3auu)RWWg?UGI6or`z_bEK1@P@+s3T^1e zUpSl;Hc;rUu%p6W3Ii1y6pm3iU16HSY=x&3-ctBbVL4@&brrT!*im7kvOjJ7>{R|~ zslsH1TE3kcZ+|I&{FA~{3bk?xY95JE7_Kl-VSvK!3fn6bzqbLAy6Sv_!p9*}e)kpL zQg}(>DTPNBW-H86n67Z8!i5TFEBb2`Zc&)6&_Q9cDnC}?R|*XZ!xi>Z=%w&Ig{>97 zqOi6?CxvzjpVCh(a1KV2)^zqeLYiH^>azTfx-*L{HIng z+s?)VYhaeE@Pfiy3JVlIRcK#9mUmI;u27@*JTzRNA4d7a88%b0=3Q)9^S30Qf4WG_ zMmi2}U9oezGNvP2d^7J}O#_#^Ui){iM7O~wy;FVjWo^b{`o!3{WcBd;=KF7>V;^*F ztfe<)ZCERW0kS^xWJVUrLRgeK_h+#zp2e^*)(^2L#G{!W@ko3RLz(`FQEn{G;y|e< z(!&IWR;(LxOgJ|JiDD~Ij~BVI%vVs0WT9+0>x6g=3rDTNs#V6Kt9|r(@2LFFt9lzo zba*g*Kv(Y%)^@zHxO4g&0j`GDp|3P$O<8MDYzlhe=30zJ)Mi3&Q3|^%Osy%?GwVqV zMVrxV5crA%hpj**O7z(;wu|268ET3g8e)o!jqwf%HN_fn1=c?@CM>BIF#eN3pH*i!jk`Y3&odVaCpVj@kEAyJWEh;n|hePbf6QhI_jBOAhc zgR?N!nGFS(G0acNnK6GBA>v(xzQUM~(1RYmk%vF(Qyp)lM=33kKEjZefFfFbN(%?2 z0F*Nzoit>^xgW}fgEmo&Kzab;A<$JfXpFR_9UVbE1ZhK9M}>n?QnMLLJ3R$mJ^B+< zDy`!5dkPw)oBn8Dk3VUV{@{)zV*;mftSh)C`H*bnLH0_y)Q*&z5nT60-@;IjtWh2$ zU$R_p(HoOWrT$03lBuV0ir3g8a`8v4FF=KCR*r@kRkCOsMdhytFEK)LG&W+a?7Xyo zQX2J%j?(gdk!J)o>a_`+M1WtSNi{URdZC>-qz?j@G$Ot5JrrqFi)=(I*B3Pnq9=m| zg_0$S3n>>GpE0bL@BxEB#|TOR;MEUw2ZIZeqCe89#ZV#ZFf(^CB3;snf|L#Tv-T*W z5mBEcPkM@GZlI=Fl4jR()VDE@d3pXO&dDP%gg6!m??N_c02Q(Y@>N>>FCi0m{1D0= zKJQ%-Z;9^^_BLzFS|Zh*wS!-23Aw!kn(Yv43v7cLZ{eG4h-~p)q<$?}rg0)il~J5` zLJ0%fj7Q6)56MsQ?~>_MxdnQx9j!4+nq{#Q_GQ({0q`AUaL4?Ww%G;G8%u{QIyZJ z`eUipmG!__{fGs@qxOcpJb^(Ng`S9ah4<@)+(2QM;pi243es~RO3>IMtBXURyue~} zV<0GyRZw{vxBY-Lf+A5qMo{QkY?KC~Rc*cuw6Y}XTVrwF81_N7Lp zH&B-Iaq)DT@98L-PW0u0w?N5tTVsjE*8@o^UlnJqXB91%JX@Z>(xqs6?k;(LMq%;&HI?rD#>CkE-TFe$v@Ts&G)mCCsL$pfj6dN5K8`CK^#$=3*it9|v z7g!&SB%14s9+vd?Fe=DT#NaO$mhA?Mip6;tVkWVsa6`M&ThIuUXEZX2Zj?BuwM`uU z^a!m)LjgfWVOi(PG&z2ni?&}wp>A>gV~4%%?rV$<)yILI z;_Ya3L@BSxxR60ndJ*@I^V5e#8ueH*$D&!0=BFR3H=0Vz2InTFVT*KHfVdw#Q4IW@ zCW%l;p5{^M9dSK?9><_ekdPV^QXwA^3ps0P#3QX8=n96`Lqv;KMI944Cj(eIA&#b4vAl_OOPtxVWC>aw zwG^_G*Gx2L(9s@!pb;q7y>mrhe^;*8A7%e1mBWO1fmh(Pp4ugcqLiL~@ z2T4**y=a}r-7q0}189CIYP5%CQ;T?+vpD{z?LUvM(cB7S%^xhP>SaOL2OG__m51JO*E1MzzThs>0Hy5=w>%K}$i!p0h+LgvJ>C*s9 z)2xj8BW*Og2)C3@Qnc8nJhSwN=$2NKN_=V6?-nZl9%L5?8PO4lKkcCMUt5*e(%WP7 z(0maE9w@Gd)M<1OAEX0nn?|LULb|o2k@U22E49$7n09Cm_)=(}#%&lV(dc!9k1&X< z3pv7L#k~T_UuhJikN8rIZo1x<{*~ywrRb0+7$khdf7S-g$iyl6kD@KSiIGOUzGw^Y zD>;+qtffQe7WtAs+6#`uV9io?By%^Rg|=4mj=-#7vGC${rddjH>(%u8;-gvCC~5WD zNRjzP$EBrzG%jhq9|n7fP`*D*$c?TM{%QOYRihdST8~O=@8#(fmrHRTX&fi0@j@*U zClSTQB&BE?kn*>9_V%m;>eKjf6E4gaUtr%r3-b&Ym?s?pW1Pqph_gSsC5 zPp?n4>&i1W@_9*;x;}o<>r<`1hVJS*SVMW|kDevatl15EpgW1$T?4wGs@?IB0@m(I z$SbN&uspgBv0QIi->IN`BcY&7_de;`fDY??f8^3t23;>`hjo6@`bAsTIJ7QrO|P`l z-EeP4JFKrkC@nyZLM^`wc(%R{B3{b6FGzKY=3C!4lN8I&CmGTmIFch>Rnb+Fyo#gi zv7+_p`rLB2^!YXasa?{nywgY?hSn${|Il9PHDo)Q<;3A0g0)T1nu1nRnuWZ9UdnrZ zGR+@ykHeK5-F>HfeDXP&<+@TUFQ3k6cO>wiO2D&0QEp}SWOV&0rAc>1=t`HK=g|FT z(g@wRmseYiy^fjAB>bS{pUy+U!%%rD@}+-#-zu(Iv8*rWQA_>LL|^5-giy3b2i?cB ze)1tp)Aea6?8)-{uD{@up7O~n`qJ*mTXN~SfwfdwiB_n;+7l1?OiM2)Q$J}9p^Z2? ze;rJbSEu!-e=M7Cwn5@P;AswJt{@kSM)x0QSV?)-`X!0smPNm; z^wR5V+zbYF>()yvEys`Ld7X86jgFj&hzB~ferXT1LsK^CLY_dyz6`~6cni@njcvb;|zy|ncn0*zmyW|`+oYgcHCHa<|+W^@Mby(U$bc zvVPQZc^_R{4;8;3?Xyhg=PK0Jby|51yH_T=<0*!kHIKGvogt9&JL8*8F!rrV6|R4&Caaj zb8|aAH`lAoT-mZexG%e9ujl4=#8d9F>-v|OD_idN+GM0wS<~M?666W$F|a8>iR!wBpcG zM)Kt!9|umtRB|tqpFS==TCbJrWeU(6qa$OmD@aQcEXs{S0kN@B5&9@Y+cy3&A%?h! zSTp}UW5eR3^ljUCM;hZOQ9JLYPY`9)gBWuG^SLTO*QK;}j>IUieqx5*OOjN_kT`v> zkZ65>DY4Em@zIKg%$L(9Gne;{j1uKL)7zdfE-mHtRMcF~N>WrV5PVp6Y|u#3}z zTqk%UKm2vZbDIFHsQaSr&N!EcbvYk!y-a zrkQAAu(?JvjQ(hFrBRUgwXC0aXwR-BM|2-n?p&igpR|IP&nV;_HQ6`Hk!_X5K3x=&?C4%I&DDd&)vUZv zBX=QMo|go|HtDKKyVfJCqV@t|JM=8q@&s0ULM;2)6l;N2Vm)AqQBG^5wZ=VC!p;cV zAy;*@UZ6Xdmb)lgyR?3!D>mZTvPQv1T$~f<#h>RErB^&xUe}V|s0YKX+N1u-l?m;t ziL%O(ocf@x!6Jv|UIXe9N7}AChLI)DNv>31kIwjloSsvrO;Hm9}74@f|xzcM=ll*Lce`M9U z0msLki}=xNdX4DiBMQH{kTfU1-pq*c9^a0g|9Vujfd{giY_#crAg|hlf3cJ!O$Y8} zxdY#`KA&ZKb>|yDe&s#ogPi`;r$x3p71nRH%j~y$ukyY+dCS|iej4FX;q^ITwY$$5 z*XGHByo*zB<+oga^y8Mj*LZ*8T=?%ztB=3c?X`Cvf7NSwy|qmPg0lwMPn+0pk5{Hg zvfbDXKW<*>w(!qNfuh}FYUOs?1?jHA( z=g!m3G8T`RHmvS|Szg<8Q@f`Jn(9W){h;~i7S{qBjPdR~r?JBmkA%kafBSs(o=IcHc^ww`SP6dU%uIp%!|d`3qu`_|`q|8jtjzy0pR(-`xwNLy~H^w%y#i zN5+ZU{y(~Yl+}9C?wM)+HeE+A{O!Ymr@O@0Uog9EY~HAjS?`Qk^Y2Ehv+u0$e69P| zAw4#%YZAU?jjq+y%&jBq`!Ds{xoP0pCGMLptRiPsUOZv?ITR zjAauhexIAWtJ3)7h!Y-tJpp|CqQx6&cNTSD74?lk||9e{p^;!DnD({Rz zAzL1KSEK*y%0k}@#5_$aueiW|{%u;fek;&FHa;e-Uu+asLH#22!vYiDb`SK!{$?yV zis!X5gsB@MI{Z(La^PHNXT$9LY+yd z1nX*5WVOOtA+II!TCiHW*O?P07#-g2>J(OyIiX!AKL>;8hta0BuA(igs2jm-u#rPo z!G=}PjbyqS_N;~pA*=$c;cmxjw5gV@t5Tj-arb6bbY9Gk=-Jyadt zVJ^%yz?r$aS7)vpYZuhaSN*6{iPdp;V0GHmD6nu^*;iM;BC8)}?^Hjaf>V8WJE!`( za!!I*=kn-xrE<53ZG&tEsZiHLQ}(HK3x-)!km_s-G~{hWny6RNQcv*f2!6q{ zHNW*Lv3jsC7qsCLP!aV(-w*XkC#K4*4$|u&y$;gr_|?dk_A?RoGoICQPlEl7mG&ce zK>MIyl&{K218t42C=JjP^y{Zm8Q8b-iU>8mnQ>NUP4OKZGuXJvp&Tr(jQFoOsD` zBKin922?;F?U+j&^wG5vY^g@kG4K%btXG-U3u_D>8nJrrZmgcpl}Sqi5A_{bedwS* zbWk5Us1F&|Z&N3~RvzlrwuAjapU`*hjV?-G)u1mY=*toMs>$1S2UJ6z6LXzzl}Ek|`gM&c4|^+SufjetmO!(JkE>k){j6+*G@IIRDq?KZ zs?2J+hrsswF{gs6`4;bozSXS6YJx^h_sXnho9g*ac~YZNmen9jC&sQ5iHa-i@H)U}FF9DS;TKEW5~i8*aF@@a0vJg1You!k<}p$mJV3%AP5EhmV%g?+-@0s@(v zZUC!OP%FPi9?I9P#Omtw_Eich=U2?DkX_zTF4)!={j5=u)d;u>9r;p=}q8 zJJ`6{?+95fXSD*B!FHCyc2cC@5_1p6O7Xcz*uI^W?R!f*5Oa+^<{mq%xu+}6z2$kM zis(a)a*YC9$~AI#F4susRL+I`xfmB!F)lD}U?Z-67H)NoE3?L?H|-mTwYF~@@P>V3 z_g40ebuH}093lGM9sTYq`z_{-zD(MRBW$HAbKD495q4h%b`RV4v$Fff4pepI$&m8VNZXJ5&)BaM^gm~T^<*&fB1vlq6k!;kzblYNPK`zq$`E10)0W8S_b z=WXHR8)6=AfO)t+=3!UN!*%66Ed1vMUA=(wx_a*CboF$nbT0X{Is!dmBaL(o1Ke~C z-5cr}>T2sOQb)e<)5YdJ(bjnQ=_KfC9CS4nV`hw;PlP|N0v`gujQI;|3s&Dx`4B-1 z_7(uY?+(9DK3|MyD<2`oSrv>ktaVVo4(iu2!be-{&e~@QUTUDxU*HR0 z(3`or_kus@&ZLA;&&>gOD1*Mcp&hq2t_5}TYv#{4DxKi0Pan1|dk4{39du%)rE zr7_Z$npI)VOy4^-3(IwC7I4g|nfp4b;O#{6Kh5ftQkL)YsTvK@Wqa_ z&bG)!*vEYMka_SSbKyg#7xf{cKW~EP*5LUK@Z1XhdEL?o5D5wtu!#!EvTJeGtVWvx}jRIldl{zju+IGD02A5AJTPk*+2y$~E%vpMUNBFgw%yBIfOy(qB84hm3l` z_jHHvK{;`5uXDttCeDkDTeViJk1-yx3F8yXDqV!G=x73sIvBeFw~_xdtE0Sf%M$Te;Bs7;9(0qN^FXhN!5kj_Z!<(1Vz7 z#az*-V&gEkij4yrR&4Cvpkia4YsDG`Xj|x}FMMGDe4#&lVK0j>tXYxO3<%TJbPv_l z)P1UR%%iJ8)X~N=t?`7dIl|UpZ`QUZ=1zO8Gb&)6VTW}_Il0atIY1wEppOzV663Iv zE!M5JShw2N*V)+$pDe~qk+o)lyq2YFIv+b{U3WXNE^pz$T0|^?m+x#_V*Mcc*#I_J zA2#L+8?0@y!Loc1tx2-cUtt>)q-_X2PD3A((Fa^p6lkkOloNUPk#`SyKNZ!nSf@O- zZ4~ggZ6o);Y#Zqw+nUD!t(B_d_x+#RF!M3Uh9%UrVJQPFX`@5&8+a!hmTFb*6w+Fw zT;fNTa=QoPI=Y$-i}=`*_8s2sP*5Ggir!(opCJMC4qMT4G1xF)&`Yo?7lt&3a_y|> z4*z}ckg^B$9-#lecSy?P zzwaHAa#)Hvj*kDncSy-0MB@L~-#au{7ZevW%z*dGn6O3jzRj@ExX=)6qfQu zu_!$@e>JlS8fJgQNA%W z!pL3~btBnzQ7%RlOwa`x;_(aKkfilS+|W>?sZ|)dhG(ilL5Rf0=o2DMEJhckV(%cmQxJCfghqx( zh6Y81#Dql=e{Zv(0igyHcE*@+I+6v228o&*n8eXY5edx4M&d={BswfA22Wsvf`$#& zo1#OoH^?|RjjcFtM$I1LIh;%E8xD#hOvg09IoAN6OQ zdiU%U;M2B^)Ik;2Pj3{PgZhO;#nbL28`@h$yN{3&hu@IX1|xsG6{$a7)n78FKkL`o z&%aymUV_|G9ZJwXBCur?zgz#CdBbjeA_o~m@Y<$C%9s0{wEazJv5yVyJyT_HKE_h2 ztI$2tI#XJwcq3lqq-|7UAKMaNvEkSO78`G*_dCT=DW8xyQ)i6Isk( zaIWPs=uGN>a{Du1>wbxSTyHb6%T4x*{iW*?6+0*-%2N#0$Ha;DdMfb)e!S=kyyQ^2 z+_&dtNuF=pKQc_;DI&xe5Ze=8C9>milb)Tojgo^9^+M2k=nxBU1Lz!^c4!!%BbovF zXag~-cBMJkNN#A~UnqXqep1kaw*ajRT6wwIhh?+Q2?nU9bIj04V{A+`lp^231>u!f zbf|T_2Zw4>E2JuxF6HWrZHCyO2SW*`9P#!pw6a?4a?>~_wn!KAh(ggC-C|7r+2WGv zW=cCK&)*cL1kD0%{iF1H19P>-ND{UIBZR$-WAa5{QeU~Eme?Ulo8Ua-D4Al+i*l6p zSt)_-gd8Hx_ULU@N^Pd=7REA#y{Pk^`e=H68Q%OOUR+k%e#Fr>w9`_RXESV6Ozk}E z&t_RtXzw8;)Xl10x#D7s4>uBUdu$V#mGd~+>N1m{jkeb-SfKvS_-79KvH?eh<_P)xVNwPGs zTm(H!i6}Z(OjKbV^@Ag0eBt=f@&uwKb9`el3+Sk1r>IC+u-G4U7?KE6Wj8Y~W5_UX zBMec!?1ZuRP(LP2F=KWs2dS19w#3B_W>YLJ1W5fjTXBasq}%j0gu(h1d#sb7XePxz zqWY=NMTNwXE&QTW9CnO_yzOitJd=gzLBvl_eM~&tU=v^r ziHVB>tDd2uP@ypdFYN0NTOv(cC4uNv;zMzPIRmZah*Dy9!`VK0hRL))3sk)mw#Jf* z_8(=1=a;ldB1rj|%6P|y#zPM8!VdMJLx@8%BIZuiv@U7!vOzgnd|A@(T%zj1|q{X#iQ7Fmqvb?`!RAq_Ixy8Dh{h z0!xja^k zYoFp<69Qv@>d5a{ii{+>l7Mt7%8`(7;j|JErXeSfkEyJG{2*ZhY&z@}D=D*p`?Gn_ zbR6CmG+Z$>!iu&WBe_D-RKGvF3*N)9Z&9ATLuEBKr5nN%YNTO|bi#_Wn-^nqH2Q*p ze#$?{0SPgMM8h5yGkFdJR!L5Riau_LDOQOd@2k`-SvnhXdTh2vU99Uq)%JU` zYeIB-=!erfOV36U1)4Rm{x~4~Learik$Gad9TO&dHx0DuRg{7dbCABO*zlZTCC~mW zOV=YZDoQrc3geU(lJV4o}K%-@9dmPVsRiIzG>;?f7#bM1&aypA4P?HEOCar`RV#*zT-5H)EW zCioXIW4CS7pVg2(QPSw*)lCV?fqa2~$WXCbR%^*hlt%7^R!}O#=n*DGRG{VSzWYp6g8o?iVteKq#5 zn^EkA?wp_x#p<5&D;G)W&(<-qtDSgN{-qVxbtc%kiAKC=!$sDy+ELvfwL1G~iy;@* zDORpZ;QLG=;%&3hNGtjl*$zmdH>hdvnT^DG050FKEEMZ<4;`GWL9EMS;9vD2@FB7E zvlMx;CU4>Yzxtc@lv*D*FCW@}_|Lrut{gn^`}g}!jygAO(&`PwEt(m{xxVf#c@J9Z>?-nk}zxULd z8N^4v*`RVwr)mwHYdTa9s_78apjuGPYIPb^t6bB$Zcyb~HLKO`6ZCdv%BftdZcz2Q z4IG^7Is`e@398)RKrfA1yh(|hJL{z?D8zGQuRTb*_N`!89)a^KO9YeH2xmFWc}*J>OXzS z`p#DMTcH0>Ua~&5e}ykuAKPrq^{M}VzGVHHR`vfvoWErKT2}Q(BhJX7m2r@- zF7CfMFJQc&fE5&QQH!Uf;Q!pEuoMwEOG)93i;yDn|EKfie>yKx3jJ*uwl*_w81USlHVnUaXH6iS_I+%a2{;{!%?_{;d#ULEK2AcM zuqqT*ySoi@0^)xNuyTkKz5_*2dOIL~bI%I8fhKS;LQ)qSW&mcXIAQfVj1B0G-w^;u zsrVS+M|Dvi`~(7N&(}<}ONjqdAb7|Do^zG-F95ykqa1kd3S6$@gcBQ}ZVwwa3D~V6 zo||;FVLrg+2$W8EyOHF9Ft;&d31~O13FaUK;+eg|*yW~3M_&j-Ud8)*{2({r9fWp> z-vh2}j-rUC0_WppD~0ISLf~lxYWEDV-oL;z(p`boTjDwwZMgskBLpHo7q}WPZ6=wd z0mrsxEFJR1`k@K=eDFy> zZ z?XW9wF2V)yNmwBO@<&_tz+?nUC)|xd`pO1Y!}VAW=(_+HA!NeNQ-DJTARXnSfbV_; z+l39Z2TnpDSrHyW*hw-0wiqb;MK}e4+D!&J1mgdZAWpas!3psk;BTLz&pzlc5bs15 zHbz(vPPHBM~@GUqEinjx%B9LD4fVg!n z?H~9t0;LB6mm*O4<-o_$jHRKiLSTF>^nrLfu$)2h4{VP>_Cr|lbJT@w9f120k`d1V zeu*nA8ed-l--<(B&}j$!zy$dq-2?bxJnRa6^Z*V=Ae;XRcpbq9Wo`jy48@p2-I>5s z2x8m;TMYvpq_+k}BT!!qz}FI_+*$w!BT)GW;M>E|eaNjHa5w_#;w#{C1krb(?h7S< z;A;qEdo6%l5y;-MfPZ`mnWC*i;DoQF?j`|Sel5$i2HqY4djfrzk>DMH^hM~KD9aG0 zBDjNp!a<`z2mK8L*7-)3cLg3EgI_?R458PzvX6ugW2G-59D_i8C%lf(j^qZMFb=*2 z>63szAhbaGWnldzj2GCp8*n|sBgiKm`0;qmp(qmwT#rEI(}9;KK&PNXxN0JPbcuK> z@U_X{0sU$LT!=tz5w4p8+dw=WI6fKv6Jul&@XU13LU}^}8EPB>*-XrT=p*4XghJQ@ zn+16yP`}y%_o;XeaLR0%j`!EIt16xkbeZcu9AgcEd=B1C&tg`=CmEL)Ew9ofoS>y@qY(I zJQ=uuKjt&oOb+mJ4#p4Y5Pp6L{2=`+;139-yURe2!!k~Y|2H7g3Gt3amW8(PzXw>- zQP>T9JE7Mx(1i>MXCe?C!aBK9=B~gC2stQ2=yF`96K*?!zJZ^ez_0TlPtp-E`4r|V zq!Zpe4LzdWTfjSKq`ln(dS8>it1IyJoA4uOw*|1?Eva``;Aj;e1HAa7T(eyUCj5jp zK$Gw)0?pU?z(zkyAN~rk>uu11oe(}ns0~>WPX9&P!%W~0zrtsN&Sl`L-(d%crvmXl zBq0YvugB6(2$vuTzY3i91pK4F3xTr=Wxwb@SKs*)V+8!%1CHPr7noDi;ms$#k>z}gZtbTN>*_3E#R0Ni2jH6s z)E9T4`$sx<8}cFSK0?PXfQ}FF>}b#h4;O&;;~^i!3BwTRT7&Qk0{O9g;I_#+mW#gZ z1ooOD<>?FDH&rLbehzSMvXUV%ahi-1#!p9IKqmqCa3<)0pGUy)Zf6Xgj<&WFrt%mVEf$TD8Qtq8<(7Vz_hl4lbT?;~RiQI`^d38dvVwr1oD%3mkzsvKx;O~8?DQl?I7btrblC=;F;@~+*$x{ZIRL-fp%ea)@EAfA(sO~%JLR~*`&j7R zEMjd*I01p^PXd0lODC@B0)cgQ>)1W$(G@r^8{-swE(E^27j}#E_P`woIf!@N2U|iQ z`9A{I*)M6j0%H+K4hG;I1iIF{2lPA$U865vz-@<62I)J2>yDytXe%9J{LIZp^jyv%oyOv-yvtDCjvV@LSMkA7w{v)I9T%^V+%|LQa+~k#PUEVn4}%h16UD=A)V*|JPfP~%m+FF@$;X=+Q8O8 zfH-73I?+zo66v;hI}d}5SRyW0SORb?Fu6oIJ4hXTA)WZk1`^#1K;o|eNc`E`vcw8N zHy}V9vVB_?jq)ULw1o`MtXKB=@n7&0x3fs z8l5VL+%6@Z$`_WAZcoZAg$`a}VLq(sH!mZ-T^Z?J%SazkMtVdk>BN6R8R~)x zmXV%XMtW8m>4!^67xFJ7{dO7Yg=M7M(;!lZCTF6LpYEFv>-6Sjq_-<0y=xii1IkE` zC?h?gjP#^3(&v_vPI^f#C7t@4RZ2RQKU_xog)-7_myuprM!LPdSM<$UHN%>Sru&i|w6xz}UN({SQi{yjTe76Dq?c|hf& z=R5H}B#g`pbGOCrUQ2$&TekQW zgXNr@`CnG~PjNmUlY>TYZhKqS!b%=FAJ}4dqa{D7gDrdA>O9lK7H?p*W541Y3-4Xqs^w`Fxz?(${=` z!P{0myVC04!+B$?e5Wo&^Is`;-mciWPqFi0oZq&RZ`bblwSkrX$y90XazpSykTbf7 z;H%`JJ(nt(Q!M2f&JlFDp0`D)z!ODq1Um9;)=-6afp0#`W+7X$nN9@M*@BRckXSNM zE8yX|$1{}B!c(mF3K0re2NCjt`R3Lef}4hX41xoKE5cLcKSlkgIs`|A4n@PWk(`sf zk`bH`a5aT*92k@TH;?3?fs+D1D)lpeQ2+lEoc*}%>!056_}`^`^w5EYGbj89H2>p@ zCEF@hhSrHOAGSBw3ToDDl)L+=mMs^TI7T&SKp*6~yPM0KBQrt+`JoJ+yOST=PLV_D zeBWxmcO~Do*sQM$IopG~wtoJ{OzjxDP| zS?SNM2wzd`Q-lm>iRSa5pr8T$;=uXCW7+&fHqYJ8k8b5hGWo%DejttSTg7wc@$6X! zj}EfR%BX?6di1~t8ks+4Ovb>0CsI=W`s=T|ckf=ld|B#C=9-%@N9Klq!Y^*-2dD7^ zQ+UoqzHdC=o5Zun@{Cm$eI42Utgzr&L4LvQ{DS}q~#Ge5JIpI*gtzvD;e z@dH!&fk|I<^)geij;vq1dhF=0{{wyfQ$?vPb5l4ia#-uD$J9Mw^cCtcdQj$c2( zuV(X0Tlm>^{Pb#mJcS>d&kxOjc;h>HN)p!k+PiGo@^{`@7Z-Q@_;IPS=j-dcAwi_C zlAj{_%6ayzAUicBJtYMyOHWBopKJL*WzW%<_ZI%}N6!!CCp9${>9h5L5Asjo z!$z_Bx#Rn84ro5BUij*ccMc^BeNBSCM)c@nreKb&UbTG07egnH|8{eR8ee;J?vEUE z&^Y||+VvOA#Y#p>RxDXY8(*d5Kw0u5^O`5~57#_x9ljSTySjZtX)0R?l_hlPEX!N# z>%!Kp(_epm#>XGSFG`h_)Yq)gKulZmv)mBMuk7GQW|IvZoX!s<(?Hudq3HM`ebJ|k zQT8JGO3{v=cO>5G_JRB$4P96m`Dww8)A&GN)}Q9H>e3JcV~1vp?$u3p7T-xDheZbW zb8pl7^XNznu4$9ULtmJ`-2Rovs^r=_{)P{$dbxJv4d*XDFxPrcMe8y}tt5ej*%mGELt>U z#tf+J=-$21*BCc9YkxRD;!~Qi#3$p+aCo6oJ|)Iix)@(jS@H3eo$`Er-3xgI<7=*V z{IYBGO~3Z?gEVw@S?r*PKVQa2D$8zM`MpOc@yl6G6CFrj#}?u{+`1~(Uri?Q?#!abM8Y^_`o0Hw06@Q ztm-W!N-339v5bYK^@LSiWgrD*naRos^u-_FKbbQA;+n+!=a)SF>&*R2N6xRCb!>hj zKfeKU*_gh4WJ$BWFm09kks37XYh_$0zq~_!wvUeFzg{fm^BjE%$C!OM+auT@mmekd z)zbX~^9TAOm4!7*634xL<8Fk$2bJL)3FyMcuLnPVa7TW4!FBn$b14%aXicm=R!<$! z+bo}@^QNv`G{dKR*QMXhPfJ~n4~?TL2OjcFVfV3kd$OJL%bfpy|I*eYv*P9QWBf<`Bnhc6XsWP` z16kJoa9x6)9N?kp_+*b8!XN%fm!*F{;g9d~2l@Q&1%BZ`k@@R*PGVwW{_TR`gv9*( zd|G=P&hrg6v`)=)Yv+MQh*@8&H%;+K^aWQ^N5#JH>D@r**PQV;K6_VwaEih^zWMCQ z!~0krnU!T8Xv8O$ix)Dcf2@oX<$pMN;KKL&`uFw6u-d<82R_hOImbso-+nT3?0u;$ z#Nik#RR6hG{W#cM}miMYA|XQ)08}gS4>DqcyMsy zb>lnogHsefFd_QMlPA!ZU6LKvhtL|nrOHTO7-E;U%=y$`GJ|gnt~E>MKwr?**^}Sn zbLHY$I6%x<_AYlZvTojbjDfax{cq6M@>QpwUvX3FLURFg#5}H$VjZ_CkZ6v}NN#cz zL`dW7G#FmPA7%3U8}6U|?ntuXY)&o~`N@Omh8PZOeU&=Gis@_D*w6Ugi*mL)KEuGD z77A^NFQGChD{s;#yGDge`ZrFi@kJ`jFMyh);)3%E;1XM>=ey0#b4yBh^ELP;1^cE4 z%Ub4dv$720C^$U%`tY~q2MH*4e7b=K7w1^Xn@5&KUr^cA?0Lb;nV>w}8YUgzUOwA4 zXPi2I6w11le;I0mvhX>1`ZflaR2KAQW}~c%yE9f=myvM{e5lEMATC{jRZ5~cE+gmX z#-a-sGnb!TGyVSgolk#Sb3bq1$@!+UYi99F2l$EYv%-R8No#$rTeoh*h7B1R85m@l znVA@5CCAr+iBaSO#pmq2xRW!Yi+!N4qm$?foIIefysQ*_vavKYt1Q33V}JoF^F3VP zkzL>ht)=7z&o%gFkqWF2=nEq&LOUKFoti)DE%`wjN`i;K|NdLrj9F!Jh|&1U%HRtZ zE~KBwt3$5dp)XuUVTeIx&{xjZwNP0FmrIz)v7v>+kv&aZF72~Sp>Gg9*rOKczj5WiB0<+5C8x5(6!P$w$YGL7l)kUz? z@q6yH{3LgLkcPrn77u+Y4%uI@#S4EY4jing`_^%(w#Q%ZuRg4Ym(~=dfb7kB-#7Yh>6ey{$pH>ai_!s!>DV85tF`D@|cLDms z+7C_-1FF;mS?0wd+B!Wqj{mLsK%p`-&5~@FOu2skm{b|`WmZ{mdR}5ua(Z@3a$ZWw zD$v(}qQ@V{XIx8ei!>Czxgz12IIQ}sRQB+~0r2+owD)!<>PwPE3M3D&2$v}JRce`1 z6JMmFd5D#yP*PvNrhQ?>zeQ!>|8Dl!6N^TdB=UkOm%gKs3m5eG@#BXNAO3px4rpFU z8+mBLXD>+M`uXpnvg8zeQWEE;BqpaoS-ZDxO36xr%1RQE7hEO3Wyw2hhRdFUCHRL& zj~;=m`@jBr_vf28PV573hbBbs8#}lpS(yUKLtmvX8_KHkf)(WuD>ie`$5d^P$bp=j8dh_YSNrMiw-0?Ji~7eLkK4t=vbs>x%JXeG1;LZ6{y%oa5e| z+b=u5B(;C6h#^+$VXd#`C_(#(DBx|#qC8FFIY^*T^hm_o1u3|a5rA(*5fWK;b~ZB9 zbV;i;aVD-_vEb%RUcV-l#Hq+FMEbadq^X2SKwQ}-B@IjB0>>p4N*pOMX(V0wmPt=$ zi07sMSNoNu@i|WMt@T6em+Gh1?{UM14NG{jez`%m9thZJhwuNh2fupKhVNOf zV9z&9sru)%usTns4r+fk+N1Mt;{*3>_iOsooHuOlBK(!<(~vK8ufS8}~g=)-@A^XA9H-{l8CeUo4O-0L6P-@I%(WSxP^WH4SH*~2h+B+QFmY@3U1JQ0l z(#QPwiJ$VnrtA5?XGL&6CyM|2?Et}BS;Ed-w<==O+7rX^Ke?;HDAL7Y3cCXL*x``Zm5dEV*Kb-K1Xp=9-YHIli z&eO)@=exssenO8D{hv8~YLorjH=SCxXfB^We?I@=;zf?BjsNlc@BG@;%lyE;Y`$&t zM!s>wTE1!JBGL9Qqx*_}oiTLg>0Rq_{^Jk4FgaATe+PNgcfRav4%_2=%N))(%@910 zEUf!KdE$g#d$Tt3+0&-*9ox6_2ls#F1rHwZ1N-;zO=&Cn_Kj(L*XDJ6*QPYSJ!3WB zv^s@vHT2=TBfa>og(LWh;Q^d%jsJw(QV1k}bKkel;}{>Xxy_vCtmQm&mdMlEhfQ_N z-M53!pFJJ2-p%jby~D3wxy(1OU(NSz-^jnu*}+d8+Qah>?&8PyU+w84d?D!`?a#~Ix1BFvyil}%;=~EQC1V{w3i;+8+`~^F-6v&$zLO3v z96u=9r~c+_&)|Evt`~hLS?mTcM-Fc1E0)gY>Fbv8CErcwk%K?x(VqngzNSr^#;=_| zN`21I?2h7OQ_B`F;J7crj~+eBcWl{+{$%mv2X>*YJ^ZY)KWhIXWI_Euyn74Z2i+go zu}Q?q4jvZV=9@RJ;H#F;<*QfC=bO@(@uiDr@ZLSU2$_(45ADw4^XJSY9%`umANd~R zDRbRwzG%@Re)#ZV*v&@BbSuW(PJTl0Kze{3Kn7$B)IQn%-Yv54IpAUcwhVsp+)>ds zwMl{aAbBjDH;MOmVq z)&7^5V}xB#pE{ZE+ObyK;%|+>r%(EBTa(Nqqd+Z#ela z@{hZB;MwS@gCfv4ps`OnpmvEjt^Mg!reZ`!>30ix%Ay7Q zMD9_3^zcERcl;RowFEw96#wdr1U_oy*L>%ebkWY~yd1t~XF5;%c6f<)sr}E|I#b`1 zwYD`x+KlZR*YZcVF7lgabNSqv$vi77i~sTH5&s>0WMriCWlI+GWlK}|`ZcMr={Yv()yqK5KY4Q;fY=wRSZ``}JsDa{xZ`_#Uj!)~`#051GuvT2?X3f%usn)Pcvw#;z}`P3!d3 zuD3<{M7yu#@|<_)q7K@Z0B)@@psd^Zi*H_{P;s`H}^*dCL6RJQaG% zf-lO=-pYTye1iY=^96qM)B(OZZ8>!GEsu(dT4deE^WrIs=AX`7vy9K5HC>GTZ5!7K z`A?dV#8)kz$9JS-U68eoAK#P7^I_|EE*|6eE`1No6}%nVk-?WOoFm$ekB{Hbt5+}e zgz0&$TJq-1nLQcnPri80bc}&%{4D(btqaHb9{BpS6^r={_`Gkv8I3WN2>FcW{%zN`@OyNmm68VPIr92P* z?f0KA3Lmv|<7&PRI!c8+)+|}T7tNi<6O2*(mBx*JtyRN?ZkIQF@fM4g^zGwYF*Y`O z;+foo{0i0^D;CY+!{QA53zHFTEyS2yz~@Yzz^9BG&Bu%w&Oh?&&3kw2%-?&v?X#LS zYm|C_<;^$Wv@6=m^Uqqge4|E0#NgT4+crNtdsN!nn#HsE=r2t?JS32R+CPAQ9MFe< z($9~N7-r=Dy}CVXP`_TLt*tHHrYLf}^2#gaiX^u zd--%f*w?T31;1WBu663r{&b^;^_MwRuIvYb)n1rrv4Wgu>#)HZ?^i-V-ctmcobnN} zRY(M?LkF})AS@CRkxn67h6?$NIf70@gboNJ3$S~<0K3NvnB$&8<~Z^x_Gdq19mM`? zC&t7bivQ_-iobq1?)YowqSvo49@Fu!rC)u-_pC_fw=PZv`7)1n-+Zn+I>lK1>7w}V zCkDSaX07jQzpU+4^G~{tqHFZLi1+!!b;*0mA~4+$qQmvYz(YZ8E^O-2;O~pWeE4Nk z7yifOVE*s<2L9Ll&-sB*-WJz6WzpCg{GRd7H~p{SzQk|;#`V$l;XeH7;snljF6Mmm z9KQc!_agOs_Usu<^8oy+2qU5frGKw)cN*a_|ErMSUx>QPN6PEHolE%kh|XsIyL9Q| zOLehJj;~p>My&bwVXZOqi&(xs=smGs+LXSWUznihcgF|u9sOH8Ue~#9qQ-mQzJ0F^ zA3mI~UAvYaI&_F1IdVkM*t2I3U%Pr4)_w{Hwkn zn(Gc|q~lnxSohy|6|SF1-tF48yIr>&M8n>0CgX>x4StoL#$EHD! zU4x?T?CDd)x_IlR4SepTZ-4)%_OWM!W1kJjE;d|4&FAY@ui)6oCax70%t-q4pXy_4 z6t0W+aqQCJ)F$>3h=2`JeD%V~^wYNI9;?2ai8a$4QJ?B!lMg?4>Nv-fH@r+QlKdAp(S1ow_aWA_oBBIN%=pfb-8s1DWr-}EyM z4wGD}R$V;6(Q)r`u*v4?armY_ukn9 z&dx`!RjfD{n|8WZC|RRuO8xq~C%84*yXT#E_O)_#T~OWGIeT=~sy{q*c0PKwV#TDl ziZoih7FHrRIR$%@>7kX)y!x8`t8r`@c#Pao}T+|dU)h~ z*{apH%600bwsCPeblTZ@t7W6Bxkk}Qc=-1{LxNBIE_KReA=YRO#Q2`X6<)t zvVUcR2ATi3-fr7Ax4glS*YmT_zR&F6fB&hTJ@*~$)MsR*@5($H zHB8ghcTG*MUw;jEbKAsUd1Vi8+&G)pt&_5^TRXTQ215y+TY(hs-nkXFw1X0vLLK6rkLF`f#d%<2%v3JETV!4W4v0t(G-h0=4 zGrL(55cS^o-uJ)%@Ath$*xhsH%$YMYXU@!=b@w)PJ!Hr&MVmGofsb=`*WDWapSSJS zcz4%wh@HMrJmc}o6s=oB&K`Jq^5hqg$x4N(>HPR#v}Xa1k#4TLARalO=-KnIB0Bnj z!n-5jvE86(+IZ>61`SGnsRPg+am2&#MqAshuWYP0y#|?X;ymKl#Cn*MH0QUhOCREI-pB@J?9! zfO`+zd~U9C>3F}jpxq-)((SgXqsy&@4$hAiJl?VI{rK&_{VYG@8}@L!YllOZTG>pt zM4>r(RB8)6E{@=P+#c9z_W56bmY)ge_UO4if6gW|lL&TjaI{FAvRnbQ zC<21FD*7j8mj!n?_MYUe`Tn!~Oknh*xAx?Oz24qIJra^OC^~exrAWy*0Q(Lb6?^yY zSA>QfR=5h*{4_rk67!(S%YWz7j=qJj10pUd5>k)BUfzBx$1rBxQiWgeEky_4lAq>h z!h75s5fpvUOG(VFIHU!>^O8puu&(uAZ*Uru0eXaZq zj>#DOp#XThS331ZEMuY@oD<Ej_>j**$cS+T-xkN@WpJGd5aU>xm z20*J{wa_K}0H+jsY72PrSo4?KaS0xUS4`2E2sp$PJ%Q9!1|cKh%Vkg}uf{7X4gpGq z6x7d0piU3I;f6kpfl4X#Af&K!34gej5&hs!LS#{Nq`;LF?h}DC_?9sER&jYKbxl91 zeZM>!IF_MS@}Rojkx;KgZshzH; z#LsX)DUXB^p=@zvNq{k%;vIcI0QyDg(>(Wsk!AuNl4>fR{*Bhi6O>9S@B$^43Vq@6 z>~G*e3Nk2OW>S=)R8o~xA+DEy0~hKDzNv^go|gy0=n|FEmQkbbPK`C2qL>TcGqwic zT;c_A8fOKq7e9xDw_F$7fD{ITrqDWqQuvkLs%AtdimZ*rHCxj(a|v}_j|5#w1<9el z*KU8TI2`)M*;`wl!B9E~N^#6wYL+Jhr6?6zhiR?^0{!$nM=Yr2sc=@GVW?v`1EmZb zfj_=LwGE7##)TF|osS5Z3p4#+(>h(SP6Dd0g1>>IF5N>+)Z;(I$iIQI)n84s zt+?jAttkT>#CG76Y8L-kPYsla2PrTX<#!qsK)6wjdPAGqw&hov7XaV-#$#A9)6O%m0yHs^ZVC1!D2p?vTx{amRip=64XI*VbTHZAF{&ir+d8v+>mTFcV zm}^6-OG{PvKq_fltKO>* z^qNbAQ<6lhuY&#&)*oPv!S12Gw1HI}k514BdJ8!e{?1@CQ-SAPc%L3}AMGk0-D^Na z0X(j^XnC`g5WQfI3ZZpB3QsuT5D~cAqsK*CbL@l0cBP3w^F1S08K^RIJ{{=7(0l(iv6X!5~ZIjRfdMe*>kK4jw}W za1VVBJVJmzAIguC07N8kWN_0Q>}cmf_l~?w_D9nF81|;qA0I#+oxX2F4?YdM@xEWkd5J21>;6 zLwhRrr8QR+5t2dhN>MU7fO!zK!o#7IZim{y_ykOo!Q5_17F+I^JbkJ`)7fisqt z%G*-LTBxX`YiVh`E3MfEtqZPxxy1WAYH+o~ewhf0iN?4v!_hyL0B6x&Bm+~%(@6lAJKnBno%dislQP$ z6LB8kaY7A0z=FsnO;T#{c;!$`rT-Kan_=R zre_!0deu<~uuP0WbAnzEq|~~NzF-#cl#XJt)y%(O4Vh8YZaW3qs_YE|g)3DSMbJ*F zJ{9DMatQ-T;rflMT_W78EIV3jE@&BA^KMX^3^QCk`WOpis=3N$0xa4{#D=^;d13s4 z$GB>lpx&rQvg%L>nM?Gkv69rZ;P@_5r= zr8QQc0GKzM_;0@9U(YxEPezKU(=eB)Ir^{W zkN({l9x3=wMem4;8i8z59&D0qbE?O;rTYca03xK1Zgwag!Qhj~OjRwd-!458&=Q=vVH|CjM@IiT&vMvQyTtj$(9!G(`2%XvZu}6v^ z_(Qkr16~KVLtfME(CZP>J_7W^pr4iLitLdKisE~!Vmh`%zuwz z`s@aMni5Z`J_DgQE{qYg%h)yx+DMc;5E;}x9V62okc_a0GW5l1e$rkVQiuKyy%!{| zL#j9m9F-LODHSKMU-SwYEQu7avM5~W4~wYY=!^~*sKXhG5d%ihwcFy32I8SPfjd^} zqr%+`jCL`4MxHoOLWqFpn$`8P0=!uI__YSA^sET-p>3=6MKxPM^(pxp5=KM=_)-Br zY?2RuX&s{F&<5)AAqGVKRm~#0t~GqkU;xYmT1HBu2u6NR5U=SF&y+fY_6z4V=6{$x z1S4_yK9f?Q0B z_`_1dI#RI6pPI1#AZaGwfo&LgZwemAp9_9JlcA%rGB8~!m={VIIs#*6Jp+?K!Mvnk zYDOS6amgsSR}@^$_=d?M)Px^J!M|4G#rzuTd+ppKw#nrdP&^n)EGL>029zG;(a6AV z(Y#C|_EGp~JEqc06+h^D+IIO-8ibaZ&U7F$MsKuA@V!D)@aNEK}k zZ4FfAjO@991+4*E7ltadGdT0nK49(x?InX5?HQegL}>b8#=jCEb(C`yJpd8a3T-S} z7_CK%k_cF3t*b2_?!mOC z+8a=Ik*-nKEu^4kWQo=?fyxbFrjcGtno#S%O0$Fj!|dls-yC{rLyXdE?Aso_|2>^m<;`98m2TV$a%?wCL=XFO2Nh zhc0+Q=s~D+s1cN*he78Gaa8DMA*HC_Kf;?R;nC{Qoc|G~q1qk@BMJKXhc47eao2&) zItc+w{hva22P)_tbM*1j!B)h=+J!qS4Av%~1N8F@s7<(L{C)(wL=bQ-9A=FQ56w4~ zZBX$MXNOAX=}3Ul1Ntc$T%{R$7z?LruYRRPQOM+DQFhssHN_m5oTvx+$)EeQG@hEX zy7rt`?~8%=0sTeK1ucyZ^p9H0Jo6%gKGAldKH$jcbrI);F17PrGn=f!u0uWZfIDIg zW-Xvyb*Bw^c6?$ZmBLByu&7%9c*OxKXxqpE?HU9v#XuQKli?NSKxqg~&;VS2P(O8R z?f;-vG^|w?0g%$_Icxx}=~biV&Jni6Iw8ea#;Nl}mm+CX zofd82B8~$i0o>2krbevFx*FDj1GOFPuN-1PS{Kne$>0aEqlHOT{7#msCRZ_OxA2K04z7eYpzo#6Y|SnfuwrOpB(lRCQ+QtIqXWKm~#cq#<@83@nr z;8_M4Q9MhD0_xl!aJIQ&s^&3hVU(DWBLxi zT?k+J^T9j#!m(N@RQyn;Sw|UWc51iPhHvQMvp3y=TYcbfCzuhqH;L<7A2^2r_v!y0 zwwv<;zj{Y-fI_5%Id_CRJUH-!BTzpA02ICkPR%XaV`NsiK+r|>P6)!1f+68~EfOMi zJSV_k_y#PjGc2W*$O6611WUkJZ|oxsbX*F!>8t_#FFx?AS=B2j&I=jXYqTV)+!eM{ zt;6cJyF0*%K|=__4!mO(9@=+6zZ~ru9s=-qot4k4c~gDjSkMAX;f(%;ipt;hl>#{g z_@X+rO{Q9+t-`DgQqAna{aD5&R)Ka1fJ1zsgZ7BRvq@jKo)01QS*1n4aCnqD77V7JiXt494hJn&Qm#p9aoYjawi&;HOSe6<_s zlvY!%(jbDc0^K8EEvhTe0u_WVr)lw~*!;9GJtgqf*HVwt?VSSm}EWO`Zh9Y{;MIl>HZ`YjSC%MR^D z;w*8dNSv8CNOdjuQ4-Uknyf$ok*CTBapi-u#9o#$shLg!OKzqx18zbY@*JR8B*~FE z_(?J{B$+%vNv2#XNtao22QW0cI*|Bd$<2~V62&r^MCv0=Oi7iC6Xn@bz$MHOxjI8_ zc0zh;Vz78nl%$^+hUV(x;gaC$%y)K9Oibd7k}SFD!p!7sVKOwagHbp#>|^MJB4%W2 zqp2>#(Na9}6lP^XPeM6xu!Dgf7Y=1b4n3Agv0R>-nJi=Ek|LL9c{(~OMdMH{8V3-K zV`ONcqktq`9F>e<8e1w+19DMTs!xtkD()bcW{ZJH=2qD^3;30J3IlCu`Tu33`Md%?MhT>3b3AF3#;_r{>_(`sAu8D50Ja_=IDM7?@7Yjr@p}^Uh6bM|BJe>Z{Xq|!;K6ftcINXEgdT}Jk*kNuS?DZu6+6{AT9CV%jsm^y9czeDP5V1UMELmo2ZnWy zja6R!_x}GbKe||e0cW!bGT*{YOV_TjOW_9tmNvU2-=Z}XS+Q9xz7c7lrQ5f#3rnoQ zVY4&|vX_=#TP+rcmG8!8aZ19-5VDb4i4m!*!GdqtFJ{Bja{O9tIowed;|9b`DEw-$ z^Qg!1;)D;~H+oj^ee?z|Jm7k{(btotc!8!F6%rAP*s zd}GoOE49tEBZcWn5~+yam~4n8dS*tEV8v6DCESqI46x`Tz7=VSH5z8kn3`^pK3qSE zNX(5)P0j?f%JuUhTQoA@dyx1;9dV5e1WtG>Z~~+65l8k{qb@LnA;3gm+l>!1l<&sZ zC-o?j^ff{xB7Re{G2ZFwoA?X+38h>C-<}(tnJVYH3;3p_F-5ArZdj_68!1VG0V&Hg z)XKoJyyH7eF^w4Vm#-r-u!_hHAdBKB;|d+@5{q>yn#lj9TYxNH#|RLrz0Z{bdu+-_|>8YZR`1vc7*%@`hd& z>xNMZ)!c~1QgApLZ0%f9AHmJ=o=p;@8QxiQP{=wkZ8SBtH8e;QdPWs~^kuQI6$d^E zg>YPs2kAz-Xz5cEY!jcwW@)fVYqAy7md$F|2$ygfuHm%0QY*J#L?r2l7+P}Llb)n| ziED{-p+LFVCrZ;D)Pa@S49bkUtW;$`9|wRVWlT>%STZRlJ23YoTMeK5FXEG{C?{Ek z6y-p;BvF>7j(}`|l$&rGkqz?9luNI7W~vljMu$wXTwRx8zJo5ah`cOK*Y^jHSxxaj zU>yD;{_KU|dY_92o1MR}cWy&i5B(vl9xd*Y-uzGL-xRb8>Y~ zI_JHg-|X79%D5LdUb5|8xt?EbGL(B^mSxtEDF!QS?Mklh;1v&YlR7_JCw^)!>2NRq z%7AiSR7j6Fm%ZucG3!?B+AkPo|Dtf(!2HTLzNZA*$?i9DWA`3*F?cm|NY$gEE%ycP zYJ5gse`dpl7f-f*CDmxmeG>U>;oHskE5^AV=wS6c$9Zn2`kfB<6=f~Aw$AvPdDBk> zHu4>ruW^mGk;cszcOfP7Y#N`=zd3W5)44Qtmg$3u%p0R-g`y+*W}t##WpvE+!o)e; zAaHNRgZTAHQ!Lart49}b{lF>Xw;;{2RL`tIREn4z9oeZ{RJZ8JC~lZoo+FX=1Fzj4 z>ov@*sXA%4OwOh3q*y9XO;Y-%T%T-tibM+dD0}J7=ab-Q;sN&3iR25M;2&)-|5a^fp9lEgosXI2?tdrV!7e)oh`3VP&pKTg#yL`^^8RQ?` z-!08Pw8}6dVOiHA3G2VT{MdNy3dM5=C+=tcCSzBo4_QZ42p3&V6=$@}w4GTs zt83x%x3>3&rj4J`qG#VfhgUV6T1+^me3hvAIeXWEgbU5;O+1{`ENJ2UyaQ72t+)HUsMkrcrEO-$JI<{ot72X z;%d{$Dx~y}Hk~@87FbX=i=a_ha~hKM8B?U8QGKOD=`q+%JrY`IalnKUWCUVu$q6Jo zlbuTZN_+}Csu{v;8R#V{DP=dSRThn*y24iLD`gc2Fdc$bOe0gsj?6t+MW<@ya)+NI z_YsMruvf~=%1P^L1gyd)N(7Wytbw7{EY`>eeuO4QJnQTnBwTvsdYavYd9MhfMe~Fq zo>hYV?>FB}-26pIjG5ak!&sXWzGuhdu>&oi7zI3i6VTf`V65redO2~$c0Qh8FT8#7 z;6sBMP8Ev^T5rGZcz61TQBDQCAnz`F=k5;=+Bn(j?dCkUbK6Cw%Uh-ONa^O=N5Oe= zOD-Oy^KR9&>;3wB&zZS%;H$DDw<~lSZd{WXccejXhP-!B_V8spD$=)}x)C&Jbg#qS zp@}Z<$#bW+>KWGKj3^0SU%q77>5;^=4w3QB%Ltd$^gRo?*2V@+zC=tB?(vvBcv9&5 zg@!G@Yj-tGDzItNWyZ;%qx(j@@zk96NpfS%h(B5_INQQ+a3}u0;?Pwudu-~_d3*i|mkFt*NV;5_b=>j}T9cXx)rN|@H#I;PE+u1yXvvi43J z8FOgU{EfTJ^)3%h9zM+BQR|S#!w(GM`}Ch1+~#RLub7Cl$F6-_pxO1F|G;ak>l?FY z8$?-sdl`1;_Q!(V(w)b>JfFYp-Dqk`#u4GC<*)4Y>MbjKyWsh3f##?MPd*j2Id{l@ z*ly1phw*N{C29H3+6AqiZoKrC!N*1ix5W)|YCQGM*6fdq9!=7-ZgIE8tA%l+TPYSU z?YiKLN3Kcbm8}O>tjv|%i#&hx&f@)Ndp7+nu;Hb}WSO^L8tbs+P0wKMtAZYHMz3g^ z5zEhSFpaVat14X3=c7y1q}qMg)!^%^RdZmeQ32QE z8-g#Tx^F-_sv2_mmK?53Jkc^^M0(1|J@ds|77#3w8zf(&33r7ST)bZTYdh3 z6U@nH5d)v!jW{~^wnOprv-6vM+}JAOt;wzPw+wez9y?=rqvD|Hhn_dLx@fL)J3WN8 zGVAvF1|OX>zmARAeqcg7^0C&7wAYnRJ?DRCPcdzD{8@RcjvK$R6=e-}avw&7TNKor z(HK4lJ$Grd4ZCzFz5OhG;WEJ0P17goF}rclto^G>tfP)C=$#PVAqCp8Z_U%WhK#G>n7f(6%B z`isJj?3fn1&9Cc|9{x7{9yrBk2u<6*9zONR-pE!abES8LEvsDS--utb%rb9QlS6O$ zoGSfu#Z!VtE*t0+u&Z0q^hNn*VPw8pP&HpQG+6w6GfyaUt>!KUfM&K()C&C1ngJji zorxD(^_20Kt}%pwc{ezDSIN9fppr_ooA{`nbwq!5%X2Qi|%YSG@$V>^4&j`CXcTPeL^%4dm6&4AcWF3iZn;0`@wh#p%xm*g8f zBJ;Jt;o>k3mq&>8t)V3uwkyTA+g)mId0t)B(BT?Zle+&{Op37p9=vtLxUIP zq4&HdL}?VA{>nQQ?c_dK*IfII<;}#x6??v&Es&YkpEox|H(u*errHLr$z6$C-90rm#fj+BR?2=VtSUj~RWj+n}-EGjrUk$|DwjZfqTH z_PY1bRU5f+ZPqOD9i4OYZ3w%|wb*5m%a@cqYcf;Zpv}Q&PNenynFV=s-}g6^Xw%a)?k2d}Zb)3&WNe*S*q;Oz^tH=J7*+<1}x!kE(D)}4#wmD#3eiY0e@8_au} zZS(y5xpz|?yN!By*}tpZ;uFNN(Mf(wnh`G+Z=E?mVV!Ng*Uf^CR!sMG8^ayBa`gI& zovoxpK1RLx*w40f)Vmdaqb|1fkY9<*VV_z0`CVnP*NK+vyPh7Bez7^v#5c0PeE&U{ z)x2(|m#j~{Ja@Xt&$LP~Y)mB=GGXSs|VydWDu5fIew&1t>|0luSVZGT3X3@`Yed~{SjA7ju+XE z^eAyFaZ&RSe-?+M-C@j*G7y(D9wNA$DP+=5T~3)=7C>ndz?c02;a`lz)iP^f#VCg} zWS%(o^UXo0UNoo>WL$SDdXf`+`^oO(LG81z51*bpJa56Sw3N8H=O`;yZzT+Umn-#&7KeM9S#hC_VHQD-of3w zJQCiU>yfi-&(0#zm~2J-sD{qAXWJZCJmXzf?3t$jG-I*BiHcX5yDf^Ewj#Q%n{_^6 zpxwwpE33TLT1?KWuo~n!>y^QhB|RfAt|4~DesReS@6)<}dXwJ!3#3!l-#m2j^_(w{ zw@eie)gJ2N-+Wzc<9-QWuBRW+Ta|v!A|UIkL^Q)UgYGhOSwFRSMq zJ@3!C8PkPBj%1b{Nlv!v6)};;y{`R%b^fD+N#*>4dmoQkoBMm&-YYHaY*(+@$N|HK zq=Zl0{=sa^vl8BOnb9TVo%83|53R~>IobHY^!vlD?#&1qGr{)4sHEnf){6!dJ?!%R zNVMLwHWe+eyD$Hv{uhDQ$5jOhu16~N5Tni4d=Bz8AFFZoU9X{8m*NM8JzviwB^@GP zY>xY5z=fuqWhXlqY<+UzM4~pH_$(+US1yQ_yXY!_mb3X7jccc4TQzO_Erw z`Ln?#k$9lcT&9~dg_ZglnNoyggqb}hc1j14@u_l-w++0s_NYfm?*7_ne+ zuPXOf4L4<-TXNvW=7Z9;4thCX4W@V7{U+TiSQ{j?i}o9j}~zJZi$Y?)}g3riUDv z(EeQg#l^dA6z3wjhx?|^i`9#ItNGv?f9 z%RZ+`+i5dq7tdPoWYWHY-4FR&-_SChtHoQW(feD$;>hG<8$VB6Bsw}~YLDY91O7Pr zdbwg)%oWoCpU3%Fop1J4x~%@<^XvS~mj;ga@u;uSqRr=K%PiL~s|d|MJ@InC?N>fD z_KuOg?7!qo(6COf`#wy}|6F#|AToE@=WQObf)|p;5o4>u=R56gx^@1d>oa@?uP9*F z#W+|OyVqD3f0JsdbJ_eWsg^o&)&Kt`STdsWmN~^YytOqluPFP5#$Z7`p7ltRO{G`t zhS_Pp+ttqYZCpuNV2>$n(uK#jMqIf)?9)Qxg?GiebGPqy{}k(U>Xq+3<6(+YdCSR% zdg*?0>ixLH+$Ofyn=K|rfBsob z#HL7c#?lLKXLi@wQQ2z*Yvvwlx3qS?ZbKUm^q4kn`n-)-H)Z%_Bv&4lOz|4gNxH9D zs|)+1M<(6fd@xDt{hJHHz1J;i>pRY3>i3nBIP0v0JXvDzlJbjJtAfsXZ79%kH*Ib; zns@gr`$C5t&MuDY)(1JqiybDmYFxHtf9K{e<1Z|d#b~C?U2)V++okS>*~gw=-!!rlD{k!8 zyo`Dc){}Y9)VgEq^YgBed6&t&DiU_f)+cPp*3;kJ`9eU;?3CNPCOkM6KAp_l#i`G} z&~wJ^f~pRP+;l7Q18%DJA zwz6E(wY_H3M)4ZGoPyF9UVdlPKd1im<>PvmSdX81^V8L(bH$u)IYp@@GaL5xtjxW! zJM7}-=l$2UP919Qr`u+=<1(i?Hj&S>>hE`8Ssp207`poHqeUN_55=Bt)aKr(0}03W zUTBou>dO6GyR3G>QQPX9DH4c2{j#4JZ`Ja&?O@l9e+c(u&bLLHwwCb9s(++bHXSLvnw7F(glm3qg$5B=;?R;GZUv(K* zw(eu6!Y@Uq9kb=%=iPrUz1T?f`Po&gwNW1@?H-nVe(fBGw5|g4+_nu%-!|cc+=7MI|}s^`g2|-YuZOSpIzE(OhERV z^W2L8*SbhbxT`JRU+sJ*WsB(v{e~Qz XHi6$MX1&XX;<+8{CO*bT;E4YNbQI2I literal 0 HcmV?d00001 diff --git a/assets/notificationAreaIcon.png b/assets/notificationAreaIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..025233261a0746d45ee9bcedf8d9a430b3e75377 GIT binary patch literal 159931 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

yntZZuqQY-Ovg+gg;24agy2{;)OhFL=XJ|nKE8S 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

Zm;9Ug^d?mU)=Kv;`9Ax>M>Ts<>gQp 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 O+1!Nxm-73=7yk#+hpz(w literal 0 HcmV?d00001 diff --git a/bueno.bat b/bueno.bat index cd190f0..2c8ac1c 100644 --- a/bueno.bat +++ b/bueno.bat @@ -1,2 +1,4 @@ qmake -o build\Makefile .\qtest.pro -mingw32-make.exe -C .\build -f Makefile.Release +copy /Y /B .\assets\SoundVolumeView.exe .\build\debug +copy /Y /B .\assets\SoundVolumeView.exe .\build\release +mingw32-make.exe -C .\build -f Makefile diff --git a/qtest.pro b/qtest.pro index abfbb93..942d75d 100644 --- a/qtest.pro +++ b/qtest.pro @@ -1,8 +1,17 @@ -CONFIG += debug console -QT += widgets +QMAKE_CXXFLAGS += --target=x86_64-w64-mingw32 -g -gcodeview +QMAKE_LFLAGS += --target=x86_64-w64-mingw32 -g -Wl,-pdb= -v +LIBS += -LC:/capybara/libclang/x86_64-w64-mingw32/lib -lWinmm -lodbc32 -lodbccp32 -luuid -loleaut32 -lole32 -lshell32 -ladvapi32 -lcomdlg32 -lwinspool -lgdi32 -luser32 -lkernel32 +#"kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" -luuid -loleaut32 -lole32 -lshell32 -ladvapi32 -lcomdlg32 -lwinspool -lgdi32 -luser32 -lkernel32 +DEFINES += DEBUG QT_LOGGING_TO_CONSOLE=1 WIN32_LEAN_AND_MEAN +CONFIG += debug + +QT += widgets network INCLUDEPATH += "$$PWD\src" "$$PWD\src\qt" "$$PWD\src\back" "$$PWD\src\cont" DESTPATH += "$$PWD\src" "$$PWD\src\qt" "$$PWD\src\back" "$$PWD\src\cont" VPATH += "$$PWD\src" "$$PWD\src\qt" "$$PWD\src\back" "$$PWD\src\cont" -SOURCES += qtestmain.cpp qtclasses.cpp backlasses.cpp contclasses.cpp -HEADERS += qtclasses.h backlasses.h contclasses.h global.h debug.h + +SOURCES += qtestmain.cpp qtclasses.cpp backlasses.cpp backsessionclasses.cpp contclasses.cpp contsessionclasses.cpp +HEADERS += qtclasses.h backlasses.h backsessionclasses.h contclasses.h contsessionclasses.h global.h debug.h backfuncs.h ipolicyconfig.h msinclude.h +RESOURCES = assets.qrc + #DESTDIR += "build" diff --git a/src/back/backfuncs.h b/src/back/backfuncs.h new file mode 100644 index 0000000..b8e1c6c --- /dev/null +++ b/src/back/backfuncs.h @@ -0,0 +1,32 @@ +GUID inline NGuidToGUID(NGuid guid) { + GUID msGuid = GUID(); + msGuid.Data1 = guid.data1; + msGuid.Data2 = guid.data2; + msGuid.Data3 = guid.data3; + for (int i = 0; i < 8; i++){ + msGuid.Data4[i] = guid.data4[i]; + //log_debugcpp("MSGUID DATA4 BYTE " << i << ": "); + //log_debugcpp(print_as_binary(8, uint32_t, msGuid.Data4[i])); + } + //log_debugcpp("MSGUID DATA1: " << msGuid.Data1); + //log_debugcpp("MSGUID DATA2: " << msGuid.Data2); + //log_debugcpp("MSGUID DATA3: " << msGuid.Data3); + + return msGuid; +} + +NGuid inline GUIDToNGuid(LPGUID msGuid){ + NGuid guid = NGuid(); + guid.data1 = msGuid->Data1; + guid.data2 = msGuid->Data2; + guid.data3 = msGuid->Data3; + for (int i = 0; i < 8; i++){ + guid.data4[i] = msGuid->Data4[i]; + //log_debugcpp("GUID DATA4 BYTE " << i << ": "); + //log_debugcpp(print_as_binary(8, uint32_t, guid.data4[i])); + } + //log_debugcpp("GUID DATA1: " << guid.data1); + //log_debugcpp("GUID DATA2: " << guid.data2); + //log_debugcpp("GUID DATA3: " << guid.data3); + return guid; +} diff --git a/src/back/backlasses.cpp b/src/back/backlasses.cpp index acd4f3a..a25d4c4 100644 --- a/src/back/backlasses.cpp +++ b/src/back/backlasses.cpp @@ -1,83 +1,562 @@ #include +#include -Endpoint::Endpoint(IMMDevice* ep){ +EndpointNewSessionCallback::EndpointNewSessionCallback(EndpointHandler* eph){ + this->eph = eph; +} + +ULONG EndpointNewSessionCallback::AddRef(){ + return InterlockedIncrement(&ref); +} + +ULONG EndpointNewSessionCallback::Release(){ + ULONG tempRef = InterlockedDecrement(&ref); + if (tempRef == 0) { + delete this; + } + return tempRef; +} + +HRESULT EndpointNewSessionCallback::QueryInterface(REFIID riid, VOID **ppvInterface) { + if (IID_IUnknown == riid) + { + AddRef(); + *ppvInterface = (IUnknown*)this; + } + else if (__uuidof(IAudioSessionNotification) == riid) + { + AddRef(); + *ppvInterface = (IMMNotificationClient*)this; + } + else + { + *ppvInterface = NULL; + return E_NOINTERFACE; + } + return S_OK; +} + +HRESULT EndpointNewSessionCallback::OnSessionCreated(IAudioSessionControl *NewSession) { + if (eph->getFlow() == Flows::FLOW_CAPTURE) return S_OK; + + HRESULT result = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); + IAudioSessionControl2* sessionControl; + //ISimmpleAudioVolume* sessionVolume; + if (FAILED(NewSession->QueryInterface(__uuidof(IAudioSessionControl2), (void**)&sessionControl))) { log_wdebugcpp(L"no nueva sesion......"); }; + if (sessionControl) { + sessionControl->AddRef(); + //sessionControl->QueryInterface(__uuidof(ISimpleAudioVolume), (void**)&sessionVolume); + Session* newSession = new Session(this->eph->getEndpoint(), sessionControl); + eph->addSessionSendFront(newSession); + } + + if (result == S_OK) + CoUninitialize(); + + return S_OK; +} + +EndpointVolumeCallback::EndpointVolumeCallback(Endpoint* ep){ + this->ep = ep; +} + +ULONG EndpointVolumeCallback::AddRef(){ + return InterlockedIncrement(&ref); +} + +ULONG EndpointVolumeCallback::Release(){ + ULONG tempRef = InterlockedDecrement(&ref); + if (tempRef == 0) { + delete this; + } + return tempRef; +} + +HRESULT EndpointVolumeCallback::QueryInterface(REFIID riid, VOID **ppvInterface) { + if (IID_IUnknown == riid) + { + AddRef(); + *ppvInterface = (IUnknown*)this; + } + else if (__uuidof(IAudioEndpointVolumeCallback) == riid) + { + AddRef(); + *ppvInterface = (IAudioEndpointVolumeCallback*)this; + } + else + { + *ppvInterface = NULL; + return E_NOINTERFACE; + } + return S_OK; +} + +HRESULT EndpointVolumeCallback::OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA pNotify) { + if (pNotify == NULL) return E_INVALIDARG; + + //delete osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller; + //osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller.freeData4(); + //Could've made a function or = override to hide this within Nguid, but back in cont = bad. + osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller.data1 \ + = pNotify->guidEventContext.Data1; + osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller.data2 \ + = pNotify->guidEventContext.Data2; + osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller.data3 \ + = pNotify->guidEventContext.Data3; + for(int i = 0; i < 8 /* Data4 size */; i++){ + osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller.data4[i] = pNotify->guidEventContext.Data4[i]; + } + + //memcpy(&osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller, &pNotify->guidEventContext,sizeof(NGuid) ); + Flows flow = this->ep->getFlow(); + EndpointHandler* eph = nullptr; + if (flow & Flows::FLOW_PLAYBACK) { + eph = osh->getPlaybackEndpointHandlers().at(this->ep->getIndex()); + } else { + eph = osh->getCaptureEndpointHandlers().at(this->ep->getIndex()); + } + + eph->getCallbackInfo()->muted = pNotify->bMuted; + eph->getCallbackInfo()->mainVolume = pNotify->fMasterVolume; + eph->getCallbackInfo()->channels = pNotify->nChannels; + + + UINT j = 0; + //todo: do while here caused stack corruption; sus + while(j < pNotify->nChannels) { + if (flow & Flows::FLOW_PLAYBACK) + eph->getCallbackInfo()->channelVolumes[j] = pNotify->afChannelVolumes[j]; + else + eph->getCallbackInfo()->channelVolumes[j] = pNotify->afChannelVolumes[j]; + j++; + } + return S_OK; +} + +/* + * EndpointSituationCallback::EndpointSituationCallback(IMMDeviceEnumerator *deviceEnumerator, std::vector playbackDevices){ + * this->deviceEnumerator = deviceEnumerator; + * this->playbackDevices = playbackDevices; + * } + * + */ +//todo: not on construct since it expects them to already exist; smells like refactor! +void EndpointSituationCallback::fill(IMMDeviceEnumerator *deviceEnumerator, std::vector playbackDevices, std::vector captureDevices){ + this->deviceEnumerator = deviceEnumerator; + this->playbackDevices = playbackDevices; + this->captureDevices = captureDevices; +} + +ULONG EndpointSituationCallback::AddRef(){ + return InterlockedIncrement(&ref); +} + +ULONG EndpointSituationCallback::Release(){ + ULONG tempRef = InterlockedDecrement(&ref); + if (tempRef == 0) { + delete this; + } + return tempRef; +} + +HRESULT EndpointSituationCallback::QueryInterface(REFIID riid, VOID **ppvInterface) { + if (IID_IUnknown == riid) + { + AddRef(); + *ppvInterface = (IUnknown*)this; + } + else if (__uuidof(IMMNotificationClient) == riid) + { + AddRef(); + *ppvInterface = (IMMNotificationClient*)this; + } + else + { + *ppvInterface = NULL; + return E_NOINTERFACE; + } + return S_OK; +} + +HRESULT EndpointSituationCallback::OnDefaultDeviceChanged(EDataFlow flow, ERole role,LPCWSTR pwstrDeviceId) { + if (flow == EDataFlow::eCapture) return E_INVALIDARG; + + Roles nRole; + switch (role) { + case ERole::eConsole: + nRole = Roles::ROLE_CONSOLE; + break; + case ERole::eMultimedia: + nRole = Roles::ROLE_MULTIMEDIA; + break; + case ERole::eCommunications: + nRole = Roles::ROLE_COMMUNICATIONS; + break; + } + std::wstring wstringEndpointId = pwstrDeviceId; + log_wdebugcpp(L"we got za defol 4 " + wstringEndpointId); + osh->changeFrontDefaultsCallback(nRole, wstringEndpointId); + + return S_OK; +} + +HRESULT EndpointSituationCallback::OnDeviceAdded(LPCWSTR pwstrDeviceId) { + log_wdebugcpp(L"ayo we eventing za adin " + std::wstring(pwstrDeviceId)); + return S_OK; +}; + +HRESULT EndpointSituationCallback::OnDeviceRemoved(LPCWSTR pwstrDeviceId) { + log_wdebugcpp(L"ayo we eventing za rmovin " + std::wstring(pwstrDeviceId)); + return S_OK; +} + +HRESULT EndpointSituationCallback::OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState) { + std::wstring endpointId = std::wstring(pwstrDeviceId); + switch (dwNewState){ + case DEVICE_STATE_ACTIVE: + osh->reviseEndpointShowing(endpointId, EndpointState::ENDPOINT_ACTIVE); + break; + case DEVICE_STATE_DISABLED: + osh->reviseEndpointShowing(endpointId, EndpointState::ENDPOINT_DISABLED); + break; + case DEVICE_STATE_NOTPRESENT: + osh->reviseEndpointShowing(endpointId, EndpointState::ENDPOINT_NOTPRESENT); + break; + case DEVICE_STATE_UNPLUGGED: + osh->reviseEndpointShowing(endpointId, EndpointState::ENDPOINT_UNPLUGGED); + break; + } + + return S_OK; +} + +HRESULT EndpointSituationCallback::OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key) { + + /* + * log_debugcpp(" -->Changed device property " + + * key.fmtid.Data1 + key.fmtid.Data2 + key.fmtid.Data3 + "\n" + + * key.fmtid.Data4[0]+ key.fmtid.Data4[1]+ "\n"+ + * key.fmtid.Data4[2]+ key.fmtid.Data4[3] + "\n"+ + * key.fmtid.Data4[4]+ key.fmtid.Data4[5] + "\n"+ + * key.fmtid.Data4[6]+ key.fmtid.Data4[7]+ "\n"+ + * " pid " + key.pid); + */ + + return S_OK; +} + +Endpoint::Endpoint(IMMDevice* ep, uint64_t idx){ this->endpoint = ep; - if(FAILED(endpoint->Activate(IID_IAudioEndpointVolume, CLSCTX_ALL, NULL, (void**)&endpointVolume))) { log_debugcpp("si"); }; - //Obtaining friendly name: IPropertyStore creates PROPVARIANT per field - // hr = endpointPtr->GetId(&endpointID); + this->idx = idx; + + /* + * It can't multiflag, it's that stupid. MS momento. + * Only shows most relevant flag according to MS, i.e. 0110 sends 0010 + */ + //todo: preguntitas owindows dword no es uint32_t even tho mingw mingas + if(FAILED(endpoint->GetState(&this->endpointState))) {exit(-1);}; + + if(this->endpointState == EndpointState::ENDPOINT_ACTIVE) { + activateEndpointVolume(); + } + + reloadEndpointChannels(); + + /* todo: check header + * if(FAILED(endpoint->Activate(__uuidof(IAudioMeterInformation), + * CLSCTX_ALL, NULL, (void**)&endpointPeakMeter))) { log_debugcpp("peakbros..."); } + */ + + //todo:: atexit into exit Gather ID + LPWSTR tempString = nullptr; + if (FAILED(endpoint->GetId(&tempString))) {exit(-1);}; + endpointId = std::wstring(tempString); + log_wdebugcpp(endpointId); + CoTaskMemFree(tempString); + endpoint->OpenPropertyStore(STGM_READ, &properties); PROPVARIANT pv; properties->GetValue(PKEY_Device_FriendlyName , &pv); - friendlyName = pv.pwszVal; + if (pv.pwszVal == nullptr) + friendlyName = L"Unnamed Not Present Endpoint"; + else + friendlyName = std::wstring(pv.pwszVal); + + this->setFlow(); + if (this->flow == Flows::FLOW_PLAYBACK) { + activateEndpointSessions(); + } } -LPWSTR Endpoint::getName(){ +/* + * Endpoint::Endpoint(IMMDevice* endpoint) : Endpoint(endpoint, 0) {}; + */ + +void Endpoint::activateEndpointSessions() { + //sessionManager; + if (FAILED(endpoint->Activate(__uuidof(IAudioSessionManager2), CLSCTX_ALL, NULL, (void**) &sessionManager))) { log_wdebugcpp(L"sesionbros..."); return; } + + IAudioSessionEnumerator* sessionEnumerator = nullptr; + if (FAILED(sessionManager->GetSessionEnumerator(&sessionEnumerator))) { log_wdebugcpp(L"sesEnumeratorBros..."); return; } + + int sessionCount; + sessionEnumerator->GetCount(&sessionCount); + for (int i = 0; i < sessionCount; i++) { + IAudioSessionControl* sessionControlTmp; + sessionEnumerator->GetSession(i, (IAudioSessionControl**)&sessionControlTmp); + //todo:: asegurar lo del dynamic_cast + IAudioSessionControl2* sessionControl; + sessionControlTmp->QueryInterface(__uuidof(IAudioSessionControl2), (void**)&sessionControl); + sessionControl->AddRef(); + sessionControlTmp->Release(); + Session* session = new Session(this, sessionControl, (size_t)i); + endpointSessions.push_back(session); + } + sessionEnumerator->Release(); +} + +void Endpoint::addSession(Session* session) { + session->setIndex(this->getSessionCount()); + endpointSessions.push_back(session); +} + +void Endpoint::activateEndpointVolume() { + //bool extraThread = false; + /* + * Forgive me, for MS has sinned, and now I must too. + */ + if (this->endpointVolume == nullptr){ + HRESULT result = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); + endpoint->Activate(IID_IAudioEndpointVolume, CLSCTX_ALL, NULL, (void**)&this->endpointVolume); + //if (endpointVolume == nullptr) { //why they returning 0 after dealing with the error jfc CO_E_NOTINITIALIZED) { + //CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE); + //extraThread = true; + //goto initialized; + //} + //log_debugcpp(std::string("no endpointVolume (IAudioEndpointVolume)")); + if (result == S_OK) + CoUninitialize(); + } +} + +void Endpoint::reloadEndpointChannels() { + if (this->endpointState == DEVICE_STATE_ACTIVE) { + if (FAILED(endpointVolume->GetChannelCount(&channelCount))) {log_debugcpp("get channel count fail");};/* */ + } +} + +void Endpoint::setIndex(uint64_t idx){ + this->idx = idx; +} + +uint64_t Endpoint::getIndex(){ + return idx; +} + +std::wstring Endpoint::getName(){ return friendlyName; } +std::wstring Endpoint::getId(){ + return endpointId; +} + float Endpoint::getVolume(int channel){ float volume; - if (channel == ENDPOINT_MASTER_VOLUME) { - if(FAILED(endpointVolume->GetMasterVolumeLevelScalar(&volume))) { log_debugcpp("si");} + if (channel == AudioChannel::CHANNEL_MAIN) { + if(FAILED(endpointVolume->GetMasterVolumeLevelScalar(&volume))) { /* log_debugcpp("si") */;} } else { - if(FAILED(endpointVolume->GetChannelVolumeLevelScalar(channel, &volume))) { log_debugcpp("si");} + if(FAILED(endpointVolume->GetChannelVolumeLevelScalar(channel, &volume))) { /* log_debugcpp("si"); */} } return volume; } +uint32_t Endpoint::getChannelCount(){ + return (uint32_t)channelCount; +} bool Endpoint::getMute(){ BOOL mut; - if(FAILED(endpointVolume->GetMute(&mut))) { log_debugcpp("si"); } - log_debugcpp("back BOOL is " << mut); + if(FAILED(endpointVolume->GetMute(&mut))) { /* TIP: Below */ } bool mute = (bool)mut; - log_debugcpp("translate to bool " << mute); return mute; } -/* - * float Endpoint::getLeftChannelVolume(){ - * float volume; - * if(FAILED(endpointVolume-> GetChannelVolumeLevelScalar(0, &volume)) { log_debugcpp("si"); } ); - * return volume; - * } - * - * float Endpoint::getRightChannelVolume(){ - * float volume; - * if(FAILED(endpointVolume-> GetChannelVolumeLevelScalar(1, &volume)) { log_debugcpp("si");} - * return volume; - * } - */ - - -void Endpoint::setVolume(int channel, float volume) { - if (channel == ENDPOINT_MASTER_VOLUME) { - if(FAILED(endpointVolume->SetMasterVolumeLevelScalar(volume, NULL))) { log_debugcpp("si"); }; - } else { - if(FAILED(endpointVolume->SetChannelVolumeLevelScalar(channel, volume, NULL))) { log_debugcpp("si"); }; +void Endpoint::setState(uint8_t state){ + this->endpointState = state; + if(state == EndpointState::ENDPOINT_ACTIVE) { + this->activateEndpointVolume(); + this->reloadEndpointChannels(); } } -void Endpoint::setMute() { - log_debugcpp("bool mute arrives as " << mut); - BOOL mut; - if(FAILED(endpointVolume->GetMute(&mut))) { log_debugcpp("si"); } - log_debugcpp("translate to BOOL as " << mute); - if(FAILED(endpointVolume->SetMute((mut == false ? 1 : 0), NULL))) { log_debugcpp("si"); }; +size_t Endpoint::getState(){ + return this->endpointState; } +void Endpoint::setVolume(NGuid guid, int channel, float volume) { + //TIP: There used to be log messages here. Now, it's a ghost town. + GUID tempMsGuid = NGuidToGUID(guid); + if (channel == AudioChannel::CHANNEL_MAIN) { + if(FAILED(endpointVolume->SetMasterVolumeLevelScalar(volume, &tempMsGuid))) {}; + } else { + if(FAILED(endpointVolume->SetChannelVolumeLevelScalar(channel, volume, &tempMsGuid))) {}; + } +} + +void Endpoint::setMute(NGuid guid, bool muted) { + GUID tempMsGuid = NGuidToGUID(guid); + if(FAILED(endpointVolume->SetMute(muted, &tempMsGuid))) { log_wdebugcpp(std::wstring(L"EndpointVolume null?")); }; +} + +void Endpoint::setVolumeCallback(EndpointVolumeCallback *epc){ + if(endpointVolume == nullptr) { + this->activateEndpointVolume(); + } + endpointVolume->RegisterControlChangeNotify((IAudioEndpointVolumeCallback*)epc); +} + +void Endpoint::removeVolumeCallback(EndpointVolumeCallback *epc){ + endpointVolume->UnregisterControlChangeNotify((IAudioEndpointVolumeCallback*)epc); +} + +Roles Endpoint::getRoles(){ + return this->endpointRoles; +} + +void Endpoint::setRoles(Roles role){ + //otro exe momento + STARTUPINFOEXW startupConfig; + PROCESS_INFORMATION processInfo; + SecureZeroMemory(&startupConfig, sizeof(STARTUPINFOEXW)); + SecureZeroMemory(&startupConfig.StartupInfo, sizeof(STARTUPINFOW)); + startupConfig.StartupInfo.cb = sizeof(STARTUPINFOEXW); + SecureZeroMemory(&processInfo, sizeof(PROCESS_INFORMATION)); + + std::wstring command = L"SoundVolumeView.exe /SetDefault " + endpointId + L" "; + std::wstring troublePair = L"0"; + switch (role) { + case Roles::ROLE_ALL: + /* + * console or multimedia, one sends both, at least for now; + * either cos of ms or dis guy; + * no choice but to treat them as one for now. + * command += L"all"; and nothing else would've been nice... + */ + troublePair = command + troublePair; + if(CreateProcessW( + NULL, + (wchar_t*)troublePair.c_str(), + NULL, + NULL, + false, + CREATE_UNICODE_ENVIRONMENT, + NULL, + NULL, + (LPSTARTUPINFOW)&startupConfig, + &processInfo + ) == true) { + WaitForSingleObject(processInfo.hProcess, INFINITE ); + CloseHandle(processInfo.hProcess); + CloseHandle(processInfo.hThread); + } + command += L"2"; + break; + case Roles::ROLE_CONSOLE: + command += std::to_wstring(0); + break; + case Roles::ROLE_MULTIMEDIA: + command += std::to_wstring(1); + break; + case Roles::ROLE_COMMUNICATIONS: + command += std::to_wstring(2); + break; + } + + if(CreateProcessW( + NULL, + (wchar_t*)command.c_str(), + NULL, + NULL, + false, + CREATE_UNICODE_ENVIRONMENT, + NULL, + NULL, + (LPSTARTUPINFOW)&startupConfig, + &processInfo + ) == true) { + WaitForSingleObject(processInfo.hProcess, INFINITE ); + CloseHandle(processInfo.hProcess); + CloseHandle(processInfo.hThread); + } +} + +void Endpoint::assignRoles(Roles role){ + Roles roles = (Roles)(endpointRoles | role); + this->endpointRoles = roles; +} + +void Endpoint::removeRoles(Roles role){ + Roles roles = (Roles)(endpointRoles ^ role); + this->endpointRoles = roles; +} + +void Endpoint::setFlow() { + IMMEndpoint* flowGetter; + //this should be as simple as writing IID_IMMEndpoint, but it just won't find the macro, so I copied it. Sad. + GUID manual; + manual.Data1 = 0x1be09788; + manual.Data2 = 0x6894; + manual.Data3 = 0x4089; + manual.Data4[0] = 0x85; + manual.Data4[1] = 0x86; + manual.Data4[2] = 0x9a; + manual.Data4[3] = 0x2a; + manual.Data4[4] = 0x6c; + manual.Data4[5] = 0x26; + manual.Data4[6] = 0x5a; + manual.Data4[7] = 0xc5; + if(FAILED(this->endpoint->QueryInterface((const _GUID)manual, (void**)&flowGetter))) + { log_debugcpp("no flow..."); } + EDataFlow MSflow; + HRESULT vafllar = flowGetter->GetDataFlow(&MSflow); + this->flow = (MSflow == EDataFlow::eRender ? Flows::FLOW_PLAYBACK : Flows::FLOW_CAPTURE); + flowGetter->Release(); +} + +Flows Endpoint::getFlow() { + return this->flow; +} + +/* sessions */ +std::vector Endpoint::getSessions() { + return endpointSessions; +} + +size_t Endpoint::getSessionCount() { + return endpointSessions.size(); +} + +void Endpoint::registerNewSessionNotification(EndpointNewSessionCallback* ensc){ + sessionManager->RegisterSessionNotification(ensc); +} + +void Endpoint::unregisterNewSessionNotification(EndpointNewSessionCallback* ensc){ + sessionManager->UnregisterSessionNotification(ensc); +} Endpoint::~Endpoint(){ - log_debugcpp("cum"); - free(friendlyName); + log_wdebugcpp(L"murio endpoint-san uwu"); properties->Release(); endpointVolume->Release(); endpoint->Release(); + sessionManager->Release(); } - -void Overseer::initCOMLibrary(){ - if(FAILED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED))) { log_debugcpp("si"); }; +void Overseer::initCOMLibrary() { + OutputDebugStringW(L"EPWidget creation\n"); + if(FAILED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE))) { + log_debugcpp("si"); }; //Retrieving endpoint enumerator @@ -87,52 +566,141 @@ void Overseer::initCOMLibrary(){ (void**)&deviceEnumerator)) ) { log_debugcpp("si"); }; + GUID tempGuid; + if(FAILED(CoCreateGuid(&tempGuid))) { log_debugcpp("Failed to obtain GUID: " ); }; + //todo: wtf? why is it working? floats are ptrs... + this->guid = GUIDToNGuid(&tempGuid); + + //if(FAILED(CoCreateInstance(__uuidof(CPolicyConfigClient), + // NULL, CLSCTX_ALL, __uuidof(IPolicyConfig10), (LPVOID *)&policyConfig))) {exit(-1);} + + //TODO: Release lpguid? + //TODO: Uninitialize COM } -void Overseer::reloadEndpoints() { +void Overseer::reloadEndpoints(Flows flow) { IMMDeviceCollection *deviceCollection; + unsigned int numEndpoints; + EDataFlow MSflow = (flow == Flows::FLOW_PLAYBACK ? EDataFlow::eRender : EDataFlow::eCapture); // | DEVICE_STATE_DISABLED | DEVICE_STATE_NOTPRESENT | DEVICE_STATE_UNPLUGGED - if(FAILED(deviceEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &deviceCollection) )) + // NOTPRESENT shows a lot of garbage, unnamed devices. + if(FAILED(deviceEnumerator->EnumAudioEndpoints(MSflow, DEVICE_STATE_ACTIVE | DEVICE_STATE_DISABLED | DEVICE_STATE_NOTPRESENT | DEVICE_STATE_UNPLUGGED, &deviceCollection) )) { log_debugcpp("si"); }; - - //Counting them - if(FAILED(deviceCollection->GetCount(&numPlaybackEndpoints))) { log_debugcpp("si");}; - if(numPlaybackEndpoints == 0) { log_debugcpp("si"); }; - + /* + * Counting them + */ + if(FAILED(deviceCollection->GetCount(&numEndpoints))) { log_debugcpp("si");}; + if(numEndpoints == 0) { log_debugcpp("si"); }; - //Retrieving actual endpoints and storing them on their own class - for (unsigned int i = 0; i < numPlaybackEndpoints; i++){ - IMMDevice *temp; + /* + * Retrieving actual endpoints and storing them on their own class + */ + IMMDevice *temp; + for (unsigned int i = 0; i < numEndpoints; i++){ if(deviceCollection->Item(i, &temp) != 0) { log_debugcpp("si"); }; - Endpoint *endpoint = new Endpoint(temp); - this->playbackDevices.push_back(endpoint); - //TODO: le porblemx std::cout << "ola" << std::endl; + Endpoint *endpoint = new Endpoint(temp, i); + + if (flow == Flows::FLOW_PLAYBACK) + this->playbackDevices.push_back(endpoint); + else + this->captureDevices.push_back(endpoint); + //TODO: le porblemx std::cout + "ola" + std::endl; } deviceCollection->Release(); + + /* + * Discerning default endpoints per role + * order: console, multimedia, communications + */ + for(int i = 0; i < ERole_enum_count; i++){ + ERole val; + switch(i) { + case 0: + val = eConsole; + break; + case 1: + val = eMultimedia; + break; + case 2: + val = eCommunications; + break; + } + deviceEnumerator->GetDefaultAudioEndpoint(MSflow, val, &temp); + LPWSTR id = nullptr; + + if (flow == Flows::FLOW_PLAYBACK) { + for (unsigned int j = 0; j < numEndpoints; j++) { + std::wstring eptId = playbackDevices.at(j)->getId(); + temp->GetId(&id); + int comparison = CompareStringEx(LOCALE_NAME_USER_DEFAULT, 0, eptId.c_str(), -987, id, -987, NULL, NULL, 0); + if (comparison - 2 == 0) { + log_wdebugcpp(L"ola defaul playback de " + + std::to_wstring(i) + L" es " + id); + playbackDevices.at(j)->assignRoles((Roles)(1 << i)); + } + } + } else { + for (unsigned int j = 0; j < numEndpoints; j++){ + std::wstring eptId = captureDevices.at(j)->getId(); + temp->GetId(&id); + int comparison = CompareStringEx(LOCALE_NAME_USER_DEFAULT, 0, eptId.c_str(), -987, id, -987, NULL, NULL, 0); + if (comparison - 2 == 0) { + log_wdebugcpp(L"ola defaul capture de " + + std::to_wstring(i) + L" es " + id); + captureDevices.at(j)->assignRoles((Roles)(1 << i)); + } + } + } + } } -Overseer::Overseer(){ +Endpoint* Overseer::addEndpoint(std::wstring endpointId, /* out */Flows* flow = nullptr) { + IMMDevice* newep; + if(FAILED(deviceEnumerator->GetDevice((LPCWSTR)endpointId.c_str(), &newep))) + log_debugcpp("ay caramba con la hot metida."); + + Endpoint *endpoint = new Endpoint(newep); + + Flows getFlow = endpoint->getFlow(); + if (getFlow == Flows::FLOW_PLAYBACK) { + endpoint->setIndex(osh->getPlaybackEndpointsCount()); + this->playbackDevices.push_back(endpoint); + } else { + endpoint->setIndex(osh->getCaptureEndpointsCount()); + this->captureDevices.push_back(endpoint); + } + if (flow != nullptr) *flow = getFlow; + return endpoint; +} + +Overseer::Overseer() { //: epsc(deviceEnumerator, playbackDevices){ //Initializing COM library + log_debugcpp("Initializing Overseer"); initCOMLibrary(); //Obtaining playback endpoint collection on this point in time - reloadEndpoints(); + reloadEndpoints(Flows::FLOW_PLAYBACK); + //reloadEndpoints(Flows::FLOW_CAPTURE); + + //Registering for endpoint information callback + this->epsc.fill(deviceEnumerator, playbackDevices, captureDevices); + if(FAILED(deviceEnumerator->RegisterEndpointNotificationCallback(((IMMNotificationClient*)&epsc)))) { log_debugcpp("when no enchufas......"); } } -//Overseer::int getDefaultPlaybackEndpoint(Endpoint** defaultEndpoint){ -//if (FAILED(deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &endpointPtr))) -// return 1; -//return 0; -//} - -//int Overseer::getDefaultCaptureEndpoint(Endpoint** defaultEndpoint); - +NGuid Overseer::getGuid() { + return guid; +} + std::vector Overseer::getPlaybackEndpoints() { return playbackDevices; } +std::vector Overseer::getCaptureEndpoints() { + return captureDevices; +} + Overseer::~Overseer(){ log_debugcpp("cum"); deviceEnumerator->Release(); diff --git a/src/back/backlasses.h b/src/back/backlasses.h index ebfe0e5..f70e6d1 100644 --- a/src/back/backlasses.h +++ b/src/back/backlasses.h @@ -1,43 +1,105 @@ #pragma once -#define WIN32_LEAN_AND_MEAN -#include "debug.h" +#include "msinclude.h" +#include "backsessionclasses.h" #include "global.h" -#include -#include +#include "contclasses.h" -#include -#include -#include -#include -#include - -#include -#include -#include -//#include -//#include -#include +class EndpointVolumeCallback; +class Session; class Endpoint { public: - Endpoint(IMMDevice* endpoint); - void setVolume(int channel, float volume); - /* float getLeftChannelVolume(); */ - /* float getRightChannelVolume(); */ + Endpoint(IMMDevice* endpoint, uint64_t idx); + //todo: how to forward declare delegate constructors? + Endpoint(IMMDevice* endpoint) : Endpoint(endpoint, 0) {}; + void reloadEndpointChannels(); + uint64_t getIndex(); + void setIndex(uint64_t idx); + void setVolume(NGuid guid, int channel, float volume); + uint32_t getChannelCount(); float getVolume(int channel); - void setMute(); + void setMute(NGuid guid, bool muted); bool getMute(); - LPWSTR getName(); + void setState(uint8_t state); + size_t getState(); + Roles getRoles(); + void setRoles(Roles role); + void assignRoles(Roles role); + void removeRoles(Roles role); + void setFlow(); + Flows getFlow(); + std::wstring getId(); + std::wstring getName(); + + void setVolumeCallback(EndpointVolumeCallback *epc); + void removeVolumeCallback(EndpointVolumeCallback *epc); + + /* sessions */ + std::vector getSessions(); + size_t getSessionCount(); + void addSession(Session* session); + void registerNewSessionNotification(EndpointNewSessionCallback* ensc); + void unregisterNewSessionNotification(EndpointNewSessionCallback* ensc); + ~Endpoint(); private: + void inline activateEndpointVolume(); + void inline activateEndpointSessions(); + + std::vector endpointSessions; + uint32_t channelCount = 0; IMMDevice* endpoint; - IAudioEndpointVolume *endpointVolume ; + IAudioSessionManager2 *sessionManager; + Flows flow; + IAudioEndpointVolume *endpointVolume = nullptr; IPropertyStore *properties; - LPWSTR friendlyName; - // LPWSTR endpointID = NULL; + std::wstring friendlyName; + std::wstring endpointId; + unsigned long endpointState; + Roles endpointRoles = (Roles)0; + uint64_t idx; + /* Not implemented in llvm-mingw. Sad! + * IAudioMeterInformation *endpointPeakMeter = nullptr; + */ +}; + +class EndpointVolumeCallback : public IAudioEndpointVolumeCallback { + + public: + EndpointVolumeCallback(Endpoint* ep); + + ULONG AddRef(); + ULONG Release(); + HRESULT QueryInterface(REFIID riid, VOID **ppvInterface); + HRESULT OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA update); + //~EndpointVolumeCallback(); + + private: + ULONG ref = 1; + Endpoint* ep; +}; + +class EndpointSituationCallback : public IMMNotificationClient { + public: + //EndpointSituationCallback(IMMDeviceEnumerator *deviceEnumerator, std::vector playbackDevices); + ULONG AddRef(); + ULONG Release(); + HRESULT QueryInterface(REFIID riid, VOID **ppvInterface); + HRESULT OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId); + HRESULT OnDeviceAdded(LPCWSTR pwstrDeviceId); + HRESULT OnDeviceRemoved(LPCWSTR pwstrDeviceId); + HRESULT OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState); + HRESULT OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key); + + void fill(IMMDeviceEnumerator *deviceEnumerator, std::vector playbackDevices, std::vector captureDevices); + private: + ULONG ref = 1; + IMMDeviceEnumerator *deviceEnumerator; + std::vector playbackDevices; + std::vector captureDevices; }; class Overseer { @@ -45,7 +107,14 @@ class Overseer { public: Overseer(); std::vector getPlaybackEndpoints(); - void reloadEndpoints(); + std::vector getCaptureEndpoints(); + + void reloadEndpoints(Flows flow); + Endpoint* addEndpoint(std::wstring endpointId, /* out */ Flows* flow); + NGuid getGuid(); + //void setEndpointStatusCallback(); + //void setEndpointStatusCallback(); + //~Overseer(); //int getDefaultPlaybackEndpoint(Endpoint** defaultEndpoint); //int getDefaultCaptureEndpoint(Endpoint** defaultEndpoint); @@ -54,12 +123,28 @@ class Overseer { ~Overseer(); private: - unsigned int numPlaybackEndpoints; + NGuid guid; + IMMDeviceEnumerator *deviceEnumerator; + EndpointSituationCallback epsc; + //IPolicyConfig *policyConfig; std::vector playbackDevices; + std::vector captureDevices; void initCOMLibrary(); //IMMDeviceCollection *deviceCollection; //int numCaptureEndpoints; //std::vector *captureDevices; }; +class EndpointNewSessionCallback : public IAudioSessionNotification { + public: + EndpointNewSessionCallback(EndpointHandler *eph); + ULONG AddRef(); + ULONG Release(); + HRESULT QueryInterface(REFIID riid, VOID **ppvInterface); + HRESULT OnSessionCreated(IAudioSessionControl *NewSession); + + private: + ULONG ref = 1; + EndpointHandler *eph; +}; diff --git a/src/back/backsessionclasses.cpp b/src/back/backsessionclasses.cpp new file mode 100644 index 0000000..ced7fb1 --- /dev/null +++ b/src/back/backsessionclasses.cpp @@ -0,0 +1,282 @@ +#include "backsessionclasses.h" +#include "backfuncs.h" + +SessionStateCallback::SessionStateCallback(SessionHandler *sh) { + this->sh = sh; +} + +ULONG SessionStateCallback::AddRef() { + return InterlockedIncrement(&ref); +} + +ULONG SessionStateCallback::Release() { + ULONG tempRef = InterlockedDecrement(&ref); + if (tempRef == 0) { + delete this; + } + return tempRef; +} + +HRESULT SessionStateCallback::QueryInterface(REFIID riid, VOID **ppvInterface) { + if (IID_IUnknown == riid) + { + AddRef(); + *ppvInterface = (IUnknown*)this; + } + else if (__uuidof(IAudioSessionNotification) == riid) + { + AddRef(); + *ppvInterface = (IMMNotificationClient*)this; + } + else + { + *ppvInterface = NULL; + return E_NOINTERFACE; + } + return S_OK; +} + +HRESULT SessionStateCallback::OnDisplayNameChanged(LPCWSTR NewDisplayName, LPCGUID EventContext) { + return S_OK; +} + +HRESULT SessionStateCallback::OnIconPathChanged(LPCWSTR NewIconPath, LPCGUID EventContex) { + return S_OK; +} + +HRESULT SessionStateCallback::OnSimpleVolumeChanged(float NewVolume, BOOL NewMute, LPCGUID EventContext) { + sh->getVolumeInfo()->muted = NewMute; + sh->getVolumeInfo()->mainVolume = NewVolume; + sh->getVolumeInfo()->caller = GUIDToNGuid((LPGUID)EventContext); + /* + * if (NewMute) + * { + * printf("MUTE\n"); + * } + * else + * { + * printf("Volume = %d percent\n", + * (UINT32)(100*NewVolume + 0.5)); + * } + */ + return S_OK; +} + +HRESULT SessionStateCallback::OnChannelVolumeChanged(DWORD ChannelCount, float NewChannelVolumeArray[], DWORD ChangedChannel, LPCGUID EventContext) { + return S_OK; +} + +HRESULT SessionStateCallback::OnGroupingParamChanged(LPCGUID NewGroupingParam, LPCGUID EventContext) { + return S_OK; +} + +HRESULT SessionStateCallback::OnStateChanged(AudioSessionState NewState) { + SessionState newState;// = sh->getState(); + switch (NewState) { + case AudioSessionStateActive: + newState = SessionState::ACTIVE; + break; + case AudioSessionStateInactive: + newState = SessionState::INACTIVE; + break; + case AudioSessionStateExpired: + newState = SessionState::EXPIRED; + break; + } + + sh->reviseSessionShowing(newState); + return S_OK; +} + +HRESULT SessionStateCallback::OnSessionDisconnected(AudioSessionDisconnectReason DisconnectReason) { + sh->setState(SessionState::DISCONNECTED); + sh->reviseSessionShowing(SessionState::DISCONNECTED); + return S_OK; +} + +Session::Session(Endpoint* ep, IAudioSessionControl2* sessionControl, size_t idx) { + this->ep = ep; + this->sessionControl = sessionControl; + this->idx = idx; + + AudioSessionState msState; + sessionControl->GetState(&msState); + switch (msState) { + case AudioSessionState::AudioSessionStateActive: + this->sessionState = SessionState::ACTIVE; + break; + case AudioSessionState::AudioSessionStateInactive: + this->sessionState = SessionState::INACTIVE; + break; + case AudioSessionState::AudioSessionStateExpired: + this->sessionState = SessionState::EXPIRED; + break; + } + + sessionControl->QueryInterface(__uuidof(ISimpleAudioVolume), (void**)&sessionVolume); + DWORD pid; + sessionControl->GetProcessId(&pid); + if (sessionControl->IsSystemSoundsSession() == S_OK) + this->sessionName = std::wstring(LSTRING_SYSTEM_SOUNDS); + else { + LPWSTR sessionDisplayName; + this->sessionControl->GetDisplayName(&sessionDisplayName); + if (!wcscmp(sessionDisplayName, L"")) + this->sessionName = this->fetchProcessName(pid); + else + this->sessionName = std::wstring(sessionDisplayName); + CoTaskMemFree(sessionDisplayName); + } +} + +float Session::getVolume(int channel){ + float volume; + if (channel == AudioChannel::CHANNEL_MAIN) { + if(FAILED(sessionVolume->GetMasterVolume(&volume))) { /* log_debugcpp("si") */;} + } else { + return 0.0; + //if(FAILED(endpointVolume->GetChannelVolumeLevelScalar(channel, &volume))) { /* log_debugcpp("si"); */} + } + return volume; +} + +/* + * uint32_t Endpoint::getChannelCount(){ + * return (uint32_t)channelCount; + * } + */ + +std::wstring Session::getName() { + return sessionName; +} + +bool Session::getMute() { + BOOL mut; + if(FAILED(sessionVolume->GetMute(&mut))) { /* TIP: Below */ } + bool mute = (bool)mut; + return mute; +} + +void Session::setVolume(NGuid guid, int channel, float volume) { + //TIP: There used to be log messages here. Now, it's a ghost town. + GUID tempMsGuid = NGuidToGUID(guid); + if (channel == AudioChannel::CHANNEL_MAIN) { + if(FAILED(sessionVolume->SetMasterVolume(volume, &tempMsGuid))) {}; + } else { + //if(FAILED(sessionVolume->SetChannelVolumeLevelScalar(channel, volume, &tempMsGuid))) {}; + } +} + +void Session::setIndex(size_t idx) { + this->idx = idx; +} + +void Session::setMute(NGuid guid, bool muted) { + GUID tempMsGuid = NGuidToGUID(guid); + if(FAILED(sessionVolume->SetMute(muted, &tempMsGuid))) { log_wdebugcpp(std::wstring(L"SessionVolume null?")); }; +} + +std::wstring Session::fetchProcessName(DWORD pid) { + /* + * https://learn.microsoft.com/en-us/windows/win32/api/tlhelp32/nf-tlhelp32-createtoolhelp32snapshot + * https://stackoverflow.com/questions/11843368/how-to-get-process-description + */ + + /* Executable path retrieval */ + std::wstring exePath = L""; + + HANDLE processList = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, pid); + if (processList == INVALID_HANDLE_VALUE) { + log_wdebugcpp(L"aye no procname."); + return exePath; + } + + MODULEENTRY32W me32w; + me32w.dwSize = sizeof(MODULEENTRY32W); + if(Module32FirstW(processList, &me32w)) { + do { + if (me32w.th32ProcessID == pid) { + exePath = std::wstring(me32w.szExePath); + break; + /* + * However, if the calling process is a 32-bit process, you must call the + * QueryFullProcessImageName function to retrieve the full path of the + * executable file for a 64-bit process. + */ + } + } while(Module32NextW(processList, &me32w)); + } + CloseHandle(processList); + + /* File description retrieval */ + struct LANGANDCODEPAGE { + WORD wLanguage; + WORD wCodePage; + } *translationArray; + + DWORD filler; + DWORD fileVersionInfoSize = GetFileVersionInfoSizeW(exePath.c_str(), &filler); + if (!fileVersionInfoSize) return exePath; + + void* fileVersionInfo = malloc(fileVersionInfoSize); + if(!GetFileVersionInfoW(exePath.c_str(),0,fileVersionInfoSize, fileVersionInfo)) + return exePath; + + UINT translationArrayLen = 0; + if (!VerQueryValueW(fileVersionInfo, L"\\VarFileInfo\\Translation", (LPVOID*)&translationArray, &translationArrayLen)) + return exePath; + + bool match = false; + for (UINT i = 0; i < (translationArrayLen / sizeof(LANGANDCODEPAGE)); i++) { + wchar_t fileDescriptionKey[256]; + LANGID defaultUILanguage = GetUserDefaultUILanguage(); + if (defaultUILanguage != translationArray[i].wLanguage) + continue; + + match = true; + wchar_t* fileDescription = NULL; + UINT fileDescriptionSize = 0; + swprintf(fileDescriptionKey, L"\\StringFileInfo\\%04x%04x\\FileDescription", + translationArray[i].wLanguage, translationArray[i].wCodePage); + if (VerQueryValueW(fileVersionInfo, fileDescriptionKey, (LPVOID*)&fileDescription, &fileDescriptionSize)) { + exePath = std::wstring(fileDescription); + } + } + + if (!match && 1 <= (translationArrayLen / sizeof(LANGANDCODEPAGE))) { + wchar_t fileDescriptionKey[256]; + +wchar_t* fileDescription = NULL; + UINT fileDescriptionSize = 0; + swprintf(fileDescriptionKey, L"\\StringFileInfo\\%04x%04x\\FileDescription", + translationArray[0].wLanguage, translationArray[0].wCodePage); + if (VerQueryValueW(fileVersionInfo, fileDescriptionKey, (LPVOID*)&fileDescription, &fileDescriptionSize)) { + exePath = std::wstring(fileDescription); + } + } + + free(fileVersionInfo); + return exePath; +} + +//todo: conflicting names. change callback name +void Session::setState(SessionState state) { + sessionState = state; +} + +SessionState Session::getState() { + return sessionState; +} + +void Session::setStateCallback(SessionStateCallback *ssc){ + sessionControl->RegisterAudioSessionNotification((IAudioSessionEvents*) ssc); +} + +void Session::removeStateCallback(SessionStateCallback *ssc){ + sessionControl->UnregisterAudioSessionNotification((IAudioSessionEvents*) ssc); +} + +Session::~Session() { + sessionControl->Release(); + sessionVolume->Release(); +} diff --git a/src/back/backsessionclasses.h b/src/back/backsessionclasses.h new file mode 100644 index 0000000..e6c8490 --- /dev/null +++ b/src/back/backsessionclasses.h @@ -0,0 +1,57 @@ +#pragma once +#include "msinclude.h" + +#include "global.h" +#include "contclasses.h" + +class Endpoint; + +class SessionStateCallback : public IAudioSessionEvents { + public: + SessionStateCallback(SessionHandler *sh); + ULONG AddRef(); + ULONG Release(); + HRESULT QueryInterface(REFIID riid, VOID **ppvInterface); + HRESULT OnSessionCreated(IAudioSessionControl *NewSession); + HRESULT OnChannelVolumeChanged(DWORD ChannelCount, float NewChannelVolumeArray[], DWORD ChangedChannel, LPCGUID EventContext); + HRESULT OnDisplayNameChanged(LPCWSTR NewDisplayName, LPCGUID EventContext); + HRESULT OnGroupingParamChanged( LPCGUID NewGroupingParam, LPCGUID EventContext); + HRESULT OnIconPathChanged(LPCWSTR NewIconPath, LPCGUID EventContext); + HRESULT OnSessionDisconnected(AudioSessionDisconnectReason DisconnectReason); + HRESULT OnSimpleVolumeChanged(float NewVolume, BOOL NewMute, LPCGUID EventContext); + HRESULT OnStateChanged(AudioSessionState NewState); + + private: + ULONG ref = 1; + SessionHandler *sh; +}; + +class Session { + + public: + Session(Endpoint* ep, IAudioSessionControl2* sessionControl, size_t idx); + Session(Endpoint* ep, IAudioSessionControl2* sessionControl) : Session(ep, sessionControl, SIZE_MAX) {}; + void setVolume(NGuid guid, int channel, float volume); + float getVolume(int channel); + void setMute(NGuid guid, bool muted); + bool getMute(); + SessionState getState(); + void setState(SessionState state); + void setIndex(size_t idx); + std::wstring getName(); + + void setStateCallback(SessionStateCallback *ssc); + void removeStateCallback(SessionStateCallback *ssc); + ~Session(); + //uint32_t getChannelCount(); + + private: + std::wstring fetchProcessName(DWORD pid); + std::wstring sessionName; + SessionState sessionState; + Endpoint* ep; + IAudioSessionControl2* sessionControl = nullptr; + ISimpleAudioVolume* sessionVolume = nullptr; + size_t idx; +}; + diff --git a/src/back/ipolicyconfig.h b/src/back/ipolicyconfig.h new file mode 100644 index 0000000..2e10aab --- /dev/null +++ b/src/back/ipolicyconfig.h @@ -0,0 +1,192 @@ +// ---------------------------------------------------------------------------- +// PolicyConfig.h +// Undocumented COM-interface IPolicyConfig. +// Use for set default audio render endpoint +// @author EreTIk +// ---------------------------------------------------------------------------- + + +#pragma once + + +interface DECLSPEC_UUID("CA286FC3-91FD-42C3-8E9B-CAAFA66242E3") +IPolicyConfig10; + +interface DECLSPEC_UUID("00000000-0000-0000-C000-000000000046") +IPolicyConfig10_1; + +interface DECLSPEC_UUID("F8679F50-850A-41CF-9C72-430F290290C8") +IPolicyConfig7; + +/* interface DECLSPEC_UUID("568B9108-44BF-40B4-9006-86AFE5B5A620") */ +/* IPolicyConfigVista; */ + +interface DECLSPEC_UUID("f8679f50-850a-41cf-9c72-430f290290c8") +IPolicyConfig; +class DECLSPEC_UUID("870af99c-171d-4f9e-af0d-e63df40c2bc9") +CPolicyConfigClient; +// ---------------------------------------------------------------------------- +// class CPolicyConfigClient +// {870af99c-171d-4f9e-af0d-e63df40c2bc9} +// +// interface IPolicyConfig +// {f8679f50-850a-41cf-9c72-430f290290c8} +// +// Query interface: +// CComPtr PolicyConfig; +// PolicyConfig.CoCreateInstance(__uuidof(CPolicyConfigClient)); +// +// @compatible: Windows 7 and Later +// ---------------------------------------------------------------------------- +interface IPolicyConfig : public IUnknown +{ +public: + + virtual HRESULT GetMixFormat( + PCWSTR, + WAVEFORMATEX ** + ); + + virtual HRESULT STDMETHODCALLTYPE GetDeviceFormat( + PCWSTR, + INT, + WAVEFORMATEX ** + ); + + virtual HRESULT STDMETHODCALLTYPE ResetDeviceFormat( + PCWSTR + ); + + virtual HRESULT STDMETHODCALLTYPE SetDeviceFormat( + PCWSTR, + WAVEFORMATEX *, + WAVEFORMATEX * + ); + + virtual HRESULT STDMETHODCALLTYPE GetProcessingPeriod( + PCWSTR, + INT, + PINT64, + PINT64 + ); + + virtual HRESULT STDMETHODCALLTYPE SetProcessingPeriod( + PCWSTR, + PINT64 + ); + + virtual HRESULT STDMETHODCALLTYPE GetShareMode( + PCWSTR, + struct DeviceShareMode * + ); + + virtual HRESULT STDMETHODCALLTYPE SetShareMode( + PCWSTR, + struct DeviceShareMode * + ); + + virtual HRESULT STDMETHODCALLTYPE GetPropertyValue( + PCWSTR, + const PROPERTYKEY &, + PROPVARIANT * + ); + + virtual HRESULT STDMETHODCALLTYPE SetPropertyValue( + PCWSTR, + const PROPERTYKEY &, + PROPVARIANT * + ); + + virtual HRESULT STDMETHODCALLTYPE SetDefaultEndpoint( + PCWSTR wszDeviceId, + ERole eRole + ); + + virtual HRESULT STDMETHODCALLTYPE SetEndpointVisibility( + PCWSTR, + INT + ); +}; + +/* interface DECLSPEC_UUID("568b9108-44bf-40b4-9006-86afe5b5a620") */ +/* IPolicyConfigVista; */ +/* class DECLSPEC_UUID("294935CE-F637-4E7C-A41B-AB255460B862") */ +/* CPolicyConfigVistaClient; */ +/* // ---------------------------------------------------------------------------- */ +/* // class CPolicyConfigVistaClient */ +/* // {294935CE-F637-4E7C-A41B-AB255460B862} */ +/* // */ +/* // interface IPolicyConfigVista */ +/* // {568b9108-44bf-40b4-9006-86afe5b5a620} */ +/* // */ +/* // Query interface: */ +/* // CComPtr PolicyConfig; */ +/* // PolicyConfig.CoCreateInstance(__uuidof(CPolicyConfigVistaClient)); */ +/* // */ +/* // @compatible: Windows Vista and Later */ +/* // ---------------------------------------------------------------------------- */ +/* interface IPolicyConfigVista : public IUnknown */ +/* { */ +/* public: */ + +/* virtual HRESULT GetMixFormat( */ +/* PCWSTR, */ +/* WAVEFORMATEX ** */ +/* ); // not available on Windows 7, use method from IPolicyConfig */ + +/* virtual HRESULT STDMETHODCALLTYPE GetDeviceFormat( */ +/* PCWSTR, */ +/* INT, */ +/* WAVEFORMATEX ** */ +/* ); */ + +/* virtual HRESULT STDMETHODCALLTYPE SetDeviceFormat( */ +/* PCWSTR, */ +/* WAVEFORMATEX *, */ +/* WAVEFORMATEX * */ +/* ); */ + +/* virtual HRESULT STDMETHODCALLTYPE GetProcessingPeriod( */ +/* PCWSTR, */ +/* INT, */ +/* PINT64, */ +/* PINT64 */ +/* ); // not available on Windows 7, use method from IPolicyConfig */ + +/* virtual HRESULT STDMETHODCALLTYPE SetProcessingPeriod( */ +/* PCWSTR, */ +/* PINT64 */ +/* ); // not available on Windows 7, use method from IPolicyConfig */ + +/* virtual HRESULT STDMETHODCALLTYPE GetShareMode( */ +/* PCWSTR, */ +/* struct DeviceShareMode * */ +/* ); // not available on Windows 7, use method from IPolicyConfig */ + +/* virtual HRESULT STDMETHODCALLTYPE SetShareMode( */ +/* PCWSTR, */ +/* struct DeviceShareMode * */ +/* ); // not available on Windows 7, use method from IPolicyConfig */ + +/* virtual HRESULT STDMETHODCALLTYPE GetPropertyValue( */ +/* PCWSTR, */ +/* const PROPERTYKEY &, */ +/* PROPVARIANT * */ +/* ); */ + +/* virtual HRESULT STDMETHODCALLTYPE SetPropertyValue( */ +/* PCWSTR, */ +/* const PROPERTYKEY &, */ +/* PROPVARIANT * */ +/* ); */ + +/* virtual HRESULT STDMETHODCALLTYPE SetDefaultEndpoint( */ +/* __in PCWSTR wszDeviceId, */ +/* __in ERole eRole */ +/* ); */ + +/* virtual HRESULT STDMETHODCALLTYPE SetEndpointVisibility( */ +/* PCWSTR, */ +/* INT */ +/* ); // not available on Windows 7, use method from IPolicyConfig */ +/* }; */ diff --git a/src/back/msinclude.h b/src/back/msinclude.h new file mode 100644 index 0000000..fc7beac --- /dev/null +++ b/src/back/msinclude.h @@ -0,0 +1,26 @@ +#pragma once + +#define _WIN32_WINNT 0x0A00 +#include + +//done by qt by def #define UNICODE + +#include +#include +#include +#include +#include +#include +#include +//#include + +#include +#include +#include +//#include +//#include +#include +#include +#include "ipolicyconfig.h" +#include +#include diff --git a/src/cont/contclasses.cpp b/src/cont/contclasses.cpp index bd05f88..38c30b1 100644 --- a/src/cont/contclasses.cpp +++ b/src/cont/contclasses.cpp @@ -1,50 +1,337 @@ +#include "backlasses.h" #include "contclasses.h" +//TODO: pragma once -Overseer OverseerHandler::os; - -EndpointHandler::EndpointHandler(Endpoint *ept, QObject *parent) : QObject(parent) { - this->ept = ept; - eptName = QString::fromStdWString(ept->getName()); +EndpointHandler::EndpointHandler(uint64_t idx, Flows flow) { + //std::vector endpoints = osh->getPlaybackEndpoints().at(idx); + this->idx = idx; + this->flow = flow; + this->ep = (flow == Flows::FLOW_PLAYBACK ? osh->getPlaybackEndpoints().at(idx) : osh->getCaptureEndpoints().at(idx)); + + epc = new EndpointVolumeCallback(ep); + ensc = new EndpointNewSessionCallback(this); + this->callbackInfo.caller = osh->getGuid(); + ep->registerNewSessionNotification(ensc); + //epName = ep->getName(); + this->setBackEndpointVolumeCallbackInfoContent(this->getState()); + osh->pushBackEndpointHandler(this, flow); + + if (this->flow == Flows::FLOW_PLAYBACK) { + for (int i = 0; i < this->getSessionCount(); i++) { + SessionHandler* sessionHandler = new SessionHandler(this, this->getSessions().at(i),i); + sessionHandlers.push_back(sessionHandler); + } + } } + +void OverseerHandler::pushBackEndpointHandler(EndpointHandler* eph, Flows flow) { + if (eph == nullptr) return; + if (flow == Flows::FLOW_PLAYBACK) + this->playbackEndpointHandlers.push_back(eph); + else + this->captureEndpointHandlers.push_back(eph); + return; +} + +void EndpointHandler::setFrontVisibilityInfo(EndpointState state, uint64_t frontIdx){ + ephfv.visibility = state; + ephfv.frontIdx = frontIdx; +} + +uint64_t EndpointHandler::getFrontVisibilityIndex(){ + return ephfv.frontIdx; +} + +EndpointState EndpointHandler::getFrontVisibilityState(){ + return ephfv.visibility; +} + +Flows EndpointHandler::getFlow(){ + return ep->getFlow(); +} + +/* these two, currently unused. If I use them, I should feel bad. + * Endpoint* EndpointHandler::getEndpoint() { + * return this->ep; + * } + * + * EndpointVolumeCallback* EndpointHandler::getEndpointVolumeCallback() { + * return this->epc; + * } + */ + +BackEndpointVolumeCallbackInfo* EndpointHandler::getCallbackInfo(){ + return &this->callbackInfo; +} + +uint32_t EndpointHandler::getChannelCount(){ + return ep->getChannelCount(); +} + +void EndpointHandler::setIndex(uint64_t idx){ + this->idx = idx; +} + +uint64_t EndpointHandler::getIndex(){ + return idx; +} + /* * -1 for master volume */ -void EndpointHandler::setValue(int channel, int value){ - if (channel == ENDPOINT_MASTER_VOLUME) - ept->setVolume(channel, (float)value / 100); - else ept->setVolume(channel, (float)value / 100); +void EndpointHandler::setVolume(NGuid guid, int channel, int value){ + if (channel == AudioChannel::CHANNEL_MAIN) + ep->setVolume(guid, channel, (float)value / 100); + else ep->setVolume(guid, channel, (float)value / 100); } -void EndpointHandler::setMute(){ - //Qt momento, de ahi el param? - log_debugcpp("kinda handling the muting tbh"); - ept->setMute(); +void EndpointHandler::setMute(NGuid guid, bool muted){ + ep->setMute(guid, muted); } -QString EndpointHandler::getName(){ - return eptName; +std::wstring EndpointHandler::getName(){ + return ep->getName(); +} + +std::wstring EndpointHandler::getId(){ + return ep->getId(); } float EndpointHandler::getVolume(int channel){ - return ept->getVolume(channel); + return ep->getVolume(channel); } bool EndpointHandler::getMute(){ - return ept->getMute(); + return ep->getMute(); } -Overseer* OverseerHandler::getOverseer(){ - return &os; +size_t EndpointHandler::getState(){ + return ep->getState(); } -OverseerHandler::OverseerHandler(QObject *parent) : QObject(parent) { +void EndpointHandler::setBackEndpointVolumeCallbackInfoContent(uint8_t state) { + if(state == EndpointState::ENDPOINT_ACTIVE) { + callbackInfo.muted = this->getMute(); + callbackInfo.mainVolume = this->getVolume(AudioChannel::CHANNEL_MAIN); + callbackInfo.channels = this->getChannelCount(); + ep->setVolumeCallback(epc); + callbackInfo.channelVolumes.resize(this->callbackInfo.channels); + for(uint32_t i = 0; i < this->getChannelCount(); i++){ + callbackInfo.channelVolumes.at(i) = this->getVolume(i); + } + } +} + +void EndpointHandler::setState(uint8_t state){ + ep->setState(state); + this->setBackEndpointVolumeCallbackInfoContent(state); +} + +void EndpointHandler::setState(uint8_t state, uint64_t index){ + ep->setState(state); + this->setFrontVisibilityInfo((EndpointState)state, index); + this->setBackEndpointVolumeCallbackInfoContent(state); +} + +uint8_t EndpointHandler::getRoles(){ + return ep->getRoles(); +} + +void EndpointHandler::setRoles(Roles newRole){ + ep->setRoles(newRole); +} + +void EndpointHandler::assignRoles(Roles newRole){ + ep->assignRoles(newRole); +} + +void EndpointHandler::removeRoles(Roles newRole){ + ep->removeRoles(newRole); +} + +void EndpointHandler::setAddSessionWidgetFunction(std::function addSessionWidget) { + this->addSessionWidget = addSessionWidget; +} + +void EndpointHandler::setRemoveSessionWidgetFunction(std::function removeSessionWidget) { + this->removeSessionWidget = removeSessionWidget; +} + +/* sessions */ +size_t EndpointHandler::getSessionCount() { + return ep->getSessionCount(); +} + +std::vector EndpointHandler::getSessions(){ + return ep->getSessions(); +} + +std::vector EndpointHandler::getSessionHandlers(){ + return this->sessionHandlers; +} + +Endpoint* EndpointHandler::getEndpoint() { + return this->ep; +} + +void EndpointHandler::addSessionSendFront(Session* session) { + ep->addSession(session); + + SessionHandler* sessionHandler = new SessionHandler(this, session, (getSessionCount() - 1)); + sessionHandlers.push_back(sessionHandler); + this->addSessionWidget(sessionHandler); +} + +void EndpointHandler::sendSessionToFront(SessionHandler* sh) { + this->addSessionWidget(sh); +} + +void EndpointHandler::removeSessionFromFront(SessionHandler* sh) { + this->removeSessionWidget(sh); +} + +EndpointHandler::~EndpointHandler() { + ep->removeVolumeCallback(epc); + ep->unregisterNewSessionNotification(ensc); + epc->Release(); + delete ep; +} + +OverseerHandler::OverseerHandler() { + this->os = new Overseer(); +} + +std::vector OverseerHandler::getPlaybackEndpoints() { + return this->os->getPlaybackEndpoints(); +} + +std::vector OverseerHandler::getCaptureEndpoints() { + return this->os->getCaptureEndpoints(); +} + +std::vector OverseerHandler::getPlaybackEndpointHandlers(){ + return playbackEndpointHandlers; +} + +std::vector OverseerHandler::getCaptureEndpointHandlers(){ + return captureEndpointHandlers; +} + +uint64_t OverseerHandler::getPlaybackEndpointsCount(){ + return this->os->getPlaybackEndpoints().size(); +} + +uint64_t OverseerHandler::getCaptureEndpointsCount(){ + return this->os->getCaptureEndpoints().size(); +} + +void OverseerHandler::reloadEndpointHandlers(){ + //todo: add capture + //std::vector* ephs = new std::vector; + log_debugcpp("Playback VSize: " + std::to_string(this->getPlaybackEndpointsCount())); + + for(uint64_t i = 0; i < this->getPlaybackEndpointsCount(); i++){ + log_debugcpp("Creating Playback handler " + std::to_string(i)); + + EndpointHandler* ephexx = new EndpointHandler(i, Flows::FLOW_PLAYBACK); + //this->playbackEndpointHandlers.push_back(ephexx); + log_debugcpp("Created Playback handler " + std::to_string(i) + ", adding to vector. " + " VSize: " + std::to_string(this->playbackEndpointHandlers.size())); + + } + + log_debugcpp("Capture VSize: " + + std::to_string(this->getCaptureEndpointsCount())); + + for(uint64_t i = 0; i < this->getCaptureEndpointsCount(); i++){ + log_debugcpp("Creating Capture handler " + std::to_string(i)); + + /* + * if(i < (this->captureEndpointHandlers.size()) && + * this->captureEndpointHandlers.at(i) != nullptr) + * delete captureEndpointHandlers.at(i); + */ + + EndpointHandler* ephoo = new EndpointHandler(i, Flows::FLOW_CAPTURE); + //this->captureEndpointHandlers.push_back(ephoo); + log_debugcpp("Created Capture handler " + std::to_string(i) + ", adding to vector. " + " VSize: " + std::to_string(this->captureEndpointHandlers.size())); + + /* + * if (i >= this->captureEndpointHandlers.size()) + * captureEndpointHandlers.push_back(eph); + * else captureEndpointHandlers.at(i) = eph; + */ + } + //setEndpointHandlers(ephs); } -std::vector* OverseerHandler::getEndpointHandlers(){ - return endpointHandlers; +EndpointHandler* OverseerHandler::addEndpoint(std::wstring endpointId, /* out */ Flows *flow = nullptr){ + Flows localFlow; + Endpoint* newEp = this->os->addEndpoint(endpointId, &localFlow); + + uint64_t ephIdx = (localFlow == Flows::FLOW_PLAYBACK ? this->getPlaybackEndpointsCount() : this->getCaptureEndpointsCount()) - 1; + + EndpointHandler* newEph = new EndpointHandler(ephIdx, localFlow); + // std::vector getPlaybackEndpointHandlers(); + //std::vector getCaptureEndpointHandlers(); + if (flow != nullptr) *flow = localFlow; + return newEph; } -void OverseerHandler::setEndpointHandlers(std::vector *ephs){ - this->endpointHandlers = ephs; +NGuid OverseerHandler::getGuid() { + return this->os->getGuid(); +} + +void OverseerHandler::setChangeFrontDefaultsFunction(std::function changeFrontDefaults){ + this->changeFrontDefaults = changeFrontDefaults; +} + +void OverseerHandler::changeFrontDefaultsCallback(Roles role, std::wstring endpointId) { + this->changeFrontDefaults(role, endpointId); +} + +void OverseerHandler::reviseEndpointShowing(std::wstring endpointId, EndpointState state) { + std::vector allHandlers; + allHandlers.insert(allHandlers.end(), this->captureEndpointHandlers.begin(), this->captureEndpointHandlers.end()); + allHandlers.insert(allHandlers.end(), this->playbackEndpointHandlers.begin(), this->playbackEndpointHandlers.end()); + EndpointHandler* eph = nullptr; + for (auto loopEph : allHandlers) { + if (loopEph->getId() == endpointId) { + eph = loopEph; + break; + } + } + + //debug + Flows flow; + if (!eph) { + if (state ^ EndpointState::ENDPOINT_ACTIVE) return; + //return; + //flow = Flows::FLOW_CAPTURE; + eph = osh->addEndpoint(endpointId, &flow); + } else + flow = eph->getFlow(); + + //todo: mic done but disabled. Tab-kun will come... + if (flow == Flows::FLOW_CAPTURE) return; + + + if(eph && EndpointState::ENDPOINT_ACTIVE & state) { + this->addEndpointWidget(eph); + } else if (eph && eph->getFrontVisibilityState() == EndpointState::ENDPOINT_ACTIVE){ + this->removeEndpointWidget(eph->getFrontVisibilityIndex()); + } + return; +} + +void OverseerHandler::setAddEndpointWidgetFunction(std::function addEndpointWidget){ + this->addEndpointWidget = addEndpointWidget; +} + +void OverseerHandler::setRemoveEndpointWidgetFunction(std::function removeEndpointWidget){ + this->removeEndpointWidget = removeEndpointWidget; +} + +void OverseerHandler::setEndpointHandlers(std::vector ephs){ + this->playbackEndpointHandlers = ephs; } diff --git a/src/cont/contclasses.h b/src/cont/contclasses.h index c562b73..25fcaea 100644 --- a/src/cont/contclasses.h +++ b/src/cont/contclasses.h @@ -1,48 +1,138 @@ #pragma once -#include -#include "backlasses.h" +#include "global.h" +#include "contsessionclasses.h" +//#define invoke_mem_fn(object,ptrToMember) ((object).*(ptrToMember)) +//#define pinvoke_mem_fn(object,ptrToMember) ((object)->*(ptrToMember)) -class EndpointHandler : public QObject { - Q_OBJECT +class EndpointWidget; +class Endpoint; +class EndpointVolumeCallback; +class Overseer; +class SessionHandler; +class EndpointNewSessionCallback; +struct BackEndpointVolumeCallbackInfo { + NGuid caller; + bool muted; + float mainVolume; + size_t channels; + std::vector channelVolumes; +}; + +class EndpointHandler { + public: - EndpointHandler(Endpoint *ept, QObject *parent = nullptr); - QString getName(); + EndpointHandler(uint64_t idx, Flows flow); + void setBackEndpointVolumeCallbackInfoContent(uint8_t state); + + //these two, currently unused. If I use them, I should feel bad. + //EndpointVolumeCallback* getEndpointVolumeCallback(); + //Endpoint* getEndpoint(); + + //std::wstring epName; + BackEndpointVolumeCallbackInfo* getCallbackInfo(); + uint32_t getChannelCount(); + + void setIndex(uint64_t idx); + uint64_t getIndex(); + void setVolume(int channel, float volume); + + std::wstring getName(); + std::wstring getId(); + + void setFrontVisibilityInfo(EndpointState state, uint64_t frontIdx); + uint64_t getFrontVisibilityIndex(); + EndpointState getFrontVisibilityState(); + + Flows getFlow(); float getVolume(int channel); bool getMute(); + size_t getState(); + uint8_t getRoles(); + void setRoles(Roles newRole); + void assignRoles(Roles newRole); + void removeRoles(Roles newRole); + void setVolume(NGuid guid, int channel, int value); + void setMute(NGuid guid, bool muted); + void setState(uint8_t state); + void setState(uint8_t state, uint64_t idx); + + /* sessions */ + size_t getSessionCount(); + std::vector getSessionHandlers(); + void createNewSession(); + Endpoint* getEndpoint(); + + /*Session*/ + void addSessionSendFront(Session* session); + void setAddSessionWidgetFunction(std::function addSessionWidget); + void setRemoveSessionWidgetFunction(std::function removeSessionWidget); + void sendSessionToFront(SessionHandler* sh); + void removeSessionFromFront(SessionHandler* sh); + + ~EndpointHandler(); private: - Endpoint *ept; - QString eptName; - //QSlider *slidy; - -public slots: - void setValue(int channel, int value); - void setMute(); -//signals: - + std::vector getSessions(); + uint64_t idx; + Endpoint *ep = nullptr; + EndpointVolumeCallback *epc = nullptr; + Flows flow; + BackEndpointVolumeCallbackInfo callbackInfo; + struct EndpointHandlerFrontVisibility { + EndpointState visibility = EndpointState::ENDPOINT_ALL; + uint64_t frontIdx = INT_MAX; + }; + EndpointHandlerFrontVisibility ephfv; + EndpointNewSessionCallback* ensc; + std::vector sessionHandlers; + std::function addSessionWidget; + std::function removeSessionWidget; + //QSlider *slidy; }; - -class OverseerHandler : public QObject { - Q_OBJECT +class OverseerHandler { public: - OverseerHandler(QObject *parent = nullptr); - void setEndpointHandlers(std::vector *ephs); - std::vector* getEndpointHandlers(); - static Overseer* getOverseer(); + OverseerHandler(); + void setChangeFrontDefaultsFunction(std::function changeFrontDefaults); + void changeFrontDefaultsCallback(Roles role, std::wstring endpointId); + + //void setReviseEndpointShowingFunction(std::function reviseEndpointShowing); + void reviseEndpointShowing(std::wstring endpointId, EndpointState state); + void setRemoveEndpointWidgetFunction(std::function removeEndpointWidget); + void setAddEndpointWidgetFunction(std::function addEndpointWidget); + + void setEndpointHandlers(std::vector ephs); + std::vector getPlaybackEndpointHandlers(); + std::vector getCaptureEndpointHandlers(); + std::vector getPlaybackEndpoints(); + std::vector getCaptureEndpoints(); + void pushBackEndpointHandler(EndpointHandler* eph, Flows flow); + uint64_t getPlaybackEndpointsCount(); + uint64_t getCaptureEndpointsCount(); + void reloadEndpointHandlers(); + EndpointHandler* addEndpoint(std::wstring endpointId, Flows *flow); + NGuid getGuid(); + + /* + * void setSessionVolumeCallback(std::function changeSessionVolume); + * void setSessionVolume(float newValue, ); + */ private: - static Overseer os; - std::vector *endpointHandlers; - //QSlider *slidy; - - //public slots: - //void setValue(int value); - - + Overseer *os; + std::vector playbackEndpointHandlers; + std::vector captureEndpointHandlers; + std::function changeFrontDefaults; + std::function removeEndpointWidget; + std::function addEndpointWidget; + /* Session's */ + std::function changeSessionVolume; + //std::function updateFrontVolumeCallback; + //std::function updateFrontMuteCallback; + }; diff --git a/src/cont/contsessionclasses.cpp b/src/cont/contsessionclasses.cpp new file mode 100644 index 0000000..155c0e1 --- /dev/null +++ b/src/cont/contsessionclasses.cpp @@ -0,0 +1,79 @@ +#include "contsessionclasses.h" +#include "backsessionclasses.h" + +SessionHandler::SessionHandler(EndpointHandler* eph, Session* session, size_t idx) { + this->eph = eph; + this->idx = idx; + this->session = session; + + this->svi.mainVolume = session->getVolume(AudioChannel::CHANNEL_MAIN); + this->svi.muted = session->getMute(); + this->svi.caller = osh->getGuid(); + + ssc = new SessionStateCallback(this); + this->session->setStateCallback(ssc); +} + +void SessionHandler::setVolume(NGuid guid, int channel, int value){ + if (channel == AudioChannel::CHANNEL_MAIN) + session->setVolume(guid, channel, (float)value / 100); + else session->setVolume(guid, channel, (float)value / 100); +} + +float SessionHandler::getVolume(int channel){ + return session->getVolume(channel); +} + +void SessionHandler::setMute(NGuid guid, bool muted){ + session->setMute(guid, muted); +} + +std::wstring SessionHandler::getName(){ + return session->getName(); +} + +bool SessionHandler::getMute(){ + return session->getMute(); +} + +void SessionHandler::setFrontIndex(uint64_t frontIdx) { + this->frontIdx = frontIdx; +} + +uint64_t SessionHandler::getFrontIndex() { + return frontIdx; +} + +SessionVolumeInfo* SessionHandler::getVolumeInfo() { + return &svi; +} + +SessionState SessionHandler::getState() { + return session->getState(); +} + +void SessionHandler::setState(SessionState state) { + session->setState(state); +} + +void SessionHandler::reviseSessionShowing(SessionState state) { + SessionState currentState = this->getState(); + switch (currentState) { + case SessionState::ACTIVE: + case SessionState::INACTIVE: + if (state == SessionState::EXPIRED) { + eph->removeSessionFromFront(this); + } + break; + case SessionState::EXPIRED: + if (state == SessionState::ACTIVE || INACTIVE) { + eph->sendSessionToFront(this); + } + break; + case SessionState::DISCONNECTED: + if (frontIdx != INT_MAX) + eph->removeSessionFromFront(this); + break; + } +} + diff --git a/src/cont/contsessionclasses.h b/src/cont/contsessionclasses.h new file mode 100644 index 0000000..51f620f --- /dev/null +++ b/src/cont/contsessionclasses.h @@ -0,0 +1,42 @@ +#pragma once + +#include "global.h" +//#include "contclasses.h" + +class EndpointHandler; +class Session; +class SessionStateCallback; + +struct SessionVolumeInfo { + //SessionVolumeInfo(bool muted, float mainVolume); + bool muted; + float mainVolume; + NGuid caller; + + //size_t channels; + //std::vector channelVolumes; +}; + +class SessionHandler { + public: + SessionHandler(EndpointHandler* eph, Session* session, size_t idx); + void setVolume(NGuid guid, int channel, int value); + float getVolume(int channel); + void setMute(NGuid guid, bool muted); + bool getMute(); + void setFrontIndex(uint64_t frontIdx); + SessionState getState(); + void setState(SessionState state); + uint64_t getFrontIndex(); + std::wstring getName(); + void reviseSessionShowing(SessionState state); + SessionVolumeInfo* getVolumeInfo(); + + private: + SessionVolumeInfo svi; + EndpointHandler* eph; + Session* session; + SessionStateCallback* ssc; + size_t idx; + uint64_t frontIdx = INT_MAX; +}; diff --git a/src/debug.h b/src/debug.h index f91232b..da6285c 100644 --- a/src/debug.h +++ b/src/debug.h @@ -2,9 +2,46 @@ #if defined (QT_DEBUG) || defined (DEBUG) || defined (_DEBUG) +template +std::bitset varToBitset(T info) { + std::bitset content(info); + return content; +} +#ifndef _WIN32 #define log_debugcpp(str) do { \ std::cout << "[DEBUG]" << "(" << __FILE__ << ":" << __LINE__ << "): " << str << std::endl; \ } while (0) -#else -#define log_debugcpp(str) + +#define log_wdebugcpp(str) do { \ + std::wcout << "[DEBUG]" << "(" << __FILE__ << ":" << __LINE__ << "): " << str << std::endl; \ + } while (0) +#else + +#include +#include +#define WIDE2(x) L##x +#define WIDE1(x) WIDE2(x) +#define WFILE WIDE1(__FILE__) +#define log_debugcpp(str) { \ + OutputDebugStringA(std::string("[DEBUG] (" + std::string(__FILE__) + ":" + std::to_string(__LINE__) + "): " + std::string(str) + "\n").c_str()); \ + } while (0) + +#define log_wdebugcpp(str) do { \ + OutputDebugStringW(std::wstring(L"[DEBUG] (" + std::wstring(WFILE) + L":" + std::to_wstring(__LINE__) + L"): " + std::wstring(str) +L"\n").c_str()); \ + } while (0) #endif + +#define print_as_binary(len, type, info) varToBitset(info) + +#else +#define log_debugcpp(str) +#define log_wdebugcpp(str) +#define print_as_binary(len, type, info) +#endif + +/* Here as a quick reference, in case smthn similar is needed again */ +/* typedef void (EndpointWidget::*epwMuteFunc)(bool muted); */ +/* typedef void (EndpointWidget::*epwMainVolumeFunc)(float newValue); */ +/* typedef void (EndpointWidget::*epwChannelVolumeFunc)(uint32_t channel, float newValue); */ +/* typedef void (EndpointWidget::*epwToggleFrontFunc)(bool active); */ + diff --git a/src/global.h b/src/global.h index 96d177b..035c95b 100644 --- a/src/global.h +++ b/src/global.h @@ -1,14 +1,80 @@ #pragma once -//TODO enum capullo -#define ENDPOINT_MASTER_VOLUME -1 -#define ENDPOINT_LEFT_CHANNEL_VOLUME 0 -#define ENDPOINT_RIGHT_CHANNEL_VOLUME 1 +#include +#include +#include +#include +#include +#include +#include "debug.h" + +//TODO: Use tr();? QTranslator #define STRING_MUTE "Mute" #define STRING_UNMUTE "Unmute" +#define STRING_QUIT "Quit" +#define STRING_TITLE "Mixer Fachero" + +#define STRING_ROLE_CONSOLE "Console" +#define STRING_ROLE_MULTIMEDIA "Multimedia" +#define STRING_ROLE_COMMUNICATIONS "Communications" +#define STRING_ROLE_ALL "All" + +#define STRING_SYSTEM_SOUNDS "System Sounds" +#define LSTRING_SYSTEM_SOUNDS L"System Sounds" //INIT BACK +enum AudioChannel { + CHANNEL_LEFT = (1 << 0), + CHANNEL_RIGHT = (1 << 1), + CHANNEL_MAIN = ~0, +}; + +enum EndpointState { + ENDPOINT_ACTIVE = (1 << 0), + ENDPOINT_DISABLED = (1 << 1), + ENDPOINT_NOTPRESENT = (1 << 2), + ENDPOINT_UNPLUGGED = (1 << 3), + ENDPOINT_ALL = 0x0F +}; + +enum SessionState { + ACTIVE = (1 << 0), + INACTIVE = (1 << 1), + EXPIRED = (1 << 2), + DISCONNECTED = (1 << 3), + ALL = 0x0F +}; + +enum Flows { + FLOW_PLAYBACK = (1 << 0), + FLOW_CAPTURE = (1 << 1), + FLOW_BOTH = (1 << 2), +}; + +enum Roles { + ROLE_CONSOLE = (1 << 0), + ROLE_MULTIMEDIA = (1 << 1), + ROLE_COMMUNICATIONS = (1 << 2), + ROLE_ALL = 0x07, +}; + +struct NGuid { + //todo: still leaking? + uint32_t data1; + uint16_t data2; + uint16_t data3; + unsigned char data4[8]; + + + /* void freeData4(){ */ + /* int i = 0; */ + /* do{ */ + /* if(this->data4 + i != nullptr) free(data4 + i); */ + /* i++; */ + /* }while (i < 8); */ + /* } */ +}; class OverseerHandler; extern OverseerHandler *osh; diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 0cac13d..a9da261 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -1,114 +1,584 @@ #include "qtclasses.h" +template +CustomWidgetEvent::CustomWidgetEvent(QEvent::Type type, T payload) : QEvent(type){ + this->payload = payload; +} -EndpointWidget::EndpointWidget(EndpointHandler* eph, QWidget *parent) : QWidget(parent){ - this->eph = eph; - layout = new QGridLayout(); - this->setLayout(layout); - log_debugcpp("olaW"); - if (parent == nullptr) { log_debugcpp("owo?"); } +void ExtendedCheckBox::customEvent(QEvent* ev) { + QEvent::Type tipo = ev->type(); + if (ev->type() == (QEvent::Type)CustomQEvent::EndpointDefaultChange) { + //todo: still prone to bugs; whack-a-mole to come + ev->setAccepted(true); + this->blockSignals(true); + if (this->isEnabled()) { + this->setCheckState(Qt::Checked); + this->setDisabled(true); + } else { + this->setDisabled(false); + this->setCheckState(Qt::Unchecked); + } + this->blockSignals(false); + return; + } + // Make sure the rest of events are handled + QCheckBox::customEvent(ev); +} - muteButton = new QPushButton(); - mainLabel = new QLabel(eph->getName()); - leftChannelLabel = new QLabel("88"); - rightChannelLabel = new QLabel("77"); - mainSlider = new QSlider(Qt::Horizontal); - leftChannelSlider = new QSlider(Qt::Horizontal); - rightChannelSlider = new QSlider(Qt::Horizontal); +SessionWidget::SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent) : QWidget(parent){ + //todo: based on qgridlayout, name+mute should be its own widget, same with channels + this->idx = idx; + this->sh = sh; + + layout = new QGridLayout(this); + //this->setLayout( + + muteButton = new QCheckBox(this); + mainLabel = new QLabel(QString::fromStdWString(sh->getName()), this); + mainSlider = new QSlider(Qt::Horizontal, this); - muteButton->setStyleSheet("background-color: #A3C1DA; color: red"); + mainSlider->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); mainSlider->setFocusPolicy(Qt::StrongFocus); mainSlider->setTickPosition(QSlider::TicksBothSides); mainSlider->setTickInterval(5); mainSlider->setSingleStep(1); mainSlider->setRange(0,100); - leftChannelSlider->setTickInterval(5); - leftChannelSlider->setSingleStep(1); - leftChannelSlider->setRange(0,100); - rightChannelSlider->setTickInterval(5); - rightChannelSlider->setSingleStep(1); - rightChannelSlider->setRange(0,100); - muteButton->setText(eph->getMute() ? STRING_UNMUTE : STRING_MUTE); - float volume = eph->getVolume(ENDPOINT_MASTER_VOLUME) * 100; + muteButton->setCheckState((sh->getMute() == false ? Qt::Unchecked : Qt::Checked)); + muteButton->setText(sh->getMute() ? STRING_UNMUTE : STRING_MUTE); + float volume = sh->getVolume(AudioChannel::CHANNEL_MAIN) * 100; mainSlider->setValue((int)volume); - volume = eph->getVolume(ENDPOINT_LEFT_CHANNEL_VOLUME) * 100; - leftChannelSlider->setValue((int)volume); - leftChannelLabel->setText(QString::number(volume)); - volume = eph->getVolume(ENDPOINT_RIGHT_CHANNEL_VOLUME) * 100; - rightChannelSlider->setValue((int)volume); - rightChannelLabel->setText(QString::number(volume)); - log_debugcpp("ENDPOINT SET WITH VOLUME " << volume); + log_debugcpp("SESSION SET WITH VOLUME " + std::to_string(volume)); - mainMuteLayout = new QGridLayout(); - layout->addLayout(mainMuteLayout, 0, 0); - mainMuteLayout->addWidget(mainLabel, 0, 0); - mainMuteLayout->addWidget(muteButton, 0, 1); - layout->addWidget(mainSlider, 0, 1); - layout->addWidget(leftChannelSlider, 1, 0); - layout->addWidget(leftChannelLabel, 2, 0); - layout->addWidget(rightChannelSlider, 1, 1); - layout->addWidget(rightChannelLabel, 2, 1); - layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), 3, 0); - layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), 3, 1); + //tip: would need to be new widget with layout in it + //mainMuteLayout = new QGridLayout(); + layout->addWidget(mainLabel, 0, 0, Qt::AlignLeft | Qt::AlignBottom); + layout->addWidget(muteButton, 0, 1, Qt::AlignLeft | Qt::AlignBottom); + layout->addWidget(mainSlider, 0, 2, 1, 2); + + //TODO:0 = mute and muted, change volume = unmuted are client side tricks = 2 callbacks, one for volume, one for mute state. Implement as an user selectable option? + connect(mainSlider, &QSlider::valueChanged, this,&SessionWidget::updateMainVolume); + connect(muteButton, &QCheckBox::stateChanged, this, (&SessionWidget::updateMute)); + + /* + * Session Volume Polling + */ + volumePoller = new QTimer(); + connect(volumePoller, &QTimer::timeout, [this, sh](){ + //if (memcmp(osh->callbackInfo[idx]->caller, osh->getGuid(), sizeof(NGuid)) == 0) return; CHECK IF THIS PROGRAM GENERATED THE FUNSIES IS NO LONGER IN USE FOR NOW. + //todo: global + constexpr + ratio + const float roundingFactor = 0.005; + mainSlider->blockSignals(true); + muteButton->blockSignals(true); + mainSlider->setValue((int)((sh->getVolumeInfo()->mainVolume + roundingFactor) * 100)); + muteButton->setCheckState((sh->getVolumeInfo()->muted == false ? Qt::Unchecked : Qt::Checked)); + muteButton->setText(sh->getVolumeInfo()->muted ? STRING_UNMUTE : STRING_MUTE); + + //memcpy(osh->callbackInfo[idx]->caller, osh->getGuid(), sizeof(NGuid)); + + //TODO: el default = objcopy frees? + //Todo: like fr pregunta + sh->getVolumeInfo()->caller = osh->getGuid(); + mainSlider->blockSignals(false); + muteButton->blockSignals(false); + }); + volumePoller->start(10); +} + +void SessionWidget::updateMute(int checked){ + bool muted = (checked == 2 ? true : false); + this->sh->setMute(osh->getGuid(), muted); + this->muteButton->setText(this->sh->getMute() ? STRING_UNMUTE : STRING_MUTE); +} + +void SessionWidget::updateMainVolume(int newValue){ + this->sh->setVolume(osh->getGuid(), AudioChannel::CHANNEL_MAIN, newValue); +} + +SessionWidget::~SessionWidget() { + volumePoller->stop(); + delete volumePoller; +} + +EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *parent) : QWidget(parent){ + //todo: based on qgridlayout, name+mute should be its own widget, same with channels + row = 0; + this->idx = idx; + this->eph = eph; + //todo: sussy + this->eph->setState(EndpointState::ENDPOINT_ACTIVE, idx); + + layout = new QGridLayout(this); + //this->setLayout(layout); + log_debugcpp("epw main layout parent: " + std::to_string((intptr_t)(layout->parent()))); + if (parent == nullptr) { log_debugcpp("ayooooo?"); } + + defaultRolesCheckBoxes = { + {Roles::ROLE_ALL, new ExtendedCheckBox(this)}, + {Roles::ROLE_CONSOLE, new ExtendedCheckBox(this)}, + {Roles::ROLE_MULTIMEDIA, new ExtendedCheckBox(this)}, + {Roles::ROLE_COMMUNICATIONS, new ExtendedCheckBox(this)} + }; + + /* + * Mute, main slider and label setup + */ + muteButton = new QCheckBox(this); + mainLabel = new QLabel(QString::fromStdWString(eph->getName()), this); + mainSlider = new QSlider(Qt::Horizontal, this); + + if (this->eph->getState() != EndpointState::ENDPOINT_ACTIVE) { + layout->addWidget(mainLabel, row, 0); + layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Minimum), 1, 0); + return; + } + + //muteButton->setStyleSheet("background-color: #A3C1DA; color: red"); + mainSlider->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + mainSlider->setFocusPolicy(Qt::StrongFocus); + mainSlider->setTickPosition(QSlider::TicksBothSides); + mainSlider->setTickInterval(5); + mainSlider->setSingleStep(1); + mainSlider->setRange(0,100); + + muteButton->setCheckState((eph->getMute() == false ? Qt::Unchecked : Qt::Checked)); + muteButton->setText(eph->getMute() ? STRING_UNMUTE : STRING_MUTE); + float volume = eph->getVolume(AudioChannel::CHANNEL_MAIN) * 100; + mainSlider->setValue((int)volume); + log_debugcpp("ENDPOINT SET WITH VOLUME " + std::to_string(volume)); + + //tip: would need to be new widget with layout in it + //mainMuteLayout = new QGridLayout(); + layout->addWidget(mainLabel, row, 0, Qt::AlignLeft | Qt::AlignBottom); + layout->addWidget(muteButton, row, 1, Qt::AlignLeft | Qt::AlignBottom); + layout->addWidget(mainSlider, row, 2, 1, 2); + row++; + + //TODO:0 = mute and muted, change volume = unmuted are client side tricks = 2 callbacks, one for volume, one for mute state. Implement as an user selectable option? + connect(mainSlider, &QSlider::valueChanged, this,&EndpointWidget::updateMainVolume); + connect(muteButton, &QCheckBox::stateChanged, this, (&EndpointWidget::updateMute)); + + +/* + * Channel sliders setup + */ + uint32_t epChannelCount = eph->getChannelCount(); + for(uint32_t i = 0; i < epChannelCount && epChannelCount > 1; i++){ + QSlider* tmp = new QSlider(Qt::Horizontal); + QLabel* tmpLb = new QLabel(""); + tmp->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + tmp->setTickInterval(5); + tmp->setSingleStep(1); + tmp->setRange(0,100); + + volume = eph->getVolume(i) * 100; + tmp->setValue((int) volume); + tmpLb->setText(QString::number(volume)); + this->channelSliders.push_back(tmp); + this->channelLabels.push_back(tmpLb); + layout->addWidget(tmp, row + 1, i); + layout->addWidget(tmpLb, row + 2, i); + + //TODO: check if there's a need to prevent deadlocks; probably this will eventually turn into its own func + //this causes channel bar desync when back -> front. blocksignals below fix it. huh. + connect(tmp, &QSlider::valueChanged, [this, i](int newValue){ + this->eph->setVolume(osh->getGuid(), i, newValue); + this->channelLabels.at(i)->setText(QString::number(newValue)); + }); + } + row += 3; + + /* + * Role ExtendedCheckBoxes setup + */ + + uint8_t assignedRoles = eph->getRoles(); + defaultRolesCheckBoxes.at(Roles::ROLE_ALL)->setCheckState(assignedRoles == Roles::ROLE_ALL ? Qt::Checked : Qt::Unchecked); + //todo duditas de & + defaultRolesCheckBoxes.at(Roles::ROLE_ALL)->setDisabled(assignedRoles == Roles::ROLE_ALL ? true : false); + defaultRolesCheckBoxes.at(Roles::ROLE_ALL)->setText(STRING_ROLE_ALL); + + defaultRolesCheckBoxes.at(Roles::ROLE_CONSOLE)->setCheckState(assignedRoles & Roles::ROLE_CONSOLE ? Qt::Checked : Qt::Unchecked); + defaultRolesCheckBoxes.at(Roles::ROLE_CONSOLE)->setDisabled(assignedRoles & Roles::ROLE_CONSOLE ? true : false); + defaultRolesCheckBoxes.at(Roles::ROLE_CONSOLE)->setText(STRING_ROLE_CONSOLE); + + defaultRolesCheckBoxes.at(Roles::ROLE_MULTIMEDIA)->setCheckState(assignedRoles & Roles::ROLE_MULTIMEDIA ? Qt::Checked : Qt::Unchecked); + defaultRolesCheckBoxes.at(Roles::ROLE_MULTIMEDIA)->setDisabled(assignedRoles & Roles::ROLE_MULTIMEDIA ? true : false); + defaultRolesCheckBoxes.at(Roles::ROLE_MULTIMEDIA)->setText(STRING_ROLE_MULTIMEDIA); + + defaultRolesCheckBoxes.at(Roles::ROLE_COMMUNICATIONS)->setCheckState(assignedRoles & Roles::ROLE_COMMUNICATIONS ? Qt::Checked : Qt::Unchecked); + defaultRolesCheckBoxes.at(Roles::ROLE_COMMUNICATIONS)->setDisabled(assignedRoles & Roles::ROLE_COMMUNICATIONS ? true : false); + defaultRolesCheckBoxes.at(Roles::ROLE_COMMUNICATIONS)->setText(STRING_ROLE_COMMUNICATIONS); + + connect(defaultRolesCheckBoxes.at(Roles::ROLE_ALL), &QCheckBox::stateChanged,[this] { + this->eph->setRoles(Roles::ROLE_ALL); + }); + connect(defaultRolesCheckBoxes.at(Roles::ROLE_CONSOLE), &QCheckBox::stateChanged,[this] { + this->eph->setRoles(Roles::ROLE_CONSOLE); + }); + connect(defaultRolesCheckBoxes.at(Roles::ROLE_MULTIMEDIA), &QCheckBox::stateChanged,[this] { + this->eph->setRoles(Roles::ROLE_MULTIMEDIA); + }); + + connect(defaultRolesCheckBoxes.at(Roles::ROLE_COMMUNICATIONS), &QCheckBox::stateChanged,[this] { + this->eph->setRoles(Roles::ROLE_COMMUNICATIONS); + }); + + layout->addWidget(defaultRolesCheckBoxes.at(Roles::ROLE_ALL), row, 0); + layout->addWidget(defaultRolesCheckBoxes.at(Roles::ROLE_CONSOLE), row, 1); + layout->addWidget(defaultRolesCheckBoxes.at(Roles::ROLE_MULTIMEDIA), row, 2); + layout->addWidget(defaultRolesCheckBoxes.at(Roles::ROLE_COMMUNICATIONS), row, 3); + row++; +/* ----------------------------------------------------------- */ /* - * connect(mainSlider, &QSlider::valueChanged, eph, &EndpointHandler::setValue); + * EndpointVolume Polling time */ - connect(mainSlider, &QSlider::valueChanged, [this](int newValue){this->eph->setValue(ENDPOINT_MASTER_VOLUME, newValue); }); - connect(leftChannelSlider, &QSlider::valueChanged, [this](int newValue){ this->eph->setValue(ENDPOINT_LEFT_CHANNEL_VOLUME, newValue); this->leftChannelLabel->setText(QString::number(newValue)); }); - connect(rightChannelSlider, &QSlider::valueChanged, [this](int newValue){ this->eph->setValue(ENDPOINT_RIGHT_CHANNEL_VOLUME, newValue); this->rightChannelLabel->setText(QString::number(newValue)); }); - connect(muteButton, &QPushButton::clicked, [this](bool clicked){ log_debugcpp("cliqui" << clicked << "cloqui"); this->eph->setMute(); this->muteButton->setText(this->eph->getMute() ? STRING_UNMUTE : STRING_MUTE); }); + timer = new QTimer(); + connect(timer, &QTimer::timeout, [this, eph](){ + //if (memcmp(osh->callbackInfo[idx]->caller, osh->getGuid(), sizeof(NGuid)) == 0) return; CHECK IF THIS PROGRAM GENERATED THE FUNSIES IS NO LONGER IN USE FOR NOW. + //todo: global + constexpr + ratio + const float roundingFactor = 0.005; + mainSlider->blockSignals(true); + muteButton->blockSignals(true); + mainSlider->setValue((int)((eph->getCallbackInfo()->mainVolume + roundingFactor) * 100)); + muteButton->setCheckState((eph->getCallbackInfo()->muted == false ? Qt::Unchecked : Qt::Checked)); + muteButton->setText(eph->getCallbackInfo()->muted ? STRING_UNMUTE : STRING_MUTE); + for(uint32_t i = 0; i < eph->getCallbackInfo()->channels && eph->getChannelCount() > 1; i++){ + this->channelSliders.at(i)->blockSignals(true); + this->channelSliders.at(i)->setValue((int)((eph->getCallbackInfo()->channelVolumes[i] + roundingFactor) * 100)); + this->channelLabels.at(i)->setText(QString::number((int)((eph->getCallbackInfo()->channelVolumes[i] + roundingFactor) * 100))); + this->channelSliders.at(i)->blockSignals(false); + } + //memcpy(osh->callbackInfo[idx]->caller, osh->getGuid(), sizeof(NGuid)); + + //TODO: el default = objcopy frees? + //Todo: like fr pregunta + eph->getCallbackInfo()->caller = osh->getGuid(); + mainSlider->blockSignals(false); + muteButton->blockSignals(false); + }); + timer->start(10); + + /* First SessionWidget batch */ + for (size_t i = 0; i < eph->getSessionCount(); i++) { + SessionWidget* sessionWidget = new SessionWidget(i, eph->getSessionHandlers().at(i), this); + layout->addWidget(sessionWidget, row, 4); + row++; + sessionWidgets.push_back(sessionWidget); + eph->getSessionHandlers().at(i)->setFrontIndex(i); + } + + /* Add/Remove SessionWidget callback */ + eph->setAddSessionWidgetFunction([this](SessionHandler* sessionHandler) { + QCoreApplication::instance()->postEvent(this, new CustomWidgetEvent((QEvent::Type)CustomQEvent::SessionWidgetCreated, sessionHandler)); + }); + + eph->setRemoveSessionWidgetFunction([this](SessionHandler* sessionHandler) { + QCoreApplication::instance()->postEvent(this, new CustomWidgetEvent((QEvent::Type)CustomQEvent::SessionWidgetObsolete, sessionHandler)); + }); + + //todo parent? + layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Minimum), 1, 0); + layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Minimum), 4, 0); + layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), 6, 0); + layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), 6, 1); log_debugcpp("ENDPOINT_WIDGETED"); } +void EndpointWidget::addSessionWidget(CustomWidgetEvent* ev){ + uint64_t index = this->sessionWidgets.size(); + SessionWidget* sw = new SessionWidget(index, ev->payload, this); + ev->payload->setFrontIndex(index); + this->layout->addWidget(sw, row, 4); + row++; + sessionWidgets.push_back(sw); + return; +} -MainWindow::MainWindow(std::vector *ephs, QWidget *parent) : QMainWindow(parent) { - // setWindowState(Qt::WindowFullScreen); - // setCentralWidget(centralWidget); - widget = new QWidget(); - layout = new QGridLayout(); - - widget->setLayout(layout); - setCentralWidget(widget); - //layout->addWidget(pintas, 0, 0); +void EndpointWidget::removeSessionWidget(CustomWidgetEvent* ev){ + uint64_t i = ev->payload->getFrontIndex(); + this->sessionWidgets.at(i)->setParent(nullptr); + this->layout->removeWidget(sessionWidgets.at(i)); + delete sessionWidgets.at(i); + sessionWidgets.at(i) = nullptr; + ev->payload->setFrontIndex(INT_MAX); + //this->sessionWidgetsUpdateTimer->start(); + return; +} - setWindowTitle("slidea resbala nu c"); - - /*s - * setEndpointHandlers(ephs); - */ - unsigned int i = 0; - for (; i < ephs->size(); i++) { - log_debugcpp("EPWidget creation"); - EndpointWidget *epw = new EndpointWidget(ephs->at(i), widget); - ews.push_back(epw); - layout->addWidget(epw, i, 0); +void EndpointWidget::customEvent(QEvent* ev) { + if (ev->type() == (QEvent::Type)CustomQEvent::SessionWidgetCreated) { + ev->setAccepted(true); + this->addSessionWidget((CustomWidgetEvent*) ev); + } else if (ev->type() == (QEvent::Type)CustomQEvent::SessionWidgetObsolete) { + ev->setAccepted(true); + this->removeSessionWidget((CustomWidgetEvent*) ev); } - layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), i, 0); + QWidget::customEvent(ev); +} + + +EndpointWidget::~EndpointWidget() { + timer->stop(); + delete timer; + this->eph->setFrontVisibilityInfo(EndpointState::ENDPOINT_ALL, INT_MAX); +} + +void MainWindow::customEvent(QEvent* ev) { + if (ev->type() == CustomQEvent::EndpointWidgetObsolete) { + ev->setAccepted(true); + this->removeEndpointWidget((CustomWidgetEvent*)ev); + } else if (ev->type() == (QEvent::Type)CustomQEvent::EndpointWidgetCreated) { + ev->setAccepted(true); + this->addEndpointWidget((CustomWidgetEvent*)ev); + } + QMainWindow::customEvent(ev); +} + +void MainWindow::removeEndpointWidget(CustomWidgetEvent* ev){ + uint64_t i = ev->payload; + this->ews.at(i)->setParent(nullptr); + this->layout->removeWidget(ews.at(i)); + //uint64_t saisu = ews.size(); + //delete ews.at(index); + delete ews.at(i); + ews.at(i) = nullptr; + this->ewsUpdateTimer->start(); + return; +} + +void MainWindow::addEndpointWidget(CustomWidgetEvent* ev){ + EndpointWidget* epw = new EndpointWidget(this->ews.size(), ev->payload, widget); + this->layout->addWidget(epw); + ews.push_back(epw); + return; +} + +void MainWindow::reorderEndpointWidgetCollection() { + /* Flatten */ + size_t firstNullPosition = 0; + size_t ewsSize = ews.size(); + bool breakSorting = false; + + //todo: is all of gui really atomic by definition? im afraid of cutting through amazing add momentos, but I think I did my homework. Must check back. + for (size_t i = 0; i < ewsSize; i++) { + if (ews.at(i) == nullptr) { + for (size_t j = (i + 1); j < ewsSize; j++) { + + if (ews.at(j) != nullptr) { + ews.at(i) = ews.at(j); + ews.at(i)->setIndex(i); + ews.at(j) = nullptr; + break; + } + if (j == ewsSize - 1) { + firstNullPosition = i; + breakSorting = true; + } + + } + if (breakSorting) break; + } + } + ews.resize(firstNullPosition + 1); +} + +void EndpointWidget::updateMute(int checked){ + bool muted = (checked == 2 ? true : false); + this->eph->setMute(osh->getGuid(), muted); + this->muteButton->setText(this->eph->getMute() ? STRING_UNMUTE : STRING_MUTE); +} + +void EndpointWidget::updateMainVolume(int newValue){ + this->eph->setVolume(osh->getGuid(), AudioChannel::CHANNEL_MAIN, newValue); } /* - * void MainWindow::setEndpointHandlers(std::vector *ephs){ - * this->ephs = ephs; + * void EndpointWidget::updateVolume(uint32_t channel, float newValue){ + * //this->blockSignals(true); + * int newVal = newValue * 100; + * if (channel == (uint32_t)AudioChannel::CHANNEL_MAIN) { + * //TIP: Above + * //this->mainSlider->blockSignals(true); + * + * if(this->mainSlider->value() != newVal) { + * this->mainSlider->blockSignals(true); + * this->mainSlider->setValue(newVal); + * this->mainSlider->blockSignals(false); + * } + * return; + * } + * + * for (size_t i = 0; i < sizeof(uint32_t) * 8 && i < channelSliders.size(); ++i) { + * if (((channel >> i) & 1) && this->channelSliders.at(i)->value() != newVal) { + * //this->channelSliders.at(i)->blockSignals(true); + * + * this->channelSliders.at(i)->setValue(newVal); + * this->channelLabels.at(i)->setText(QString::number((int)(newValue * 100))); + * + * //this->channelSliders.at(i)->blockSignals(false); + * } + * } + * + * //this->blockSignals(false); + * } */ - +EndpointHandler* EndpointWidget::getEndpointHandler(){ + return this->eph; +} /* - * void MainWindow::setPlotButton() { - * button = new QPushButton("push"), - * button->setCheckable(true); - * connect(button, SIGNAL(toggled(bool)), this, SLOT(toggled(bool))) - * QHBoxLayout *plotsLayout = new QHBoxLayout; - * plotsLayout->setSpacing(10); - * plotsLayout->addWidget(funPlot); - * QHBoxLayout *buttonsLayout = new QHBoxLayout ; - * buttonsLayout->addWidget(button); - * QVBoxLayout *widgetLayout = new QVBoxLayout; - * widgetLayout->addLayout(plotsLayout); - * widgetLayout->addLayout(buttonsLayout); - * setLayout(widgetLayout); - * ... - */ + * void EndpointWidget::updateFrontIndex(uint64_t index){ + * this->idx = index; + * } + */ + +void EndpointWidget::setIndex(uint64_t idx){ + this->idx = idx; + this->eph->setFrontVisibilityInfo(EndpointState::ENDPOINT_ACTIVE, this->idx); +} + +uint64_t EndpointWidget::getIndex(){ + return idx; +} + +std::map EndpointWidget::getDefaultRolesWidgets() { + return defaultRolesCheckBoxes; +} + + +MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { + // setWindowState(Qt::WindowFullScreen); + // setCentralWidget(centralWidget); + + /* + * Registering needed custom events + */ + QEvent::registerEventType(CustomQEvent::EndpointWidgetObsolete); + QEvent::registerEventType(CustomQEvent::EndpointWidgetCreated); + QEvent::registerEventType(CustomQEvent::EndpointDefaultChange); + + ewsUpdateTimer = new QTimer(this); + widget = new QWidget(); + layout = new QGridLayout(); + trayIcon = new QSystemTrayIcon(); + trayIconMenu = new QMenu(); + trayIconMenuQuit = new QAction(STRING_QUIT); + + ewsUpdateTimer->setSingleShot(true); + ewsUpdateTimer->setInterval(ewsUpdateTimerFrequency); + connect(ewsUpdateTimer, &QTimer::timeout, this, &MainWindow::reorderEndpointWidgetCollection); + widget->setLayout(layout); + setCentralWidget(widget); + //layout->addWidget(pintas, 0, 0); + setWindowTitle(STRING_TITLE); + + reloadEndpointWidgets(); + + /* + * Tray Icon code + */ + trayIconMenu->addSeparator(); + trayIconMenu->addAction(trayIconMenuQuit); + connect(trayIconMenuQuit, &QAction::triggered, qApp, &QCoreApplication::quit); + trayIcon->setIcon(QIcon(":/assets/notificationAreaIcon.png")); + setWindowIcon(QIcon(":/assets/notificationAreaIcon.png")); + //TODO: Extend qsystemtrayicon to change mouse click? + //show before setting tooltip required; smells like bug to me! + trayIcon->show(); + trayIcon->setToolTip(STRING_TITLE); + trayIcon->setContextMenu(trayIconMenu); + connect(trayIcon, &QSystemTrayIcon::activated, this, &MainWindow::trayIconActivated); + + /* + * Set of function callback definitons for EndpointSituationCallback + */ + osh->setChangeFrontDefaultsFunction([this](Roles role, std::wstring endpointId) { + for (auto epw : this->ews) { + /* + * Is this the new default endpoint? + */ + if (epw->getEndpointHandler()->getId() == endpointId) { + //not necessary to keep endpointState flags up to date right now, but updating it will allow for later config files / profiles + epw->getDefaultRolesWidgets().at(role)->blockSignals(true); + epw->getEndpointHandler()->assignRoles(role); + epw->getDefaultRolesWidgets().at(role)->blockSignals(false); + QCoreApplication::instance()->postEvent(epw->getDefaultRolesWidgets().at(role), new QEvent((QEvent::Type)CustomQEvent::EndpointDefaultChange)); + //epw->defaultRolesCheckBoxes.at(role)->postEnableChange(); + /* + * And were you THE default? + */ + if (epw->getEndpointHandler()->getRoles() == Roles::ROLE_ALL) { + QCoreApplication::instance()->postEvent(epw->getDefaultRolesWidgets().at(Roles::ROLE_ALL), new QEvent((QEvent::Type)CustomQEvent::EndpointDefaultChange)); + } + /* + * Are you the dethroned king? + */ + } else if (epw->getEndpointHandler()->getRoles() & role) { + /* + * And were you THE default up until now? + */ + if (epw->getEndpointHandler()->getRoles() == Roles::ROLE_ALL) { + QCoreApplication::instance()->postEvent(epw->getDefaultRolesWidgets().at(Roles::ROLE_ALL), new QEvent((QEvent::Type)CustomQEvent::EndpointDefaultChange)); + } + + epw->getDefaultRolesWidgets().at(role)->blockSignals(true); + //Same as before. ini-san will come... + epw->getEndpointHandler()->removeRoles(role); + epw->getDefaultRolesWidgets().at(role)->blockSignals(false); + QCoreApplication::instance()->postEvent(epw->getDefaultRolesWidgets().at(role), new QEvent((QEvent::Type)CustomQEvent::EndpointDefaultChange)); + + } + } + }); + + osh->setRemoveEndpointWidgetFunction([this](uint64_t index) { + QCoreApplication::instance()->postEvent(this, new CustomWidgetEvent((QEvent::Type)CustomQEvent::EndpointWidgetObsolete, index)); + }); + + osh->setAddEndpointWidgetFunction([this](EndpointHandler* eph) { + QCoreApplication::instance()->postEvent(this, new CustomWidgetEvent((QEvent::Type)CustomQEvent::EndpointWidgetCreated, eph)); + }); +} + +void MainWindow::closeEvent(QCloseEvent *event) { + if (!event->spontaneous() || !isVisible()) return; + + if (trayIcon->isVisible()) { + //todo: would be nice to show this to 1st time users; ini-san will come... + //this->trayIcon->showMessage("ini file calling","tratarte como un gilipollas la primera vez", QSystemTrayIcon::Information); + + hide(); + event->ignore(); + } +} + +void MainWindow::trayIconActivated(QSystemTrayIcon::ActivationReason reason) { + switch (reason) { + case QSystemTrayIcon::Trigger: + this->showNormal(); + break; + default: + break; + } +} + +void MainWindow::reloadEndpointWidgets() { + size_t i = 0; + for (size_t epwIndex = 0; i < (osh->getPlaybackEndpointHandlers().size()); i++) { + if (osh->getPlaybackEndpointHandlers().at(i)->getState() == EndpointState::ENDPOINT_ACTIVE){ + log_debugcpp("EPWidget creation"); + //osh->getPlaybackEndpointHandlers().at(i)->getCallbackInfo()->caller = osh->getGuid(); + EndpointWidget *epw = new EndpointWidget(epwIndex, osh->getPlaybackEndpointHandlers().at(i), widget); + epwIndex++; + //alfinal estoes solopara inicializarlmao + ews.push_back(epw); + layout->addWidget(epw, i, 0); + } + } + //todo:: tas aqui tirao, no me gustas y probablemente yo a ti tampoco + layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), i, 0); +} diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index a02bff8..bec8f5e 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -1,41 +1,163 @@ #pragma once + #ifndef MAINWINDOW_H #define MAINWINDOW_H -#include #include #include +#include + +#include +#include +#include +//#include + #include #include #include #include +#include +#include +/* + * #else + * class QSlider; + * class QLabel; + * class QGridLayout; + * class QPushButton; + * class QWidget; + * class QMainWindow; + * #endif + */ + +#include "global.h" #include "contclasses.h" -//#include -//#include +//class EndpointHandler; +/* + * class ToggleButton : public QAbstractButton { + * Q_OBJECT + * + * public: + * ToggleButton(QWidget *parent = nullptr); + * void checkStateSet(); + * bool hitButton(const QPoint &pos) const; + * void nextCheckState(); + * void changeEvent(QEvent *e) override; + * bool event(QEvent *e) override; + * void focusInEvent(QFocusEvent *e) override; + * void focusOutEvent(QFocusEvent *e) override; + * void keyPressEvent(QKeyEvent *e) override; + * void keyReleaseEvent(QKeyEvent *e) override; + * void mouseMoveEvent(QMouseEvent *e) override; + * void mousePressEvent(QMouseEvent *e) override; + * void mouseReleaseEvent(QMouseEvent *e) override; + * void paintEvent(QPaintEvent *e) override = 0; + * void timerEvent(QTimerEvent *e) override; + * ToggleButton(QWidget *parent = nullptr); + * }; + */ +enum CustomQEvent { + EndpointWidgetObsolete = 1001, + EndpointWidgetCreated = 1002, + EndpointDefaultChange = 1003, + SessionWidgetCreated = 1004, + SessionWidgetObsolete = 1005 +}; -class EndpointWidget : public QWidget { - Q_OBJECT - +template +class CustomWidgetEvent : public QEvent { + public: - EndpointWidget(EndpointHandler* eph, QWidget *parent = nullptr); - //void populateEndpointWidget(EndpointHandler *eph); - //void setEndpointHandlers(std::vector *ephs); + CustomWidgetEvent(QEvent::Type type, T payload); + T payload; + +}; +//Q_DECLARE_METATYPE(EndpointWidgetEvent) + +class ExtendedCheckBox : public QCheckBox { + Q_OBJECT +protected: + void customEvent(QEvent* ev) override; + +public: + //c++11: this inherits all parent's constructors unconditionally + using QCheckBox::QCheckBox; + //alternative being calling parent ctor directly after declaring child ctor: + //B(int x) : A(x) { } +}; + + +class SessionWidget : public QWidget { +Q_OBJECT + +public: + SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent /* = nullptr */); + ~SessionWidget(); + +public slots: + void updateMainVolume(int newValue); + void updateMute(int checked); private: - EndpointHandler* eph; - QPushButton *muteButton = nullptr; + QLabel *mainLabel = nullptr; + QSlider *mainSlider = nullptr; + uint64_t idx; + QGridLayout *layout = nullptr; + QCheckBox *muteButton = nullptr; + SessionHandler* sh; + QTimer* volumePoller = nullptr; +}; + +class EndpointWidget : public QWidget { +Q_OBJECT + +public: + EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *parent = nullptr); + + EndpointHandler* getEndpointHandler(); + std::map getDefaultRolesWidgets(); + + void setIndex(uint64_t idx); + uint64_t getIndex(); + void setVolume(int channel, float volume); + + ~EndpointWidget(); + //void updateMainVolume(float newValue); + //void updateVolume(uint32_t channel, float newValue); + //void updateMute(bool muted); + + //void populateEndpointWidget(EndpointHandler *eph); + //void setEndpointHandlers(std::vector *ephs); + +public slots: + void updateMainVolume(int newValue); + void updateMute(int checked); + +protected: + void customEvent(QEvent* ev) override; + +private slots: + void addSessionWidget(CustomWidgetEvent* ev); + void removeSessionWidget(CustomWidgetEvent* ev); + +private: + int row; + QCheckBox *muteButton = nullptr; QLabel *mainLabel = nullptr, *leftChannelLabel = nullptr, *rightChannelLabel = nullptr; QSlider *mainSlider = nullptr; - QSlider *leftChannelSlider = nullptr; - QSlider *rightChannelSlider = nullptr; + std::vector channelSliders; + std::vector channelLabels; QGridLayout *layout = nullptr; QGridLayout *mainMuteLayout = nullptr; + std::map defaultRolesCheckBoxes; + + EndpointHandler* eph; + size_t defaultRolesVectorSize = 4; + QTimer* timer = nullptr; + uint64_t idx; + std::vector sessionWidgets; //std::vector *ephs; //std::vector *sliders; - - //public slots: - // void setEndpointHandlers(std::vector *ephs); //signals: //void valueChanged(int value); @@ -48,7 +170,19 @@ class MainWindow : public QMainWindow { //QWidget *centralWidget; public: - MainWindow(std::vector *ephs, QWidget *parent = nullptr); + MainWindow(QWidget *parent = nullptr); + void reloadEndpointWidgets(); + +protected: + void closeEvent(QCloseEvent *event) override; + void customEvent(QEvent* ev) override; + +private slots: + void trayIconActivated(QSystemTrayIcon::ActivationReason reason); + void removeEndpointWidget(CustomWidgetEvent* ev); + void addEndpointWidget(CustomWidgetEvent* ev); + void reorderEndpointWidgetCollection(); + //TODO: destroy/empty existing EndpointWidgets //void setEndpointHandlers(std::vector *ephs); private: @@ -56,8 +190,12 @@ private: std::vector ews; QWidget *widget; QGridLayout *layout; - //QLabel *pintas; + QSystemTrayIcon *trayIcon; + QMenu *trayIconMenu; + QAction *trayIconMenuQuit; + QTimer *ewsUpdateTimer; + static constexpr uint64_t ewsUpdateTimerFrequency = 500; //public slots: // void setEndpointHandlers(std::vector *ephs); diff --git a/src/qtestmain.cpp b/src/qtestmain.cpp index 883d7e7..19cf627 100644 --- a/src/qtestmain.cpp +++ b/src/qtestmain.cpp @@ -1,39 +1,64 @@ -#include -#include - //#include //#include //#include + +//#define QTBLESSED #include -#include +#include +#include +#include +#include +//#include "contclasses.h" #include "qtclasses.h" +#include "global.h" -OverseerHandler *osh = new OverseerHandler(); +OverseerHandler *osh = nullptr; QApplication* createApplication(int &argc, char *argv[]) { return new QApplication(argc, argv); } +bool isSingleInstanceRunning(QString appName) { + QLocalSocket socket; + socket.connectToServer(appName); + bool isOpen = socket.isOpen(); + socket.close(); + return isOpen; +} + +QLocalServer* startSingleInstanceServer(QString appName) { + QLocalServer* server = new QLocalServer; + server->setSocketOptions(QLocalServer::WorldAccessOption); + server->listen(appName); + return server; +} int main (int argc, char* argv[]) { //QApplication::setStyle("windowsvista"); - //INIT CONT - std::vector epts = OverseerHandler::getOverseer()->getPlaybackEndpoints(); - std::vector* ephs = new std::vector; - for(unsigned int i = 0; i < epts.size(); i++){ - EndpointHandler *eph = new EndpointHandler(epts.at(i)); - ephs->push_back(eph); - } + //Check if running + //https://stackoverflow.com/questions/48060989/qt-show-application-if-currently-running + if (!isSingleInstanceRunning("Mixer")) + startSingleInstanceServer("Mixer"); + else exit(0); + + osh = new OverseerHandler(); + //qRegisterMetaType(); + + //INIT CONT + log_debugcpp("main init"); + osh->reloadEndpointHandlers(); + log_debugcpp("Reloaded endpoint handlers"); - osh->setEndpointHandlers(ephs); //INIT FRONT QScopedPointer app(createApplication(argc, argv)); - MainWindow window = MainWindow(ephs); + MainWindow window = MainWindow(); //window.setEndpointHandlers(ephs); + QApplication::setQuitOnLastWindowClosed(false); app->setStyle("windowsvista"); window.show(); return app->exec(); } + From a373c706acdbb117699ea998747dfb68864cbfad Mon Sep 17 00:00:00 2001 From: Hane Date: Tue, 26 Mar 2024 19:00:34 +0100 Subject: [PATCH 06/78] Basic scrollArea work, win32 sound controlpanel shortcut --- assets.qrc | 3 +- assets/style.qss | 6 ++++ bueno.bat | 1 + src/back/backlasses.cpp | 26 +++++++++++++++ src/back/backlasses.h | 1 + src/back/msinclude.h | 1 + src/cont/contclasses.cpp | 4 +++ src/cont/contclasses.h | 2 ++ src/global.h | 4 +++ src/qt/qtclasses.cpp | 69 ++++++++++++++++++++++++++++++++++------ src/qt/qtclasses.h | 43 +++++++++++++++++++++++++ src/qtestmain.cpp | 12 +++++-- 12 files changed, 159 insertions(+), 13 deletions(-) create mode 100644 assets/style.qss diff --git a/assets.qrc b/assets.qrc index 8b63932..daa91ee 100644 --- a/assets.qrc +++ b/assets.qrc @@ -1,5 +1,6 @@ - assets/notificationAreaIcon.png + assets/notificationAreaIcon.png + assets/style.qss diff --git a/assets/style.qss b/assets/style.qss new file mode 100644 index 0000000..4f6466c --- /dev/null +++ b/assets/style.qss @@ -0,0 +1,6 @@ +QMainWindow { background: rgba(100,100,100,100); } + + + + +QCheckBox:hover, QCheckBox:checked { color: white } \ No newline at end of file diff --git a/bueno.bat b/bueno.bat index 2c8ac1c..edb534d 100644 --- a/bueno.bat +++ b/bueno.bat @@ -1,3 +1,4 @@ +taskkill /F /IM "qtest.exe" qmake -o build\Makefile .\qtest.pro copy /Y /B .\assets\SoundVolumeView.exe .\build\debug copy /Y /B .\assets\SoundVolumeView.exe .\build\release diff --git a/src/back/backlasses.cpp b/src/back/backlasses.cpp index a25d4c4..a53c052 100644 --- a/src/back/backlasses.cpp +++ b/src/back/backlasses.cpp @@ -689,6 +689,32 @@ Overseer::Overseer() { //: epsc(deviceEnumerator, playbackDevices){ if(FAILED(deviceEnumerator->RegisterEndpointNotificationCallback(((IMMNotificationClient*)&epsc)))) { log_debugcpp("when no enchufas......"); } } +void Overseer::openControlPanel() { + STARTUPINFOEXW startupConfig; + PROCESS_INFORMATION processInfo; + SecureZeroMemory(&startupConfig, sizeof(STARTUPINFOEXW)); + SecureZeroMemory(&startupConfig.StartupInfo, sizeof(STARTUPINFOW)); + startupConfig.StartupInfo.cb = sizeof(STARTUPINFOEXW); + SecureZeroMemory(&processInfo, sizeof(PROCESS_INFORMATION)); + + std::wstring command = L"rundll32 shell32, Control_RunDLL mmsys.cpl"; + if(CreateProcessW( + NULL, + (wchar_t*)command.c_str(), + NULL, + NULL, + false, + CREATE_UNICODE_ENVIRONMENT, + NULL, + NULL, + (LPSTARTUPINFOW)&startupConfig, + &processInfo + ) == true) { + CloseHandle(processInfo.hProcess); + CloseHandle(processInfo.hThread); + } +} + NGuid Overseer::getGuid() { return guid; } diff --git a/src/back/backlasses.h b/src/back/backlasses.h index f70e6d1..8796466 100644 --- a/src/back/backlasses.h +++ b/src/back/backlasses.h @@ -106,6 +106,7 @@ class Overseer { //TODO singleton? public: Overseer(); + void openControlPanel(); std::vector getPlaybackEndpoints(); std::vector getCaptureEndpoints(); diff --git a/src/back/msinclude.h b/src/back/msinclude.h index fc7beac..7bc7f4e 100644 --- a/src/back/msinclude.h +++ b/src/back/msinclude.h @@ -6,6 +6,7 @@ //done by qt by def #define UNICODE #include +#include #include #include #include diff --git a/src/cont/contclasses.cpp b/src/cont/contclasses.cpp index 38c30b1..d9fe2d0 100644 --- a/src/cont/contclasses.cpp +++ b/src/cont/contclasses.cpp @@ -201,6 +201,10 @@ OverseerHandler::OverseerHandler() { this->os = new Overseer(); } +void OverseerHandler::openControlPanel() { + this->os->openControlPanel(); +} + std::vector OverseerHandler::getPlaybackEndpoints() { return this->os->getPlaybackEndpoints(); } diff --git a/src/cont/contclasses.h b/src/cont/contclasses.h index 25fcaea..e46fc34 100644 --- a/src/cont/contclasses.h +++ b/src/cont/contclasses.h @@ -97,6 +97,8 @@ class OverseerHandler { public: OverseerHandler(); + void openControlPanel(); + void setChangeFrontDefaultsFunction(std::function changeFrontDefaults); void changeFrontDefaultsCallback(Roles role, std::wstring endpointId); diff --git a/src/global.h b/src/global.h index 035c95b..85f203a 100644 --- a/src/global.h +++ b/src/global.h @@ -22,6 +22,10 @@ #define STRING_SYSTEM_SOUNDS "System Sounds" #define LSTRING_SYSTEM_SOUNDS L"System Sounds" + +#define STRING_CP "Open Control Panel" +#define STRING_ABOUT "About" +#define STRING_STARTUP "Run at startup" //INIT BACK enum AudioChannel { diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index a9da261..125352b 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -6,7 +6,7 @@ CustomWidgetEvent::CustomWidgetEvent(QEvent::Type type, T payload) : QEvent(t } void ExtendedCheckBox::customEvent(QEvent* ev) { - QEvent::Type tipo = ev->type(); + //QEvent::Type tipo = ev->type(); if (ev->type() == (QEvent::Type)CustomQEvent::EndpointDefaultChange) { //todo: still prone to bugs; whack-a-mole to come ev->setAccepted(true); @@ -56,6 +56,8 @@ SessionWidget::SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent) layout->addWidget(muteButton, 0, 1, Qt::AlignLeft | Qt::AlignBottom); layout->addWidget(mainSlider, 0, 2, 1, 2); + layout->setSizeConstraint(QLayout::SetMinAndMaxSize); + //TODO:0 = mute and muted, change volume = unmuted are client side tricks = 2 callbacks, one for volume, one for mute state. Implement as an user selectable option? connect(mainSlider, &QSlider::valueChanged, this,&SessionWidget::updateMainVolume); connect(muteButton, &QCheckBox::stateChanged, this, (&SessionWidget::updateMute)); @@ -108,6 +110,7 @@ EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *pare //todo: sussy this->eph->setState(EndpointState::ENDPOINT_ACTIVE, idx); + //setAttribute(Qt::WA_TranslucentBackground); layout = new QGridLayout(this); //this->setLayout(layout); log_debugcpp("epw main layout parent: " + std::to_string((intptr_t)(layout->parent()))); @@ -149,9 +152,11 @@ EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *pare //tip: would need to be new widget with layout in it //mainMuteLayout = new QGridLayout(); - layout->addWidget(mainLabel, row, 0, Qt::AlignLeft | Qt::AlignBottom); - layout->addWidget(muteButton, row, 1, Qt::AlignLeft | Qt::AlignBottom); - layout->addWidget(mainSlider, row, 2, 1, 2); + layout->addWidget(mainLabel, row, 0, Qt::AlignLeft | Qt::AlignVCenter); + layout->addWidget(muteButton, row, 1, Qt::AlignLeft | Qt::AlignVCenter); + layout->addWidget(mainSlider, row, 2, 1, 2, Qt::AlignLeft | Qt::AlignVCenter); + layout->setSizeConstraint(QLayout::SetMinAndMaxSize); + int debug2 = this->minimumWidth(); row++; //TODO:0 = mute and muted, change volume = unmuted are client side tricks = 2 callbacks, one for volume, one for mute state. Implement as an user selectable option? @@ -176,7 +181,7 @@ EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *pare tmpLb->setText(QString::number(volume)); this->channelSliders.push_back(tmp); this->channelLabels.push_back(tmpLb); - layout->addWidget(tmp, row + 1, i); + layout->addWidget(tmp, row + 1, i); layout->addWidget(tmpLb, row + 2, i); //TODO: check if there's a need to prevent deadlocks; probably this will eventually turn into its own func @@ -263,7 +268,7 @@ EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *pare /* First SessionWidget batch */ for (size_t i = 0; i < eph->getSessionCount(); i++) { SessionWidget* sessionWidget = new SessionWidget(i, eph->getSessionHandlers().at(i), this); - layout->addWidget(sessionWidget, row, 4); + layout->addWidget(sessionWidget, row, 1, 1, 4); row++; sessionWidgets.push_back(sessionWidget); eph->getSessionHandlers().at(i)->setFrontIndex(i); @@ -290,7 +295,7 @@ void EndpointWidget::addSessionWidget(CustomWidgetEvent* ev){ uint64_t index = this->sessionWidgets.size(); SessionWidget* sw = new SessionWidget(index, ev->payload, this); ev->payload->setFrontIndex(index); - this->layout->addWidget(sw, row, 4); + this->layout->addWidget(sw, row, 1, 1, 4); row++; sessionWidgets.push_back(sw); return; @@ -326,7 +331,7 @@ EndpointWidget::~EndpointWidget() { } void MainWindow::customEvent(QEvent* ev) { - if (ev->type() == CustomQEvent::EndpointWidgetObsolete) { + if (ev->type() == (QEvent::Type)CustomQEvent::EndpointWidgetObsolete) { ev->setAccepted(true); this->removeEndpointWidget((CustomWidgetEvent*)ev); } else if (ev->type() == (QEvent::Type)CustomQEvent::EndpointWidgetCreated) { @@ -448,6 +453,23 @@ std::map EndpointWidget::getDefaultRolesWidgets() { return defaultRolesCheckBoxes; } +HeaderWidget::HeaderWidget(QWidget *parent) : QWidget(parent) { + layout = new QGridLayout(this); + QString text = "&" STRING_ABOUT; + about = new QPushButton(text, this); + #ifdef WIN32 + text = "&" STRING_CP; + openCP = new QPushButton(text, this); + connect(openCP, &QPushButton::clicked, [this](){ osh->openControlPanel(); }); + text = "&" STRING_STARTUP; + startup = new QPushButton(text, this); + + layout->addWidget(openCP , 0, 0); + layout->addWidget(startup, 0, 1); + #endif + layout->addWidget(about , 0, 2); + this->setLayout(layout); +} MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { // setWindowState(Qt::WindowFullScreen); @@ -459,7 +481,13 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { QEvent::registerEventType(CustomQEvent::EndpointWidgetObsolete); QEvent::registerEventType(CustomQEvent::EndpointWidgetCreated); QEvent::registerEventType(CustomQEvent::EndpointDefaultChange); + QEvent::registerEventType(CustomQEvent::SessionWidgetObsolete); + QEvent::registerEventType(CustomQEvent::SessionWidgetCreated); + //setWindowFlags(Qt::FramelessWindowHint); + //setParent(0); // Create TopLevel-Widget + //setAttribute(Qt::WA_NoSystemBackground, true); + //setAttribute(Qt::WA_TranslucentBackground, true); ewsUpdateTimer = new QTimer(this); widget = new QWidget(); layout = new QGridLayout(); @@ -470,13 +498,34 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { ewsUpdateTimer->setSingleShot(true); ewsUpdateTimer->setInterval(ewsUpdateTimerFrequency); connect(ewsUpdateTimer, &QTimer::timeout, this, &MainWindow::reorderEndpointWidgetCollection); + //widget->setMinimumSize(QSize(300,300)); widget->setLayout(layout); - setCentralWidget(widget); + /* + * Scroll bar code + */ + scrollArea = new QScrollArea(this); + scrollArea->setWidget(widget); + scrollArea->setWidgetResizable(true); + scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + scrollArea->setMinimumWidth(500); + setCentralWidget(scrollArea); + + /* + * Menu bar code + */ + QMenuBar* menuBar = (this->menuBar)(); + hw = new HeaderWidget(this); + menuBar->setCornerWidget(hw,Qt::TopLeftCorner); + menuBar->show(); + this->setMenuBar(menuBar); + + //setCentralWidget(widget); //layout->addWidget(pintas, 0, 0); setWindowTitle(STRING_TITLE); reloadEndpointWidgets(); - + scrollArea->setMinimumWidth(ews.at(0)->minimumWidth()); + int debug = scrollArea->minimumWidth(); /* * Tray Icon code */ diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index bec8f5e..7a89400 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -18,6 +18,12 @@ #include #include #include +#include +#include +#include +#include + +//#include /* * #else * class QSlider; @@ -142,6 +148,7 @@ private slots: private: int row; + const int sessionCol = 2; QCheckBox *muteButton = nullptr; QLabel *mainLabel = nullptr, *leftChannelLabel = nullptr, *rightChannelLabel = nullptr; QSlider *mainSlider = nullptr; @@ -164,6 +171,38 @@ private: }; +class HeaderWidget : public QWidget { +Q_OBJECT + +public: + HeaderWidget(QWidget *parent = nullptr); + + + //~HeaderWidget(); + //void updateMainVolume(float newValue); + //void updateVolume(uint32_t channel, float newValue); + //void updateMute(bool muted); + + //void populateEndpointWidget(EndpointHandler *eph); + //void setEndpointHandlers(std::vector *ephs); + +//public slots: + +//protected: + //void customEvent(QEvent* ev) override; + +//private slots: + //void addSessionWidget(CustomWidgetEvent* ev); + //void removeSessionWidget(CustomWidgetEvent* ev); + +private: + QGridLayout *layout; + QPushButton *about; + #ifdef WIN32 + QPushButton *openCP; + QPushButton *startup; + #endif +}; class MainWindow : public QMainWindow { Q_OBJECT @@ -196,6 +235,10 @@ private: QAction *trayIconMenuQuit; QTimer *ewsUpdateTimer; static constexpr uint64_t ewsUpdateTimerFrequency = 500; + + QScrollArea *scrollArea; + HeaderWidget* hw; + //QMenuBar *menuBar; //public slots: // void setEndpointHandlers(std::vector *ephs); diff --git a/src/qtestmain.cpp b/src/qtestmain.cpp index 19cf627..14c1aa8 100644 --- a/src/qtestmain.cpp +++ b/src/qtestmain.cpp @@ -10,6 +10,7 @@ #include #include #include +#include //#include "contclasses.h" #include "qtclasses.h" #include "global.h" @@ -55,9 +56,16 @@ int main (int argc, char* argv[]) { //INIT FRONT QScopedPointer app(createApplication(argc, argv)); MainWindow window = MainWindow(); - //window.setEndpointHandlers(ephs); + QApplication::setQuitOnLastWindowClosed(false); - app->setStyle("windowsvista"); + /* + * QFile styleFile(":/assets/style.qss"); + * styleFile.open(QFile::ReadOnly); + * QString styleSheet { QLatin1String(styleFile.readAll()) }; + */ + + //app->setStyleSheet(styleSheet); + //window.setMinimumSize(100, 100); window.show(); return app->exec(); } From 8d1a0d190b40cc398d8e3a11f3f9aefddf590a84 Mon Sep 17 00:00:00 2001 From: Hane Date: Wed, 3 Apr 2024 21:53:07 +0200 Subject: [PATCH 07/78] width + screen position --- src/qt/qtclasses.cpp | 219 +++++++++++++++++++++++++++++++------------ src/qt/qtclasses.h | 42 ++++++++- src/qtestmain.cpp | 3 +- 3 files changed, 199 insertions(+), 65 deletions(-) diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 125352b..3887cb0 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -25,19 +25,61 @@ void ExtendedCheckBox::customEvent(QEvent* ev) { QCheckBox::customEvent(ev); } +QRect MainWindow::setSizePosition() { + //setGeometry ignores decoration size xdddd + QRect trayIconPos = this->trayIcon->geometry(); + int tix1, tix2, tiy1, tiy2; + trayIconPos.getCoords(&tix1, &tiy1, &tix2, &tiy2); + + screen = QGuiApplication::primaryScreen(); + QRect screenRes = screen->geometry(); + int srx1, srx2, sry1, sry2; + screenRes.getCoords(&srx1, &sry1, &srx2, &sry2); + QRect availableRes = screen->availableGeometry(); + int arx1, arx2, ary1, ary2; + availableRes.getCoords(&arx1, &ary1, &arx2, &ary2); + + uint8_t pos = 0; + pos = tiy2 < (sry2 / 2) ? SpawnPos::UP : SpawnPos::DOWN; + pos = tix2 < (srx2 / 2) ? pos | SpawnPos::LEFT : pos | SpawnPos::RIGHT; + + switch (pos) { + case SpawnPos::UP | SpawnPos::RIGHT: + return QRect((arx2 - windowWidth), ary1, windowWidth, 440); + break; + case SpawnPos::DOWN | SpawnPos::LEFT: + return QRect(arx1, (ary2-440), windowWidth, 440); + break; + case SpawnPos::DOWN | SpawnPos::RIGHT: + return QRect((arx2 - windowWidth), (ary2-440), windowWidth, 440); + break; + default: + return QRect(500, 400, windowWidth, 440); + break; + } +} + SessionWidget::SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent) : QWidget(parent){ - //todo: based on qgridlayout, name+mute should be its own widget, same with channels + //todo: based on qgridlayout, name+mute should be its own widget, same with channels this->idx = idx; this->sh = sh; - layout = new QGridLayout(this); + layout = new QHBoxLayout(this); + //layout->setSizeConstraint(QLayout::SetFixedSize); + layout->setSizeConstraint(QLayout::SetMinAndMaxSize); + //layout->setMaximumSize(minimumSize()); //this->setLayout( muteButton = new QCheckBox(this); mainLabel = new QLabel(QString::fromStdWString(sh->getName()), this); mainSlider = new QSlider(Qt::Horizontal, this); - mainSlider->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + mainLabel->setMaximumWidth(150 /*1/16ish 1080p*/); + mainLabel->setMinimumWidth(150 /*1/16ish 1080p*/); + mainLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); + + mainSlider->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + mainSlider->setMinimumWidth(120 /*1/16 1080p*/); mainSlider->setFocusPolicy(Qt::StrongFocus); mainSlider->setTickPosition(QSlider::TicksBothSides); mainSlider->setTickInterval(5); @@ -46,17 +88,27 @@ SessionWidget::SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent) muteButton->setCheckState((sh->getMute() == false ? Qt::Unchecked : Qt::Checked)); muteButton->setText(sh->getMute() ? STRING_UNMUTE : STRING_MUTE); + muteButton->setMaximumWidth(60 /*1/32th 1080p*/); + muteButton->setMinimumWidth(60 /*1/32th 1080p*/); float volume = sh->getVolume(AudioChannel::CHANNEL_MAIN) * 100; mainSlider->setValue((int)volume); log_debugcpp("SESSION SET WITH VOLUME " + std::to_string(volume)); //tip: would need to be new widget with layout in it //mainMuteLayout = new QGridLayout(); - layout->addWidget(mainLabel, 0, 0, Qt::AlignLeft | Qt::AlignBottom); - layout->addWidget(muteButton, 0, 1, Qt::AlignLeft | Qt::AlignBottom); - layout->addWidget(mainSlider, 0, 2, 1, 2); + /* + * layout->addWidget(mainLabel, 0, 0, Qt::AlignLeft | Qt::AlignBottom); + * layout->addWidget(muteButton, 0, 1, Qt::AlignLeft | Qt::AlignBottom); + * layout->addWidget(mainSlider, 0, 2, 1, 2); + * + * layout->setSizeConstraint(QLayout::SetMinAndMaxSize); + */ + layout->addItem(new QSpacerItem(200, 1, QSizePolicy::Expanding, QSizePolicy::Minimum)); + layout->addWidget(mainLabel, Qt::AlignLeft | Qt::AlignBottom); + layout->addWidget(muteButton, Qt::AlignRight | Qt::AlignBottom); + layout->addWidget(mainSlider, Qt::AlignRight | Qt::AlignBottom); - layout->setSizeConstraint(QLayout::SetMinAndMaxSize); + //layout->setSizeConstraint(QLayout::SetMinAndMaxSize); //TODO:0 = mute and muted, change volume = unmuted are client side tricks = 2 callbacks, one for volume, one for mute state. Implement as an user selectable option? connect(mainSlider, &QSlider::valueChanged, this,&SessionWidget::updateMainVolume); @@ -102,6 +154,49 @@ SessionWidget::~SessionWidget() { delete volumePoller; } +ChannelWidget::ChannelWidget(uint32_t channelCount, EndpointHandler* eph, QWidget *parent) : QWidget(parent){ + this->eph = eph; + this->channelCount = channelCount; + layout = new QGridLayout(this); + float volume = 100; +/* + * Channel sliders setup + */ + //uint32_t epChannelCount = eph->getChannelCount(); + for(uint32_t i = 0; i < channelCount && channelCount > 1; i++){ + QSlider* tmp = new QSlider(Qt::Horizontal); + QLabel* tmpLb = new QLabel(""); + tmp->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + tmp->setTickInterval(5); + tmp->setSingleStep(1); + tmp->setRange(0,100); + + volume = eph->getVolume(i) * 100; + tmp->setValue((int) volume); + tmpLb->setText(QString::number(volume)); + this->channelSliders.push_back(tmp); + this->channelLabels.push_back(tmpLb); + layout->addWidget(tmp, 0, i); + layout->addWidget(tmpLb, 1, i); + + //TODO: check if there's a need to prevent deadlocks; probably this will eventually turn into its own func + //this causes channel bar desync when back -> front. blocksignals below fix it. huh. + connect(tmp, &QSlider::valueChanged, [this, i](int newValue){ + this->eph->setVolume(osh->getGuid(), i, newValue); + this->channelLabels.at(i)->setText(QString::number(newValue)); + }); + } + this->setLayout(layout); + +} + +void ChannelWidget::updateChannel(int channel) { + this->channelSliders.at(channel)->blockSignals(true); + this->channelSliders.at(channel)->setValue((int)((eph->getCallbackInfo()->channelVolumes[channel] + roundingFactor) * 100)); + this->channelLabels.at(channel)->setText(QString::number((int)((eph->getCallbackInfo()->channelVolumes[channel] + roundingFactor) * 100))); + this->channelSliders.at(channel)->blockSignals(false); +} + EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *parent) : QWidget(parent){ //todo: based on qgridlayout, name+mute should be its own widget, same with channels row = 0; @@ -135,6 +230,10 @@ EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *pare layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Minimum), 1, 0); return; } + + mainLabel->setMaximumWidth(240 /*1/8 1080p*/); + mainLabel->setMinimumWidth(240 /*1/8 1080p*/); + //mainLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); //muteButton->setStyleSheet("background-color: #A3C1DA; color: red"); mainSlider->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); @@ -152,11 +251,11 @@ EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *pare //tip: would need to be new widget with layout in it //mainMuteLayout = new QGridLayout(); - layout->addWidget(mainLabel, row, 0, Qt::AlignLeft | Qt::AlignVCenter); - layout->addWidget(muteButton, row, 1, Qt::AlignLeft | Qt::AlignVCenter); + layout->addWidget(mainLabel, row, 0, Qt::AlignLeft | Qt::AlignVCenter); + layout->addWidget(muteButton, row, 1, Qt::AlignLeft | Qt::AlignVCenter); layout->addWidget(mainSlider, row, 2, 1, 2, Qt::AlignLeft | Qt::AlignVCenter); layout->setSizeConstraint(QLayout::SetMinAndMaxSize); - int debug2 = this->minimumWidth(); + //int debug2 = this->minimumWidth(); row++; //TODO:0 = mute and muted, change volume = unmuted are client side tricks = 2 callbacks, one for volume, one for mute state. Implement as an user selectable option? @@ -164,34 +263,15 @@ EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *pare connect(muteButton, &QCheckBox::stateChanged, this, (&EndpointWidget::updateMute)); -/* - * Channel sliders setup - */ + /* + * Channel sliders setup + */ + uint32_t epChannelCount = eph->getChannelCount(); - for(uint32_t i = 0; i < epChannelCount && epChannelCount > 1; i++){ - QSlider* tmp = new QSlider(Qt::Horizontal); - QLabel* tmpLb = new QLabel(""); - tmp->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - tmp->setTickInterval(5); - tmp->setSingleStep(1); - tmp->setRange(0,100); - - volume = eph->getVolume(i) * 100; - tmp->setValue((int) volume); - tmpLb->setText(QString::number(volume)); - this->channelSliders.push_back(tmp); - this->channelLabels.push_back(tmpLb); - layout->addWidget(tmp, row + 1, i); - layout->addWidget(tmpLb, row + 2, i); - - //TODO: check if there's a need to prevent deadlocks; probably this will eventually turn into its own func - //this causes channel bar desync when back -> front. blocksignals below fix it. huh. - connect(tmp, &QSlider::valueChanged, [this, i](int newValue){ - this->eph->setVolume(osh->getGuid(), i, newValue); - this->channelLabels.at(i)->setText(QString::number(newValue)); - }); + if(epChannelCount) { + cw = new ChannelWidget(epChannelCount, eph, this); + layout->addWidget(cw, row++, 0, 1, 4 /*colmax*/); } - row += 3; /* * Role ExtendedCheckBoxes setup @@ -229,16 +309,18 @@ EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *pare this->eph->setRoles(Roles::ROLE_COMMUNICATIONS); }); - layout->addWidget(defaultRolesCheckBoxes.at(Roles::ROLE_ALL), row, 0); - layout->addWidget(defaultRolesCheckBoxes.at(Roles::ROLE_CONSOLE), row, 1); - layout->addWidget(defaultRolesCheckBoxes.at(Roles::ROLE_MULTIMEDIA), row, 2); + layout->addWidget(defaultRolesCheckBoxes.at(Roles::ROLE_ALL), row, 0); + layout->addWidget(defaultRolesCheckBoxes.at(Roles::ROLE_CONSOLE), row, 1); + layout->addWidget(defaultRolesCheckBoxes.at(Roles::ROLE_MULTIMEDIA), row, 2); layout->addWidget(defaultRolesCheckBoxes.at(Roles::ROLE_COMMUNICATIONS), row, 3); row++; -/* ----------------------------------------------------------- */ + + /* ----------------------------------------------------------- */ /* * EndpointVolume Polling time */ + timer = new QTimer(); connect(timer, &QTimer::timeout, [this, eph](){ //if (memcmp(osh->callbackInfo[idx]->caller, osh->getGuid(), sizeof(NGuid)) == 0) return; CHECK IF THIS PROGRAM GENERATED THE FUNSIES IS NO LONGER IN USE FOR NOW. @@ -250,10 +332,7 @@ EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *pare muteButton->setCheckState((eph->getCallbackInfo()->muted == false ? Qt::Unchecked : Qt::Checked)); muteButton->setText(eph->getCallbackInfo()->muted ? STRING_UNMUTE : STRING_MUTE); for(uint32_t i = 0; i < eph->getCallbackInfo()->channels && eph->getChannelCount() > 1; i++){ - this->channelSliders.at(i)->blockSignals(true); - this->channelSliders.at(i)->setValue((int)((eph->getCallbackInfo()->channelVolumes[i] + roundingFactor) * 100)); - this->channelLabels.at(i)->setText(QString::number((int)((eph->getCallbackInfo()->channelVolumes[i] + roundingFactor) * 100))); - this->channelSliders.at(i)->blockSignals(false); + cw->updateChannel(i); } //memcpy(osh->callbackInfo[idx]->caller, osh->getGuid(), sizeof(NGuid)); @@ -268,7 +347,7 @@ EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *pare /* First SessionWidget batch */ for (size_t i = 0; i < eph->getSessionCount(); i++) { SessionWidget* sessionWidget = new SessionWidget(i, eph->getSessionHandlers().at(i), this); - layout->addWidget(sessionWidget, row, 1, 1, 4); + layout->addWidget(sessionWidget, row, 0, 1, 4); row++; sessionWidgets.push_back(sessionWidget); eph->getSessionHandlers().at(i)->setFrontIndex(i); @@ -295,7 +374,7 @@ void EndpointWidget::addSessionWidget(CustomWidgetEvent* ev){ uint64_t index = this->sessionWidgets.size(); SessionWidget* sw = new SessionWidget(index, ev->payload, this); ev->payload->setFrontIndex(index); - this->layout->addWidget(sw, row, 1, 1, 4); + this->layout->addWidget(sw, row, 0, 1, 4); row++; sessionWidgets.push_back(sw); return; @@ -455,14 +534,15 @@ std::map EndpointWidget::getDefaultRolesWidgets() { HeaderWidget::HeaderWidget(QWidget *parent) : QWidget(parent) { layout = new QGridLayout(this); + QString text = "&" STRING_ABOUT; - about = new QPushButton(text, this); + about = new QPushButton(text, this); #ifdef WIN32 text = "&" STRING_CP; - openCP = new QPushButton(text, this); - connect(openCP, &QPushButton::clicked, [this](){ osh->openControlPanel(); }); - text = "&" STRING_STARTUP; - startup = new QPushButton(text, this); + openCP = new QPushButton(text, this); + connect(openCP, &QPushButton::clicked, [](){ osh->openControlPanel(); }); + text = "&" STRING_STARTUP; + startup = new QPushButton(text, this); layout->addWidget(openCP , 0, 0); layout->addWidget(startup, 0, 1); @@ -474,10 +554,17 @@ HeaderWidget::HeaderWidget(QWidget *parent) : QWidget(parent) { MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { // setWindowState(Qt::WindowFullScreen); // setCentralWidget(centralWidget); - + //todo: ratio + resize(windowWidth, 440); + setWindowFlags(Qt::Window | Qt::MSWindowsFixedSizeDialogHint); + #ifdef DEBUG + setWindowTitle(STRING_TITLE); + setWindowFlags(Qt::FramelessWindowHint); + #endif /* * Registering needed custom events */ + //| Qt::FramelessWindowHint QEvent::registerEventType(CustomQEvent::EndpointWidgetObsolete); QEvent::registerEventType(CustomQEvent::EndpointWidgetCreated); QEvent::registerEventType(CustomQEvent::EndpointDefaultChange); @@ -487,13 +574,14 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { //setWindowFlags(Qt::FramelessWindowHint); //setParent(0); // Create TopLevel-Widget //setAttribute(Qt::WA_NoSystemBackground, true); - //setAttribute(Qt::WA_TranslucentBackground, true); + //setAttribute(Qt::WA_TranslucentBackground, true); ewsUpdateTimer = new QTimer(this); widget = new QWidget(); layout = new QGridLayout(); trayIcon = new QSystemTrayIcon(); trayIconMenu = new QMenu(); trayIconMenuQuit = new QAction(STRING_QUIT); + trayIconMenuOpenCP = new QAction(STRING_CP); ewsUpdateTimer->setSingleShot(true); ewsUpdateTimer->setInterval(ewsUpdateTimerFrequency); @@ -513,24 +601,27 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { /* * Menu bar code */ - QMenuBar* menuBar = (this->menuBar)(); + mainMenuBar = this->menuBar(); hw = new HeaderWidget(this); - menuBar->setCornerWidget(hw,Qt::TopLeftCorner); - menuBar->show(); - this->setMenuBar(menuBar); + mainMenuBar->setCornerWidget(hw,Qt::TopLeftCorner); + mainMenuBar->show(); + this->setMenuBar(mainMenuBar); //setCentralWidget(widget); //layout->addWidget(pintas, 0, 0); - setWindowTitle(STRING_TITLE); reloadEndpointWidgets(); scrollArea->setMinimumWidth(ews.at(0)->minimumWidth()); - int debug = scrollArea->minimumWidth(); + log_debugcpp(std::to_string(scrollArea->minimumWidth())); + /* * Tray Icon code */ + //trayIconMenu->addSeparator(); + trayIconMenu->addAction(trayIconMenuOpenCP); trayIconMenu->addSeparator(); - trayIconMenu->addAction(trayIconMenuQuit); + trayIconMenu->addAction(trayIconMenuQuit); + connect(trayIconMenuOpenCP, &QAction::triggered, ([]() {osh->openControlPanel();}) ); connect(trayIconMenuQuit, &QAction::triggered, qApp, &QCoreApplication::quit); trayIcon->setIcon(QIcon(":/assets/notificationAreaIcon.png")); setWindowIcon(QIcon(":/assets/notificationAreaIcon.png")); @@ -540,6 +631,12 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { trayIcon->setToolTip(STRING_TITLE); trayIcon->setContextMenu(trayIconMenu); connect(trayIcon, &QSystemTrayIcon::activated, this, &MainWindow::trayIconActivated); + + /* + * Establishing initial window size and position + */ + //TODO: test. hardcode. var. + setGeometry(setSizePosition()); /* * Set of function callback definitons for EndpointSituationCallback @@ -598,7 +695,6 @@ void MainWindow::closeEvent(QCloseEvent *event) { if (trayIcon->isVisible()) { //todo: would be nice to show this to 1st time users; ini-san will come... //this->trayIcon->showMessage("ini file calling","tratarte como un gilipollas la primera vez", QSystemTrayIcon::Information); - hide(); event->ignore(); } @@ -607,6 +703,7 @@ void MainWindow::closeEvent(QCloseEvent *event) { void MainWindow::trayIconActivated(QSystemTrayIcon::ActivationReason reason) { switch (reason) { case QSystemTrayIcon::Trigger: + this->setGeometry(this->setSizePosition()); this->showNormal(); break; default: diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index 7a89400..4521120 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -22,6 +22,7 @@ #include #include #include +#include //#include /* @@ -62,6 +63,14 @@ * ToggleButton(QWidget *parent = nullptr); * }; */ + +enum SpawnPos { + LEFT = (1 << 1), + RIGHT = (0 << 1), + UP = (1 << 0), + DOWN = (0 << 0) +}; + enum CustomQEvent { EndpointWidgetObsolete = 1001, EndpointWidgetCreated = 1002, @@ -108,12 +117,30 @@ private: QLabel *mainLabel = nullptr; QSlider *mainSlider = nullptr; uint64_t idx; - QGridLayout *layout = nullptr; + //QGridLayout *layout = nullptr; + QHBoxLayout *layout = nullptr; QCheckBox *muteButton = nullptr; SessionHandler* sh; QTimer* volumePoller = nullptr; }; +class ChannelWidget : public QWidget { +Q_OBJECT + +public: + ChannelWidget(uint32_t channelCount, EndpointHandler* eph, QWidget *parent = nullptr); + void updateChannel(int channel); + +private: + const double roundingFactor = 0.005; + EndpointHandler* eph; + uint32_t channelCount; + std::vector channelSliders; + std::vector channelLabels; + QGridLayout *layout; + +}; + class EndpointWidget : public QWidget { Q_OBJECT @@ -162,6 +189,7 @@ private: size_t defaultRolesVectorSize = 4; QTimer* timer = nullptr; uint64_t idx; + ChannelWidget* cw; std::vector sessionWidgets; //std::vector *ephs; //std::vector *sliders; @@ -177,7 +205,6 @@ Q_OBJECT public: HeaderWidget(QWidget *parent = nullptr); - //~HeaderWidget(); //void updateMainVolume(float newValue); //void updateVolume(uint32_t channel, float newValue); @@ -215,6 +242,7 @@ public: protected: void closeEvent(QCloseEvent *event) override; void customEvent(QEvent* ev) override; + QRect setSizePosition(); private slots: void trayIconActivated(QSystemTrayIcon::ActivationReason reason); @@ -233,12 +261,20 @@ private: QSystemTrayIcon *trayIcon; QMenu *trayIconMenu; QAction *trayIconMenuQuit; + QAction *trayIconMenuOpenCP; QTimer *ewsUpdateTimer; static constexpr uint64_t ewsUpdateTimerFrequency = 500; + //TODO: Test + //TODO: Come back here and check all are parametrized + uint64_t windowWidth = 600; QScrollArea *scrollArea; HeaderWidget* hw; - //QMenuBar *menuBar; + QMenuBar *mainMenuBar; + QScreen *screen; + //Win10 1080p 120% + QSize mwSize; + //public slots: // void setEndpointHandlers(std::vector *ephs); diff --git a/src/qtestmain.cpp b/src/qtestmain.cpp index 14c1aa8..85e1928 100644 --- a/src/qtestmain.cpp +++ b/src/qtestmain.cpp @@ -55,8 +55,9 @@ int main (int argc, char* argv[]) { //INIT FRONT QScopedPointer app(createApplication(argc, argv)); + MainWindow window = MainWindow(); - + //window.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::QSizePolicy::MinimumExpanding) QApplication::setQuitOnLastWindowClosed(false); /* * QFile styleFile(":/assets/style.qss"); From 90286b68532187e9367fe31c13c6cb37f578309e Mon Sep 17 00:00:00 2001 From: Hane Date: Wed, 3 Apr 2024 23:53:33 +0200 Subject: [PATCH 08/78] dynamic height + width 1st commit --- src/qt/qtclasses.cpp | 149 ++++++++++++++++++++++++------------------- src/qt/qtclasses.h | 62 +++--------------- src/qtestmain.cpp | 4 +- 3 files changed, 97 insertions(+), 118 deletions(-) diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 3887cb0..6e5e4ff 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -25,7 +25,7 @@ void ExtendedCheckBox::customEvent(QEvent* ev) { QCheckBox::customEvent(ev); } -QRect MainWindow::setSizePosition() { +QRect MainWindow::setSizePosition(int width, int height) { //setGeometry ignores decoration size xdddd QRect trayIconPos = this->trayIcon->geometry(); int tix1, tix2, tiy1, tiy2; @@ -45,29 +45,51 @@ QRect MainWindow::setSizePosition() { switch (pos) { case SpawnPos::UP | SpawnPos::RIGHT: - return QRect((arx2 - windowWidth), ary1, windowWidth, 440); + return QRect((arx2 - width), ary1, width, height); break; case SpawnPos::DOWN | SpawnPos::LEFT: - return QRect(arx1, (ary2-440), windowWidth, 440); + return QRect(arx1, (ary2-height), width, height); break; case SpawnPos::DOWN | SpawnPos::RIGHT: - return QRect((arx2 - windowWidth), (ary2-440), windowWidth, 440); + return QRect((arx2 - width), (ary2-height), windowWidth, height); break; default: - return QRect(500, 400, windowWidth, 440); + return QRect(500, 400, width, height); break; } } +void MainWindow::calculateChildWidgetsSize() { + //We need dynamically added child widgets to expand so that we know their height + //TODO: MenuBar height + this->setAttribute(Qt::WA_DontShowOnScreen, true); + this->show(); + this->layout()->invalidate(); + this->hide(); + this->setAttribute(Qt::WA_DontShowOnScreen, false); + + int height = 0, width = 0; + for (auto *epw : this->ews) { + height += epw->height(); + width = (epw->width() > width) ? epw->width() : width; + } + + /* + * Establishing initial window size and position + */ + //TODO: test. hardcode. var. + setGeometry(setSizePosition(width, height)); +} + SessionWidget::SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent) : QWidget(parent){ //todo: based on qgridlayout, name+mute should be its own widget, same with channels this->idx = idx; this->sh = sh; - layout = new QHBoxLayout(this); - //layout->setSizeConstraint(QLayout::SetFixedSize); - layout->setSizeConstraint(QLayout::SetMinAndMaxSize); - //layout->setMaximumSize(minimumSize()); + widgetLayout = new QHBoxLayout(this); + //widgetLayout->setSizeConstraint(QLayout::SetFixedSize); + widgetLayout->setSizeConstraint(QLayout::SetMinAndMaxSize); + //widgetLayout->setMaximumSize(minimumSize()); //this->setLayout( muteButton = new QCheckBox(this); @@ -103,12 +125,12 @@ SessionWidget::SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent) * * layout->setSizeConstraint(QLayout::SetMinAndMaxSize); */ - layout->addItem(new QSpacerItem(200, 1, QSizePolicy::Expanding, QSizePolicy::Minimum)); - layout->addWidget(mainLabel, Qt::AlignLeft | Qt::AlignBottom); - layout->addWidget(muteButton, Qt::AlignRight | Qt::AlignBottom); - layout->addWidget(mainSlider, Qt::AlignRight | Qt::AlignBottom); + widgetLayout->addItem(new QSpacerItem(200, 1, QSizePolicy::Expanding, QSizePolicy::Minimum)); + widgetLayout->addWidget(mainLabel, Qt::AlignLeft | Qt::AlignBottom); + widgetLayout->addWidget(muteButton, Qt::AlignRight | Qt::AlignBottom); + widgetLayout->addWidget(mainSlider, Qt::AlignRight | Qt::AlignBottom); - //layout->setSizeConstraint(QLayout::SetMinAndMaxSize); + //widgetLayout->setSizeConstraint(QLayout::SetMinAndMaxSize); //TODO:0 = mute and muted, change volume = unmuted are client side tricks = 2 callbacks, one for volume, one for mute state. Implement as an user selectable option? connect(mainSlider, &QSlider::valueChanged, this,&SessionWidget::updateMainVolume); @@ -157,7 +179,7 @@ SessionWidget::~SessionWidget() { ChannelWidget::ChannelWidget(uint32_t channelCount, EndpointHandler* eph, QWidget *parent) : QWidget(parent){ this->eph = eph; this->channelCount = channelCount; - layout = new QGridLayout(this); + widgetLayout = new QGridLayout(this); float volume = 100; /* * Channel sliders setup @@ -176,8 +198,8 @@ ChannelWidget::ChannelWidget(uint32_t channelCount, EndpointHandler* eph, QWidge tmpLb->setText(QString::number(volume)); this->channelSliders.push_back(tmp); this->channelLabels.push_back(tmpLb); - layout->addWidget(tmp, 0, i); - layout->addWidget(tmpLb, 1, i); + widgetLayout->addWidget(tmp, 0, i); + widgetLayout->addWidget(tmpLb, 1, i); //TODO: check if there's a need to prevent deadlocks; probably this will eventually turn into its own func //this causes channel bar desync when back -> front. blocksignals below fix it. huh. @@ -186,7 +208,7 @@ ChannelWidget::ChannelWidget(uint32_t channelCount, EndpointHandler* eph, QWidge this->channelLabels.at(i)->setText(QString::number(newValue)); }); } - this->setLayout(layout); + this->setLayout(widgetLayout); } @@ -206,9 +228,9 @@ EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *pare this->eph->setState(EndpointState::ENDPOINT_ACTIVE, idx); //setAttribute(Qt::WA_TranslucentBackground); - layout = new QGridLayout(this); - //this->setLayout(layout); - log_debugcpp("epw main layout parent: " + std::to_string((intptr_t)(layout->parent()))); + widgetLayout = new QGridLayout(this); + //this->setLayout(widgetLayout); + log_debugcpp("epw main layout parent: " + std::to_string((intptr_t)(widgetLayout->parent()))); if (parent == nullptr) { log_debugcpp("ayooooo?"); } defaultRolesCheckBoxes = { @@ -226,8 +248,8 @@ EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *pare mainSlider = new QSlider(Qt::Horizontal, this); if (this->eph->getState() != EndpointState::ENDPOINT_ACTIVE) { - layout->addWidget(mainLabel, row, 0); - layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Minimum), 1, 0); + widgetLayout->addWidget(mainLabel, row, 0); + widgetLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Minimum), 1, 0); return; } @@ -251,10 +273,10 @@ EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *pare //tip: would need to be new widget with layout in it //mainMuteLayout = new QGridLayout(); - layout->addWidget(mainLabel, row, 0, Qt::AlignLeft | Qt::AlignVCenter); - layout->addWidget(muteButton, row, 1, Qt::AlignLeft | Qt::AlignVCenter); - layout->addWidget(mainSlider, row, 2, 1, 2, Qt::AlignLeft | Qt::AlignVCenter); - layout->setSizeConstraint(QLayout::SetMinAndMaxSize); + widgetLayout->addWidget(mainLabel, row, 0, Qt::AlignLeft | Qt::AlignVCenter); + widgetLayout->addWidget(muteButton, row, 1, Qt::AlignLeft | Qt::AlignVCenter); + widgetLayout->addWidget(mainSlider, row, 2, 1, 2, Qt::AlignLeft | Qt::AlignVCenter); + widgetLayout->setSizeConstraint(QLayout::SetMinAndMaxSize); //int debug2 = this->minimumWidth(); row++; @@ -270,7 +292,7 @@ EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *pare uint32_t epChannelCount = eph->getChannelCount(); if(epChannelCount) { cw = new ChannelWidget(epChannelCount, eph, this); - layout->addWidget(cw, row++, 0, 1, 4 /*colmax*/); + widgetLayout->addWidget(cw, row++, 0, 1, 4 /*colmax*/); } /* @@ -309,10 +331,10 @@ EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *pare this->eph->setRoles(Roles::ROLE_COMMUNICATIONS); }); - layout->addWidget(defaultRolesCheckBoxes.at(Roles::ROLE_ALL), row, 0); - layout->addWidget(defaultRolesCheckBoxes.at(Roles::ROLE_CONSOLE), row, 1); - layout->addWidget(defaultRolesCheckBoxes.at(Roles::ROLE_MULTIMEDIA), row, 2); - layout->addWidget(defaultRolesCheckBoxes.at(Roles::ROLE_COMMUNICATIONS), row, 3); + widgetLayout->addWidget(defaultRolesCheckBoxes.at(Roles::ROLE_ALL), row, 0); + widgetLayout->addWidget(defaultRolesCheckBoxes.at(Roles::ROLE_CONSOLE), row, 1); + widgetLayout->addWidget(defaultRolesCheckBoxes.at(Roles::ROLE_MULTIMEDIA), row, 2); + widgetLayout->addWidget(defaultRolesCheckBoxes.at(Roles::ROLE_COMMUNICATIONS), row, 3); row++; /* ----------------------------------------------------------- */ @@ -347,7 +369,7 @@ EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *pare /* First SessionWidget batch */ for (size_t i = 0; i < eph->getSessionCount(); i++) { SessionWidget* sessionWidget = new SessionWidget(i, eph->getSessionHandlers().at(i), this); - layout->addWidget(sessionWidget, row, 0, 1, 4); + widgetLayout->addWidget(sessionWidget, row, 0, 1, 4); row++; sessionWidgets.push_back(sessionWidget); eph->getSessionHandlers().at(i)->setFrontIndex(i); @@ -363,10 +385,10 @@ EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *pare }); //todo parent? - layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Minimum), 1, 0); - layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Minimum), 4, 0); - layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), 6, 0); - layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), 6, 1); + widgetLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Minimum), 1, 0); + widgetLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Minimum), 4, 0); + widgetLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), 6, 0); + widgetLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), 6, 1); log_debugcpp("ENDPOINT_WIDGETED"); } @@ -374,7 +396,7 @@ void EndpointWidget::addSessionWidget(CustomWidgetEvent* ev){ uint64_t index = this->sessionWidgets.size(); SessionWidget* sw = new SessionWidget(index, ev->payload, this); ev->payload->setFrontIndex(index); - this->layout->addWidget(sw, row, 0, 1, 4); + this->widgetLayout->addWidget(sw, row, 0, 1, 4); row++; sessionWidgets.push_back(sw); return; @@ -383,7 +405,7 @@ void EndpointWidget::addSessionWidget(CustomWidgetEvent* ev){ void EndpointWidget::removeSessionWidget(CustomWidgetEvent* ev){ uint64_t i = ev->payload->getFrontIndex(); this->sessionWidgets.at(i)->setParent(nullptr); - this->layout->removeWidget(sessionWidgets.at(i)); + this->widgetLayout->removeWidget(sessionWidgets.at(i)); delete sessionWidgets.at(i); sessionWidgets.at(i) = nullptr; ev->payload->setFrontIndex(INT_MAX); @@ -423,7 +445,7 @@ void MainWindow::customEvent(QEvent* ev) { void MainWindow::removeEndpointWidget(CustomWidgetEvent* ev){ uint64_t i = ev->payload; this->ews.at(i)->setParent(nullptr); - this->layout->removeWidget(ews.at(i)); + this->widgetLayout->removeWidget(ews.at(i)); //uint64_t saisu = ews.size(); //delete ews.at(index); delete ews.at(i); @@ -434,7 +456,7 @@ void MainWindow::removeEndpointWidget(CustomWidgetEvent* ev){ void MainWindow::addEndpointWidget(CustomWidgetEvent* ev){ EndpointWidget* epw = new EndpointWidget(this->ews.size(), ev->payload, widget); - this->layout->addWidget(epw); + this->widgetLayout->addWidget(epw); ews.push_back(epw); return; } @@ -533,7 +555,7 @@ std::map EndpointWidget::getDefaultRolesWidgets() { } HeaderWidget::HeaderWidget(QWidget *parent) : QWidget(parent) { - layout = new QGridLayout(this); + widgetLayout = new QGridLayout(this); QString text = "&" STRING_ABOUT; about = new QPushButton(text, this); @@ -544,18 +566,18 @@ HeaderWidget::HeaderWidget(QWidget *parent) : QWidget(parent) { text = "&" STRING_STARTUP; startup = new QPushButton(text, this); - layout->addWidget(openCP , 0, 0); - layout->addWidget(startup, 0, 1); + widgetLayout->addWidget(openCP , 0, 0); + widgetLayout->addWidget(startup, 0, 1); #endif - layout->addWidget(about , 0, 2); - this->setLayout(layout); + widgetLayout->addWidget(about , 0, 2); + this->setLayout(widgetLayout); } MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { - // setWindowState(Qt::WindowFullScreen); - // setCentralWidget(centralWidget); + //setWindowState(Qt::WindowFullScreen); + //setCentralWidget(centralWidget); //todo: ratio - resize(windowWidth, 440); + //resize(windowWidth, 440); setWindowFlags(Qt::Window | Qt::MSWindowsFixedSizeDialogHint); #ifdef DEBUG setWindowTitle(STRING_TITLE); @@ -577,7 +599,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { //setAttribute(Qt::WA_TranslucentBackground, true); ewsUpdateTimer = new QTimer(this); widget = new QWidget(); - layout = new QGridLayout(); + widgetLayout = new QGridLayout(); trayIcon = new QSystemTrayIcon(); trayIconMenu = new QMenu(); trayIconMenuQuit = new QAction(STRING_QUIT); @@ -587,7 +609,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { ewsUpdateTimer->setInterval(ewsUpdateTimerFrequency); connect(ewsUpdateTimer, &QTimer::timeout, this, &MainWindow::reorderEndpointWidgetCollection); //widget->setMinimumSize(QSize(300,300)); - widget->setLayout(layout); + widget->setLayout(widgetLayout); /* * Scroll bar code */ @@ -595,6 +617,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { scrollArea->setWidget(widget); scrollArea->setWidgetResizable(true); scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + scrollArea->setStyleSheet("QScrollBar:vertical { width: 4px; }"); scrollArea->setMinimumWidth(500); setCentralWidget(scrollArea); @@ -608,12 +632,12 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { this->setMenuBar(mainMenuBar); //setCentralWidget(widget); - //layout->addWidget(pintas, 0, 0); + //widgetLayout->addWidget(pintas, 0, 0); reloadEndpointWidgets(); - scrollArea->setMinimumWidth(ews.at(0)->minimumWidth()); + //scrollArea->setMinimumWidth(ews.at(0)->minimumWidth()); log_debugcpp(std::to_string(scrollArea->minimumWidth())); - + /* * Tray Icon code */ @@ -631,12 +655,6 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { trayIcon->setToolTip(STRING_TITLE); trayIcon->setContextMenu(trayIconMenu); connect(trayIcon, &QSystemTrayIcon::activated, this, &MainWindow::trayIconActivated); - - /* - * Establishing initial window size and position - */ - //TODO: test. hardcode. var. - setGeometry(setSizePosition()); /* * Set of function callback definitons for EndpointSituationCallback @@ -695,15 +713,16 @@ void MainWindow::closeEvent(QCloseEvent *event) { if (trayIcon->isVisible()) { //todo: would be nice to show this to 1st time users; ini-san will come... //this->trayIcon->showMessage("ini file calling","tratarte como un gilipollas la primera vez", QSystemTrayIcon::Information); - hide(); - event->ignore(); + + hide(); + event->ignore(); } } void MainWindow::trayIconActivated(QSystemTrayIcon::ActivationReason reason) { switch (reason) { case QSystemTrayIcon::Trigger: - this->setGeometry(this->setSizePosition()); + this->calculateChildWidgetsSize(); this->showNormal(); break; default: @@ -721,10 +740,10 @@ void MainWindow::reloadEndpointWidgets() { epwIndex++; //alfinal estoes solopara inicializarlmao ews.push_back(epw); - layout->addWidget(epw, i, 0); + widgetLayout->addWidget(epw, i, 0); } } //todo:: tas aqui tirao, no me gustas y probablemente yo a ti tampoco - layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), i, 0); + widgetLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), i, 0); } diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index 4521120..1098b5c 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -1,7 +1,7 @@ #pragma once -#ifndef MAINWINDOW_H -#define MAINWINDOW_H +//#ifndef MAINWINDOW_H +//#define MAINWINDOW_H #include #include @@ -38,31 +38,6 @@ #include "global.h" #include "contclasses.h" -//class EndpointHandler; - -/* - * class ToggleButton : public QAbstractButton { - * Q_OBJECT - * - * public: - * ToggleButton(QWidget *parent = nullptr); - * void checkStateSet(); - * bool hitButton(const QPoint &pos) const; - * void nextCheckState(); - * void changeEvent(QEvent *e) override; - * bool event(QEvent *e) override; - * void focusInEvent(QFocusEvent *e) override; - * void focusOutEvent(QFocusEvent *e) override; - * void keyPressEvent(QKeyEvent *e) override; - * void keyReleaseEvent(QKeyEvent *e) override; - * void mouseMoveEvent(QMouseEvent *e) override; - * void mousePressEvent(QMouseEvent *e) override; - * void mouseReleaseEvent(QMouseEvent *e) override; - * void paintEvent(QPaintEvent *e) override = 0; - * void timerEvent(QTimerEvent *e) override; - * ToggleButton(QWidget *parent = nullptr); - * }; - */ enum SpawnPos { LEFT = (1 << 1), @@ -117,8 +92,7 @@ private: QLabel *mainLabel = nullptr; QSlider *mainSlider = nullptr; uint64_t idx; - //QGridLayout *layout = nullptr; - QHBoxLayout *layout = nullptr; + QHBoxLayout *widgetLayout = nullptr; QCheckBox *muteButton = nullptr; SessionHandler* sh; QTimer* volumePoller = nullptr; @@ -137,7 +111,7 @@ private: uint32_t channelCount; std::vector channelSliders; std::vector channelLabels; - QGridLayout *layout; + QGridLayout *widgetLayout; }; @@ -181,7 +155,7 @@ private: QSlider *mainSlider = nullptr; std::vector channelSliders; std::vector channelLabels; - QGridLayout *layout = nullptr; + QGridLayout *widgetLayout = nullptr; QGridLayout *mainMuteLayout = nullptr; std::map defaultRolesCheckBoxes; @@ -205,25 +179,8 @@ Q_OBJECT public: HeaderWidget(QWidget *parent = nullptr); - //~HeaderWidget(); - //void updateMainVolume(float newValue); - //void updateVolume(uint32_t channel, float newValue); - //void updateMute(bool muted); - - //void populateEndpointWidget(EndpointHandler *eph); - //void setEndpointHandlers(std::vector *ephs); - -//public slots: - -//protected: - //void customEvent(QEvent* ev) override; - -//private slots: - //void addSessionWidget(CustomWidgetEvent* ev); - //void removeSessionWidget(CustomWidgetEvent* ev); - private: - QGridLayout *layout; + QGridLayout *widgetLayout; QPushButton *about; #ifdef WIN32 QPushButton *openCP; @@ -238,11 +195,12 @@ class MainWindow : public QMainWindow { public: MainWindow(QWidget *parent = nullptr); void reloadEndpointWidgets(); + void calculateChildWidgetsSize(); protected: void closeEvent(QCloseEvent *event) override; void customEvent(QEvent* ev) override; - QRect setSizePosition(); + QRect setSizePosition(int width, int height); private slots: void trayIconActivated(QSystemTrayIcon::ActivationReason reason); @@ -256,7 +214,7 @@ private: //std::vector *ephs; std::vector ews; QWidget *widget; - QGridLayout *layout; + QGridLayout *widgetLayout; QSystemTrayIcon *trayIcon; QMenu *trayIconMenu; @@ -283,4 +241,4 @@ private: }; -#endif +//#endif diff --git a/src/qtestmain.cpp b/src/qtestmain.cpp index 85e1928..a1fbeea 100644 --- a/src/qtestmain.cpp +++ b/src/qtestmain.cpp @@ -66,8 +66,10 @@ int main (int argc, char* argv[]) { */ //app->setStyleSheet(styleSheet); - //window.setMinimumSize(100, 100); + window.calculateChildWidgetsSize(); + #ifdef DEBUG window.show(); + #endif return app->exec(); } From 9d79757a4952085897a7b286c1ad54f5158f7f8f Mon Sep 17 00:00:00 2001 From: Hane Date: Sat, 6 Apr 2024 18:42:19 +0200 Subject: [PATCH 09/78] main window behaving as overlay --- src/qt/qtclasses.cpp | 67 +++++++++++++++++++++++++------------------- src/qt/qtclasses.h | 3 ++ src/qtestmain.cpp | 16 ++++------- 3 files changed, 47 insertions(+), 39 deletions(-) diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 6e5e4ff..97182af 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -40,7 +40,7 @@ QRect MainWindow::setSizePosition(int width, int height) { availableRes.getCoords(&arx1, &ary1, &arx2, &ary2); uint8_t pos = 0; - pos = tiy2 < (sry2 / 2) ? SpawnPos::UP : SpawnPos::DOWN; + pos = tiy2 < (sry2 / 2) ? SpawnPos::UP : SpawnPos::DOWN; pos = tix2 < (srx2 / 2) ? pos | SpawnPos::LEFT : pos | SpawnPos::RIGHT; switch (pos) { @@ -51,7 +51,7 @@ QRect MainWindow::setSizePosition(int width, int height) { return QRect(arx1, (ary2-height), width, height); break; case SpawnPos::DOWN | SpawnPos::RIGHT: - return QRect((arx2 - width), (ary2-height), windowWidth, height); + return QRect((arx2 - width), (ary2-height), width, height); break; default: return QRect(500, 400, width, height); @@ -61,18 +61,21 @@ QRect MainWindow::setSizePosition(int width, int height) { void MainWindow::calculateChildWidgetsSize() { //We need dynamically added child widgets to expand so that we know their height - //TODO: MenuBar height + //TODO: more heights this->setAttribute(Qt::WA_DontShowOnScreen, true); this->show(); this->layout()->invalidate(); this->hide(); this->setAttribute(Qt::WA_DontShowOnScreen, false); - + int height = 0, width = 0; for (auto *epw : this->ews) { height += epw->height(); - width = (epw->width() > width) ? epw->width() : width; + //width = (epw->width() > width) ? epw->width() : width; } + width = scrollArea->width(); + height += mainMenuBar->height(); + height += hw->height(); /* * Establishing initial window size and position @@ -181,14 +184,15 @@ ChannelWidget::ChannelWidget(uint32_t channelCount, EndpointHandler* eph, QWidge this->channelCount = channelCount; widgetLayout = new QGridLayout(this); float volume = 100; + /* * Channel sliders setup */ //uint32_t epChannelCount = eph->getChannelCount(); for(uint32_t i = 0; i < channelCount && channelCount > 1; i++){ - QSlider* tmp = new QSlider(Qt::Horizontal); + QSlider* tmp = new QSlider(Qt::Horizontal); QLabel* tmpLb = new QLabel(""); - tmp->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + //tmp->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); tmp->setTickInterval(5); tmp->setSingleStep(1); tmp->setRange(0,100); @@ -196,6 +200,7 @@ ChannelWidget::ChannelWidget(uint32_t channelCount, EndpointHandler* eph, QWidge volume = eph->getVolume(i) * 100; tmp->setValue((int) volume); tmpLb->setText(QString::number(volume)); + //tmpLb->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); this->channelSliders.push_back(tmp); this->channelLabels.push_back(tmpLb); widgetLayout->addWidget(tmp, 0, i); @@ -219,7 +224,7 @@ void ChannelWidget::updateChannel(int channel) { this->channelSliders.at(channel)->blockSignals(false); } -EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *parent) : QWidget(parent){ +EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *parent) : QWidget(parent) { //todo: based on qgridlayout, name+mute should be its own widget, same with channels row = 0; this->idx = idx; @@ -234,9 +239,9 @@ EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *pare if (parent == nullptr) { log_debugcpp("ayooooo?"); } defaultRolesCheckBoxes = { - {Roles::ROLE_ALL, new ExtendedCheckBox(this)}, - {Roles::ROLE_CONSOLE, new ExtendedCheckBox(this)}, - {Roles::ROLE_MULTIMEDIA, new ExtendedCheckBox(this)}, + {Roles::ROLE_ALL, new ExtendedCheckBox(this)}, + {Roles::ROLE_CONSOLE, new ExtendedCheckBox(this)}, + {Roles::ROLE_MULTIMEDIA, new ExtendedCheckBox(this)}, {Roles::ROLE_COMMUNICATIONS, new ExtendedCheckBox(this)} }; @@ -249,7 +254,7 @@ EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *pare if (this->eph->getState() != EndpointState::ENDPOINT_ACTIVE) { widgetLayout->addWidget(mainLabel, row, 0); - widgetLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Minimum), 1, 0); + //widgetLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Minimum), 1, 0); return; } @@ -258,14 +263,14 @@ EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *pare //mainLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); //muteButton->setStyleSheet("background-color: #A3C1DA; color: red"); - mainSlider->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + mainSlider->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding); mainSlider->setFocusPolicy(Qt::StrongFocus); mainSlider->setTickPosition(QSlider::TicksBothSides); mainSlider->setTickInterval(5); mainSlider->setSingleStep(1); mainSlider->setRange(0,100); - muteButton->setCheckState((eph->getMute() == false ? Qt::Unchecked : Qt::Checked)); + muteButton->setCheckState((eph->getMute() == false ? Qt::Unchecked : Qt::Checked)); muteButton->setText(eph->getMute() ? STRING_UNMUTE : STRING_MUTE); float volume = eph->getVolume(AudioChannel::CHANNEL_MAIN) * 100; mainSlider->setValue((int)volume); @@ -276,7 +281,7 @@ EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *pare widgetLayout->addWidget(mainLabel, row, 0, Qt::AlignLeft | Qt::AlignVCenter); widgetLayout->addWidget(muteButton, row, 1, Qt::AlignLeft | Qt::AlignVCenter); widgetLayout->addWidget(mainSlider, row, 2, 1, 2, Qt::AlignLeft | Qt::AlignVCenter); - widgetLayout->setSizeConstraint(QLayout::SetMinAndMaxSize); + //widgetLayout->setSizeConstraint(QLayout::SetMinAndMaxSize); //int debug2 = this->minimumWidth(); row++; @@ -292,6 +297,7 @@ EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *pare uint32_t epChannelCount = eph->getChannelCount(); if(epChannelCount) { cw = new ChannelWidget(epChannelCount, eph, this); + //cw->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); widgetLayout->addWidget(cw, row++, 0, 1, 4 /*colmax*/); } @@ -369,11 +375,13 @@ EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *pare /* First SessionWidget batch */ for (size_t i = 0; i < eph->getSessionCount(); i++) { SessionWidget* sessionWidget = new SessionWidget(i, eph->getSessionHandlers().at(i), this); - widgetLayout->addWidget(sessionWidget, row, 0, 1, 4); + widgetLayout->addWidget(sessionWidget, row, 0, 1, 4 /* colmax */); row++; sessionWidgets.push_back(sessionWidget); eph->getSessionHandlers().at(i)->setFrontIndex(i); } + /* This spacer provides proper spacing when window vertically > widgets */ + //widgetLayout->addItem(&lastRowSpacer, row, 0); /* Add/Remove SessionWidget callback */ eph->setAddSessionWidgetFunction([this](SessionHandler* sessionHandler) { @@ -384,11 +392,6 @@ EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *pare QCoreApplication::instance()->postEvent(this, new CustomWidgetEvent((QEvent::Type)CustomQEvent::SessionWidgetObsolete, sessionHandler)); }); - //todo parent? - widgetLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Minimum), 1, 0); - widgetLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Minimum), 4, 0); - widgetLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), 6, 0); - widgetLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), 6, 1); log_debugcpp("ENDPOINT_WIDGETED"); } @@ -455,7 +458,8 @@ void MainWindow::removeEndpointWidget(CustomWidgetEvent* ev){ } void MainWindow::addEndpointWidget(CustomWidgetEvent* ev){ - EndpointWidget* epw = new EndpointWidget(this->ews.size(), ev->payload, widget); + EndpointWidget* epw = new EndpointWidget(this->ews.size(), ev->payload, widget); + //epw->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); this->widgetLayout->addWidget(epw); ews.push_back(epw); return; @@ -573,16 +577,16 @@ HeaderWidget::HeaderWidget(QWidget *parent) : QWidget(parent) { this->setLayout(widgetLayout); } -MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { +MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), lastRowSpacer(1, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding) { //setWindowState(Qt::WindowFullScreen); //setCentralWidget(centralWidget); //todo: ratio - //resize(windowWidth, 440); setWindowFlags(Qt::Window | Qt::MSWindowsFixedSizeDialogHint); - #ifdef DEBUG + setWindowFlags(Qt::FramelessWindowHint | Qt::ToolTip); setWindowTitle(STRING_TITLE); - setWindowFlags(Qt::FramelessWindowHint); - #endif + connect(qApp, &QGuiApplication::applicationStateChanged, this, [=](Qt::ApplicationState state){ + if(state == Qt::ApplicationState::ApplicationInactive) hide(); +}); /* * Registering needed custom events */ @@ -619,7 +623,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); scrollArea->setStyleSheet("QScrollBar:vertical { width: 4px; }"); - scrollArea->setMinimumWidth(500); + //scrollArea->setMinimumWidth(500); setCentralWidget(scrollArea); /* @@ -705,6 +709,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { osh->setAddEndpointWidgetFunction([this](EndpointHandler* eph) { QCoreApplication::instance()->postEvent(this, new CustomWidgetEvent((QEvent::Type)CustomQEvent::EndpointWidgetCreated, eph)); }); + } void MainWindow::closeEvent(QCloseEvent *event) { @@ -724,6 +729,7 @@ void MainWindow::trayIconActivated(QSystemTrayIcon::ActivationReason reason) { case QSystemTrayIcon::Trigger: this->calculateChildWidgetsSize(); this->showNormal(); + this->activateWindow(); break; default: break; @@ -732,6 +738,7 @@ void MainWindow::trayIconActivated(QSystemTrayIcon::ActivationReason reason) { void MainWindow::reloadEndpointWidgets() { size_t i = 0; + //widgetLayout->addItem(&lastRowSpacer, i++, 0); for (size_t epwIndex = 0; i < (osh->getPlaybackEndpointHandlers().size()); i++) { if (osh->getPlaybackEndpointHandlers().at(i)->getState() == EndpointState::ENDPOINT_ACTIVE){ log_debugcpp("EPWidget creation"); @@ -744,6 +751,8 @@ void MainWindow::reloadEndpointWidgets() { } } //todo:: tas aqui tirao, no me gustas y probablemente yo a ti tampoco - widgetLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), i, 0); + //seguramente falle al querer rematar esto con redimensionar la ventana sólo + //con los default endpoints en vista + widgetLayout->addItem(&lastRowSpacer, i, 0); } diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index 1098b5c..bbb9672 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -230,8 +230,11 @@ private: HeaderWidget* hw; QMenuBar *mainMenuBar; QScreen *screen; + //todo: ratio //Win10 1080p 120% QSize mwSize; + QSpacerItem lastRowSpacer; + //public slots: // void setEndpointHandlers(std::vector *ephs); diff --git a/src/qtestmain.cpp b/src/qtestmain.cpp index a1fbeea..a44f8cc 100644 --- a/src/qtestmain.cpp +++ b/src/qtestmain.cpp @@ -1,9 +1,3 @@ -//#include -//#include - -//#include - -//#define QTBLESSED #include #include @@ -66,10 +60,12 @@ int main (int argc, char* argv[]) { */ //app->setStyleSheet(styleSheet); - window.calculateChildWidgetsSize(); - #ifdef DEBUG - window.show(); - #endif + /* + * #ifdef DEBUG + * window.calculateChildWidgetsSize(); + * window.show(); + * #endif + */ return app->exec(); } From 2e7662161675354eee783e69b472bb1eccb57604 Mon Sep 17 00:00:00 2001 From: Hane Date: Tue, 9 Apr 2024 20:00:04 +0200 Subject: [PATCH 10/78] more front work --- .gitignore | 1 + assets.qrc | 1 + assets/logo.ico | Bin 0 -> 331993 bytes assets/logo.png | Bin 0 -> 38680 bytes assets/logo.xcf | Bin 0 -> 157154 bytes qtest.pro | 1 + src/qt/qtclasses.cpp | 44 ++++++++++++++++++++++++++++++------------- src/qt/qtclasses.h | 3 ++- src/qtestmain.cpp | 7 ------- 9 files changed, 36 insertions(+), 21 deletions(-) create mode 100644 assets/logo.ico create mode 100644 assets/logo.png create mode 100644 assets/logo.xcf diff --git a/.gitignore b/.gitignore index fde2162..9eec94c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ build *.rdbg *.pdb +*.ps1 Makefile Makefile.Debug Makefile.Release \ No newline at end of file diff --git a/assets.qrc b/assets.qrc index daa91ee..a9cbd38 100644 --- a/assets.qrc +++ b/assets.qrc @@ -2,5 +2,6 @@ assets/notificationAreaIcon.png assets/style.qss + assets/logo.ico diff --git a/assets/logo.ico b/assets/logo.ico new file mode 100644 index 0000000000000000000000000000000000000000..daac4a491efc34fe55f7c2b04b53a177a736df39 GIT binary patch literal 331993 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⧀F(;MrA061Hj}F?V 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

n0K7{moYI18i`&p%Y|-{V_xn4a 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 literal 0 HcmV?d00001 diff --git a/assets/logo.png b/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..259e37b3dbe564f3fad2f46aee470f2e05975edc GIT binary patch literal 38680 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�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∋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 literal 0 HcmV?d00001 diff --git a/qtest.pro b/qtest.pro index 942d75d..2de66b2 100644 --- a/qtest.pro +++ b/qtest.pro @@ -13,5 +13,6 @@ VPATH += "$$PWD\src" "$$PWD\src\qt" "$$PWD\src\back" "$$PWD\src\cont" SOURCES += qtestmain.cpp qtclasses.cpp backlasses.cpp backsessionclasses.cpp contclasses.cpp contsessionclasses.cpp HEADERS += qtclasses.h backlasses.h backsessionclasses.h contclasses.h contsessionclasses.h global.h debug.h backfuncs.h ipolicyconfig.h msinclude.h RESOURCES = assets.qrc +RC_ICONS += assets/logo.ico #DESTDIR += "build" diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 97182af..d6605d2 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -184,6 +184,9 @@ ChannelWidget::ChannelWidget(uint32_t channelCount, EndpointHandler* eph, QWidge this->channelCount = channelCount; widgetLayout = new QGridLayout(this); float volume = 100; + int left = 0, top = 0, right = 0, bottom = 0; + widgetLayout->getContentsMargins(&left, &top, &right, &bottom); + widgetLayout->setContentsMargins(0, top, 0, bottom); /* * Channel sliders setup @@ -201,6 +204,8 @@ ChannelWidget::ChannelWidget(uint32_t channelCount, EndpointHandler* eph, QWidge tmp->setValue((int) volume); tmpLb->setText(QString::number(volume)); //tmpLb->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + tmp->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); + tmpLb->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); this->channelSliders.push_back(tmp); this->channelLabels.push_back(tmpLb); widgetLayout->addWidget(tmp, 0, i); @@ -251,6 +256,7 @@ EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *pare muteButton = new QCheckBox(this); mainLabel = new QLabel(QString::fromStdWString(eph->getName()), this); mainSlider = new QSlider(Qt::Horizontal, this); + mainVolumeLabel = new QLabel(this); if (this->eph->getState() != EndpointState::ENDPOINT_ACTIVE) { widgetLayout->addWidget(mainLabel, row, 0); @@ -258,12 +264,14 @@ EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *pare return; } - mainLabel->setMaximumWidth(240 /*1/8 1080p*/); - mainLabel->setMinimumWidth(240 /*1/8 1080p*/); + mainLabel->setMaximumWidth(350 /* 1080p 120%*/); + mainLabel->setMinimumWidth(350 /* 1080p 120%*/); + mainLabel->setWordWrap(true); + mainLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); //mainLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); //muteButton->setStyleSheet("background-color: #A3C1DA; color: red"); - mainSlider->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding); + mainSlider->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); mainSlider->setFocusPolicy(Qt::StrongFocus); mainSlider->setTickPosition(QSlider::TicksBothSides); mainSlider->setTickInterval(5); @@ -274,15 +282,18 @@ EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *pare muteButton->setText(eph->getMute() ? STRING_UNMUTE : STRING_MUTE); float volume = eph->getVolume(AudioChannel::CHANNEL_MAIN) * 100; mainSlider->setValue((int)volume); + mainVolumeLabel->setText(QString::number(volume)); log_debugcpp("ENDPOINT SET WITH VOLUME " + std::to_string(volume)); //tip: would need to be new widget with layout in it //mainMuteLayout = new QGridLayout(); - widgetLayout->addWidget(mainLabel, row, 0, Qt::AlignLeft | Qt::AlignVCenter); - widgetLayout->addWidget(muteButton, row, 1, Qt::AlignLeft | Qt::AlignVCenter); - widgetLayout->addWidget(mainSlider, row, 2, 1, 2, Qt::AlignLeft | Qt::AlignVCenter); + widgetLayout->addWidget(mainLabel, row, 0, 1, 3, Qt::AlignLeft | Qt::AlignVCenter); + widgetLayout->addWidget(muteButton, row, 2, Qt::AlignRight | Qt::AlignVCenter); + widgetLayout->addWidget(mainVolumeLabel, row, 3, Qt::AlignRight | Qt::AlignVCenter); + row++; + + widgetLayout->addWidget(mainSlider, row, 0, 1, 4, Qt::AlignVCenter); //widgetLayout->setSizeConstraint(QLayout::SetMinAndMaxSize); - //int debug2 = this->minimumWidth(); row++; //TODO:0 = mute and muted, change volume = unmuted are client side tricks = 2 callbacks, one for volume, one for mute state. Implement as an user selectable option? @@ -303,7 +314,7 @@ EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *pare /* * Role ExtendedCheckBoxes setup - */ + */ uint8_t assignedRoles = eph->getRoles(); defaultRolesCheckBoxes.at(Roles::ROLE_ALL)->setCheckState(assignedRoles == Roles::ROLE_ALL ? Qt::Checked : Qt::Unchecked); @@ -336,11 +347,16 @@ EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *pare connect(defaultRolesCheckBoxes.at(Roles::ROLE_COMMUNICATIONS), &QCheckBox::stateChanged,[this] { this->eph->setRoles(Roles::ROLE_COMMUNICATIONS); }); - - widgetLayout->addWidget(defaultRolesCheckBoxes.at(Roles::ROLE_ALL), row, 0); - widgetLayout->addWidget(defaultRolesCheckBoxes.at(Roles::ROLE_CONSOLE), row, 1); - widgetLayout->addWidget(defaultRolesCheckBoxes.at(Roles::ROLE_MULTIMEDIA), row, 2); - widgetLayout->addWidget(defaultRolesCheckBoxes.at(Roles::ROLE_COMMUNICATIONS), row, 3); + + defaultRolesCheckBoxes.at(Roles::ROLE_ALL)->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); + widgetLayout->addWidget(defaultRolesCheckBoxes.at(Roles::ROLE_ALL), + row, 0); + widgetLayout->addWidget(defaultRolesCheckBoxes.at(Roles::ROLE_CONSOLE), + row, 1); + widgetLayout->addWidget(defaultRolesCheckBoxes.at(Roles::ROLE_MULTIMEDIA), + row, 2); + widgetLayout->addWidget(defaultRolesCheckBoxes.at(Roles::ROLE_COMMUNICATIONS), + row, 3); row++; /* ----------------------------------------------------------- */ @@ -357,6 +373,7 @@ EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *pare mainSlider->blockSignals(true); muteButton->blockSignals(true); mainSlider->setValue((int)((eph->getCallbackInfo()->mainVolume + roundingFactor) * 100)); + mainVolumeLabel->setText(QString::number(mainSlider->value())); muteButton->setCheckState((eph->getCallbackInfo()->muted == false ? Qt::Unchecked : Qt::Checked)); muteButton->setText(eph->getCallbackInfo()->muted ? STRING_UNMUTE : STRING_MUTE); for(uint32_t i = 0; i < eph->getCallbackInfo()->channels && eph->getChannelCount() > 1; i++){ @@ -501,6 +518,7 @@ void EndpointWidget::updateMute(int checked){ } void EndpointWidget::updateMainVolume(int newValue){ + mainVolumeLabel->setText(QString::number(newValue)); this->eph->setVolume(osh->getGuid(), AudioChannel::CHANNEL_MAIN, newValue); } diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index bbb9672..d5ee9dc 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -151,7 +151,8 @@ private: int row; const int sessionCol = 2; QCheckBox *muteButton = nullptr; - QLabel *mainLabel = nullptr, *leftChannelLabel = nullptr, *rightChannelLabel = nullptr; + QLabel *mainLabel = nullptr; + QLabel *mainVolumeLabel = nullptr; QSlider *mainSlider = nullptr; std::vector channelSliders; std::vector channelLabels; diff --git a/src/qtestmain.cpp b/src/qtestmain.cpp index a44f8cc..19f3491 100644 --- a/src/qtestmain.cpp +++ b/src/qtestmain.cpp @@ -59,13 +59,6 @@ int main (int argc, char* argv[]) { * QString styleSheet { QLatin1String(styleFile.readAll()) }; */ - //app->setStyleSheet(styleSheet); - /* - * #ifdef DEBUG - * window.calculateChildWidgetsSize(); - * window.show(); - * #endif - */ return app->exec(); } From b3c663046f886dd5b595dec44e650bc65946ee60 Mon Sep 17 00:00:00 2001 From: Hane Date: Tue, 9 Apr 2024 22:03:53 +0200 Subject: [PATCH 11/78] slight code cleanup --- src/back/backlasses.cpp | 9 ++---- src/back/backlasses.h | 13 ++++----- src/qt/qtclasses.cpp | 64 ++++++++++++----------------------------- 3 files changed, 27 insertions(+), 59 deletions(-) diff --git a/src/back/backlasses.cpp b/src/back/backlasses.cpp index a53c052..2370f08 100644 --- a/src/back/backlasses.cpp +++ b/src/back/backlasses.cpp @@ -140,12 +140,6 @@ HRESULT EndpointVolumeCallback::OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA pNotify * } * */ -//todo: not on construct since it expects them to already exist; smells like refactor! -void EndpointSituationCallback::fill(IMMDeviceEnumerator *deviceEnumerator, std::vector playbackDevices, std::vector captureDevices){ - this->deviceEnumerator = deviceEnumerator; - this->playbackDevices = playbackDevices; - this->captureDevices = captureDevices; -} ULONG EndpointSituationCallback::AddRef(){ return InterlockedIncrement(&ref); @@ -685,7 +679,8 @@ Overseer::Overseer() { //: epsc(deviceEnumerator, playbackDevices){ //reloadEndpoints(Flows::FLOW_CAPTURE); //Registering for endpoint information callback - this->epsc.fill(deviceEnumerator, playbackDevices, captureDevices); + //this->epsc.fill(deviceEnumerator, playbackDevices, captureDevices); + //this->epsc.fill(deviceEnumerator); if(FAILED(deviceEnumerator->RegisterEndpointNotificationCallback(((IMMNotificationClient*)&epsc)))) { log_debugcpp("when no enchufas......"); } } diff --git a/src/back/backlasses.h b/src/back/backlasses.h index 8796466..6469c6d 100644 --- a/src/back/backlasses.h +++ b/src/back/backlasses.h @@ -11,9 +11,9 @@ class Session; class Endpoint { public: - Endpoint(IMMDevice* endpoint, uint64_t idx); + Endpoint(IMMDevice* endpoint, uint64_t idx = 0); //todo: how to forward declare delegate constructors? - Endpoint(IMMDevice* endpoint) : Endpoint(endpoint, 0) {}; + //Endpoint(IMMDevice* endpoint) : Endpoint(endpoint, 0) {}; void reloadEndpointChannels(); uint64_t getIndex(); void setIndex(uint64_t idx); @@ -84,7 +84,7 @@ class EndpointVolumeCallback : public IAudioEndpointVolumeCallback { class EndpointSituationCallback : public IMMNotificationClient { public: - //EndpointSituationCallback(IMMDeviceEnumerator *deviceEnumerator, std::vector playbackDevices); + //EndpointSituationCallback(IMMDeviceEnumerator *deviceEnumerator, std::vector playbackDevices, std::vector captureDevices); ULONG AddRef(); ULONG Release(); HRESULT QueryInterface(REFIID riid, VOID **ppvInterface); @@ -94,12 +94,11 @@ class EndpointSituationCallback : public IMMNotificationClient { HRESULT OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState); HRESULT OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key); - void fill(IMMDeviceEnumerator *deviceEnumerator, std::vector playbackDevices, std::vector captureDevices); private: ULONG ref = 1; - IMMDeviceEnumerator *deviceEnumerator; - std::vector playbackDevices; - std::vector captureDevices; + //IMMDeviceEnumerator *deviceEnumerator; + //std::vector playbackDevices; + //std::vector captureDevices; }; class Overseer { diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index d6605d2..901fe27 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -285,7 +285,6 @@ EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *pare mainVolumeLabel->setText(QString::number(volume)); log_debugcpp("ENDPOINT SET WITH VOLUME " + std::to_string(volume)); - //tip: would need to be new widget with layout in it //mainMuteLayout = new QGridLayout(); widgetLayout->addWidget(mainLabel, row, 0, 1, 3, Qt::AlignLeft | Qt::AlignVCenter); widgetLayout->addWidget(muteButton, row, 2, Qt::AlignRight | Qt::AlignVCenter); @@ -314,53 +313,28 @@ EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *pare /* * Role ExtendedCheckBoxes setup - */ + */ - uint8_t assignedRoles = eph->getRoles(); - defaultRolesCheckBoxes.at(Roles::ROLE_ALL)->setCheckState(assignedRoles == Roles::ROLE_ALL ? Qt::Checked : Qt::Unchecked); - //todo duditas de & - defaultRolesCheckBoxes.at(Roles::ROLE_ALL)->setDisabled(assignedRoles == Roles::ROLE_ALL ? true : false); - defaultRolesCheckBoxes.at(Roles::ROLE_ALL)->setText(STRING_ROLE_ALL); - - defaultRolesCheckBoxes.at(Roles::ROLE_CONSOLE)->setCheckState(assignedRoles & Roles::ROLE_CONSOLE ? Qt::Checked : Qt::Unchecked); - defaultRolesCheckBoxes.at(Roles::ROLE_CONSOLE)->setDisabled(assignedRoles & Roles::ROLE_CONSOLE ? true : false); - defaultRolesCheckBoxes.at(Roles::ROLE_CONSOLE)->setText(STRING_ROLE_CONSOLE); - - defaultRolesCheckBoxes.at(Roles::ROLE_MULTIMEDIA)->setCheckState(assignedRoles & Roles::ROLE_MULTIMEDIA ? Qt::Checked : Qt::Unchecked); - defaultRolesCheckBoxes.at(Roles::ROLE_MULTIMEDIA)->setDisabled(assignedRoles & Roles::ROLE_MULTIMEDIA ? true : false); - defaultRolesCheckBoxes.at(Roles::ROLE_MULTIMEDIA)->setText(STRING_ROLE_MULTIMEDIA); - - defaultRolesCheckBoxes.at(Roles::ROLE_COMMUNICATIONS)->setCheckState(assignedRoles & Roles::ROLE_COMMUNICATIONS ? Qt::Checked : Qt::Unchecked); - defaultRolesCheckBoxes.at(Roles::ROLE_COMMUNICATIONS)->setDisabled(assignedRoles & Roles::ROLE_COMMUNICATIONS ? true : false); - defaultRolesCheckBoxes.at(Roles::ROLE_COMMUNICATIONS)->setText(STRING_ROLE_COMMUNICATIONS); - - connect(defaultRolesCheckBoxes.at(Roles::ROLE_ALL), &QCheckBox::stateChanged,[this] { - this->eph->setRoles(Roles::ROLE_ALL); - }); - connect(defaultRolesCheckBoxes.at(Roles::ROLE_CONSOLE), &QCheckBox::stateChanged,[this] { - this->eph->setRoles(Roles::ROLE_CONSOLE); - }); - connect(defaultRolesCheckBoxes.at(Roles::ROLE_MULTIMEDIA), &QCheckBox::stateChanged,[this] { - this->eph->setRoles(Roles::ROLE_MULTIMEDIA); - }); - - connect(defaultRolesCheckBoxes.at(Roles::ROLE_COMMUNICATIONS), &QCheckBox::stateChanged,[this] { - this->eph->setRoles(Roles::ROLE_COMMUNICATIONS); - }); - defaultRolesCheckBoxes.at(Roles::ROLE_ALL)->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); - widgetLayout->addWidget(defaultRolesCheckBoxes.at(Roles::ROLE_ALL), - row, 0); - widgetLayout->addWidget(defaultRolesCheckBoxes.at(Roles::ROLE_CONSOLE), - row, 1); - widgetLayout->addWidget(defaultRolesCheckBoxes.at(Roles::ROLE_MULTIMEDIA), - row, 2); - widgetLayout->addWidget(defaultRolesCheckBoxes.at(Roles::ROLE_COMMUNICATIONS), - row, 3); + uint8_t assignedRoles = eph->getRoles(); + uint8_t col = 0; + #define checkbox_setup(role, optor) do { \ + defaultRolesCheckBoxes.at(role)->setCheckState(assignedRoles optor role ? Qt::Checked : Qt::Unchecked); \ + defaultRolesCheckBoxes.at(role)->setDisabled(assignedRoles optor role ? true : false); \ + defaultRolesCheckBoxes.at(role)->setText(STRING_##role); \ + connect(defaultRolesCheckBoxes.at(role), &QCheckBox::stateChanged,[this] { \ + this->eph->setRoles(role); \ + }); \ + widgetLayout->addWidget(defaultRolesCheckBoxes.at(role), row, col++); \ + } while(0) + + checkbox_setup(ROLE_ALL, ==); + checkbox_setup(ROLE_CONSOLE, &); + checkbox_setup(ROLE_MULTIMEDIA, &); + checkbox_setup(ROLE_COMMUNICATIONS, &); + #undef checkbox_setup row++; - - /* ----------------------------------------------------------- */ - + /* * EndpointVolume Polling time */ From e4732d086bd65b55c1e77ba0ac9b5ecda932a7b7 Mon Sep 17 00:00:00 2001 From: Hane Date: Wed, 10 Apr 2024 22:23:17 +0200 Subject: [PATCH 12/78] fixed multi monitor support --- src/qt/qtclasses.cpp | 49 ++++++++++++++++++++++++++++++++++---------- src/qt/qtclasses.h | 7 +++++-- 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 901fe27..dfdbede 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -26,22 +26,37 @@ void ExtendedCheckBox::customEvent(QEvent* ev) { } QRect MainWindow::setSizePosition(int width, int height) { - //setGeometry ignores decoration size xdddd + //setGeometry ignores decoration size, theres others for that QRect trayIconPos = this->trayIcon->geometry(); int tix1, tix2, tiy1, tiy2; trayIconPos.getCoords(&tix1, &tiy1, &tix2, &tiy2); + log_debugcpp("Tray Icon Pos: " + std::to_string(tix1) + " " + std::to_string(tix2)+" " + std::to_string(tiy1) + " " + std::to_string(tiy2)); + + screen = this->getCurrentScreen(); + log_debugcpp("Screen: " + screen->model().toStdString() + " " + screen->name().toStdString()); + this->setScreen(screen); - screen = QGuiApplication::primaryScreen(); QRect screenRes = screen->geometry(); int srx1, srx2, sry1, sry2; screenRes.getCoords(&srx1, &sry1, &srx2, &sry2); + log_debugcpp("Screen Res: " + std::to_string(srx1) + " " + std::to_string(srx2)+" " + std::to_string(sry1) + " " + std::to_string(sry2)); + QRect availableRes = screen->availableGeometry(); int arx1, arx2, ary1, ary2; availableRes.getCoords(&arx1, &ary1, &arx2, &ary2); + log_debugcpp("Available res: " + std::to_string(arx1) + " " + std::to_string(arx2)+" " + std::to_string(ary1) + " " + std::to_string(ary2)); + + if(height > (ary2 - ary1)) height = (ary2 - ary1); + log_debugcpp("Height res: " + std::to_string(height)); uint8_t pos = 0; - pos = tiy2 < (sry2 / 2) ? SpawnPos::UP : SpawnPos::DOWN; - pos = tix2 < (srx2 / 2) ? pos | SpawnPos::LEFT : pos | SpawnPos::RIGHT; + int leftDistance = std::abs(srx1 - tix1); + int rightDistance = std::abs(srx2 - tix1); + int upDistance = std::abs(sry1 - tiy1); + int downDistance = std::abs(sry2 - tiy1); + + pos = (upDistance < downDistance) ? SpawnPos::UP : SpawnPos::DOWN; + pos = (leftDistance < rightDistance) ? pos | SpawnPos::LEFT : pos | SpawnPos::RIGHT; switch (pos) { case SpawnPos::UP | SpawnPos::RIGHT: @@ -84,6 +99,21 @@ void MainWindow::calculateChildWidgetsSize() { setGeometry(setSizePosition(width, height)); } +QScreen* MainWindow::getCurrentScreen() { + //Using cursor pos as screen detector. Flawed. + QPoint cursorPos = QCursor::pos(); + log_debugcpp("Cursor pos: " + std::to_string(cursorPos.ry()) + " " + std::to_string(cursorPos.rx())); + + for (QScreen *screen : QGuiApplication::screens()) { + QRect screenRect = screen->geometry(); + if (screenRect.contains(cursorPos)) { + return screen; + } + } + + return QGuiApplication::primaryScreen(); +} + SessionWidget::SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent) : QWidget(parent){ //todo: based on qgridlayout, name+mute should be its own widget, same with channels this->idx = idx; @@ -621,14 +651,11 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), lastRowSpacer(1, /* * Menu bar code */ - mainMenuBar = this->menuBar(); + mainMenuBar = new QToolBar(this); hw = new HeaderWidget(this); - mainMenuBar->setCornerWidget(hw,Qt::TopLeftCorner); - mainMenuBar->show(); - this->setMenuBar(mainMenuBar); - - //setCentralWidget(widget); - //widgetLayout->addWidget(pintas, 0, 0); + mainMenuBar->addWidget(hw); + mainMenuBar->setMovable(false); + this->addToolBar(Qt::BottomToolBarArea, mainMenuBar); reloadEndpointWidgets(); //scrollArea->setMinimumWidth(ews.at(0)->minimumWidth()); diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index d5ee9dc..b3bb7ea 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -23,6 +23,8 @@ #include #include #include +#include +#include //#include /* @@ -201,7 +203,8 @@ public: protected: void closeEvent(QCloseEvent *event) override; void customEvent(QEvent* ev) override; - QRect setSizePosition(int width, int height); + QRect setSizePosition(int width, int height); + QScreen* getCurrentScreen(); private slots: void trayIconActivated(QSystemTrayIcon::ActivationReason reason); @@ -229,7 +232,7 @@ private: QScrollArea *scrollArea; HeaderWidget* hw; - QMenuBar *mainMenuBar; + QToolBar *mainMenuBar; QScreen *screen; //todo: ratio //Win10 1080p 120% From 5229154c45f0085a97f4b522e18745ce96548ef7 Mon Sep 17 00:00:00 2001 From: Hane Date: Fri, 12 Apr 2024 21:09:04 +0200 Subject: [PATCH 13/78] initial window scaling --- src/qt/qtclasses.cpp | 79 ++++++++++++++++++++++++++++++++++---------- src/qt/qtclasses.h | 14 +++++--- 2 files changed, 70 insertions(+), 23 deletions(-) diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index dfdbede..1ecf82f 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -25,15 +25,13 @@ void ExtendedCheckBox::customEvent(QEvent* ev) { QCheckBox::customEvent(ev); } -QRect MainWindow::setSizePosition(int width, int height) { +QRect MainWindow::setSizePosition(QScreen* screen, int width, int height) { //setGeometry ignores decoration size, theres others for that QRect trayIconPos = this->trayIcon->geometry(); int tix1, tix2, tiy1, tiy2; trayIconPos.getCoords(&tix1, &tiy1, &tix2, &tiy2); log_debugcpp("Tray Icon Pos: " + std::to_string(tix1) + " " + std::to_string(tix2)+" " + std::to_string(tiy1) + " " + std::to_string(tiy2)); - screen = this->getCurrentScreen(); - log_debugcpp("Screen: " + screen->model().toStdString() + " " + screen->name().toStdString()); this->setScreen(screen); QRect screenRes = screen->geometry(); @@ -60,15 +58,19 @@ QRect MainWindow::setSizePosition(int width, int height) { switch (pos) { case SpawnPos::UP | SpawnPos::RIGHT: + this->addToolBar(Qt::BottomToolBarArea, mainMenuBar); return QRect((arx2 - width), ary1, width, height); break; case SpawnPos::DOWN | SpawnPos::LEFT: + this->addToolBar(Qt::TopToolBarArea, mainMenuBar); return QRect(arx1, (ary2-height), width, height); break; case SpawnPos::DOWN | SpawnPos::RIGHT: + this->addToolBar(Qt::TopToolBarArea, mainMenuBar); return QRect((arx2 - width), (ary2-height), width, height); break; default: + this->addToolBar(Qt::BottomToolBarArea, mainMenuBar); return QRect(500, 400, width, height); break; } @@ -76,27 +78,49 @@ QRect MainWindow::setSizePosition(int width, int height) { void MainWindow::calculateChildWidgetsSize() { //We need dynamically added child widgets to expand so that we know their height - //TODO: more heights + screen = this->getCurrentScreen(); + log_debugcpp("Screen: " + screen->model().toStdString() + " " + screen->name().toStdString()); + QRect screenRes = screen->geometry(); + int srx1, srx2, sry1, sry2; + screenRes.getCoords(&srx1, &sry1, &srx2, &sry2); + log_debugcpp("(for Percentage) Screen Res: " + std::to_string(srx1) + " " + std::to_string(srx2)+" " + std::to_string(sry1) + " " + std::to_string(sry2)); + + width = (uint64_t)std::abs(srx2) * this->widthRatio; + log_debugcpp("Window Width: " + std::to_string(width)); + for (auto *ew : ews) { + ew->setWidth(width, widthRatio); + } + /* + * sessionwidget: mainLabel, mainSlider, muteButton + * endpointwidget: mainLabel + * + * + * + * + * + * + * + */ + this->setAttribute(Qt::WA_DontShowOnScreen, true); this->show(); this->layout()->invalidate(); this->hide(); this->setAttribute(Qt::WA_DontShowOnScreen, false); - int height = 0, width = 0; + int height = 0; for (auto *epw : this->ews) { height += epw->height(); //width = (epw->width() > width) ? epw->width() : width; } - width = scrollArea->width(); + //width = scrollArea->width(); height += mainMenuBar->height(); height += hw->height(); /* * Establishing initial window size and position */ - //TODO: test. hardcode. var. - setGeometry(setSizePosition(width, height)); + setGeometry(setSizePosition(screen, width, height)); } QScreen* MainWindow::getCurrentScreen() { @@ -114,7 +138,7 @@ QScreen* MainWindow::getCurrentScreen() { return QGuiApplication::primaryScreen(); } -SessionWidget::SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent) : QWidget(parent){ +SessionWidget::SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent) : QWidget(parent), widthSpacer(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum){ //todo: based on qgridlayout, name+mute should be its own widget, same with channels this->idx = idx; this->sh = sh; @@ -129,12 +153,12 @@ SessionWidget::SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent) mainLabel = new QLabel(QString::fromStdWString(sh->getName()), this); mainSlider = new QSlider(Qt::Horizontal, this); - mainLabel->setMaximumWidth(150 /*1/16ish 1080p*/); - mainLabel->setMinimumWidth(150 /*1/16ish 1080p*/); + //mainLabel->setMaximumWidth(150 /*1/16ish 1080p*/); + //mainLabel->setMinimumWidth(150 /*1/16ish 1080p*/); mainLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); mainSlider->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - mainSlider->setMinimumWidth(120 /*1/16 1080p*/); + //mainSlider->setMinimumWidth(120 /*1/16 1080p*/); mainSlider->setFocusPolicy(Qt::StrongFocus); mainSlider->setTickPosition(QSlider::TicksBothSides); mainSlider->setTickInterval(5); @@ -143,8 +167,8 @@ SessionWidget::SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent) muteButton->setCheckState((sh->getMute() == false ? Qt::Unchecked : Qt::Checked)); muteButton->setText(sh->getMute() ? STRING_UNMUTE : STRING_MUTE); - muteButton->setMaximumWidth(60 /*1/32th 1080p*/); - muteButton->setMinimumWidth(60 /*1/32th 1080p*/); + //muteButton->setMaximumWidth(60 /*1/32th 1080p*/); + //muteButton->setMinimumWidth(60 /*1/32th 1080p*/); float volume = sh->getVolume(AudioChannel::CHANNEL_MAIN) * 100; mainSlider->setValue((int)volume); log_debugcpp("SESSION SET WITH VOLUME " + std::to_string(volume)); @@ -158,7 +182,7 @@ SessionWidget::SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent) * * layout->setSizeConstraint(QLayout::SetMinAndMaxSize); */ - widgetLayout->addItem(new QSpacerItem(200, 1, QSizePolicy::Expanding, QSizePolicy::Minimum)); + widgetLayout->addItem(&widthSpacer); widgetLayout->addWidget(mainLabel, Qt::AlignLeft | Qt::AlignBottom); widgetLayout->addWidget(muteButton, Qt::AlignRight | Qt::AlignBottom); widgetLayout->addWidget(mainSlider, Qt::AlignRight | Qt::AlignBottom); @@ -194,6 +218,16 @@ SessionWidget::SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent) volumePoller->start(10); } +void SessionWidget::setWidth(uint64_t width, double widthRatio) { + /* og 1080p 120% testing values */ + this->mainLabel->setMaximumWidth((int)(width * 0.30) /*1/16ish 1080p*/); + this->mainLabel->setMinimumWidth((int)(width * 0.30) /*1/16ish 1080p*/); + this->muteButton->setMaximumWidth((int)(width * 0.10) /*1/32th 1080p*/); + this->muteButton->setMinimumWidth((int)(width * 0.10) /*1/32th 1080p*/); + this->mainSlider->setMinimumWidth((int)(width * 0.30) /*1/16 1080p*/); + widthSpacer.changeSize((int)width * 0.20, 1, QSizePolicy::Expanding, QSizePolicy::Minimum /*200*/); +} + void SessionWidget::updateMute(int checked){ bool muted = (checked == 2 ? true : false); this->sh->setMute(osh->getGuid(), muted); @@ -294,8 +328,8 @@ EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *pare return; } - mainLabel->setMaximumWidth(350 /* 1080p 120%*/); - mainLabel->setMinimumWidth(350 /* 1080p 120%*/); + //mainLabel->setMaximumWidth(350 /* 1080p 120%*/); + //mainLabel->setMinimumWidth(350 /* 1080p 120%*/); mainLabel->setWordWrap(true); mainLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); //mainLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); @@ -316,7 +350,7 @@ EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *pare log_debugcpp("ENDPOINT SET WITH VOLUME " + std::to_string(volume)); //mainMuteLayout = new QGridLayout(); - widgetLayout->addWidget(mainLabel, row, 0, 1, 3, Qt::AlignLeft | Qt::AlignVCenter); + widgetLayout->addWidget(mainLabel, row, 0, 1, 2, Qt::AlignLeft | Qt::AlignVCenter); widgetLayout->addWidget(muteButton, row, 2, Qt::AlignRight | Qt::AlignVCenter); widgetLayout->addWidget(mainVolumeLabel, row, 3, Qt::AlignRight | Qt::AlignVCenter); row++; @@ -515,6 +549,15 @@ void MainWindow::reorderEndpointWidgetCollection() { ews.resize(firstNullPosition + 1); } +void EndpointWidget::setWidth(uint64_t width, double widthRatio) { + /* og 1080p 120% testing values */ + this->mainLabel->setMaximumWidth((int)width * 0.35 /* 1080p 120%*/); + this->mainLabel->setMinimumWidth((int)width * 0.35 /* 1080p 120%*/); + for (auto sw : sessionWidgets){ + sw->setWidth(width, widthRatio); + } +} + void EndpointWidget::updateMute(int checked){ bool muted = (checked == 2 ? true : false); this->eph->setMute(osh->getGuid(), muted); diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index b3bb7ea..5176939 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -85,7 +85,7 @@ Q_OBJECT public: SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent /* = nullptr */); ~SessionWidget(); - + void setWidth(uint64_t width, double widthRatio); public slots: void updateMainVolume(int newValue); void updateMute(int checked); @@ -98,6 +98,8 @@ private: QCheckBox *muteButton = nullptr; SessionHandler* sh; QTimer* volumePoller = nullptr; + + QSpacerItem widthSpacer; }; class ChannelWidget : public QWidget { @@ -128,8 +130,9 @@ public: void setIndex(uint64_t idx); uint64_t getIndex(); - void setVolume(int channel, float volume); - + //void setVolume(int channel, float volume); + void setWidth(uint64_t width, double widthRatio); + ~EndpointWidget(); //void updateMainVolume(float newValue); //void updateVolume(uint32_t channel, float newValue); @@ -203,7 +206,7 @@ public: protected: void closeEvent(QCloseEvent *event) override; void customEvent(QEvent* ev) override; - QRect setSizePosition(int width, int height); + QRect setSizePosition(QScreen* screen, int width, int height); QScreen* getCurrentScreen(); private slots: @@ -228,7 +231,8 @@ private: static constexpr uint64_t ewsUpdateTimerFrequency = 500; //TODO: Test //TODO: Come back here and check all are parametrized - uint64_t windowWidth = 600; + double widthRatio = 0.28; + uint64_t width; QScrollArea *scrollArea; HeaderWidget* hw; From 621841e95410c290742134d6e53dc24a7f0e8bec Mon Sep 17 00:00:00 2001 From: Hane Date: Sat, 13 Apr 2024 18:28:20 +0200 Subject: [PATCH 14/78] fixed heap corruption --- src/qt/qtclasses.cpp | 62 ++++++++++++++++---------------------------- src/qt/qtclasses.h | 5 ++-- 2 files changed, 24 insertions(+), 43 deletions(-) diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 1ecf82f..2bf785c 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -78,6 +78,7 @@ QRect MainWindow::setSizePosition(QScreen* screen, int width, int height) { void MainWindow::calculateChildWidgetsSize() { //We need dynamically added child widgets to expand so that we know their height + //todo: own function + setsizeposition refactor + update setWidth bodies screen = this->getCurrentScreen(); log_debugcpp("Screen: " + screen->model().toStdString() + " " + screen->name().toStdString()); QRect screenRes = screen->geometry(); @@ -88,19 +89,8 @@ void MainWindow::calculateChildWidgetsSize() { width = (uint64_t)std::abs(srx2) * this->widthRatio; log_debugcpp("Window Width: " + std::to_string(width)); for (auto *ew : ews) { - ew->setWidth(width, widthRatio); + if (ew) ew->setWidth(width, widthRatio); } - /* - * sessionwidget: mainLabel, mainSlider, muteButton - * endpointwidget: mainLabel - * - * - * - * - * - * - * - */ this->setAttribute(Qt::WA_DontShowOnScreen, true); this->show(); @@ -111,9 +101,7 @@ void MainWindow::calculateChildWidgetsSize() { int height = 0; for (auto *epw : this->ews) { height += epw->height(); - //width = (epw->width() > width) ? epw->width() : width; } - //width = scrollArea->width(); height += mainMenuBar->height(); height += hw->height(); @@ -138,7 +126,7 @@ QScreen* MainWindow::getCurrentScreen() { return QGuiApplication::primaryScreen(); } -SessionWidget::SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent) : QWidget(parent), widthSpacer(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum){ +SessionWidget::SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent) : QWidget(parent) { //todo: based on qgridlayout, name+mute should be its own widget, same with channels this->idx = idx; this->sh = sh; @@ -174,15 +162,8 @@ SessionWidget::SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent) log_debugcpp("SESSION SET WITH VOLUME " + std::to_string(volume)); //tip: would need to be new widget with layout in it - //mainMuteLayout = new QGridLayout(); - /* - * layout->addWidget(mainLabel, 0, 0, Qt::AlignLeft | Qt::AlignBottom); - * layout->addWidget(muteButton, 0, 1, Qt::AlignLeft | Qt::AlignBottom); - * layout->addWidget(mainSlider, 0, 2, 1, 2); - * - * layout->setSizeConstraint(QLayout::SetMinAndMaxSize); - */ - widgetLayout->addItem(&widthSpacer); + widthSpacer = new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum); + widgetLayout->addItem(widthSpacer); widgetLayout->addWidget(mainLabel, Qt::AlignLeft | Qt::AlignBottom); widgetLayout->addWidget(muteButton, Qt::AlignRight | Qt::AlignBottom); widgetLayout->addWidget(mainSlider, Qt::AlignRight | Qt::AlignBottom); @@ -196,7 +177,7 @@ SessionWidget::SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent) /* * Session Volume Polling */ - volumePoller = new QTimer(); + volumePoller = new QTimer(this); connect(volumePoller, &QTimer::timeout, [this, sh](){ //if (memcmp(osh->callbackInfo[idx]->caller, osh->getGuid(), sizeof(NGuid)) == 0) return; CHECK IF THIS PROGRAM GENERATED THE FUNSIES IS NO LONGER IN USE FOR NOW. //todo: global + constexpr + ratio @@ -225,7 +206,7 @@ void SessionWidget::setWidth(uint64_t width, double widthRatio) { this->muteButton->setMaximumWidth((int)(width * 0.10) /*1/32th 1080p*/); this->muteButton->setMinimumWidth((int)(width * 0.10) /*1/32th 1080p*/); this->mainSlider->setMinimumWidth((int)(width * 0.30) /*1/16 1080p*/); - widthSpacer.changeSize((int)width * 0.20, 1, QSizePolicy::Expanding, QSizePolicy::Minimum /*200*/); + widthSpacer->changeSize((int)width * 0.20, 1, QSizePolicy::Expanding, QSizePolicy::Minimum /*200*/); } void SessionWidget::updateMute(int checked){ @@ -240,7 +221,7 @@ void SessionWidget::updateMainVolume(int newValue){ SessionWidget::~SessionWidget() { volumePoller->stop(); - delete volumePoller; + // volumePoller; } ChannelWidget::ChannelWidget(uint32_t channelCount, EndpointHandler* eph, QWidget *parent) : QWidget(parent){ @@ -435,8 +416,7 @@ EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *pare sessionWidgets.push_back(sessionWidget); eph->getSessionHandlers().at(i)->setFrontIndex(i); } - /* This spacer provides proper spacing when window vertically > widgets */ - //widgetLayout->addItem(&lastRowSpacer, row, 0); + /* Add/Remove SessionWidget callback */ eph->setAddSessionWidgetFunction([this](SessionHandler* sessionHandler) { @@ -455,6 +435,8 @@ void EndpointWidget::addSessionWidget(CustomWidgetEvent* ev){ SessionWidget* sw = new SessionWidget(index, ev->payload, this); ev->payload->setFrontIndex(index); this->widgetLayout->addWidget(sw, row, 0, 1, 4); + //sw->hide(); + //sw->show(); row++; sessionWidgets.push_back(sw); return; @@ -462,9 +444,11 @@ void EndpointWidget::addSessionWidget(CustomWidgetEvent* ev){ void EndpointWidget::removeSessionWidget(CustomWidgetEvent* ev){ uint64_t i = ev->payload->getFrontIndex(); - this->sessionWidgets.at(i)->setParent(nullptr); - this->widgetLayout->removeWidget(sessionWidgets.at(i)); - delete sessionWidgets.at(i); + SessionWidget* deceased = sessionWidgets.at(i); + deceased->setParent(nullptr); + deceased->hide(); + this->widgetLayout->removeWidget(deceased); + delete deceased; sessionWidgets.at(i) = nullptr; ev->payload->setFrontIndex(INT_MAX); //this->sessionWidgetsUpdateTimer->start(); @@ -554,7 +538,7 @@ void EndpointWidget::setWidth(uint64_t width, double widthRatio) { this->mainLabel->setMaximumWidth((int)width * 0.35 /* 1080p 120%*/); this->mainLabel->setMinimumWidth((int)width * 0.35 /* 1080p 120%*/); for (auto sw : sessionWidgets){ - sw->setWidth(width, widthRatio); + if (sw) sw->setWidth(width, widthRatio); } } @@ -642,7 +626,7 @@ HeaderWidget::HeaderWidget(QWidget *parent) : QWidget(parent) { this->setLayout(widgetLayout); } -MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), lastRowSpacer(1, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding) { +MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { //setWindowState(Qt::WindowFullScreen); //setCentralWidget(centralWidget); //todo: ratio @@ -661,11 +645,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), lastRowSpacer(1, QEvent::registerEventType(CustomQEvent::EndpointDefaultChange); QEvent::registerEventType(CustomQEvent::SessionWidgetObsolete); QEvent::registerEventType(CustomQEvent::SessionWidgetCreated); - - //setWindowFlags(Qt::FramelessWindowHint); - //setParent(0); // Create TopLevel-Widget - //setAttribute(Qt::WA_NoSystemBackground, true); - //setAttribute(Qt::WA_TranslucentBackground, true); +; + /* This spacer provides proper spacing when window vertically > widgets. */ + lastRowSpacer = new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding); ewsUpdateTimer = new QTimer(this); widget = new QWidget(); widgetLayout = new QGridLayout(); @@ -815,6 +797,6 @@ void MainWindow::reloadEndpointWidgets() { //todo:: tas aqui tirao, no me gustas y probablemente yo a ti tampoco //seguramente falle al querer rematar esto con redimensionar la ventana sólo //con los default endpoints en vista - widgetLayout->addItem(&lastRowSpacer, i, 0); + widgetLayout->addItem(lastRowSpacer, i, 0); } diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index 5176939..0374db6 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -98,8 +98,7 @@ private: QCheckBox *muteButton = nullptr; SessionHandler* sh; QTimer* volumePoller = nullptr; - - QSpacerItem widthSpacer; + QSpacerItem* widthSpacer; }; class ChannelWidget : public QWidget { @@ -241,7 +240,7 @@ private: //todo: ratio //Win10 1080p 120% QSize mwSize; - QSpacerItem lastRowSpacer; + QSpacerItem* lastRowSpacer; //public slots: From bf01df610d65aceb6fb7eaa1adebde9ec3713207 Mon Sep 17 00:00:00 2001 From: Hane Date: Tue, 16 Apr 2024 19:25:33 +0200 Subject: [PATCH 15/78] fixed window resize delayed for one present --- src/qt/qtclasses.cpp | 74 +++++++++++++++++++++++++++++++------------- src/qt/qtclasses.h | 19 +++++++----- 2 files changed, 63 insertions(+), 30 deletions(-) diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 2bf785c..34c4c58 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -79,6 +79,7 @@ QRect MainWindow::setSizePosition(QScreen* screen, int width, int height) { void MainWindow::calculateChildWidgetsSize() { //We need dynamically added child widgets to expand so that we know their height //todo: own function + setsizeposition refactor + update setWidth bodies + screen = this->getCurrentScreen(); log_debugcpp("Screen: " + screen->model().toStdString() + " " + screen->name().toStdString()); QRect screenRes = screen->geometry(); @@ -87,23 +88,35 @@ void MainWindow::calculateChildWidgetsSize() { log_debugcpp("(for Percentage) Screen Res: " + std::to_string(srx1) + " " + std::to_string(srx2)+" " + std::to_string(sry1) + " " + std::to_string(sry2)); width = (uint64_t)std::abs(srx2) * this->widthRatio; + int height = (uint64_t)std::abs(sry2); log_debugcpp("Window Width: " + std::to_string(width)); for (auto *ew : ews) { - if (ew) ew->setWidth(width, widthRatio); - } + if (!ew) continue; + ew->setSize(width, height); + } this->setAttribute(Qt::WA_DontShowOnScreen, true); - this->show(); - this->layout()->invalidate(); + this->showNormal(); + this->widget->layout()->invalidate(); + this->widget->updateGeometry(); this->hide(); this->setAttribute(Qt::WA_DontShowOnScreen, false); - int height = 0; + height = 0; + int left = 0, top = 0, right = 0, bottom = 0; + //this->widget->updateGeometry(); + //height = widget->height(); for (auto *epw : this->ews) { - height += epw->height(); + if (!epw) continue; + epw->layout()->getContentsMargins(&left, &top, &right, &bottom); + //epw->updateGeometry(); + height += epw->sizeHint().height(); + height += top; + height += bottom; } height += mainMenuBar->height(); - height += hw->height(); + + //height += lastRowSpacer->geometry().height(); /* * Establishing initial window size and position @@ -130,12 +143,12 @@ SessionWidget::SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent) //todo: based on qgridlayout, name+mute should be its own widget, same with channels this->idx = idx; this->sh = sh; - + + this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); widgetLayout = new QHBoxLayout(this); //widgetLayout->setSizeConstraint(QLayout::SetFixedSize); widgetLayout->setSizeConstraint(QLayout::SetMinAndMaxSize); - //widgetLayout->setMaximumSize(minimumSize()); - //this->setLayout( + widgetLayout->setContentsMargins(0, 10, 0, 10); muteButton = new QCheckBox(this); mainLabel = new QLabel(QString::fromStdWString(sh->getName()), this); @@ -199,12 +212,15 @@ SessionWidget::SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent) volumePoller->start(10); } -void SessionWidget::setWidth(uint64_t width, double widthRatio) { +void SessionWidget::setSize(uint64_t width, uint64_t height) { /* og 1080p 120% testing values */ this->mainLabel->setMaximumWidth((int)(width * 0.30) /*1/16ish 1080p*/); this->mainLabel->setMinimumWidth((int)(width * 0.30) /*1/16ish 1080p*/); + this->mainLabel->setMaximumHeight((int)(height * 0.02)); + this->mainLabel->setMinimumHeight((int)(height * 0.02)); this->muteButton->setMaximumWidth((int)(width * 0.10) /*1/32th 1080p*/); this->muteButton->setMinimumWidth((int)(width * 0.10) /*1/32th 1080p*/); + //this->muteButton->setMinimumWidth((int)(width * 0.10) /*1/16 1080p*/); this->mainSlider->setMinimumWidth((int)(width * 0.30) /*1/16 1080p*/); widthSpacer->changeSize((int)width * 0.20, 1, QSizePolicy::Expanding, QSizePolicy::Minimum /*200*/); } @@ -240,6 +256,7 @@ ChannelWidget::ChannelWidget(uint32_t channelCount, EndpointHandler* eph, QWidge for(uint32_t i = 0; i < channelCount && channelCount > 1; i++){ QSlider* tmp = new QSlider(Qt::Horizontal); QLabel* tmpLb = new QLabel(""); + this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); //tmp->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); tmp->setTickInterval(5); tmp->setSingleStep(1); @@ -249,8 +266,8 @@ ChannelWidget::ChannelWidget(uint32_t channelCount, EndpointHandler* eph, QWidge tmp->setValue((int) volume); tmpLb->setText(QString::number(volume)); //tmpLb->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); - tmp->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); - tmpLb->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); + tmp->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); + tmpLb->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); this->channelSliders.push_back(tmp); this->channelLabels.push_back(tmpLb); widgetLayout->addWidget(tmp, 0, i); @@ -264,9 +281,18 @@ ChannelWidget::ChannelWidget(uint32_t channelCount, EndpointHandler* eph, QWidge }); } this->setLayout(widgetLayout); - } +/* + * QSize ChannelWidget::minimumSizeHint() const { + * return minimum; + * } + * + * void ChannelWidget::setMinimum(QSize minimum) { + * this->minimum = minimum; + * } + */ + void ChannelWidget::updateChannel(int channel) { this->channelSliders.at(channel)->blockSignals(true); this->channelSliders.at(channel)->setValue((int)((eph->getCallbackInfo()->channelVolumes[channel] + roundingFactor) * 100)); @@ -281,7 +307,7 @@ EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *pare this->eph = eph; //todo: sussy this->eph->setState(EndpointState::ENDPOINT_ACTIVE, idx); - + this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); //setAttribute(Qt::WA_TranslucentBackground); widgetLayout = new QGridLayout(this); //this->setLayout(widgetLayout); @@ -353,7 +379,7 @@ EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *pare if(epChannelCount) { cw = new ChannelWidget(epChannelCount, eph, this); //cw->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); - widgetLayout->addWidget(cw, row++, 0, 1, 4 /*colmax*/); + widgetLayout->addWidget(cw, row++, 0, 1, 4 /*colmax*/, Qt::AlignTop); } /* @@ -408,7 +434,7 @@ EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *pare }); timer->start(10); - /* First SessionWidget batch */ + /* First Widget batch */ for (size_t i = 0; i < eph->getSessionCount(); i++) { SessionWidget* sessionWidget = new SessionWidget(i, eph->getSessionHandlers().at(i), this); widgetLayout->addWidget(sessionWidget, row, 0, 1, 4 /* colmax */); @@ -430,13 +456,13 @@ EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *pare log_debugcpp("ENDPOINT_WIDGETED"); } + + void EndpointWidget::addSessionWidget(CustomWidgetEvent* ev){ uint64_t index = this->sessionWidgets.size(); SessionWidget* sw = new SessionWidget(index, ev->payload, this); ev->payload->setFrontIndex(index); this->widgetLayout->addWidget(sw, row, 0, 1, 4); - //sw->hide(); - //sw->show(); row++; sessionWidgets.push_back(sw); return; @@ -450,6 +476,7 @@ void EndpointWidget::removeSessionWidget(CustomWidgetEvent* ev) this->widgetLayout->removeWidget(deceased); delete deceased; sessionWidgets.at(i) = nullptr; + //row--; ev->payload->setFrontIndex(INT_MAX); //this->sessionWidgetsUpdateTimer->start(); return; @@ -484,6 +511,7 @@ void MainWindow::customEvent(QEvent* ev) { QMainWindow::customEvent(ev); } +//__attribute__((optimize("O0", "unroll-loops"))) void MainWindow::removeEndpointWidget(CustomWidgetEvent* ev){ uint64_t i = ev->payload; this->ews.at(i)->setParent(nullptr); @@ -533,12 +561,14 @@ void MainWindow::reorderEndpointWidgetCollection() { ews.resize(firstNullPosition + 1); } -void EndpointWidget::setWidth(uint64_t width, double widthRatio) { +void EndpointWidget::setSize(uint64_t width, uint64_t height) { /* og 1080p 120% testing values */ this->mainLabel->setMaximumWidth((int)width * 0.35 /* 1080p 120%*/); this->mainLabel->setMinimumWidth((int)width * 0.35 /* 1080p 120%*/); + this->cw->setMinimumSize(QSize(1, height * 0.06)); + this->cw->setMaximumSize(QSize(QWIDGETSIZE_MAX, height * 0.06)); for (auto sw : sessionWidgets){ - if (sw) sw->setWidth(width, widthRatio); + if (sw) sw->setSize(width, height); } } @@ -761,7 +791,7 @@ void MainWindow::closeEvent(QCloseEvent *event) { if (trayIcon->isVisible()) { //todo: would be nice to show this to 1st time users; ini-san will come... - //this->trayIcon->showMessage("ini file calling","tratarte como un gilipollas la primera vez", QSystemTrayIcon::Information); + //this->trayIcon->showMessage("ini file calling","primerita vez", QSystemTrayIcon::Information); hide(); event->ignore(); diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index 0374db6..5ec06d7 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -85,7 +85,7 @@ Q_OBJECT public: SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent /* = nullptr */); ~SessionWidget(); - void setWidth(uint64_t width, double widthRatio); + void setSize(uint64_t width, uint64_t height); public slots: void updateMainVolume(int newValue); void updateMute(int checked); @@ -106,6 +106,8 @@ Q_OBJECT public: ChannelWidget(uint32_t channelCount, EndpointHandler* eph, QWidget *parent = nullptr); + //QSize minimumSizeHint() const override; + //void setMinimum(QSize minimum); void updateChannel(int channel); private: @@ -115,7 +117,7 @@ private: std::vector channelSliders; std::vector channelLabels; QGridLayout *widgetLayout; - + QSize minimum; }; class EndpointWidget : public QWidget { @@ -123,14 +125,16 @@ Q_OBJECT public: EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *parent = nullptr); - + //QSize minimumSizeHint() const override; + //void setMinimum(uint64_t height, double heightRatio); + EndpointHandler* getEndpointHandler(); std::map getDefaultRolesWidgets(); void setIndex(uint64_t idx); uint64_t getIndex(); //void setVolume(int channel, float volume); - void setWidth(uint64_t width, double widthRatio); + void setSize(uint64_t width, uint64_t height); ~EndpointWidget(); //void updateMainVolume(float newValue); @@ -170,6 +174,7 @@ private: uint64_t idx; ChannelWidget* cw; std::vector sessionWidgets; + QSize minimum; //std::vector *ephs; //std::vector *sliders; @@ -231,17 +236,15 @@ private: //TODO: Test //TODO: Come back here and check all are parametrized double widthRatio = 0.28; + double heightRatio = 0.05; uint64_t width; + uint64_t height; QScrollArea *scrollArea; HeaderWidget* hw; QToolBar *mainMenuBar; QScreen *screen; - //todo: ratio - //Win10 1080p 120% - QSize mwSize; QSpacerItem* lastRowSpacer; - //public slots: // void setEndpointHandlers(std::vector *ephs); From 78fabd39170df8cb27863d8c85f1423ecaaa740b Mon Sep 17 00:00:00 2001 From: Hane Date: Tue, 16 Apr 2024 19:53:57 +0200 Subject: [PATCH 16/78] recent code slightly refactored --- src/qt/qtclasses.cpp | 57 ++++++++++++++++++++------------------------ src/qt/qtclasses.h | 7 ++---- 2 files changed, 28 insertions(+), 36 deletions(-) diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 34c4c58..a004043 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -71,61 +71,56 @@ QRect MainWindow::setSizePosition(QScreen* screen, int width, int height) { break; default: this->addToolBar(Qt::BottomToolBarArea, mainMenuBar); + log_debugcpp("Failed positioning window"); return QRect(500, 400, width, height); break; } } -void MainWindow::calculateChildWidgetsSize() { +void MainWindow::compose() { //We need dynamically added child widgets to expand so that we know their height - //todo: own function + setsizeposition refactor + update setWidth bodies - + + /* + * Setting correct widget widths and heights + */ screen = this->getCurrentScreen(); log_debugcpp("Screen: " + screen->model().toStdString() + " " + screen->name().toStdString()); + QRect screenRes = screen->geometry(); int srx1, srx2, sry1, sry2; screenRes.getCoords(&srx1, &sry1, &srx2, &sry2); log_debugcpp("(for Percentage) Screen Res: " + std::to_string(srx1) + " " + std::to_string(srx2)+" " + std::to_string(sry1) + " " + std::to_string(sry2)); - width = (uint64_t)std::abs(srx2) * this->widthRatio; - int height = (uint64_t)std::abs(sry2); - log_debugcpp("Window Width: " + std::to_string(width)); - for (auto *ew : ews) { - if (!ew) continue; - ew->setSize(width, height); - } - - this->setAttribute(Qt::WA_DontShowOnScreen, true); - this->showNormal(); - this->widget->layout()->invalidate(); - this->widget->updateGeometry(); - this->hide(); - this->setAttribute(Qt::WA_DontShowOnScreen, false); - - height = 0; + uint64_t windowWidth = (uint64_t)std::abs(srx2) * this->widthRatio; + uint64_t screenHeight = (uint64_t)std::abs(sry2); + log_debugcpp("Window Width: " + std::to_string(windowWidth)); + for (auto *epw : ews) { + if (!epw) continue; + epw->setSize(windowWidth, screenHeight); + } + + /* + * Calculating window height + */ + uint64_t windowHeight = 0; int left = 0, top = 0, right = 0, bottom = 0; - //this->widget->updateGeometry(); - //height = widget->height(); for (auto *epw : this->ews) { if (!epw) continue; epw->layout()->getContentsMargins(&left, &top, &right, &bottom); - //epw->updateGeometry(); - height += epw->sizeHint().height(); - height += top; - height += bottom; + windowHeight += epw->sizeHint().height(); + windowHeight += top; + windowHeight += bottom; } - height += mainMenuBar->height(); + windowHeight += mainMenuBar->height(); - //height += lastRowSpacer->geometry().height(); - /* * Establishing initial window size and position */ - setGeometry(setSizePosition(screen, width, height)); + setGeometry(setSizePosition(screen, windowWidth, windowHeight)); } QScreen* MainWindow::getCurrentScreen() { - //Using cursor pos as screen detector. Flawed. + //todo: Using cursor pos as screen detector. Flawed. QPoint cursorPos = QCursor::pos(); log_debugcpp("Cursor pos: " + std::to_string(cursorPos.ry()) + " " + std::to_string(cursorPos.rx())); @@ -801,7 +796,7 @@ void MainWindow::closeEvent(QCloseEvent *event) { void MainWindow::trayIconActivated(QSystemTrayIcon::ActivationReason reason) { switch (reason) { case QSystemTrayIcon::Trigger: - this->calculateChildWidgetsSize(); + this->compose(); this->showNormal(); this->activateWindow(); break; diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index 5ec06d7..549569d 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -205,7 +205,7 @@ class MainWindow : public QMainWindow { public: MainWindow(QWidget *parent = nullptr); void reloadEndpointWidgets(); - void calculateChildWidgetsSize(); + void compose(); protected: void closeEvent(QCloseEvent *event) override; @@ -236,10 +236,7 @@ private: //TODO: Test //TODO: Come back here and check all are parametrized double widthRatio = 0.28; - double heightRatio = 0.05; - uint64_t width; - uint64_t height; - + QScrollArea *scrollArea; HeaderWidget* hw; QToolBar *mainMenuBar; From d801be1f61fba788a2e81417d0a763bc1f065949 Mon Sep 17 00:00:00 2001 From: Hane Date: Wed, 17 Apr 2024 15:24:47 +0200 Subject: [PATCH 17/78] wip: system sounds always on top --- src/back/backlasses.cpp | 6 ++++-- src/qt/qtclasses.h | 2 -- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/back/backlasses.cpp b/src/back/backlasses.cpp index 2370f08..029ff61 100644 --- a/src/back/backlasses.cpp +++ b/src/back/backlasses.cpp @@ -289,10 +289,11 @@ Endpoint::Endpoint(IMMDevice* ep, uint64_t idx){ void Endpoint::activateEndpointSessions() { //sessionManager; if (FAILED(endpoint->Activate(__uuidof(IAudioSessionManager2), CLSCTX_ALL, NULL, (void**) &sessionManager))) { log_wdebugcpp(L"sesionbros..."); return; } - + IAudioSessionEnumerator* sessionEnumerator = nullptr; if (FAILED(sessionManager->GetSessionEnumerator(&sessionEnumerator))) { log_wdebugcpp(L"sesEnumeratorBros..."); return; } + endpointSessions.resize(1, nullptr); int sessionCount; sessionEnumerator->GetCount(&sessionCount); for (int i = 0; i < sessionCount; i++) { @@ -304,7 +305,8 @@ void Endpoint::activateEndpointSessions() { sessionControl->AddRef(); sessionControlTmp->Release(); Session* session = new Session(this, sessionControl, (size_t)i); - endpointSessions.push_back(session); + if (sessionControl->IsSystemSoundsSession() == S_OK) endpointSessions[0] = session; + else endpointSessions.push_back(session); } sessionEnumerator->Release(); } diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index 549569d..0132e45 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -233,8 +233,6 @@ private: QAction *trayIconMenuOpenCP; QTimer *ewsUpdateTimer; static constexpr uint64_t ewsUpdateTimerFrequency = 500; - //TODO: Test - //TODO: Come back here and check all are parametrized double widthRatio = 0.28; QScrollArea *scrollArea; From 76d0afe67225c1a0a5612b32812482075682f8be Mon Sep 17 00:00:00 2001 From: Hane Date: Wed, 17 Apr 2024 16:00:01 +0200 Subject: [PATCH 18/78] wip: window size = default endpoints --- src/back/backlasses.cpp | 9 +++------ src/qt/qtclasses.cpp | 25 ++++++++++++++++--------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/back/backlasses.cpp b/src/back/backlasses.cpp index 029ff61..f307361 100644 --- a/src/back/backlasses.cpp +++ b/src/back/backlasses.cpp @@ -248,7 +248,7 @@ Endpoint::Endpoint(IMMDevice* ep, uint64_t idx){ * Only shows most relevant flag according to MS, i.e. 0110 sends 0010 */ //todo: preguntitas owindows dword no es uint32_t even tho mingw mingas - if(FAILED(endpoint->GetState(&this->endpointState))) {exit(-1);}; + if(FAILED(endpoint->GetState(&this->endpointState))) {exit(-2);}; if(this->endpointState == EndpointState::ENDPOINT_ACTIVE) { activateEndpointVolume(); @@ -282,10 +282,6 @@ Endpoint::Endpoint(IMMDevice* ep, uint64_t idx){ } } -/* - * Endpoint::Endpoint(IMMDevice* endpoint) : Endpoint(endpoint, 0) {}; - */ - void Endpoint::activateEndpointSessions() { //sessionManager; if (FAILED(endpoint->Activate(__uuidof(IAudioSessionManager2), CLSCTX_ALL, NULL, (void**) &sessionManager))) { log_wdebugcpp(L"sesionbros..."); return; } @@ -421,7 +417,7 @@ Roles Endpoint::getRoles(){ } void Endpoint::setRoles(Roles role){ - //otro exe momento + //todo: otro exe momento STARTUPINFOEXW startupConfig; PROCESS_INFORMATION processInfo; SecureZeroMemory(&startupConfig, sizeof(STARTUPINFOEXW)); @@ -648,6 +644,7 @@ void Overseer::reloadEndpoints(Flows flow) { captureDevices.at(j)->assignRoles((Roles)(1 << i)); } } + } } } diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index a004043..4b8f044 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -104,10 +104,10 @@ void MainWindow::compose() { */ uint64_t windowHeight = 0; int left = 0, top = 0, right = 0, bottom = 0; - for (auto *epw : this->ews) { - if (!epw) continue; - epw->layout()->getContentsMargins(&left, &top, &right, &bottom); - windowHeight += epw->sizeHint().height(); + for (int i = 0; i < 2; i++) { + if (!ews[i]) continue; + ews[i]->layout()->getContentsMargins(&left, &top, &right, &bottom); + windowHeight += ews[i]->sizeHint().height(); windowHeight += top; windowHeight += bottom; } @@ -806,17 +806,24 @@ void MainWindow::trayIconActivated(QSystemTrayIcon::ActivationReason reason) { } void MainWindow::reloadEndpointWidgets() { - size_t i = 0; + size_t i = 2; + ews.resize(2); //widgetLayout->addItem(&lastRowSpacer, i++, 0); for (size_t epwIndex = 0; i < (osh->getPlaybackEndpointHandlers().size()); i++) { if (osh->getPlaybackEndpointHandlers().at(i)->getState() == EndpointState::ENDPOINT_ACTIVE){ log_debugcpp("EPWidget creation"); //osh->getPlaybackEndpointHandlers().at(i)->getCallbackInfo()->caller = osh->getGuid(); EndpointWidget *epw = new EndpointWidget(epwIndex, osh->getPlaybackEndpointHandlers().at(i), widget); - epwIndex++; - //alfinal estoes solopara inicializarlmao - ews.push_back(epw); - widgetLayout->addWidget(epw, i, 0); + if ((epw->getEndpointHandler()->getRoles() & Roles::ROLE_ALL) || + (epw->getEndpointHandler()->getRoles() & Roles::ROLE_MULTIMEDIA)) + { ews[0] = epw; widgetLayout->addWidget(epw, 0, 0); } + else if (epw->getEndpointHandler()->getRoles() & Roles::ROLE_CONSOLE) + { ews[0] = epw; widgetLayout->addWidget(epw, 1, 0); } + else { + epwIndex++; + //alfinal estoes solopara inicializarlmao + ews.push_back(epw); + widgetLayout->addWidget(epw, i, 0); } } } //todo:: tas aqui tirao, no me gustas y probablemente yo a ti tampoco From 5b3b50a0d289ce35879cca15361abdb714877aac Mon Sep 17 00:00:00 2001 From: Hane Date: Wed, 17 Apr 2024 20:18:29 +0200 Subject: [PATCH 19/78] wip: fixed role setting --- src/back/backlasses.cpp | 2 +- src/cont/contclasses.cpp | 2 - src/qt/qtclasses.cpp | 107 +++++++++++++++++++++++---------------- src/qt/qtclasses.h | 2 +- 4 files changed, 64 insertions(+), 49 deletions(-) diff --git a/src/back/backlasses.cpp b/src/back/backlasses.cpp index f307361..1b54f79 100644 --- a/src/back/backlasses.cpp +++ b/src/back/backlasses.cpp @@ -426,7 +426,7 @@ void Endpoint::setRoles(Roles role){ SecureZeroMemory(&processInfo, sizeof(PROCESS_INFORMATION)); std::wstring command = L"SoundVolumeView.exe /SetDefault " + endpointId + L" "; - std::wstring troublePair = L"0"; + std::wstring troublePair = L"1"; switch (role) { case Roles::ROLE_ALL: /* diff --git a/src/cont/contclasses.cpp b/src/cont/contclasses.cpp index d9fe2d0..5e0a4ee 100644 --- a/src/cont/contclasses.cpp +++ b/src/cont/contclasses.cpp @@ -310,7 +310,6 @@ void OverseerHandler::reviseEndpointShowing(std::wstring endpointId, EndpointSta Flows flow; if (!eph) { if (state ^ EndpointState::ENDPOINT_ACTIVE) return; - //return; //flow = Flows::FLOW_CAPTURE; eph = osh->addEndpoint(endpointId, &flow); } else @@ -319,7 +318,6 @@ void OverseerHandler::reviseEndpointShowing(std::wstring endpointId, EndpointSta //todo: mic done but disabled. Tab-kun will come... if (flow == Flows::FLOW_CAPTURE) return; - if(eph && EndpointState::ENDPOINT_ACTIVE & state) { this->addEndpointWidget(eph); } else if (eph && eph->getFrontVisibilityState() == EndpointState::ENDPOINT_ACTIVE){ diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 4b8f044..0f8869a 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -295,7 +295,7 @@ void ChannelWidget::updateChannel(int channel) { this->channelSliders.at(channel)->blockSignals(false); } -EndpointWidget::EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *parent) : QWidget(parent) { +EndpointWidget::EndpointWidget(EndpointHandler* eph, QWidget *parent, uint64_t idx) : QWidget(parent) { //todo: based on qgridlayout, name+mute should be its own widget, same with channels row = 0; this->idx = idx; @@ -520,7 +520,7 @@ void MainWindow::removeEndpointWidget(CustomWidgetEvent* ev){ } void MainWindow::addEndpointWidget(CustomWidgetEvent* ev){ - EndpointWidget* epw = new EndpointWidget(this->ews.size(), ev->payload, widget); + EndpointWidget* epw = new EndpointWidget(ev->payload, widget, this->ews.size()); //epw->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); this->widgetLayout->addWidget(epw); ews.push_back(epw); @@ -621,7 +621,7 @@ EndpointHandler* EndpointWidget::getEndpointHandler(){ void EndpointWidget::setIndex(uint64_t idx){ this->idx = idx; - this->eph->setFrontVisibilityInfo(EndpointState::ENDPOINT_ACTIVE, this->idx); + this->eph->setState(EndpointState::ENDPOINT_ACTIVE, this->idx); } uint64_t EndpointWidget::getIndex(){ @@ -733,42 +733,59 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { * Set of function callback definitons for EndpointSituationCallback */ osh->setChangeFrontDefaultsFunction([this](Roles role, std::wstring endpointId) { - for (auto epw : this->ews) { - /* - * Is this the new default endpoint? - */ + EndpointWidget *newDef = nullptr, *oldDef = nullptr; + for (int i = 0; i < ews.size(); i++) { + auto epw = this->ews.at(i); + if (!epw) continue; if (epw->getEndpointHandler()->getId() == endpointId) { - //not necessary to keep endpointState flags up to date right now, but updating it will allow for later config files / profiles - epw->getDefaultRolesWidgets().at(role)->blockSignals(true); - epw->getEndpointHandler()->assignRoles(role); - epw->getDefaultRolesWidgets().at(role)->blockSignals(false); - QCoreApplication::instance()->postEvent(epw->getDefaultRolesWidgets().at(role), new QEvent((QEvent::Type)CustomQEvent::EndpointDefaultChange)); - //epw->defaultRolesCheckBoxes.at(role)->postEnableChange(); - /* - * And were you THE default? - */ - if (epw->getEndpointHandler()->getRoles() == Roles::ROLE_ALL) { - QCoreApplication::instance()->postEvent(epw->getDefaultRolesWidgets().at(Roles::ROLE_ALL), new QEvent((QEvent::Type)CustomQEvent::EndpointDefaultChange)); - } - /* - * Are you the dethroned king? - */ - } else if (epw->getEndpointHandler()->getRoles() & role) { - /* - * And were you THE default up until now? - */ - if (epw->getEndpointHandler()->getRoles() == Roles::ROLE_ALL) { - QCoreApplication::instance()->postEvent(epw->getDefaultRolesWidgets().at(Roles::ROLE_ALL), new QEvent((QEvent::Type)CustomQEvent::EndpointDefaultChange)); - } - - epw->getDefaultRolesWidgets().at(role)->blockSignals(true); - //Same as before. ini-san will come... - epw->getEndpointHandler()->removeRoles(role); - epw->getDefaultRolesWidgets().at(role)->blockSignals(false); - QCoreApplication::instance()->postEvent(epw->getDefaultRolesWidgets().at(role), new QEvent((QEvent::Type)CustomQEvent::EndpointDefaultChange)); - - } - } + newDef = epw; + continue; } + if (epw->getEndpointHandler()->getRoles() & role) { + oldDef = epw; + continue; } + } + + //debug if (role != Roles::ROLE_COMMUNICATIONS) return; + if (oldDef && newDef) { + this->ews.at(oldDef->getIndex()) = nullptr; + this->ews.at(newDef->getIndex()) = nullptr; + newDef->getDefaultRolesWidgets().at(role)->blockSignals(true); + newDef->getEndpointHandler()->assignRoles(role); + QCoreApplication::instance()->postEvent(newDef->getDefaultRolesWidgets().at(role), new QEvent((QEvent::Type)CustomQEvent::EndpointDefaultChange)); + uint8_t newDefIdx = newDef->getIndex(); + uint8_t newDefRoles = newDef->getEndpointHandler()->getRoles(); + if (newDefRoles == Roles::ROLE_ALL) { + newDef->setIndex(0); + this->ews[0] = newDef; + QCoreApplication::instance()->postEvent(newDef->getDefaultRolesWidgets().at(Roles::ROLE_ALL), new QEvent((QEvent::Type)CustomQEvent::EndpointDefaultChange)); + } else if ((newDefRoles & Roles::ROLE_MULTIMEDIA) || + (newDefRoles & Roles::ROLE_CONSOLE)){ newDef->setIndex(0); + this->ews[0] = newDef; + } else if (newDefRoles & Roles::ROLE_COMMUNICATIONS) { newDef->setIndex(1); + this->ews[1] = newDef;} + log_debugcpp("newDef new idx: " + std::to_string(newDef->getIndex())); + newDef->getDefaultRolesWidgets().at(role)->blockSignals(false); + + oldDef->getDefaultRolesWidgets().at(role)->blockSignals(true); + uint8_t oldDefRoles = oldDef->getEndpointHandler()->getRoles(); + if (oldDefRoles == Roles::ROLE_ALL) QCoreApplication::instance()->postEvent(oldDef->getDefaultRolesWidgets().at(Roles::ROLE_ALL), new QEvent((QEvent::Type)CustomQEvent::EndpointDefaultChange)); + oldDef->getEndpointHandler()->removeRoles(role); + QCoreApplication::instance()->postEvent(oldDef->getDefaultRolesWidgets().at(role), new QEvent((QEvent::Type)CustomQEvent::EndpointDefaultChange)); + oldDefRoles = oldDef->getEndpointHandler()->getRoles(); + //this->ews[oldDef->getIndex()] = nullptr; + if ((oldDefRoles & Roles::ROLE_MULTIMEDIA) && + (oldDefRoles & Roles::ROLE_CONSOLE)) { this-> ews[0] = oldDef; + oldDef->setIndex(0); + } else if (oldDefRoles & Roles::ROLE_COMMUNICATIONS) { this-> ews[1] = oldDef; + oldDef->setIndex(1); + } else { if (newDefIdx > 1) { this->ews[newDefIdx] = oldDef; + oldDef->setIndex(newDefIdx); + } else { this->ews.push_back(oldDef); + oldDef->setIndex(this->ews.size() - 1); + } } + log_debugcpp("oldDef new idx: " + std::to_string(oldDef->getIndex())); + oldDef->getDefaultRolesWidgets().at(role)->blockSignals(false); + } }); osh->setRemoveEndpointWidgetFunction([this](uint64_t index) { @@ -809,18 +826,18 @@ void MainWindow::reloadEndpointWidgets() { size_t i = 2; ews.resize(2); //widgetLayout->addItem(&lastRowSpacer, i++, 0); - for (size_t epwIndex = 0; i < (osh->getPlaybackEndpointHandlers().size()); i++) { + for (size_t epwIndex = 2; i < (osh->getPlaybackEndpointHandlers().size()); i++) { if (osh->getPlaybackEndpointHandlers().at(i)->getState() == EndpointState::ENDPOINT_ACTIVE){ log_debugcpp("EPWidget creation"); //osh->getPlaybackEndpointHandlers().at(i)->getCallbackInfo()->caller = osh->getGuid(); - EndpointWidget *epw = new EndpointWidget(epwIndex, osh->getPlaybackEndpointHandlers().at(i), widget); - if ((epw->getEndpointHandler()->getRoles() & Roles::ROLE_ALL) || + EndpointWidget *epw = new EndpointWidget(osh->getPlaybackEndpointHandlers().at(i), widget); + if ((epw->getEndpointHandler()->getRoles() == Roles::ROLE_ALL) || (epw->getEndpointHandler()->getRoles() & Roles::ROLE_MULTIMEDIA)) - { ews[0] = epw; widgetLayout->addWidget(epw, 0, 0); } - else if (epw->getEndpointHandler()->getRoles() & Roles::ROLE_CONSOLE) - { ews[0] = epw; widgetLayout->addWidget(epw, 1, 0); } + { ews[0] = epw; widgetLayout->addWidget(epw, 0, 0); epw->setIndex(0); } + else if (epw->getEndpointHandler()->getRoles() & Roles::ROLE_COMMUNICATIONS) + { ews[1] = epw; widgetLayout->addWidget(epw, 1, 0); epw->setIndex(1); } else { - epwIndex++; + epw->setIndex(epwIndex++); //alfinal estoes solopara inicializarlmao ews.push_back(epw); widgetLayout->addWidget(epw, i, 0); } diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index 0132e45..96268cb 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -124,7 +124,7 @@ class EndpointWidget : public QWidget { Q_OBJECT public: - EndpointWidget(uint64_t idx, EndpointHandler* eph, QWidget *parent = nullptr); + EndpointWidget(EndpointHandler* eph, QWidget *parent = nullptr, uint64_t idx = INT_MAX); //QSize minimumSizeHint() const override; //void setMinimum(uint64_t height, double heightRatio); From 4f637b43972b4ab8b6a53f66ea0b0848a395be9a Mon Sep 17 00:00:00 2001 From: Hane Date: Thu, 18 Apr 2024 13:18:29 +0200 Subject: [PATCH 20/78] wip: reintroduced fake show --- src/qt/qtclasses.cpp | 47 ++++++++++++++++++++++++++++++-------------- src/qt/qtclasses.h | 3 ++- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 0f8869a..e7b3a26 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -99,6 +99,12 @@ void MainWindow::compose() { epw->setSize(windowWidth, screenHeight); } + this->setAttribute(Qt::WA_DontShowOnScreen, true); + this->show(); + this->widget->layout()->update(); + this->hide(); + this->setAttribute(Qt::WA_DontShowOnScreen, false); + /* * Calculating window height */ @@ -110,9 +116,10 @@ void MainWindow::compose() { windowHeight += ews[i]->sizeHint().height(); windowHeight += top; windowHeight += bottom; + log_debugcpp("windowHeight loop: " + std::to_string(windowHeight)); } windowHeight += mainMenuBar->height(); - + log_debugcpp("windowHeight final value: " + std::to_string(windowHeight)); /* * Establishing initial window size and position */ @@ -371,7 +378,7 @@ EndpointWidget::EndpointWidget(EndpointHandler* eph, QWidget *parent, uint64_t i */ uint32_t epChannelCount = eph->getChannelCount(); - if(epChannelCount) { + if(epChannelCount > 1) { cw = new ChannelWidget(epChannelCount, eph, this); //cw->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); widgetLayout->addWidget(cw, row++, 0, 1, 4 /*colmax*/, Qt::AlignTop); @@ -670,6 +677,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { QEvent::registerEventType(CustomQEvent::EndpointDefaultChange); QEvent::registerEventType(CustomQEvent::SessionWidgetObsolete); QEvent::registerEventType(CustomQEvent::SessionWidgetCreated); + QEvent::registerEventType(CustomQEvent::RecomposeMainWindow); ; /* This spacer provides proper spacing when window vertically > widgets. */ lastRowSpacer = new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding); @@ -759,10 +767,13 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { this->ews[0] = newDef; QCoreApplication::instance()->postEvent(newDef->getDefaultRolesWidgets().at(Roles::ROLE_ALL), new QEvent((QEvent::Type)CustomQEvent::EndpointDefaultChange)); } else if ((newDefRoles & Roles::ROLE_MULTIMEDIA) || - (newDefRoles & Roles::ROLE_CONSOLE)){ newDef->setIndex(0); - this->ews[0] = newDef; - } else if (newDefRoles & Roles::ROLE_COMMUNICATIONS) { newDef->setIndex(1); - this->ews[1] = newDef;} + (newDefRoles & Roles::ROLE_CONSOLE)){ + newDef->setIndex(0); + this->ews[0] = newDef; + } else if (newDefRoles & Roles::ROLE_COMMUNICATIONS) { + newDef->setIndex(1); + this->ews[1] = newDef; + } log_debugcpp("newDef new idx: " + std::to_string(newDef->getIndex())); newDef->getDefaultRolesWidgets().at(role)->blockSignals(false); @@ -774,15 +785,21 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { oldDefRoles = oldDef->getEndpointHandler()->getRoles(); //this->ews[oldDef->getIndex()] = nullptr; if ((oldDefRoles & Roles::ROLE_MULTIMEDIA) && - (oldDefRoles & Roles::ROLE_CONSOLE)) { this-> ews[0] = oldDef; - oldDef->setIndex(0); - } else if (oldDefRoles & Roles::ROLE_COMMUNICATIONS) { this-> ews[1] = oldDef; - oldDef->setIndex(1); - } else { if (newDefIdx > 1) { this->ews[newDefIdx] = oldDef; - oldDef->setIndex(newDefIdx); - } else { this->ews.push_back(oldDef); - oldDef->setIndex(this->ews.size() - 1); - } } + (oldDefRoles & Roles::ROLE_CONSOLE)) { + this-> ews[0] = oldDef; + oldDef->setIndex(0); + } else if (oldDefRoles & Roles::ROLE_COMMUNICATIONS) { + this-> ews[1] = oldDef; + oldDef->setIndex(1); + } else { + if (newDefIdx > 1) { + this->ews[newDefIdx] = oldDef; + oldDef->setIndex(newDefIdx); + } else { + this->ews.push_back(oldDef); + oldDef->setIndex(this->ews.size() - 1); + } + } log_debugcpp("oldDef new idx: " + std::to_string(oldDef->getIndex())); oldDef->getDefaultRolesWidgets().at(role)->blockSignals(false); } diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index 96268cb..16f932b 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -53,7 +53,8 @@ enum CustomQEvent { EndpointWidgetCreated = 1002, EndpointDefaultChange = 1003, SessionWidgetCreated = 1004, - SessionWidgetObsolete = 1005 + SessionWidgetObsolete = 1005, + RecomposeMainWindow = 1006 }; template From 170d52067b39c7bd2b7eb15425ca94ed41973fc4 Mon Sep 17 00:00:00 2001 From: Hane Date: Thu, 18 Apr 2024 16:42:28 +0200 Subject: [PATCH 21/78] wip: fixed multi monitor --- src/qt/qtclasses.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index e7b3a26..478bd6f 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -91,19 +91,21 @@ void MainWindow::compose() { screenRes.getCoords(&srx1, &sry1, &srx2, &sry2); log_debugcpp("(for Percentage) Screen Res: " + std::to_string(srx1) + " " + std::to_string(srx2)+" " + std::to_string(sry1) + " " + std::to_string(sry2)); - uint64_t windowWidth = (uint64_t)std::abs(srx2) * this->widthRatio; - uint64_t screenHeight = (uint64_t)std::abs(sry2); + uint64_t windowWidth = (uint64_t)std::abs(screenRes.width()) * this->widthRatio; + uint64_t screenHeight = (uint64_t)std::abs(screenRes.height()); log_debugcpp("Window Width: " + std::to_string(windowWidth)); - for (auto *epw : ews) { - if (!epw) continue; - epw->setSize(windowWidth, screenHeight); - } this->setAttribute(Qt::WA_DontShowOnScreen, true); this->show(); this->widget->layout()->update(); this->hide(); this->setAttribute(Qt::WA_DontShowOnScreen, false); + + for (auto *epw : ews) { + if (!epw) continue; + epw->setSize(windowWidth, screenHeight); + log_debugcpp("epw loop"); + } /* * Calculating window height @@ -567,8 +569,10 @@ void EndpointWidget::setSize(uint64_t width, uint64_t height) { /* og 1080p 120% testing values */ this->mainLabel->setMaximumWidth((int)width * 0.35 /* 1080p 120%*/); this->mainLabel->setMinimumWidth((int)width * 0.35 /* 1080p 120%*/); - this->cw->setMinimumSize(QSize(1, height * 0.06)); - this->cw->setMaximumSize(QSize(QWIDGETSIZE_MAX, height * 0.06)); + if (cw) { + this->cw->setMinimumSize(QSize(1, height * 0.06)); + this->cw->setMaximumSize(QSize(QWIDGETSIZE_MAX, height * 0.06)); + } for (auto sw : sessionWidgets){ if (sw) sw->setSize(width, height); } From 0880305b23db6a007fae3ce6289d352bd014c1e9 Mon Sep 17 00:00:00 2001 From: Hane Date: Fri, 19 Apr 2024 13:40:50 +0200 Subject: [PATCH 22/78] dynamically updated session name --- qtest.pro | 2 +- src/back/backlasses.cpp | 36 ++++++++++++++++----------------- src/back/backlasses.h | 14 +++++++------ src/back/backsessionclasses.cpp | 6 ++++++ src/back/backsessionclasses.h | 2 +- src/back/msinclude.h | 1 + src/cont/contclasses.h | 1 + src/cont/contsessionclasses.cpp | 4 ++++ src/cont/contsessionclasses.h | 3 ++- src/qt/qtclasses.cpp | 14 +++++++------ src/qt/qtclasses.h | 4 ++-- 11 files changed, 51 insertions(+), 36 deletions(-) diff --git a/qtest.pro b/qtest.pro index 2de66b2..370925d 100644 --- a/qtest.pro +++ b/qtest.pro @@ -1,6 +1,6 @@ QMAKE_CXXFLAGS += --target=x86_64-w64-mingw32 -g -gcodeview QMAKE_LFLAGS += --target=x86_64-w64-mingw32 -g -Wl,-pdb= -v -LIBS += -LC:/capybara/libclang/x86_64-w64-mingw32/lib -lWinmm -lodbc32 -lodbccp32 -luuid -loleaut32 -lole32 -lshell32 -ladvapi32 -lcomdlg32 -lwinspool -lgdi32 -luser32 -lkernel32 +LIBS += -LC:/capybara/libclang/x86_64-w64-mingw32/lib -lWinmm -lodbc32 -lodbccp32 -luuid -loleaut32 -lole32 -lshell32 -ladvapi32 -lcomdlg32 -lwinspool -lgdi32 -luser32 -lkernel32 -lpropsys #"kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" -luuid -loleaut32 -lole32 -lshell32 -ladvapi32 -lcomdlg32 -lwinspool -lgdi32 -luser32 -lkernel32 DEFINES += DEBUG QT_LOGGING_TO_CONSOLE=1 WIN32_LEAN_AND_MEAN CONFIG += debug diff --git a/src/back/backlasses.cpp b/src/back/backlasses.cpp index 1b54f79..0d35bed 100644 --- a/src/back/backlasses.cpp +++ b/src/back/backlasses.cpp @@ -133,14 +133,16 @@ HRESULT EndpointVolumeCallback::OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA pNotify return S_OK; } + /* - * EndpointSituationCallback::EndpointSituationCallback(IMMDeviceEnumerator *deviceEnumerator, std::vector playbackDevices){ - * this->deviceEnumerator = deviceEnumerator; + * EndpointSituationCallback::EndpointSituationCallback(std::vector* playbackDevices, std::vector* captureDevices){ + * this->captureDevices = captureDevices; * this->playbackDevices = playbackDevices; * } - * */ + + ULONG EndpointSituationCallback::AddRef(){ return InterlockedIncrement(&ref); } @@ -224,17 +226,13 @@ HRESULT EndpointSituationCallback::OnDeviceStateChanged(LPCWSTR pwstrDeviceId, D return S_OK; } -HRESULT EndpointSituationCallback::OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key) { - - /* - * log_debugcpp(" -->Changed device property " + - * key.fmtid.Data1 + key.fmtid.Data2 + key.fmtid.Data3 + "\n" + - * key.fmtid.Data4[0]+ key.fmtid.Data4[1]+ "\n"+ - * key.fmtid.Data4[2]+ key.fmtid.Data4[3] + "\n"+ - * key.fmtid.Data4[4]+ key.fmtid.Data4[5] + "\n"+ - * key.fmtid.Data4[6]+ key.fmtid.Data4[7]+ "\n"+ - * " pid " + key.pid); - */ +HRESULT EndpointSituationCallback::OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key) { + + /* Lacking impl again? */ + PWSTR test; + HRESULT ctrl = PSGetNameFromPropertyKey(key, &test); + if (ctrl == S_OK) log_wdebugcpp(test); + return S_OK; } @@ -256,10 +254,10 @@ Endpoint::Endpoint(IMMDevice* ep, uint64_t idx){ reloadEndpointChannels(); - /* todo: check header - * if(FAILED(endpoint->Activate(__uuidof(IAudioMeterInformation), - * CLSCTX_ALL, NULL, (void**)&endpointPeakMeter))) { log_debugcpp("peakbros..."); } - */ + //todo: check header + // if(FAILED(endpoint->Activate(__uuidof(IAudioMeterInformation), + // CLSCTX_ALL, NULL, (void**)&endpointPeakMeter))) { log_debugcpp("peakbros..."); } + //todo:: atexit into exit Gather ID LPWSTR tempString = nullptr; @@ -668,7 +666,7 @@ Endpoint* Overseer::addEndpoint(std::wstring endpointId, /* out */Flows* flow = return endpoint; } -Overseer::Overseer() { //: epsc(deviceEnumerator, playbackDevices){ +Overseer::Overseer() { //: epsc(&playbackDevices, &captureDevices){ //Initializing COM library log_debugcpp("Initializing Overseer"); initCOMLibrary(); diff --git a/src/back/backlasses.h b/src/back/backlasses.h index 6469c6d..c6148c4 100644 --- a/src/back/backlasses.h +++ b/src/back/backlasses.h @@ -61,9 +61,9 @@ class Endpoint { unsigned long endpointState; Roles endpointRoles = (Roles)0; uint64_t idx; - /* Not implemented in llvm-mingw. Sad! - * IAudioMeterInformation *endpointPeakMeter = nullptr; - */ + //Not implemented in llvm-mingw. Sad! + //IAudioMeterInformation *endpointPeakMeter = nullptr; + }; class EndpointVolumeCallback : public IAudioEndpointVolumeCallback { @@ -85,6 +85,7 @@ class EndpointVolumeCallback : public IAudioEndpointVolumeCallback { class EndpointSituationCallback : public IMMNotificationClient { public: //EndpointSituationCallback(IMMDeviceEnumerator *deviceEnumerator, std::vector playbackDevices, std::vector captureDevices); + //EndpointSituationCallback(std::vector* playbackDevices, std::vector* captureDevices); ULONG AddRef(); ULONG Release(); HRESULT QueryInterface(REFIID riid, VOID **ppvInterface); @@ -97,15 +98,16 @@ class EndpointSituationCallback : public IMMNotificationClient { private: ULONG ref = 1; //IMMDeviceEnumerator *deviceEnumerator; - //std::vector playbackDevices; - //std::vector captureDevices; + //std::vector* playbackDevices; + //std::vector* captureDevices; }; class Overseer { - //TODO singleton? + public: Overseer(); void openControlPanel(); + //todo: restore/overseer std::vector getPlaybackEndpoints(); std::vector getCaptureEndpoints(); diff --git a/src/back/backsessionclasses.cpp b/src/back/backsessionclasses.cpp index ced7fb1..21e7959 100644 --- a/src/back/backsessionclasses.cpp +++ b/src/back/backsessionclasses.cpp @@ -37,6 +37,8 @@ HRESULT SessionStateCallback::QueryInterface(REFIID riid, VOID **ppvInterface) { } HRESULT SessionStateCallback::OnDisplayNameChanged(LPCWSTR NewDisplayName, LPCGUID EventContext) { + sh->setName(std::wstring(NewDisplayName)); + sh->getVolumeInfo()->isNameChanged = true; return S_OK; } @@ -150,6 +152,10 @@ std::wstring Session::getName() { return sessionName; } +void Session::setName(std::wstring newName) { + this->sessionName = newName; +} + bool Session::getMute() { BOOL mut; if(FAILED(sessionVolume->GetMute(&mut))) { /* TIP: Below */ } diff --git a/src/back/backsessionclasses.h b/src/back/backsessionclasses.h index e6c8490..421f514 100644 --- a/src/back/backsessionclasses.h +++ b/src/back/backsessionclasses.h @@ -39,7 +39,7 @@ class Session { void setState(SessionState state); void setIndex(size_t idx); std::wstring getName(); - + void setName(std::wstring newName); void setStateCallback(SessionStateCallback *ssc); void removeStateCallback(SessionStateCallback *ssc); ~Session(); diff --git a/src/back/msinclude.h b/src/back/msinclude.h index 7bc7f4e..de0593f 100644 --- a/src/back/msinclude.h +++ b/src/back/msinclude.h @@ -12,6 +12,7 @@ #include #include #include +#include #include //#include diff --git a/src/cont/contclasses.h b/src/cont/contclasses.h index e46fc34..2f70b56 100644 --- a/src/cont/contclasses.h +++ b/src/cont/contclasses.h @@ -39,6 +39,7 @@ public: void setVolume(int channel, float volume); std::wstring getName(); + void setName(std::wstring newName); std::wstring getId(); void setFrontVisibilityInfo(EndpointState state, uint64_t frontIdx); diff --git a/src/cont/contsessionclasses.cpp b/src/cont/contsessionclasses.cpp index 155c0e1..085527e 100644 --- a/src/cont/contsessionclasses.cpp +++ b/src/cont/contsessionclasses.cpp @@ -32,6 +32,10 @@ std::wstring SessionHandler::getName(){ return session->getName(); } +void SessionHandler::setName(std::wstring newName){ + session->setName(newName); +} + bool SessionHandler::getMute(){ return session->getMute(); } diff --git a/src/cont/contsessionclasses.h b/src/cont/contsessionclasses.h index 51f620f..8a93164 100644 --- a/src/cont/contsessionclasses.h +++ b/src/cont/contsessionclasses.h @@ -12,7 +12,7 @@ struct SessionVolumeInfo { bool muted; float mainVolume; NGuid caller; - + bool isNameChanged = false; //size_t channels; //std::vector channelVolumes; }; @@ -29,6 +29,7 @@ class SessionHandler { void setState(SessionState state); uint64_t getFrontIndex(); std::wstring getName(); + void setName(std::wstring newName); void reviseSessionShowing(SessionState state); SessionVolumeInfo* getVolumeInfo(); diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 478bd6f..d1bdde2 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -79,7 +79,6 @@ QRect MainWindow::setSizePosition(QScreen* screen, int width, int height) { void MainWindow::compose() { //We need dynamically added child widgets to expand so that we know their height - /* * Setting correct widget widths and heights */ @@ -103,7 +102,7 @@ void MainWindow::compose() { for (auto *epw : ews) { if (!epw) continue; - epw->setSize(windowWidth, screenHeight); + epw->calculateSize(windowWidth, screenHeight); log_debugcpp("epw loop"); } @@ -198,6 +197,8 @@ SessionWidget::SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent) connect(volumePoller, &QTimer::timeout, [this, sh](){ //if (memcmp(osh->callbackInfo[idx]->caller, osh->getGuid(), sizeof(NGuid)) == 0) return; CHECK IF THIS PROGRAM GENERATED THE FUNSIES IS NO LONGER IN USE FOR NOW. //todo: global + constexpr + ratio + if (sh->getVolumeInfo()->isNameChanged) + mainLabel->setText(QString::fromStdWString(sh->getName())); const float roundingFactor = 0.005; mainSlider->blockSignals(true); muteButton->blockSignals(true); @@ -216,7 +217,7 @@ SessionWidget::SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent) volumePoller->start(10); } -void SessionWidget::setSize(uint64_t width, uint64_t height) { +void SessionWidget::calculateSize(uint64_t width, uint64_t height) { /* og 1080p 120% testing values */ this->mainLabel->setMaximumWidth((int)(width * 0.30) /*1/16ish 1080p*/); this->mainLabel->setMinimumWidth((int)(width * 0.30) /*1/16ish 1080p*/); @@ -565,7 +566,7 @@ void MainWindow::reorderEndpointWidgetCollection() { ews.resize(firstNullPosition + 1); } -void EndpointWidget::setSize(uint64_t width, uint64_t height) { +void EndpointWidget::calculateSize(uint64_t width, uint64_t height) { /* og 1080p 120% testing values */ this->mainLabel->setMaximumWidth((int)width * 0.35 /* 1080p 120%*/); this->mainLabel->setMinimumWidth((int)width * 0.35 /* 1080p 120%*/); @@ -574,7 +575,7 @@ void EndpointWidget::setSize(uint64_t width, uint64_t height) { this->cw->setMaximumSize(QSize(QWIDGETSIZE_MAX, height * 0.06)); } for (auto sw : sessionWidgets){ - if (sw) sw->setSize(width, height); + if (sw) sw->calculateSize(width, height); } } @@ -745,8 +746,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { * Set of function callback definitons for EndpointSituationCallback */ osh->setChangeFrontDefaultsFunction([this](Roles role, std::wstring endpointId) { + //Sigh... I hope to get this right when librole is done... EndpointWidget *newDef = nullptr, *oldDef = nullptr; - for (int i = 0; i < ews.size(); i++) { + for (uint64_t i = 0; i < ews.size(); i++) { auto epw = this->ews.at(i); if (!epw) continue; if (epw->getEndpointHandler()->getId() == endpointId) { diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index 16f932b..a16dd28 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -86,7 +86,7 @@ Q_OBJECT public: SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent /* = nullptr */); ~SessionWidget(); - void setSize(uint64_t width, uint64_t height); + void calculateSize(uint64_t width, uint64_t height); public slots: void updateMainVolume(int newValue); void updateMute(int checked); @@ -135,7 +135,7 @@ public: void setIndex(uint64_t idx); uint64_t getIndex(); //void setVolume(int channel, float volume); - void setSize(uint64_t width, uint64_t height); + void calculateSize(uint64_t width, uint64_t height); ~EndpointWidget(); //void updateMainVolume(float newValue); From 1b2ab191ca1c26bd7f6cbee72d2cf85dd492a795 Mon Sep 17 00:00:00 2001 From: Hane Date: Fri, 19 Apr 2024 18:58:26 +0200 Subject: [PATCH 23/78] wip: dynamically updated endpoint name --- src/back/backlasses.cpp | 56 ++++++++++++++++++++++++---------------- src/back/backlasses.h | 12 ++++----- src/cont/contclasses.cpp | 7 +++++ src/cont/contclasses.h | 4 ++- src/qt/qtclasses.cpp | 5 ++++ 5 files changed, 55 insertions(+), 29 deletions(-) diff --git a/src/back/backlasses.cpp b/src/back/backlasses.cpp index 0d35bed..147a594 100644 --- a/src/back/backlasses.cpp +++ b/src/back/backlasses.cpp @@ -134,13 +134,10 @@ HRESULT EndpointVolumeCallback::OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA pNotify } -/* - * EndpointSituationCallback::EndpointSituationCallback(std::vector* playbackDevices, std::vector* captureDevices){ - * this->captureDevices = captureDevices; - * this->playbackDevices = playbackDevices; - * } - */ +EndpointSituationCallback::EndpointSituationCallback(Overseer* os){ + this->os = os; +} ULONG EndpointSituationCallback::AddRef(){ @@ -227,13 +224,7 @@ HRESULT EndpointSituationCallback::OnDeviceStateChanged(LPCWSTR pwstrDeviceId, D } HRESULT EndpointSituationCallback::OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key) { - - /* Lacking impl again? */ - PWSTR test; - HRESULT ctrl = PSGetNameFromPropertyKey(key, &test); - if (ctrl == S_OK) log_wdebugcpp(test); - - + os->updateEndpointInfo(std::wstring(pwstrDeviceId)); return S_OK; } @@ -266,20 +257,29 @@ Endpoint::Endpoint(IMMDevice* ep, uint64_t idx){ log_wdebugcpp(endpointId); CoTaskMemFree(tempString); - endpoint->OpenPropertyStore(STGM_READ, &properties); - PROPVARIANT pv; - properties->GetValue(PKEY_Device_FriendlyName , &pv); - if (pv.pwszVal == nullptr) - friendlyName = L"Unnamed Not Present Endpoint"; - else - friendlyName = std::wstring(pv.pwszVal); + endpoint->OpenPropertyStore(STGM_READ, &properties); + this->updateName(); - this->setFlow(); + this->setFlow(); if (this->flow == Flows::FLOW_PLAYBACK) { activateEndpointSessions(); } } +void Endpoint::updateName() { + PROPVARIANT pv; + #define store_name(key, propvariant, wstr) do { \ + properties->GetValue(key , &propvariant); \ + if (pv.pwszVal == nullptr) wstr = L"Unnamed Not Present Endpoint"; \ + else wstr = std::wstring(pv.pwszVal); \ + } while (0) + + store_name(PKEY_Device_FriendlyName, pv, friendlyName); + store_name(PKEY_Device_DeviceDesc, pv, descriptionName); + store_name(PKEY_DeviceInterface_FriendlyName, pv, deviceName); + #undef store_name +} + void Endpoint::activateEndpointSessions() { //sessionManager; if (FAILED(endpoint->Activate(__uuidof(IAudioSessionManager2), CLSCTX_ALL, NULL, (void**) &sessionManager))) { log_wdebugcpp(L"sesionbros..."); return; } @@ -666,7 +666,7 @@ Endpoint* Overseer::addEndpoint(std::wstring endpointId, /* out */Flows* flow = return endpoint; } -Overseer::Overseer() { //: epsc(&playbackDevices, &captureDevices){ +Overseer::Overseer() : epsc(this){ //Initializing COM library log_debugcpp("Initializing Overseer"); initCOMLibrary(); @@ -719,6 +719,18 @@ std::vector Overseer::getCaptureEndpoints() { return captureDevices; } +void Overseer::updateEndpointInfo(std::wstring endpointId) { + log_wdebugcpp(L"new name Endpoint id: " + endpointId); + //todo: reintroduce capture devices + for(auto ep : playbackDevices) { + if (ep->getId() == endpointId) { + ep->updateName(); + osh->updateFrontEndpointName(ep); + break; + } + } +} + Overseer::~Overseer(){ log_debugcpp("cum"); deviceEnumerator->Release(); diff --git a/src/back/backlasses.h b/src/back/backlasses.h index c6148c4..9e3aba2 100644 --- a/src/back/backlasses.h +++ b/src/back/backlasses.h @@ -32,6 +32,7 @@ class Endpoint { Flows getFlow(); std::wstring getId(); std::wstring getName(); + void updateName(); void setVolumeCallback(EndpointVolumeCallback *epc); void removeVolumeCallback(EndpointVolumeCallback *epc); @@ -57,6 +58,8 @@ class Endpoint { IAudioEndpointVolume *endpointVolume = nullptr; IPropertyStore *properties; std::wstring friendlyName; + std::wstring descriptionName; + std::wstring deviceName; std::wstring endpointId; unsigned long endpointState; Roles endpointRoles = (Roles)0; @@ -84,8 +87,7 @@ class EndpointVolumeCallback : public IAudioEndpointVolumeCallback { class EndpointSituationCallback : public IMMNotificationClient { public: - //EndpointSituationCallback(IMMDeviceEnumerator *deviceEnumerator, std::vector playbackDevices, std::vector captureDevices); - //EndpointSituationCallback(std::vector* playbackDevices, std::vector* captureDevices); + EndpointSituationCallback(Overseer* os); ULONG AddRef(); ULONG Release(); HRESULT QueryInterface(REFIID riid, VOID **ppvInterface); @@ -97,9 +99,7 @@ class EndpointSituationCallback : public IMMNotificationClient { private: ULONG ref = 1; - //IMMDeviceEnumerator *deviceEnumerator; - //std::vector* playbackDevices; - //std::vector* captureDevices; + Overseer* os; }; class Overseer { @@ -107,9 +107,9 @@ class Overseer { public: Overseer(); void openControlPanel(); - //todo: restore/overseer std::vector getPlaybackEndpoints(); std::vector getCaptureEndpoints(); + void updateEndpointInfo(std::wstring endpointId); void reloadEndpoints(Flows flow); Endpoint* addEndpoint(std::wstring endpointId, /* out */ Flows* flow); diff --git a/src/cont/contclasses.cpp b/src/cont/contclasses.cpp index 5e0a4ee..c7a2cf0 100644 --- a/src/cont/contclasses.cpp +++ b/src/cont/contclasses.cpp @@ -294,6 +294,13 @@ void OverseerHandler::changeFrontDefaultsCallback(Roles role, std::wstring endpo this->changeFrontDefaults(role, endpointId); } +void OverseerHandler::updateFrontEndpointName(Endpoint* ep) { + //todo: reintroduce capture devices + for (auto eph : playbackEndpointHandlers) { + if (eph->getEndpoint() == ep) eph->getCallbackInfo()->updateName = true; + } +} + void OverseerHandler::reviseEndpointShowing(std::wstring endpointId, EndpointState state) { std::vector allHandlers; allHandlers.insert(allHandlers.end(), this->captureEndpointHandlers.begin(), this->captureEndpointHandlers.end()); diff --git a/src/cont/contclasses.h b/src/cont/contclasses.h index 2f70b56..f52d956 100644 --- a/src/cont/contclasses.h +++ b/src/cont/contclasses.h @@ -18,6 +18,7 @@ struct BackEndpointVolumeCallbackInfo { float mainVolume; size_t channels; std::vector channelVolumes; + bool updateName = false; }; class EndpointHandler { @@ -30,7 +31,7 @@ public: //EndpointVolumeCallback* getEndpointVolumeCallback(); //Endpoint* getEndpoint(); - //std::wstring epName; + //todo: name refactor BackEndpointVolumeCallbackInfo* getCallbackInfo(); uint32_t getChannelCount(); @@ -103,6 +104,7 @@ public: void setChangeFrontDefaultsFunction(std::function changeFrontDefaults); void changeFrontDefaultsCallback(Roles role, std::wstring endpointId); + void updateFrontEndpointName(Endpoint* ep); //void setReviseEndpointShowingFunction(std::function reviseEndpointShowing); void reviseEndpointShowing(std::wstring endpointId, EndpointState state); void setRemoveEndpointWidgetFunction(std::function removeEndpointWidget); diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index d1bdde2..9411a3e 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -420,6 +420,11 @@ EndpointWidget::EndpointWidget(EndpointHandler* eph, QWidget *parent, uint64_t i //if (memcmp(osh->callbackInfo[idx]->caller, osh->getGuid(), sizeof(NGuid)) == 0) return; CHECK IF THIS PROGRAM GENERATED THE FUNSIES IS NO LONGER IN USE FOR NOW. //todo: global + constexpr + ratio const float roundingFactor = 0.005; + if (eph->getCallbackInfo()->updateName) { + eph->getCallbackInfo()->updateName = false; + mainLabel->setText(QString::fromStdWString(eph->getName())); + mainLabel->setMinimumHeight(mainLabel->sizeHint().height()); + } mainSlider->blockSignals(true); muteButton->blockSignals(true); mainSlider->setValue((int)((eph->getCallbackInfo()->mainVolume + roundingFactor) * 100)); From 4756a00156cbd707b420a554ac26d4abef52efa3 Mon Sep 17 00:00:00 2001 From: Hane Date: Wed, 24 Apr 2024 02:03:07 +0200 Subject: [PATCH 24/78] wip: fixed wrong index for epw creation --- src/back/backlasses.cpp | 20 ++++++++++---------- src/global.h | 1 + src/qt/qtclasses.cpp | 24 ++++++++++++++++++++---- src/qt/qtclasses.h | 1 + 4 files changed, 32 insertions(+), 14 deletions(-) diff --git a/src/back/backlasses.cpp b/src/back/backlasses.cpp index 147a594..6c6c5d4 100644 --- a/src/back/backlasses.cpp +++ b/src/back/backlasses.cpp @@ -231,14 +231,14 @@ HRESULT EndpointSituationCallback::OnPropertyValueChanged(LPCWSTR pwstrDeviceId, Endpoint::Endpoint(IMMDevice* ep, uint64_t idx){ this->endpoint = ep; this->idx = idx; - + /* * It can't multiflag, it's that stupid. MS momento. * Only shows most relevant flag according to MS, i.e. 0110 sends 0010 */ //todo: preguntitas owindows dword no es uint32_t even tho mingw mingas if(FAILED(endpoint->GetState(&this->endpointState))) {exit(-2);}; - + if(this->endpointState == EndpointState::ENDPOINT_ACTIVE) { activateEndpointVolume(); } @@ -250,7 +250,7 @@ Endpoint::Endpoint(IMMDevice* ep, uint64_t idx){ // CLSCTX_ALL, NULL, (void**)&endpointPeakMeter))) { log_debugcpp("peakbros..."); } - //todo:: atexit into exit Gather ID + //todo: atexit into exit Gather ID LPWSTR tempString = nullptr; if (FAILED(endpoint->GetId(&tempString))) {exit(-1);}; endpointId = std::wstring(tempString); @@ -259,10 +259,10 @@ Endpoint::Endpoint(IMMDevice* ep, uint64_t idx){ endpoint->OpenPropertyStore(STGM_READ, &properties); this->updateName(); - this->setFlow(); if (this->flow == Flows::FLOW_PLAYBACK) { activateEndpointSessions(); + log_debugcpp("plays back"); } } @@ -277,7 +277,8 @@ void Endpoint::updateName() { store_name(PKEY_Device_FriendlyName, pv, friendlyName); store_name(PKEY_Device_DeviceDesc, pv, descriptionName); store_name(PKEY_DeviceInterface_FriendlyName, pv, deviceName); - #undef store_name + #undef store_name + log_wdebugcpp(L"Endpoint name: " + friendlyName); } void Endpoint::activateEndpointSessions() { @@ -511,6 +512,7 @@ void Endpoint::setFlow() { EDataFlow MSflow; HRESULT vafllar = flowGetter->GetDataFlow(&MSflow); this->flow = (MSflow == EDataFlow::eRender ? Flows::FLOW_PLAYBACK : Flows::FLOW_CAPTURE); + log_debugcpp("Endpoint flow: " + std::to_string(flow)); flowGetter->Release(); } @@ -584,13 +586,12 @@ void Overseer::reloadEndpoints(Flows flow) { if(numEndpoints == 0) { log_debugcpp("si"); }; /* - * Retrieving actual endpoints and storing them on their own class + * Retrieving actual endpoints and storing them on their own collection */ IMMDevice *temp; for (unsigned int i = 0; i < numEndpoints; i++){ if(deviceCollection->Item(i, &temp) != 0) { log_debugcpp("si"); }; Endpoint *endpoint = new Endpoint(temp, i); - if (flow == Flows::FLOW_PLAYBACK) this->playbackDevices.push_back(endpoint); else @@ -626,9 +627,8 @@ void Overseer::reloadEndpoints(Flows flow) { temp->GetId(&id); int comparison = CompareStringEx(LOCALE_NAME_USER_DEFAULT, 0, eptId.c_str(), -987, id, -987, NULL, NULL, 0); if (comparison - 2 == 0) { - log_wdebugcpp(L"ola defaul playback de " - + std::to_wstring(i) + L" es " + id); - playbackDevices.at(j)->assignRoles((Roles)(1 << i)); + log_wdebugcpp(L"ola defaul playback de " + std::to_wstring(i) + L" es " + id); + playbackDevices.at(j)->assignRoles((Roles)(1 << i)); } } } else { diff --git a/src/global.h b/src/global.h index 85f203a..f1ac09c 100644 --- a/src/global.h +++ b/src/global.h @@ -2,6 +2,7 @@ #include #include +#include #include #include #include diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 9411a3e..afd2f62 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -104,6 +104,11 @@ void MainWindow::compose() { if (!epw) continue; epw->calculateSize(windowWidth, screenHeight); log_debugcpp("epw loop"); + log_debugcpp("epw roles: " + std::to_string(epw->getEndpointHandler()->getRoles())); + //std::bitset content = + print_as_binary(8, uint8_t, (epw->getEndpointHandler()->getRoles())); + //log_debugcpp(content); + varToBitset(epw->getEndpointHandler()->getRoles()); } /* @@ -120,7 +125,11 @@ void MainWindow::compose() { log_debugcpp("windowHeight loop: " + std::to_string(windowHeight)); } windowHeight += mainMenuBar->height(); - log_debugcpp("windowHeight final value: " + std::to_string(windowHeight)); + log_debugcpp("windowHeight final value: " + std::to_string(windowHeight)); + + //Undoing scrolling + scrollArea->verticalScrollBar()->setValue(0); + /* * Establishing initial window size and position */ @@ -310,6 +319,7 @@ EndpointWidget::EndpointWidget(EndpointHandler* eph, QWidget *parent, uint64_t i row = 0; this->idx = idx; this->eph = eph; + //todo: sussy this->eph->setState(EndpointState::ENDPOINT_ACTIVE, idx); this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); @@ -573,8 +583,8 @@ void MainWindow::reorderEndpointWidgetCollection() { void EndpointWidget::calculateSize(uint64_t width, uint64_t height) { /* og 1080p 120% testing values */ - this->mainLabel->setMaximumWidth((int)width * 0.35 /* 1080p 120%*/); - this->mainLabel->setMinimumWidth((int)width * 0.35 /* 1080p 120%*/); + this->mainLabel->setMaximumWidth((int)width * 0.50 /* 1080p 120%*/); + this->mainLabel->setMinimumWidth((int)width * 0.50 /* 1080p 120%*/); if (cw) { this->cw->setMinimumSize(QSize(1, height * 0.06)); this->cw->setMaximumSize(QSize(QWIDGETSIZE_MAX, height * 0.06)); @@ -674,6 +684,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { //todo: ratio setWindowFlags(Qt::Window | Qt::MSWindowsFixedSizeDialogHint); setWindowFlags(Qt::FramelessWindowHint | Qt::ToolTip); + setAttribute(Qt::WA_TranslucentBackground); + setStyleSheet("background-color: rgba(255,182,193,90%);"); setWindowTitle(STRING_TITLE); connect(qApp, &QGuiApplication::applicationStateChanged, this, [=](Qt::ApplicationState state){ if(state == Qt::ApplicationState::ApplicationInactive) hide(); @@ -851,14 +863,18 @@ void MainWindow::trayIconActivated(QSystemTrayIcon::ActivationReason reason) { } void MainWindow::reloadEndpointWidgets() { - size_t i = 2; + size_t i = 0; ews.resize(2); //widgetLayout->addItem(&lastRowSpacer, i++, 0); + //todo: -log flag + //std::wofstream log("log.txt"); for (size_t epwIndex = 2; i < (osh->getPlaybackEndpointHandlers().size()); i++) { if (osh->getPlaybackEndpointHandlers().at(i)->getState() == EndpointState::ENDPOINT_ACTIVE){ log_debugcpp("EPWidget creation"); //osh->getPlaybackEndpointHandlers().at(i)->getCallbackInfo()->caller = osh->getGuid(); EndpointWidget *epw = new EndpointWidget(osh->getPlaybackEndpointHandlers().at(i), widget); + + log_wdebugcpp(L"epw name: " + epw->getEndpointHandler()->getName()); if ((epw->getEndpointHandler()->getRoles() == Roles::ROLE_ALL) || (epw->getEndpointHandler()->getRoles() & Roles::ROLE_MULTIMEDIA)) { ews[0] = epw; widgetLayout->addWidget(epw, 0, 0); epw->setIndex(0); } diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index a16dd28..ab7b7e3 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include From 46224d331c54a72b8c95ebcba474eab6ed3d7419 Mon Sep 17 00:00:00 2001 From: Hane Date: Thu, 25 Apr 2024 15:43:58 +0200 Subject: [PATCH 25/78] wip: file log --- src/debug.h | 35 ++++++++++++++++++++++++++++++++++- src/global.h | 4 +++- src/qt/qtclasses.cpp | 5 +++-- src/qtestmain.cpp | 18 ++++++++++++++++++ 4 files changed, 58 insertions(+), 4 deletions(-) diff --git a/src/debug.h b/src/debug.h index da6285c..0f993b1 100644 --- a/src/debug.h +++ b/src/debug.h @@ -2,6 +2,26 @@ #if defined (QT_DEBUG) || defined (DEBUG) || defined (_DEBUG) +#ifdef INIT_FILELOG + FILE* fileLog; + errno_t lfResult; + bool writable = false; + + bool initializeFileLogging() { + lfResult = fopen_s(&fileLog, "log.txt", "w"); + if (!lfResult) return true; + else return false; + } + +#else + extern errno_t lfResult; + extern FILE* fileLog; + extern bool writable; + extern bool initializeFileLogging(); +#endif + +#define initialize_file_log() initializeFileLogging() + template std::bitset varToBitset(T info) { std::bitset content(info); @@ -15,6 +35,7 @@ std::bitset varToBitset(T info) { #define log_wdebugcpp(str) do { \ std::wcout << "[DEBUG]" << "(" << __FILE__ << ":" << __LINE__ << "): " << str << std::endl; \ } while (0) + #else #include @@ -29,19 +50,31 @@ std::bitset varToBitset(T info) { #define log_wdebugcpp(str) do { \ OutputDebugStringW(std::wstring(L"[DEBUG] (" + std::wstring(WFILE) + L":" + std::to_wstring(__LINE__) + L"): " + std::wstring(str) +L"\n").c_str()); \ } while (0) + #endif +#define log_to_file(fmt, cnt) do { \ + if(writable) fprintf_s(fileLog, fmt, cnt); \ +} while (0) + #define print_as_binary(len, type, info) varToBitset(info) +#define close_file_log_buffer() do { \ + fclose(fileLog); \ +} while (0) + #else #define log_debugcpp(str) #define log_wdebugcpp(str) #define print_as_binary(len, type, info) +#define log_to_file(fmt, cnt) +#define initialize_file_log() false +#define close_file_log_buffer() #endif /* Here as a quick reference, in case smthn similar is needed again */ /* typedef void (EndpointWidget::*epwMuteFunc)(bool muted); */ -/* typedef void (EndpointWidget::*epwMainVolumeFunc)(float newValue); */ +/* Typedef void (EndpointWidget::*epwMainVolumeFunc)(float newValue); */ /* typedef void (EndpointWidget::*epwChannelVolumeFunc)(uint32_t channel, float newValue); */ /* typedef void (EndpointWidget::*epwToggleFrontFunc)(bool active); */ diff --git a/src/global.h b/src/global.h index f1ac09c..a96a114 100644 --- a/src/global.h +++ b/src/global.h @@ -1,8 +1,10 @@ #pragma once +#define __STDC_WANT_LIB_EXT1__ 1 +#include +#include #include #include -#include #include #include #include diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index afd2f62..7aee177 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -106,9 +106,9 @@ void MainWindow::compose() { log_debugcpp("epw loop"); log_debugcpp("epw roles: " + std::to_string(epw->getEndpointHandler()->getRoles())); //std::bitset content = - print_as_binary(8, uint8_t, (epw->getEndpointHandler()->getRoles())); + //print_as_binary(8, uint8_t, (epw->getEndpointHandler()->getRoles())); //log_debugcpp(content); - varToBitset(epw->getEndpointHandler()->getRoles()); + //varToBitset(epw->getEndpointHandler()->getRoles()); } /* @@ -585,6 +585,7 @@ void EndpointWidget::calculateSize(uint64_t width, uint64_t height) { /* og 1080p 120% testing values */ this->mainLabel->setMaximumWidth((int)width * 0.50 /* 1080p 120%*/); this->mainLabel->setMinimumWidth((int)width * 0.50 /* 1080p 120%*/); + if (cw) { this->cw->setMinimumSize(QSize(1, height * 0.06)); this->cw->setMaximumSize(QSize(QWIDGETSIZE_MAX, height * 0.06)); diff --git a/src/qtestmain.cpp b/src/qtestmain.cpp index 19f3491..5a11676 100644 --- a/src/qtestmain.cpp +++ b/src/qtestmain.cpp @@ -6,6 +6,7 @@ #include #include //#include "contclasses.h" +#define INIT_FILELOG #include "qtclasses.h" #include "global.h" @@ -31,10 +32,27 @@ QLocalServer* startSingleInstanceServer(QString appName) { return server; } +void closeDebugFileLog() { + close_file_log_buffer(); +} + +/* set_terminate + * void closeDebugFileLog2() { + * close_file_log_buffer(); + * abort(); + * } + */ + int main (int argc, char* argv[]) { //QApplication::setStyle("windowsvista"); //Check if running //https://stackoverflow.com/questions/48060989/qt-show-application-if-currently-running + writable = initialize_file_log(); + log_to_file("%d hola", 3); + //int bro = fprintf_s(fileLog, "hola\n"); + //bro = fprintf(fileLog, "hola"); + atexit(closeDebugFileLog); + //std::set_terminate(closeDebugFileLog2); if (!isSingleInstanceRunning("Mixer")) startSingleInstanceServer("Mixer"); else exit(0); From 2115cdf50841d2320df737bf4bc5b0147ddb8b08 Mon Sep 17 00:00:00 2001 From: Hane Date: Thu, 25 Apr 2024 20:21:21 +0200 Subject: [PATCH 26/78] wip: detected width bug (channels) --- src/debug.h | 16 +++++++---- src/global.h | 3 ++- src/qt/qtclasses.cpp | 63 +++++++++++++++++++++++++++++++------------- src/qt/qtclasses.h | 7 +++-- src/qtestmain.cpp | 5 +--- 5 files changed, 63 insertions(+), 31 deletions(-) diff --git a/src/debug.h b/src/debug.h index 0f993b1..2878c37 100644 --- a/src/debug.h +++ b/src/debug.h @@ -3,17 +3,19 @@ #if defined (QT_DEBUG) || defined (DEBUG) || defined (_DEBUG) #ifdef INIT_FILELOG + std::wstring_convert, wchar_t> converter; FILE* fileLog; errno_t lfResult; bool writable = false; - bool initializeFileLogging() { + void inline initializeFileLogging() { lfResult = fopen_s(&fileLog, "log.txt", "w"); - if (!lfResult) return true; - else return false; + if (!lfResult) writable = true; + else writable = false; } #else + extern std::wstring_convert, wchar_t> converter; extern errno_t lfResult; extern FILE* fileLog; extern bool writable; @@ -53,10 +55,13 @@ std::bitset varToBitset(T info) { #endif -#define log_to_file(fmt, cnt) do { \ + +#define log_to_file_simple(fmt) log_to_file(fmt, "") +#define log_to_file(fmt, cnt...) do { \ if(writable) fprintf_s(fileLog, fmt, cnt); \ } while (0) + #define print_as_binary(len, type, info) varToBitset(info) #define close_file_log_buffer() do { \ @@ -67,7 +72,8 @@ std::bitset varToBitset(T info) { #define log_debugcpp(str) #define log_wdebugcpp(str) #define print_as_binary(len, type, info) -#define log_to_file(fmt, cnt) +#define log_to_file_simple(fmt) +#define log_to_file(fmt, cnt...) #define initialize_file_log() false #define close_file_log_buffer() #endif diff --git a/src/global.h b/src/global.h index a96a114..b8b9bcb 100644 --- a/src/global.h +++ b/src/global.h @@ -3,11 +3,12 @@ #define __STDC_WANT_LIB_EXT1__ 1 #include #include +#include +#include #include #include #include #include -#include #include #include "debug.h" diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 7aee177..b7a90ba 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -78,10 +78,12 @@ QRect MainWindow::setSizePosition(QScreen* screen, int width, int height) { } void MainWindow::compose() { + //todo: invalidate layout when adding sessions with window open //We need dynamically added child widgets to expand so that we know their height /* * Setting correct widget widths and heights */ + log_to_file_simple("[Compose]\n"); screen = this->getCurrentScreen(); log_debugcpp("Screen: " + screen->model().toStdString() + " " + screen->name().toStdString()); @@ -160,7 +162,9 @@ SessionWidget::SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent) widgetLayout = new QHBoxLayout(this); //widgetLayout->setSizeConstraint(QLayout::SetFixedSize); widgetLayout->setSizeConstraint(QLayout::SetMinAndMaxSize); - widgetLayout->setContentsMargins(0, 10, 0, 10); + int left = 0, top = 0, right = 0, bottom = 0; + widgetLayout->getContentsMargins(&left, &top, &right, &bottom); + widgetLayout->setContentsMargins(0, top, 0, bottom); muteButton = new QCheckBox(this); mainLabel = new QLabel(QString::fromStdWString(sh->getName()), this); @@ -236,7 +240,19 @@ void SessionWidget::calculateSize(uint64_t width, uint64_t height) { this->muteButton->setMinimumWidth((int)(width * 0.10) /*1/32th 1080p*/); //this->muteButton->setMinimumWidth((int)(width * 0.10) /*1/16 1080p*/); this->mainSlider->setMinimumWidth((int)(width * 0.30) /*1/16 1080p*/); - widthSpacer->changeSize((int)width * 0.20, 1, QSizePolicy::Expanding, QSizePolicy::Minimum /*200*/); + widthSpacer->changeSize((int)(width * 0.20), 1, QSizePolicy::Expanding, QSizePolicy::Minimum /*200*/); + + log_to_file("\t[Session %s sizes]\n", converter.to_bytes(this->getName()).c_str()); + log_to_file("\tMain label Maximum size: %d, %d \n", mainLabel->maximumWidth(), mainLabel->maximumHeight()); + log_to_file("\tMain label Minimum size: %d, %d \n", mainLabel->minimumWidth(), mainLabel->minimumHeight()); + log_to_file("\tMute btn Maximum width: %d \n", muteButton->maximumWidth()); + log_to_file("\tMute btn Minimum width: %d \n", muteButton->minimumWidth()); + log_to_file("\tSlider Minimum width: %d \n", mainSlider->minimumWidth()); + log_to_file("\tSpacer Minimum width: %d \n\n", widthSpacer->minimumSize().width()); +} + +std::wstring SessionWidget::getName() { + return sh->getName(); } void SessionWidget::updateMute(int checked){ @@ -257,6 +273,7 @@ SessionWidget::~SessionWidget() { ChannelWidget::ChannelWidget(uint32_t channelCount, EndpointHandler* eph, QWidget *parent) : QWidget(parent){ this->eph = eph; this->channelCount = channelCount; + this->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::MinimumExpanding); widgetLayout = new QGridLayout(this); float volume = 100; int left = 0, top = 0, right = 0, bottom = 0; @@ -267,31 +284,30 @@ ChannelWidget::ChannelWidget(uint32_t channelCount, EndpointHandler* eph, QWidge * Channel sliders setup */ //uint32_t epChannelCount = eph->getChannelCount(); - for(uint32_t i = 0; i < channelCount && channelCount > 1; i++){ + for(uint64_t channel = 0, col = 0, row = 0; channel < channelCount && channelCount > 1; channel++){ QSlider* tmp = new QSlider(Qt::Horizontal); QLabel* tmpLb = new QLabel(""); - this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); - //tmp->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); tmp->setTickInterval(5); tmp->setSingleStep(1); tmp->setRange(0,100); - volume = eph->getVolume(i) * 100; + volume = eph->getVolume(channel) * 100; tmp->setValue((int) volume); tmpLb->setText(QString::number(volume)); //tmpLb->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); - tmp->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); - tmpLb->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); + tmp->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + tmpLb->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); this->channelSliders.push_back(tmp); this->channelLabels.push_back(tmpLb); - widgetLayout->addWidget(tmp, 0, i); - widgetLayout->addWidget(tmpLb, 1, i); + widgetLayout->addWidget(tmp, row , col); + widgetLayout->addWidget(tmpLb, row + 1, col++); + if(channel % 2 != 0) { row += 2; col = 0; } //TODO: check if there's a need to prevent deadlocks; probably this will eventually turn into its own func //this causes channel bar desync when back -> front. blocksignals below fix it. huh. - connect(tmp, &QSlider::valueChanged, [this, i](int newValue){ - this->eph->setVolume(osh->getGuid(), i, newValue); - this->channelLabels.at(i)->setText(QString::number(newValue)); + connect(tmp, &QSlider::valueChanged, [this, channel](int newValue){ + this->eph->setVolume(osh->getGuid(), channel, newValue); + this->channelLabels.at(channel)->setText(QString::number(newValue)); }); } this->setLayout(widgetLayout); @@ -314,6 +330,10 @@ void ChannelWidget::updateChannel(int channel) { this->channelSliders.at(channel)->blockSignals(false); } +uint32_t ChannelWidget::getChannelCount() const { + return channelCount; +} + EndpointWidget::EndpointWidget(EndpointHandler* eph, QWidget *parent, uint64_t idx) : QWidget(parent) { //todo: based on qgridlayout, name+mute should be its own widget, same with channels row = 0; @@ -392,7 +412,7 @@ EndpointWidget::EndpointWidget(EndpointHandler* eph, QWidget *parent, uint64_t i uint32_t epChannelCount = eph->getChannelCount(); if(epChannelCount > 1) { - cw = new ChannelWidget(epChannelCount, eph, this); + cw = new ChannelWidget(epChannelCount, eph, nullptr); //cw->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); widgetLayout->addWidget(cw, row++, 0, 1, 4 /*colmax*/, Qt::AlignTop); } @@ -583,12 +603,17 @@ void MainWindow::reorderEndpointWidgetCollection() { void EndpointWidget::calculateSize(uint64_t width, uint64_t height) { /* og 1080p 120% testing values */ - this->mainLabel->setMaximumWidth((int)width * 0.50 /* 1080p 120%*/); - this->mainLabel->setMinimumWidth((int)width * 0.50 /* 1080p 120%*/); - + log_to_file("[EndpointWidget %s sizes]\n", converter.to_bytes(this->getEndpointHandler()->getName()).c_str()); + log_to_file("Params: {Width: %u Height: %u}\n", width, height); + this->mainLabel->setMaximumWidth((int)(width * 0.50) /* 1080p 120%*/); + this->mainLabel->setMinimumWidth((int)(width * 0.50) /* 1080p 120%*/); + log_to_file("Main label width: %d \n", this->mainLabel->maximumWidth()); + if (cw) { - this->cw->setMinimumSize(QSize(1, height * 0.06)); - this->cw->setMaximumSize(QSize(QWIDGETSIZE_MAX, height * 0.06)); + this->cw->setMinimumSize(QSize(1, (height * 0.06) * (int)((cw->getChannelCount() / 2) + 0.5))); + this->cw->setMaximumSize(QSize(QWIDGETSIZE_MAX, (height * 0.06) * (int)((cw->getChannelCount() / 2) + 0.5))); + log_to_file("Channels Maximum size: %d, %d \n", cw->maximumWidth(), cw->maximumHeight()); + log_to_file("Channels Minimum size: %d, %d \n", cw->minimumWidth(), cw->minimumHeight()); } for (auto sw : sessionWidgets){ if (sw) sw->calculateSize(width, height); diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index ab7b7e3..524c7bb 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -87,7 +87,9 @@ Q_OBJECT public: SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent /* = nullptr */); ~SessionWidget(); - void calculateSize(uint64_t width, uint64_t height); + void calculateSize(uint64_t width, uint64_t height); + std::wstring getName(); + public slots: void updateMainVolume(int newValue); void updateMute(int checked); @@ -111,7 +113,8 @@ public: //QSize minimumSizeHint() const override; //void setMinimum(QSize minimum); void updateChannel(int channel); - + uint32_t getChannelCount() const; + private: const double roundingFactor = 0.005; EndpointHandler* eph; diff --git a/src/qtestmain.cpp b/src/qtestmain.cpp index 5a11676..7f953c0 100644 --- a/src/qtestmain.cpp +++ b/src/qtestmain.cpp @@ -47,10 +47,7 @@ int main (int argc, char* argv[]) { //QApplication::setStyle("windowsvista"); //Check if running //https://stackoverflow.com/questions/48060989/qt-show-application-if-currently-running - writable = initialize_file_log(); - log_to_file("%d hola", 3); - //int bro = fprintf_s(fileLog, "hola\n"); - //bro = fprintf(fileLog, "hola"); + initialize_file_log(); atexit(closeDebugFileLog); //std::set_terminate(closeDebugFileLog2); if (!isSingleInstanceRunning("Mixer")) From cdadee58fcea6187a4090578ffe3e2ca96a6fc4c Mon Sep 17 00:00:00 2001 From: Hane Date: Sat, 27 Apr 2024 17:51:29 +0200 Subject: [PATCH 27/78] minor code cleanup --- src/debug.h | 25 +++++++++++-------------- src/qt/qtclasses.cpp | 9 ++++----- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/debug.h b/src/debug.h index 2878c37..a2c36c9 100644 --- a/src/debug.h +++ b/src/debug.h @@ -15,7 +15,7 @@ } #else - extern std::wstring_convert, wchar_t> converter; + extern std::wstring_convert, wchar_t> converter; extern errno_t lfResult; extern FILE* fileLog; extern bool writable; @@ -24,11 +24,14 @@ #define initialize_file_log() initializeFileLogging() -template -std::bitset varToBitset(T info) { - std::bitset content(info); +template +std::bitset varToBitset(T info) { + std::bitset content(info); return content; -} +} + +#define print_as_binary(info) varToBitset(info) + #ifndef _WIN32 #define log_debugcpp(str) do { \ std::cout << "[DEBUG]" << "(" << __FILE__ << ":" << __LINE__ << "): " << str << std::endl; \ @@ -55,24 +58,18 @@ std::bitset varToBitset(T info) { #endif - -#define log_to_file_simple(fmt) log_to_file(fmt, "") -#define log_to_file(fmt, cnt...) do { \ - if(writable) fprintf_s(fileLog, fmt, cnt); \ +#define log_to_file(fmt, cnt...) do { \ + if(writable) fprintf_s(fileLog, fmt,##cnt); \ } while (0) - -#define print_as_binary(len, type, info) varToBitset(info) - #define close_file_log_buffer() do { \ - fclose(fileLog); \ + if(writable) { fflush(fileLog); fclose(fileLog); } \ } while (0) #else #define log_debugcpp(str) #define log_wdebugcpp(str) #define print_as_binary(len, type, info) -#define log_to_file_simple(fmt) #define log_to_file(fmt, cnt...) #define initialize_file_log() false #define close_file_log_buffer() diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index b7a90ba..b3595f7 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -83,7 +83,7 @@ void MainWindow::compose() { /* * Setting correct widget widths and heights */ - log_to_file_simple("[Compose]\n"); + log_to_file("[Compose]\n"); screen = this->getCurrentScreen(); log_debugcpp("Screen: " + screen->model().toStdString() + " " + screen->name().toStdString()); @@ -106,10 +106,9 @@ void MainWindow::compose() { if (!epw) continue; epw->calculateSize(windowWidth, screenHeight); log_debugcpp("epw loop"); - log_debugcpp("epw roles: " + std::to_string(epw->getEndpointHandler()->getRoles())); + log_debugcpp("epw roles: " + print_as_binary((epw->getEndpointHandler()->getRoles())).to_string()); //std::bitset content = - //print_as_binary(8, uint8_t, (epw->getEndpointHandler()->getRoles())); - //log_debugcpp(content); + //content); //varToBitset(epw->getEndpointHandler()->getRoles()); } @@ -726,7 +725,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { QEvent::registerEventType(CustomQEvent::SessionWidgetObsolete); QEvent::registerEventType(CustomQEvent::SessionWidgetCreated); QEvent::registerEventType(CustomQEvent::RecomposeMainWindow); -; + /* This spacer provides proper spacing when window vertically > widgets. */ lastRowSpacer = new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding); ewsUpdateTimer = new QTimer(this); From 20a82b42d4ebeab25d43af1e7e97906c578a2f97 Mon Sep 17 00:00:00 2001 From: Hane Date: Sun, 28 Apr 2024 18:26:44 +0200 Subject: [PATCH 28/78] wip: meter bar --- qtest.pro | 6 +++--- src/back/backlasses.cpp | 30 +++++++++++------------------- src/back/backlasses.h | 6 ++++-- src/back/msinclude.h | 31 +++++++++++++++++++++++++++++++ src/cont/contclasses.h | 5 +---- src/debug.h | 4 ++-- src/qt/qtclasses.cpp | 31 +++++++++++++++++++++++++++++-- src/qt/qtclasses.h | 17 ++++++++++++++--- src/qtestmain.cpp | 10 +++++++++- 9 files changed, 104 insertions(+), 36 deletions(-) diff --git a/qtest.pro b/qtest.pro index 370925d..707ff2d 100644 --- a/qtest.pro +++ b/qtest.pro @@ -6,9 +6,9 @@ DEFINES += DEBUG QT_LOGGING_TO_CONSOLE=1 WIN32_LEAN_AND_MEAN CONFIG += debug QT += widgets network -INCLUDEPATH += "$$PWD\src" "$$PWD\src\qt" "$$PWD\src\back" "$$PWD\src\cont" -DESTPATH += "$$PWD\src" "$$PWD\src\qt" "$$PWD\src\back" "$$PWD\src\cont" -VPATH += "$$PWD\src" "$$PWD\src\qt" "$$PWD\src\back" "$$PWD\src\cont" +INCLUDEPATH += "$$PWD\src" "$$PWD\src\qt" "$$PWD\src\back" "$$PWD\src\back\reimpl" "$$PWD\src\cont" +DESTPATH += "$$PWD\src" "$$PWD\src\qt" "$$PWD\src\back" "$$PWD\src\back\reimpl" "$$PWD\src\cont" +VPATH += "$$PWD\src" "$$PWD\src\qt" "$$PWD\src\back" "$$PWD\src\back\reimpl" "$$PWD\src\cont" SOURCES += qtestmain.cpp qtclasses.cpp backlasses.cpp backsessionclasses.cpp contclasses.cpp contsessionclasses.cpp HEADERS += qtclasses.h backlasses.h backsessionclasses.h contclasses.h contsessionclasses.h global.h debug.h backfuncs.h ipolicyconfig.h msinclude.h diff --git a/src/back/backlasses.cpp b/src/back/backlasses.cpp index 6c6c5d4..8f56fa2 100644 --- a/src/back/backlasses.cpp +++ b/src/back/backlasses.cpp @@ -241,14 +241,18 @@ Endpoint::Endpoint(IMMDevice* ep, uint64_t idx){ if(this->endpointState == EndpointState::ENDPOINT_ACTIVE) { activateEndpointVolume(); + + //if(FAILED(endpoint->Activate(__uuidof(IAudioClient), + // CLSCTX_ALL, NULL, (void**)&audioClient))) { log_debugcpp("audioclntbros..."); } + //audioClient->GetDevicePeriod(&defTime, &minTime); + + //todo: check header + //if(FAILED(endpoint->Activate(__uuidof(IAudioMeterInformation), + // CLSCTX_ALL, NULL, (void**)&endpointPeakMeter))) { log_debugcpp("peakbros..."); } + //endpointPeakMeter->GetPeakValue(&test); } reloadEndpointChannels(); - - //todo: check header - // if(FAILED(endpoint->Activate(__uuidof(IAudioMeterInformation), - // CLSCTX_ALL, NULL, (void**)&endpointPeakMeter))) { log_debugcpp("peakbros..."); } - //todo: atexit into exit Gather ID LPWSTR tempString = nullptr; @@ -494,20 +498,8 @@ void Endpoint::removeRoles(Roles role){ void Endpoint::setFlow() { IMMEndpoint* flowGetter; - //this should be as simple as writing IID_IMMEndpoint, but it just won't find the macro, so I copied it. Sad. - GUID manual; - manual.Data1 = 0x1be09788; - manual.Data2 = 0x6894; - manual.Data3 = 0x4089; - manual.Data4[0] = 0x85; - manual.Data4[1] = 0x86; - manual.Data4[2] = 0x9a; - manual.Data4[3] = 0x2a; - manual.Data4[4] = 0x6c; - manual.Data4[5] = 0x26; - manual.Data4[6] = 0x5a; - manual.Data4[7] = 0xc5; - if(FAILED(this->endpoint->QueryInterface((const _GUID)manual, (void**)&flowGetter))) + //this should be as simple as writing IID_IMMEndpoint, but it just won't find the macro, so I reimpl it. Sad. + if(FAILED(this->endpoint->QueryInterface(__uuidof(IMMEndpoint), (void**)&flowGetter))) { log_debugcpp("no flow..."); } EDataFlow MSflow; HRESULT vafllar = flowGetter->GetDataFlow(&MSflow); diff --git a/src/back/backlasses.h b/src/back/backlasses.h index 9e3aba2..207cba7 100644 --- a/src/back/backlasses.h +++ b/src/back/backlasses.h @@ -52,7 +52,9 @@ class Endpoint { std::vector endpointSessions; uint32_t channelCount = 0; - IMMDevice* endpoint; + IMMDevice *endpoint; + IAudioClient *audioClient; + int64_t defTime, minTime; IAudioSessionManager2 *sessionManager; Flows flow; IAudioEndpointVolume *endpointVolume = nullptr; @@ -65,7 +67,7 @@ class Endpoint { Roles endpointRoles = (Roles)0; uint64_t idx; //Not implemented in llvm-mingw. Sad! - //IAudioMeterInformation *endpointPeakMeter = nullptr; + IAudioMeterInformation *endpointPeakMeter = nullptr; }; diff --git a/src/back/msinclude.h b/src/back/msinclude.h index de0593f..9911165 100644 --- a/src/back/msinclude.h +++ b/src/back/msinclude.h @@ -26,3 +26,34 @@ #include "ipolicyconfig.h" #include #include + +#include "audiometerinfo.h" + +// IAudioMeterInformation +/* GUID manual; */ +/* manual.Data1 = 0xc02216f6; */ +/* manual.Data2 = 0x8c67; */ +/* manual.Data3 = 0x4b5b; */ +/* manual.Data4[0] = 0x9d; */ +/* manual.Data4[1] = 0x00; */ +/* manual.Data4[2] = 0xd0; */ +/* manual.Data4[3] = 0x08; */ +/* manual.Data4[4] = 0xe7; */ +/* manual.Data4[5] = 0x3e; */ +/* manual.Data4[6] = 0x00; */ +/* manual.Data4[7] = 0x64; */ +//if(FAILED(endpoint->Activate((const _GUID) manual, + +//IMMEndpoint +/* GUID manual; */ +/* manual.Data1 = 0x1be09788; */ +/* manual.Data2 = 0x6894; */ +/* manual.Data3 = 0x4089; */ +/* manual.Data4[0] = 0x85; */ +/* manual.Data4[1] = 0x86; */ +/* manual.Data4[2] = 0x9a; */ +/* manual.Data4[3] = 0x2a; */ +/* manual.Data4[4] = 0x6c; */ +/* manual.Data4[5] = 0x26; */ +/* manual.Data4[6] = 0x5a; */ +/* manual.Data4[7] = 0xc5; */ diff --git a/src/cont/contclasses.h b/src/cont/contclasses.h index f52d956..4a5f543 100644 --- a/src/cont/contclasses.h +++ b/src/cont/contclasses.h @@ -27,10 +27,7 @@ public: EndpointHandler(uint64_t idx, Flows flow); void setBackEndpointVolumeCallbackInfoContent(uint8_t state); - //these two, currently unused. If I use them, I should feel bad. - //EndpointVolumeCallback* getEndpointVolumeCallback(); - //Endpoint* getEndpoint(); - + //todo: replace all getEndpointHandler() //todo: name refactor BackEndpointVolumeCallbackInfo* getCallbackInfo(); uint32_t getChannelCount(); diff --git a/src/debug.h b/src/debug.h index a2c36c9..176825a 100644 --- a/src/debug.h +++ b/src/debug.h @@ -30,7 +30,7 @@ std::bitset varToBitset(T info) { return content; } -#define print_as_binary(info) varToBitset(info) +#define print_as_binary(info) varToBitset(info).to_string() #ifndef _WIN32 #define log_debugcpp(str) do { \ @@ -69,7 +69,7 @@ std::bitset varToBitset(T info) { #else #define log_debugcpp(str) #define log_wdebugcpp(str) -#define print_as_binary(len, type, info) +#define print_as_binary(info) #define log_to_file(fmt, cnt...) #define initialize_file_log() false #define close_file_log_buffer() diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index b3595f7..4dea7ac 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -5,6 +5,31 @@ CustomWidgetEvent::CustomWidgetEvent(QEvent::Type type, T payload) : QEvent(t this->payload = payload; } +void MeterSlider::paintEvent(QPaintEvent *event) { + //Q_D(QSlider); + /* + * QStylePainter p(this); + * QStyleOptionSlider opt; + * initStyleOption(&opt); + * + * opt.subControls = QStyle::SC_SliderGroove | QStyle::SC_SliderHandle; + * //if (d->tickPosition != NoTicks) + * // opt.subControls |= QStyle::SC_SliderTickmarks; + * + * //p.drawComplexControl(QStyle::CC_Slider, opt); + */ + QSlider::paintEvent(event); + + QStyle *style = QApplication::style(); + int lol = style->pixelMetric(QStyle::PM_SliderSpaceAvailable); + QPainter painter(this); + painter.setPen(Qt::blue); + painter.setOpacity(1.0); + painter.setClipping(false); + painter.setCompositionMode(QPainter::CompositionMode::CompositionMode_Source); + painter.fillRect(0, (this->height() / 2) - 3, this->width(), 4, Qt::black); +} + void ExtendedCheckBox::customEvent(QEvent* ev) { //QEvent::Type tipo = ev->type(); if (ev->type() == (QEvent::Type)CustomQEvent::EndpointDefaultChange) { @@ -106,7 +131,7 @@ void MainWindow::compose() { if (!epw) continue; epw->calculateSize(windowWidth, screenHeight); log_debugcpp("epw loop"); - log_debugcpp("epw roles: " + print_as_binary((epw->getEndpointHandler()->getRoles())).to_string()); + log_debugcpp("epw roles: " + print_as_binary((epw->getEndpointHandler()->getRoles()))); //std::bitset content = //content); //varToBitset(epw->getEndpointHandler()->getRoles()); @@ -360,7 +385,7 @@ EndpointWidget::EndpointWidget(EndpointHandler* eph, QWidget *parent, uint64_t i */ muteButton = new QCheckBox(this); mainLabel = new QLabel(QString::fromStdWString(eph->getName()), this); - mainSlider = new QSlider(Qt::Horizontal, this); + mainSlider = new MeterSlider(Qt::Horizontal, this); mainVolumeLabel = new QLabel(this); if (this->eph->getState() != EndpointState::ENDPOINT_ACTIVE) { @@ -749,6 +774,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { scrollArea->setWidgetResizable(true); scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + //scrollArea->verticalScrollBar()->setSingleStep(1); + scrollArea->setStyleSheet("QScrollBar:vertical { width: 4px; }"); //scrollArea->setMinimumWidth(500); setCentralWidget(scrollArea); diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index 524c7bb..b39635d 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -26,7 +26,9 @@ #include #include #include - +#include +#include +#include //#include /* * #else @@ -64,10 +66,19 @@ class CustomWidgetEvent : public QEvent { public: CustomWidgetEvent(QEvent::Type type, T payload); T payload; - }; //Q_DECLARE_METATYPE(EndpointWidgetEvent) +class MeterSlider : public QSlider { + Q_OBJECT +protected: + void paintEvent(QPaintEvent *event) override; + +public: + using QSlider::QSlider; + +}; + class ExtendedCheckBox : public QCheckBox { Q_OBJECT protected: @@ -166,7 +177,7 @@ private: QCheckBox *muteButton = nullptr; QLabel *mainLabel = nullptr; QLabel *mainVolumeLabel = nullptr; - QSlider *mainSlider = nullptr; + MeterSlider *mainSlider = nullptr; std::vector channelSliders; std::vector channelLabels; QGridLayout *widgetLayout = nullptr; diff --git a/src/qtestmain.cpp b/src/qtestmain.cpp index 7f953c0..1d421cc 100644 --- a/src/qtestmain.cpp +++ b/src/qtestmain.cpp @@ -5,6 +5,8 @@ #include #include #include +#include +#include //#include "contclasses.h" #define INIT_FILELOG #include "qtclasses.h" @@ -44,7 +46,13 @@ void closeDebugFileLog() { */ int main (int argc, char* argv[]) { - //QApplication::setStyle("windowsvista"); + /* + * QStringList styles = QStyleFactory::keys(); + * for(QString a : styles) { + * log_debugcpp(a.toStdString()); + * } + */ + //QApplication::setStyle("Fusion"); //Check if running //https://stackoverflow.com/questions/48060989/qt-show-application-if-currently-running initialize_file_log(); From 6bda4702df12e7e74a6addab34238154c6d575c6 Mon Sep 17 00:00:00 2001 From: Hane Date: Tue, 30 Apr 2024 21:30:58 +0200 Subject: [PATCH 29/78] wip: endpoint meter --- src/back/backlasses.cpp | 17 +++++++++++++---- src/back/backlasses.h | 3 ++- src/cont/contclasses.cpp | 4 ++++ src/cont/contclasses.h | 1 + src/qt/qtclasses.cpp | 16 +++++++++++++--- src/qt/qtclasses.h | 9 +++++++-- src/qtestmain.cpp | 2 +- 7 files changed, 41 insertions(+), 11 deletions(-) diff --git a/src/back/backlasses.cpp b/src/back/backlasses.cpp index 8f56fa2..ced20f3 100644 --- a/src/back/backlasses.cpp +++ b/src/back/backlasses.cpp @@ -246,10 +246,6 @@ Endpoint::Endpoint(IMMDevice* ep, uint64_t idx){ // CLSCTX_ALL, NULL, (void**)&audioClient))) { log_debugcpp("audioclntbros..."); } //audioClient->GetDevicePeriod(&defTime, &minTime); - //todo: check header - //if(FAILED(endpoint->Activate(__uuidof(IAudioMeterInformation), - // CLSCTX_ALL, NULL, (void**)&endpointPeakMeter))) { log_debugcpp("peakbros..."); } - //endpointPeakMeter->GetPeakValue(&test); } reloadEndpointChannels(); @@ -323,6 +319,12 @@ void Endpoint::activateEndpointVolume() { if (this->endpointVolume == nullptr){ HRESULT result = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); endpoint->Activate(IID_IAudioEndpointVolume, CLSCTX_ALL, NULL, (void**)&this->endpointVolume); + + //todo: check header + if(FAILED(endpoint->Activate(__uuidof(IAudioMeterInformation), + CLSCTX_ALL, NULL, (void**)&endpointPeakMeter))) { log_debugcpp("peakbros..."); } + // + //if (endpointVolume == nullptr) { //why they returning 0 after dealing with the error jfc CO_E_NOTINITIALIZED) { //CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE); //extraThread = true; @@ -356,6 +358,13 @@ std::wstring Endpoint::getId(){ return endpointId; } +float Endpoint::getPeakVolume() { + float peakVol; + if(endpointPeakMeter) endpointPeakMeter->GetPeakValue(&peakVol); + else return 0; + return peakVol; +} + float Endpoint::getVolume(int channel){ float volume; if (channel == AudioChannel::CHANNEL_MAIN) { diff --git a/src/back/backlasses.h b/src/back/backlasses.h index 207cba7..f2c0199 100644 --- a/src/back/backlasses.h +++ b/src/back/backlasses.h @@ -30,6 +30,7 @@ class Endpoint { void removeRoles(Roles role); void setFlow(); Flows getFlow(); + float getPeakVolume(); std::wstring getId(); std::wstring getName(); void updateName(); @@ -66,7 +67,7 @@ class Endpoint { unsigned long endpointState; Roles endpointRoles = (Roles)0; uint64_t idx; - //Not implemented in llvm-mingw. Sad! + //Not implemented in llvm-mingw. Sad! todo: mingw patch IAudioMeterInformation *endpointPeakMeter = nullptr; }; diff --git a/src/cont/contclasses.cpp b/src/cont/contclasses.cpp index c7a2cf0..f46571c 100644 --- a/src/cont/contclasses.cpp +++ b/src/cont/contclasses.cpp @@ -133,6 +133,10 @@ void EndpointHandler::setState(uint8_t state, uint64_t index){ this->setBackEndpointVolumeCallbackInfoContent(state); } +float EndpointHandler::getPeakVolume() { + return ep->getPeakVolume(); +} + uint8_t EndpointHandler::getRoles(){ return ep->getRoles(); } diff --git a/src/cont/contclasses.h b/src/cont/contclasses.h index 4a5f543..b0cea92 100644 --- a/src/cont/contclasses.h +++ b/src/cont/contclasses.h @@ -58,6 +58,7 @@ public: void setState(uint8_t state); void setState(uint8_t state, uint64_t idx); + float getPeakVolume(); /* sessions */ size_t getSessionCount(); std::vector getSessionHandlers(); diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 4dea7ac..c6faba8 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -5,6 +5,10 @@ CustomWidgetEvent::CustomWidgetEvent(QEvent::Type type, T payload) : QEvent(t this->payload = payload; } +void MeterSlider::setPeakValue(float peakValue) { + this->peakValue = peakValue; +} + void MeterSlider::paintEvent(QPaintEvent *event) { //Q_D(QSlider); /* @@ -23,11 +27,15 @@ void MeterSlider::paintEvent(QPaintEvent *event) { QStyle *style = QApplication::style(); int lol = style->pixelMetric(QStyle::PM_SliderSpaceAvailable); QPainter painter(this); - painter.setPen(Qt::blue); + //painter.setPen(Qt::blue); painter.setOpacity(1.0); painter.setClipping(false); painter.setCompositionMode(QPainter::CompositionMode::CompositionMode_Source); - painter.fillRect(0, (this->height() / 2) - 3, this->width(), 4, Qt::black); + float peakLength = (this->width() * (this->peakValue)); + //const qreal dpr = painter->device()->devicePixelRatio(); + QStyleOptionSlider slider = QStyleOptionSlider(); + QRect test = QApplication::style()->subControlRect(QStyle::CC_Slider, (QStyleOptionComplex*)&slider, QStyle::SC_SliderHandle); + painter.fillRect(0, (this->height() / 2) - 3, (this->width() * (this->peakValue)), 4, Qt::green); } void ExtendedCheckBox::customEvent(QEvent* ev) { @@ -482,6 +490,8 @@ EndpointWidget::EndpointWidget(EndpointHandler* eph, QWidget *parent, uint64_t i mainSlider->blockSignals(true); muteButton->blockSignals(true); mainSlider->setValue((int)((eph->getCallbackInfo()->mainVolume + roundingFactor) * 100)); + mainSlider->setPeakValue(eph->getPeakVolume()); + mainSlider->update(); mainVolumeLabel->setText(QString::number(mainSlider->value())); muteButton->setCheckState((eph->getCallbackInfo()->muted == false ? Qt::Unchecked : Qt::Checked)); muteButton->setText(eph->getCallbackInfo()->muted ? STRING_UNMUTE : STRING_MUTE); @@ -496,7 +506,7 @@ EndpointWidget::EndpointWidget(EndpointHandler* eph, QWidget *parent, uint64_t i mainSlider->blockSignals(false); muteButton->blockSignals(false); }); - timer->start(10); + timer->start(2); /* First Widget batch */ for (size_t i = 0; i < eph->getSessionCount(); i++) { diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index b39635d..3441442 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -27,8 +27,12 @@ #include #include #include +#include +#include +#include #include #include + //#include /* * #else @@ -71,12 +75,13 @@ public: class MeterSlider : public QSlider { Q_OBJECT +private: + float peakValue; protected: void paintEvent(QPaintEvent *event) override; - public: + void setPeakValue(float peakValue); using QSlider::QSlider; - }; class ExtendedCheckBox : public QCheckBox { diff --git a/src/qtestmain.cpp b/src/qtestmain.cpp index 1d421cc..075b992 100644 --- a/src/qtestmain.cpp +++ b/src/qtestmain.cpp @@ -52,7 +52,7 @@ int main (int argc, char* argv[]) { * log_debugcpp(a.toStdString()); * } */ - //QApplication::setStyle("Fusion"); + QApplication::setStyle("Fusion"); //Check if running //https://stackoverflow.com/questions/48060989/qt-show-application-if-currently-running initialize_file_log(); From dc8951776f418452138d3c9023d3df8a87804efb Mon Sep 17 00:00:00 2001 From: Hane Date: Tue, 30 Apr 2024 23:40:52 +0200 Subject: [PATCH 30/78] wip: session meter --- src/back/backlasses.cpp | 5 +++++ src/back/backsessionclasses.cpp | 14 +++++++++++++- src/back/backsessionclasses.h | 2 ++ src/cont/contsessionclasses.cpp | 4 ++++ src/cont/contsessionclasses.h | 1 + src/qt/qtclasses.cpp | 7 ++++--- src/qt/qtclasses.h | 2 +- 7 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/back/backlasses.cpp b/src/back/backlasses.cpp index ced20f3..f5b4fa7 100644 --- a/src/back/backlasses.cpp +++ b/src/back/backlasses.cpp @@ -294,6 +294,11 @@ void Endpoint::activateEndpointSessions() { for (int i = 0; i < sessionCount; i++) { IAudioSessionControl* sessionControlTmp; sessionEnumerator->GetSession(i, (IAudioSessionControl**)&sessionControlTmp); + /*todo: borrar when donezo + * float test2; + * IAudioMeterInformation* ttmp = (IAudioMeterInformation*)sessionControlTmp; + * ttmp->GetPeakValue(&test2); + */ //todo:: asegurar lo del dynamic_cast IAudioSessionControl2* sessionControl; sessionControlTmp->QueryInterface(__uuidof(IAudioSessionControl2), (void**)&sessionControl); diff --git a/src/back/backsessionclasses.cpp b/src/back/backsessionclasses.cpp index 21e7959..fd33728 100644 --- a/src/back/backsessionclasses.cpp +++ b/src/back/backsessionclasses.cpp @@ -100,7 +100,11 @@ Session::Session(Endpoint* ep, IAudioSessionControl2* sessionControl, size_t idx this->ep = ep; this->sessionControl = sessionControl; this->idx = idx; - + //https://matthewvaneerde.wordpress.com/2012/06/08/getting-audio-peak-meter-values-for-all-active-audio-sessions/ + if (FAILED(sessionControl->QueryInterface(__uuidof(IAudioMeterInformation), (void**)&meterInformation))) { log_wdebugcpp(L"sPeakbros......"); }; + //meterInformation = (IAudioMeterInformation*)sessionControl; + + AudioSessionState msState; sessionControl->GetState(&msState); switch (msState) { @@ -142,6 +146,13 @@ float Session::getVolume(int channel){ return volume; } +float Session::getPeakVolume() { + float peakVol; + if(meterInformation) meterInformation->GetPeakValue(&peakVol); + else return 0; + return peakVol; +} + /* * uint32_t Endpoint::getChannelCount(){ * return (uint32_t)channelCount; @@ -283,6 +294,7 @@ void Session::removeStateCallback(SessionStateCallback *ssc){ } Session::~Session() { + meterInformation->Release(); sessionControl->Release(); sessionVolume->Release(); } diff --git a/src/back/backsessionclasses.h b/src/back/backsessionclasses.h index 421f514..2b5db99 100644 --- a/src/back/backsessionclasses.h +++ b/src/back/backsessionclasses.h @@ -33,6 +33,7 @@ class Session { Session(Endpoint* ep, IAudioSessionControl2* sessionControl) : Session(ep, sessionControl, SIZE_MAX) {}; void setVolume(NGuid guid, int channel, float volume); float getVolume(int channel); + float getPeakVolume(); void setMute(NGuid guid, bool muted); bool getMute(); SessionState getState(); @@ -51,6 +52,7 @@ class Session { SessionState sessionState; Endpoint* ep; IAudioSessionControl2* sessionControl = nullptr; + IAudioMeterInformation* meterInformation = nullptr; ISimpleAudioVolume* sessionVolume = nullptr; size_t idx; }; diff --git a/src/cont/contsessionclasses.cpp b/src/cont/contsessionclasses.cpp index 085527e..b63631e 100644 --- a/src/cont/contsessionclasses.cpp +++ b/src/cont/contsessionclasses.cpp @@ -36,6 +36,10 @@ void SessionHandler::setName(std::wstring newName){ session->setName(newName); } +float SessionHandler::getPeakVolume(){ + return session->getPeakVolume(); +} + bool SessionHandler::getMute(){ return session->getMute(); } diff --git a/src/cont/contsessionclasses.h b/src/cont/contsessionclasses.h index 8a93164..488d3ea 100644 --- a/src/cont/contsessionclasses.h +++ b/src/cont/contsessionclasses.h @@ -29,6 +29,7 @@ class SessionHandler { void setState(SessionState state); uint64_t getFrontIndex(); std::wstring getName(); + float getPeakVolume(); void setName(std::wstring newName); void reviseSessionShowing(SessionState state); SessionVolumeInfo* getVolumeInfo(); diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index c6faba8..4cfbcb1 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -200,7 +200,7 @@ SessionWidget::SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent) muteButton = new QCheckBox(this); mainLabel = new QLabel(QString::fromStdWString(sh->getName()), this); - mainSlider = new QSlider(Qt::Horizontal, this); + mainSlider = new MeterSlider(Qt::Horizontal, this); //mainLabel->setMaximumWidth(150 /*1/16ish 1080p*/); //mainLabel->setMinimumWidth(150 /*1/16ish 1080p*/); @@ -248,9 +248,10 @@ SessionWidget::SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent) mainSlider->blockSignals(true); muteButton->blockSignals(true); mainSlider->setValue((int)((sh->getVolumeInfo()->mainVolume + roundingFactor) * 100)); + mainSlider->setPeakValue(sh->getPeakVolume()); + mainSlider->update(); muteButton->setCheckState((sh->getVolumeInfo()->muted == false ? Qt::Unchecked : Qt::Checked)); muteButton->setText(sh->getVolumeInfo()->muted ? STRING_UNMUTE : STRING_MUTE); - //memcpy(osh->callbackInfo[idx]->caller, osh->getGuid(), sizeof(NGuid)); //TODO: el default = objcopy frees? @@ -259,7 +260,7 @@ SessionWidget::SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent) mainSlider->blockSignals(false); muteButton->blockSignals(false); }); - volumePoller->start(10); + volumePoller->start(2); } void SessionWidget::calculateSize(uint64_t width, uint64_t height) { diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index 3441442..c4eb958 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -112,7 +112,7 @@ public slots: private: QLabel *mainLabel = nullptr; - QSlider *mainSlider = nullptr; + MeterSlider *mainSlider = nullptr; uint64_t idx; QHBoxLayout *widgetLayout = nullptr; QCheckBox *muteButton = nullptr; From 75fdfaa095c57ac9188895804ddac44cd56c71a8 Mon Sep 17 00:00:00 2001 From: Hane Date: Thu, 2 May 2024 19:59:41 +0200 Subject: [PATCH 31/78] wip: peak meter visual ratio --- src/back/backsessionclasses.cpp | 1 - src/qt/qtclasses.cpp | 15 ++++++++++----- src/qtestmain.cpp | 2 +- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/back/backsessionclasses.cpp b/src/back/backsessionclasses.cpp index fd33728..80eeac8 100644 --- a/src/back/backsessionclasses.cpp +++ b/src/back/backsessionclasses.cpp @@ -104,7 +104,6 @@ Session::Session(Endpoint* ep, IAudioSessionControl2* sessionControl, size_t idx if (FAILED(sessionControl->QueryInterface(__uuidof(IAudioMeterInformation), (void**)&meterInformation))) { log_wdebugcpp(L"sPeakbros......"); }; //meterInformation = (IAudioMeterInformation*)sessionControl; - AudioSessionState msState; sessionControl->GetState(&msState); switch (msState) { diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 4cfbcb1..64a09df 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -1,4 +1,5 @@ #include "qtclasses.h" +#define POLLING_RATE 2 template CustomWidgetEvent::CustomWidgetEvent(QEvent::Type type, T payload) : QEvent(type){ @@ -32,10 +33,14 @@ void MeterSlider::paintEvent(QPaintEvent *event) { painter.setClipping(false); painter.setCompositionMode(QPainter::CompositionMode::CompositionMode_Source); float peakLength = (this->width() * (this->peakValue)); + double stepWidth = (double)this->width() * ((double)this->singleStep() / (this->maximum() - this->minimum())); + //Fusion seems to fuck around with bar's height and width //const qreal dpr = painter->device()->devicePixelRatio(); - QStyleOptionSlider slider = QStyleOptionSlider(); - QRect test = QApplication::style()->subControlRect(QStyle::CC_Slider, (QStyleOptionComplex*)&slider, QStyle::SC_SliderHandle); - painter.fillRect(0, (this->height() / 2) - 3, (this->width() * (this->peakValue)), 4, Qt::green); + QStyleOptionSlider slider = QStyleOptionSlider(); //slider.initFrom(this); + QRect test = style->subControlRect(QStyle::CC_Slider, (QStyleOptionComplex*)&slider, QStyle::SC_SliderHandle); + //QApplication::style()->subControlRect(QStyle::CC_Slider, (QStyleOptionComplex*)&slider, QStyle::SC_SliderHandle); + painter.fillRect(0, (this->height() / 2) - 3, (this->width() - ((this->maximum() - this->value()) * stepWidth)) * this->peakValue, + 4, Qt::green); } void ExtendedCheckBox::customEvent(QEvent* ev) { @@ -260,7 +265,7 @@ SessionWidget::SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent) mainSlider->blockSignals(false); muteButton->blockSignals(false); }); - volumePoller->start(2); + volumePoller->start(POLLING_RATE); } void SessionWidget::calculateSize(uint64_t width, uint64_t height) { @@ -507,7 +512,7 @@ EndpointWidget::EndpointWidget(EndpointHandler* eph, QWidget *parent, uint64_t i mainSlider->blockSignals(false); muteButton->blockSignals(false); }); - timer->start(2); + timer->start(POLLING_RATE); /* First Widget batch */ for (size_t i = 0; i < eph->getSessionCount(); i++) { diff --git a/src/qtestmain.cpp b/src/qtestmain.cpp index 075b992..1d421cc 100644 --- a/src/qtestmain.cpp +++ b/src/qtestmain.cpp @@ -52,7 +52,7 @@ int main (int argc, char* argv[]) { * log_debugcpp(a.toStdString()); * } */ - QApplication::setStyle("Fusion"); + //QApplication::setStyle("Fusion"); //Check if running //https://stackoverflow.com/questions/48060989/qt-show-application-if-currently-running initialize_file_log(); From c7d77c30ab4afe40ea287cd99120de374b8cbdcf Mon Sep 17 00:00:00 2001 From: Hane Date: Thu, 2 May 2024 23:25:06 +0200 Subject: [PATCH 32/78] wip: slider draw algo --- src/qt/qtclasses.cpp | 86 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 83 insertions(+), 3 deletions(-) diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 64a09df..12355d7 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -11,6 +11,69 @@ void MeterSlider::setPeakValue(float peakValue) { } void MeterSlider::paintEvent(QPaintEvent *event) { + QStyleOptionSlider sliderComplex = QStyleOptionSlider(); + sliderComplex.initFrom(this); + /* + * sliderComplex.initFrom(this); + * sliderComplex.subControls = QStyle::SC_None; + * sliderComplex.activeSubControls = QStyle::SC_None; + * sliderComplex.orientation = this->orientation(); + * sliderComplex.maximum = this->maximum(); + * sliderComplex.minimum = this->minimum(); + * sliderComplex.tickPosition = (QSlider::TickPosition)this->tickPosition(); + * sliderComplex.tickInterval = this->tickInterval(); + * sliderComplex.upsideDown = (this->orientation() == Qt::Horizontal) ? + * (this->invertedAppearance() != (sliderComplex.direction == Qt::RightToLeft)) + * : (!this->invertedAppearance()); + * sliderComplex.direction = Qt::LeftToRight; // we use the upsideDown option instead + * sliderComplex.sliderPosition = this->sliderPosition(); + * sliderComplex.sliderValue = this->value(); + * sliderComplex.singleStep = this->singleStep(); + * sliderComplex.pageStep = this->pageStep(); + * if (this->orientation() == Qt::Horizontal) + * sliderComplex.state |= QStyle::State_Horizontal; + * + * if (this->isSliderDown()) { + * sliderComplex.activeSubControls = QStyle::SC_SliderHandle; + * sliderComplex.state |= QStyle::State_Sunken; + * } else { + * sliderComplex.activeSubControls = QStyle::SC_SliderHandle; + * } + * + * //sliderComplex.subControls = QStyle::SC_SliderGroove; + * if (this->tickPosition() != NoTicks) sliderComplex.subControls |= QStyle::SC_SliderTickmarks; + * QStylePainter p(this); + * p.drawComplexControl(QStyle::CC_Slider, sliderComplex); + */ + + /* + * QStyleOptionSlider sliderComplex2 = QStyleOptionSlider(); + * sliderComplex2.initFrom(this); + * sliderComplex2.orientation = this->orientation(); + * sliderComplex2.maximum = this->maximum(); + * sliderComplex2.minimum = this->minimum(); + * sliderComplex2.tickPosition = (QSlider::TickPosition)this->tickPosition(); + * sliderComplex2.tickInterval = this->tickInterval(); + * sliderComplex2.upsideDown = (this->orientation() == Qt::Horizontal) ? + * (this->invertedAppearance() != (sliderComplex2.direction == Qt::RightToLeft)) + * : (!this->invertedAppearance()); + * sliderComplex2.subControls = QStyle::SC_SliderHandle; + * sliderComplex2.direction = Qt::LeftToRight; // we use the upsideDown option instead + * sliderComplex2.sliderPosition = this->sliderPosition(); + * sliderComplex2.sliderValue = this->value(); + * sliderComplex2.singleStep = this->singleStep(); + * sliderComplex2.pageStep = this->pageStep(); + * if (this->orientation() == Qt::Horizontal) + * sliderComplex2.state |= QStyle::State_Horizontal; + * + * if (this->isSliderDown()) { + * sliderComplex2.activeSubControls = QStyle::SC_SliderHandle; + * sliderComplex2.state |= QStyle::State_Sunken; + * } else { + * sliderComplex2.activeSubControls = QStyle::SC_SliderHandle; + * } + */ + //Q_D(QSlider); /* * QStylePainter p(this); @@ -23,7 +86,9 @@ void MeterSlider::paintEvent(QPaintEvent *event) { * * //p.drawComplexControl(QStyle::CC_Slider, opt); */ - QSlider::paintEvent(event); + //QSlider::paintEvent(event); + int left = 0, top = 0, right = 0, bottom = 0; + ((QWidget*)parent())->layout()->getContentsMargins(&left, &top, &right, &bottom); QStyle *style = QApplication::style(); int lol = style->pixelMetric(QStyle::PM_SliderSpaceAvailable); @@ -36,11 +101,26 @@ void MeterSlider::paintEvent(QPaintEvent *event) { double stepWidth = (double)this->width() * ((double)this->singleStep() / (this->maximum() - this->minimum())); //Fusion seems to fuck around with bar's height and width //const qreal dpr = painter->device()->devicePixelRatio(); - QStyleOptionSlider slider = QStyleOptionSlider(); //slider.initFrom(this); - QRect test = style->subControlRect(QStyle::CC_Slider, (QStyleOptionComplex*)&slider, QStyle::SC_SliderHandle); + //QStyleOptionSlider sliderComplex = QStyleOptionSlider(); //slider.initFrom(this); + QRect sliderSize = style->subControlRect(QStyle::CC_Slider, (QStyleOptionComplex*)&sliderComplex, QStyle::SC_SliderHandle); + int handleCenterPos = QStyle::sliderPositionFromValue(this->minimum(), this->maximum(), this->value(), this->width()); + int unattenuatedPeakMeter = ((this->width() * this->peakValue) >= handleCenterPos - (sliderSize.width() / 2)) + ? this->width() - (sliderSize.width() / 2) + : this->width() * this->peakValue; //QApplication::style()->subControlRect(QStyle::CC_Slider, (QStyleOptionComplex*)&slider, QStyle::SC_SliderHandle); + painter.fillRect(0, (this->height() / 2) - 3, this->width(), + 4, Qt::white); + painter.fillRect(0, (this->height() / 2) - 3, this->width() * this->peakValue, + 4, Qt::gray); painter.fillRect(0, (this->height() / 2) - 3, (this->width() - ((this->maximum() - this->value()) * stepWidth)) * this->peakValue, 4, Qt::green); + // - ((this->maximum() - this->value()) * stepWidth)) + //double ratio = ; + double handleShift = (double)((sliderSize.width() * ((double)(this->maximum() - this->value()) / 100))); + painter.fillRect((this->width() - ((this->maximum() - this->value()) * stepWidth)) - (sliderSize.width()) + handleShift, + top / 2, sliderSize.width(), sliderSize.height() - bottom, Qt::magenta); + //sliderComplex.subControls = QStyle::SC_SliderHandle; + //p.drawComplexControl(QStyle::CC_Slider, sliderComplex); } void ExtendedCheckBox::customEvent(QEvent* ev) { From 071505d3fe3a97dd3b13a3169df61b7676ce9231 Mon Sep 17 00:00:00 2001 From: Hane Date: Mon, 6 May 2024 19:32:22 +0200 Subject: [PATCH 33/78] added README/reimpl header --- README.md | 15 ++++ src/back/reimpl/audiometerinfo.h | 130 +++++++++++++++++++++++++++++++ src/qt/qtclasses.cpp | 4 +- 3 files changed, 147 insertions(+), 2 deletions(-) create mode 100644 README.md create mode 100644 src/back/reimpl/audiometerinfo.h diff --git a/README.md b/README.md new file mode 100644 index 0000000..cf1a785 --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# Mixer + +## Build + +* Toolchain: [llvm-mingw UCRT 20220906](https://github.com/mstorsjo/llvm-mingw/releases/tag/20220906). + +* Clone [this](https://code.qt.io/cgit/qt/qt5.git/tag/?h=v6.3.2) Qt branch + +* Build Qt from sources following [this](https://wiki.qt.io/Building_Qt_6_from_Git) guide and executing `configure.bat` as such: + +``` +..\qt6\configure.bat -prefix ..\install -static -debug -opensource -confirm-license -qt-zlib -qt-libpng -qt-webp -qt-libjpeg -qt-freetype -skip qt3d -skip qtactiveqt -skip qtandroidextras -skip qtcharts -skip qtconnectivity -skip qtdatavis3d -skip qtdoc -skip qtgamepad -skip qtlocation -skip qtlottie -skip qtmacextras -skip qtmultimedia -skip qtnetworkauth -skip qtpurchasing -skip qtquick3d -skip qtquick3dphysics -skip qtquicktimeline -skip qtremoteobjects -skip qtscript -skip qtsensors -skip qtwayland -skip qtwebglplugin -skip qtwebview -skip webengine -nomake examples -nomake tests +``` + +* Clone this repo and execute `bueno.bat`. \ No newline at end of file diff --git a/src/back/reimpl/audiometerinfo.h b/src/back/reimpl/audiometerinfo.h new file mode 100644 index 0000000..618ad34 --- /dev/null +++ b/src/back/reimpl/audiometerinfo.h @@ -0,0 +1,130 @@ +#ifndef __IAudioMeterInformation_FWD_DEFINED__ +#define __IAudioMeterInformation_FWD_DEFINED__ +typedef interface IAudioMeterInformation IAudioMeterInformation; + +#endif /* __IAudioMeterInformation_FWD_DEFINED__ */ + +#ifdef __cplusplus +extern "C"{ +#endif + +/* interface __MIDL_itf_endpointvolume_0000_0003 */ +/* [local] */ + +#ifndef __IAudioMeterInformation_INTERFACE_DEFINED__ +#define __IAudioMeterInformation_INTERFACE_DEFINED__ + +/* interface IAudioMeterInformation */ +/* [unique][helpstring][nonextensible][uuid][local][object] */ + +DEFINE_GUID(IID_IAudioMeterInformation, 0xc02216f6, 0x8c67, 0x4b5b, 0x9d,0x00, 0xd0,0x08,0xe7,0x3e,0x00,0x64); +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("c02216f6-8c67-4b5b-9d00-d008e73e0064") + IAudioMeterInformation : public IUnknown + { + public: + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetPeakValue( + /* [out] */ float *pfPeak) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetMeteringChannelCount( + /* [annotation][out] */ + _Out_ UINT *pnChannelCount) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetChannelsPeakValues( + /* [in] */ UINT32 u32ChannelCount, + /* [size_is][out] */ float *afPeakValues) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE QueryHardwareSupport( + /* [annotation][out] */ + _Out_ DWORD *pdwHardwareSupportMask) = 0; + + }; + +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IAudioMeterInformation, 0xc02216f6, 0x8c67, 0x4b5b, 0x9d,0x00, 0xd0,0x08,0xe7,0x3e,0x00,0x64) +#endif +#else /* C style interface */ + + typedef struct IAudioMeterInformationVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IAudioMeterInformation * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IAudioMeterInformation * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IAudioMeterInformation * This); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE *GetPeakValue )( + IAudioMeterInformation * This, + /* [out] */ float *pfPeak); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE *GetMeteringChannelCount )( + IAudioMeterInformation * This, + /* [annotation][out] */ + _Out_ UINT *pnChannelCount); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE *GetChannelsPeakValues )( + IAudioMeterInformation * This, + /* [in] */ UINT32 u32ChannelCount, + /* [size_is][out] */ float *afPeakValues); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE *QueryHardwareSupport )( + IAudioMeterInformation * This, + /* [annotation][out] */ + _Out_ DWORD *pdwHardwareSupportMask); + + END_INTERFACE + } IAudioMeterInformationVtbl; + + interface IAudioMeterInformation + { + CONST_VTBL struct IAudioMeterInformationVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IAudioMeterInformation_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define IAudioMeterInformation_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define IAudioMeterInformation_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define IAudioMeterInformation_GetPeakValue(This,pfPeak) \ + ( (This)->lpVtbl -> GetPeakValue(This,pfPeak) ) + +#define IAudioMeterInformation_GetMeteringChannelCount(This,pnChannelCount) \ + ( (This)->lpVtbl -> GetMeteringChannelCount(This,pnChannelCount) ) + +#define IAudioMeterInformation_GetChannelsPeakValues(This,u32ChannelCount,afPeakValues) \ + ( (This)->lpVtbl -> GetChannelsPeakValues(This,u32ChannelCount,afPeakValues) ) + +#define IAudioMeterInformation_QueryHardwareSupport(This,pdwHardwareSupportMask) \ + ( (This)->lpVtbl -> QueryHardwareSupport(This,pdwHardwareSupportMask) ) + +#endif /* COBJMACROS */ + +#endif /* C style interface */ + +#endif /* __IAudioMeterInformation_INTERFACE_DEFINED__ */ + +#ifdef __cplusplus +} +#endif + + + diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 12355d7..e0080a6 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -830,8 +830,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { //todo: ratio setWindowFlags(Qt::Window | Qt::MSWindowsFixedSizeDialogHint); setWindowFlags(Qt::FramelessWindowHint | Qt::ToolTip); - setAttribute(Qt::WA_TranslucentBackground); - setStyleSheet("background-color: rgba(255,182,193,90%);"); + //setAttribute(Qt::WA_TranslucentBackground); + //setStyleSheet("background-color: rgba(255,182,193,60%);"); setWindowTitle(STRING_TITLE); connect(qApp, &QGuiApplication::applicationStateChanged, this, [=](Qt::ApplicationState state){ if(state == Qt::ApplicationState::ApplicationInactive) hide(); From d75cf405f119f85fb83593a4b40b25fd54e584d7 Mon Sep 17 00:00:00 2001 From: Hane Date: Tue, 7 May 2024 18:46:24 +0200 Subject: [PATCH 34/78] new layout every compose --- src/qt/qtclasses.cpp | 26 ++++++++++++++++++++++---- src/qt/qtclasses.h | 3 ++- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index e0080a6..56fa131 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -214,6 +214,7 @@ void MainWindow::compose() { uint64_t screenHeight = (uint64_t)std::abs(screenRes.height()); log_debugcpp("Window Width: " + std::to_string(windowWidth)); + this->createLayout(new QGridLayout()); this->setAttribute(Qt::WA_DontShowOnScreen, true); this->show(); this->widget->layout()->update(); @@ -398,9 +399,9 @@ ChannelWidget::ChannelWidget(uint32_t channelCount, EndpointHandler* eph, QWidge widgetLayout->getContentsMargins(&left, &top, &right, &bottom); widgetLayout->setContentsMargins(0, top, 0, bottom); -/* - * Channel sliders setup - */ + /* + * Channel sliders setup + */ //uint32_t epChannelCount = eph->getChannelCount(); for(uint64_t channel = 0, col = 0, row = 0; channel < channelCount && channelCount > 1; channel++){ QSlider* tmp = new QSlider(Qt::Horizontal); @@ -824,6 +825,23 @@ HeaderWidget::HeaderWidget(QWidget *parent) : QWidget(parent) { this->setLayout(widgetLayout); } +void MainWindow::createLayout(QGridLayout *newLayout) { + //int row = 0; + //QGridLayout *newLayout = new QGridLayout(); + delete this->widgetLayout; + widget->setLayout(newLayout); + this->widgetLayout = newLayout; + //this->reloadEndpointWidgets(); + uint64_t i = 0; + for (EndpointWidget *epw : ews) { + if (!epw) continue; + log_debugcpp("EPWidget positioning"); + log_wdebugcpp(L"epw name: " + epw->getEndpointHandler()->getName()); + this->widgetLayout->addWidget(epw, i++, 0); + } +} + + MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { //setWindowState(Qt::WindowFullScreen); //setCentralWidget(centralWidget); @@ -861,7 +879,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { ewsUpdateTimer->setInterval(ewsUpdateTimerFrequency); connect(ewsUpdateTimer, &QTimer::timeout, this, &MainWindow::reorderEndpointWidgetCollection); //widget->setMinimumSize(QSize(300,300)); - widget->setLayout(widgetLayout); + //widget->setLayout(widgetLayout); /* * Scroll bar code */ diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index c4eb958..37e0974 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -233,7 +233,8 @@ protected: void customEvent(QEvent* ev) override; QRect setSizePosition(QScreen* screen, int width, int height); QScreen* getCurrentScreen(); - + void createLayout(QGridLayout *newLayout); + private slots: void trayIconActivated(QSystemTrayIcon::ActivationReason reason); void removeEndpointWidget(CustomWidgetEvent* ev); From 6c588d068fec064d70cb503eeea375f55db9c22e Mon Sep 17 00:00:00 2001 From: Hane Date: Tue, 7 May 2024 19:16:24 +0200 Subject: [PATCH 35/78] poc recompose --- src/qt/qtclasses.cpp | 46 +++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 56fa131..12cb687 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -215,11 +215,13 @@ void MainWindow::compose() { log_debugcpp("Window Width: " + std::to_string(windowWidth)); this->createLayout(new QGridLayout()); - this->setAttribute(Qt::WA_DontShowOnScreen, true); - this->show(); - this->widget->layout()->update(); - this->hide(); - this->setAttribute(Qt::WA_DontShowOnScreen, false); + /* + * this->setAttribute(Qt::WA_DontShowOnScreen, true); + * this->show(); + * this->widget->layout()->update(); + * this->hide(); + * this->setAttribute(Qt::WA_DontShowOnScreen, false); + */ for (auto *epw : ews) { if (!epw) continue; @@ -668,6 +670,9 @@ void MainWindow::customEvent(QEvent* ev) { } else if (ev->type() == (QEvent::Type)CustomQEvent::EndpointWidgetCreated) { ev->setAccepted(true); this->addEndpointWidget((CustomWidgetEvent*)ev); + } else if (ev->type() == (QEvent::Type)CustomQEvent::RecomposeMainWindow) { + ev->setAccepted(true); + this->compose(); } QMainWindow::customEvent(ev); } @@ -826,22 +831,22 @@ HeaderWidget::HeaderWidget(QWidget *parent) : QWidget(parent) { } void MainWindow::createLayout(QGridLayout *newLayout) { - //int row = 0; - //QGridLayout *newLayout = new QGridLayout(); + widgetLayout->removeItem(lastRowSpacer); delete this->widgetLayout; widget->setLayout(newLayout); this->widgetLayout = newLayout; - //this->reloadEndpointWidgets(); + uint64_t i = 0; for (EndpointWidget *epw : ews) { if (!epw) continue; log_debugcpp("EPWidget positioning"); log_wdebugcpp(L"epw name: " + epw->getEndpointHandler()->getName()); + epw->setIndex(i); this->widgetLayout->addWidget(epw, i++, 0); - } + } + widgetLayout->addItem(lastRowSpacer, i, 0); } - MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { //setWindowState(Qt::WindowFullScreen); //setCentralWidget(centralWidget); @@ -930,6 +935,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { */ osh->setChangeFrontDefaultsFunction([this](Roles role, std::wstring endpointId) { //Sigh... I hope to get this right when librole is done... + //todo: STILL BORKED. THEY'RE NOT FORFEITING THEIR OLD POSITION EndpointWidget *newDef = nullptr, *oldDef = nullptr; for (uint64_t i = 0; i < ews.size(); i++) { auto epw = this->ews.at(i); @@ -992,6 +998,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { log_debugcpp("oldDef new idx: " + std::to_string(oldDef->getIndex())); oldDef->getDefaultRolesWidgets().at(role)->blockSignals(false); } + QCoreApplication::instance()->postEvent + (this, new QEvent((QEvent::Type)CustomQEvent::RecomposeMainWindow)); }); osh->setRemoveEndpointWidgetFunction([this](uint64_t index) { @@ -1031,9 +1039,7 @@ void MainWindow::trayIconActivated(QSystemTrayIcon::ActivationReason reason) { void MainWindow::reloadEndpointWidgets() { size_t i = 0; ews.resize(2); - //widgetLayout->addItem(&lastRowSpacer, i++, 0); //todo: -log flag - //std::wofstream log("log.txt"); for (size_t epwIndex = 2; i < (osh->getPlaybackEndpointHandlers().size()); i++) { if (osh->getPlaybackEndpointHandlers().at(i)->getState() == EndpointState::ENDPOINT_ACTIVE){ log_debugcpp("EPWidget creation"); @@ -1043,19 +1049,15 @@ void MainWindow::reloadEndpointWidgets() { log_wdebugcpp(L"epw name: " + epw->getEndpointHandler()->getName()); if ((epw->getEndpointHandler()->getRoles() == Roles::ROLE_ALL) || (epw->getEndpointHandler()->getRoles() & Roles::ROLE_MULTIMEDIA)) - { ews[0] = epw; widgetLayout->addWidget(epw, 0, 0); epw->setIndex(0); } + ews[0] = epw; else if (epw->getEndpointHandler()->getRoles() & Roles::ROLE_COMMUNICATIONS) - { ews[1] = epw; widgetLayout->addWidget(epw, 1, 0); epw->setIndex(1); } - else { - epw->setIndex(epwIndex++); - //alfinal estoes solopara inicializarlmao - ews.push_back(epw); - widgetLayout->addWidget(epw, i, 0); } + ews[1] = epw; + else + ews.push_back(epw); } } - //todo:: tas aqui tirao, no me gustas y probablemente yo a ti tampoco - //seguramente falle al querer rematar esto con redimensionar la ventana sólo - //con los default endpoints en vista + //todo: ya no está aquí tirao como tal, y de hecho, no ha fallado de la manera arriba descrita + //pero ahora lo añado porque sí solo para garantizar que está y poder reusarlo luego lmao widgetLayout->addItem(lastRowSpacer, i, 0); } From 16604277fbb8404ad4b93e8c65a2ac6c29092bcd Mon Sep 17 00:00:00 2001 From: Hane Date: Tue, 7 May 2024 19:38:17 +0200 Subject: [PATCH 36/78] disabled slider scroll --- src/qt/qtclasses.cpp | 13 ++++++++++--- src/qt/qtclasses.h | 9 +++++++-- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 12cb687..d7aeedd 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -10,6 +10,14 @@ void MeterSlider::setPeakValue(float peakValue) { this->peakValue = peakValue; } +bool MeterSlider::event(QEvent* ev) { + if (ev->type() == QEvent::Wheel) { + ev->setAccepted(false); + return false; + } + return QSlider::event(ev); +} + void MeterSlider::paintEvent(QPaintEvent *event) { QStyleOptionSlider sliderComplex = QStyleOptionSlider(); sliderComplex.initFrom(this); @@ -246,7 +254,7 @@ void MainWindow::compose() { windowHeight += bottom; log_debugcpp("windowHeight loop: " + std::to_string(windowHeight)); } - windowHeight += mainMenuBar->height(); + windowHeight += mainMenuBar->sizeHint().height(); log_debugcpp("windowHeight final value: " + std::to_string(windowHeight)); //Undoing scrolling @@ -406,7 +414,7 @@ ChannelWidget::ChannelWidget(uint32_t channelCount, EndpointHandler* eph, QWidge */ //uint32_t epChannelCount = eph->getChannelCount(); for(uint64_t channel = 0, col = 0, row = 0; channel < channelCount && channelCount > 1; channel++){ - QSlider* tmp = new QSlider(Qt::Horizontal); + MeterSlider* tmp = new MeterSlider(Qt::Horizontal); QLabel* tmpLb = new QLabel(""); tmp->setTickInterval(5); tmp->setSingleStep(1); @@ -656,7 +664,6 @@ void EndpointWidget::customEvent(QEvent* ev) { QWidget::customEvent(ev); } - EndpointWidget::~EndpointWidget() { timer->stop(); delete timer; diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index 37e0974..adcb8b1 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -78,7 +78,9 @@ class MeterSlider : public QSlider { private: float peakValue; protected: + bool event(QEvent* ev) override; void paintEvent(QPaintEvent *event) override; + public: void setPeakValue(float peakValue); using QSlider::QSlider; @@ -151,7 +153,7 @@ public: EndpointHandler* getEndpointHandler(); std::map getDefaultRolesWidgets(); - + void setIndex(uint64_t idx); uint64_t getIndex(); //void setVolume(int channel, float volume); @@ -164,7 +166,10 @@ public: //void populateEndpointWidget(EndpointHandler *eph); //void setEndpointHandlers(std::vector *ephs); - + +//protected: + //bool event(QEvent* ev) override; + public slots: void updateMainVolume(int newValue); void updateMute(int checked); From 4f3f8b3e56d3b1bb399ce847c29aeac62a99e557 Mon Sep 17 00:00:00 2001 From: Hane Date: Wed, 8 May 2024 19:08:43 +0200 Subject: [PATCH 37/78] normalized behaviour click trayIcon w/ wndow open + lim compose call --- src/qt/qtclasses.cpp | 28 ++++++++++++++++++++-------- src/qt/qtclasses.h | 3 +++ 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index d7aeedd..3449c0f 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -679,7 +679,7 @@ void MainWindow::customEvent(QEvent* ev) { this->addEndpointWidget((CustomWidgetEvent*)ev); } else if (ev->type() == (QEvent::Type)CustomQEvent::RecomposeMainWindow) { ev->setAccepted(true); - this->compose(); + if (this->isVisible()) this->compose(); } QMainWindow::customEvent(ev); } @@ -863,9 +863,6 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { //setAttribute(Qt::WA_TranslucentBackground); //setStyleSheet("background-color: rgba(255,182,193,60%);"); setWindowTitle(STRING_TITLE); - connect(qApp, &QGuiApplication::applicationStateChanged, this, [=](Qt::ApplicationState state){ - if(state == Qt::ApplicationState::ApplicationInactive) hide(); -}); /* * Registering needed custom events */ @@ -880,6 +877,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { /* This spacer provides proper spacing when window vertically > widgets. */ lastRowSpacer = new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding); ewsUpdateTimer = new QTimer(this); + recentlyClosedTimer = new QTimer(this); widget = new QWidget(); widgetLayout = new QGridLayout(); trayIcon = new QSystemTrayIcon(); @@ -890,6 +888,10 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { ewsUpdateTimer->setSingleShot(true); ewsUpdateTimer->setInterval(ewsUpdateTimerFrequency); connect(ewsUpdateTimer, &QTimer::timeout, this, &MainWindow::reorderEndpointWidgetCollection); + + recentlyClosedTimer->setSingleShot(true); + recentlyClosedTimer->setInterval(recentlyClosedTimerFrequency); + connect(recentlyClosedTimer, &QTimer::timeout, this, [=]() { this->recentlyClosed = false; }); //widget->setMinimumSize(QSize(300,300)); //widget->setLayout(widgetLayout); /* @@ -936,7 +938,14 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { trayIcon->setToolTip(STRING_TITLE); trayIcon->setContextMenu(trayIconMenu); connect(trayIcon, &QSystemTrayIcon::activated, this, &MainWindow::trayIconActivated); - + connect(qApp, &QGuiApplication::applicationStateChanged, this, [=](Qt::ApplicationState state){ + if(state == Qt::ApplicationState::ApplicationInactive) { + this->hide(); + //This recentlyClosed... kinda campy. + this->recentlyClosed = true; + this->recentlyClosedTimer->start(); + } + }); /* * Set of function callback definitons for EndpointSituationCallback */ @@ -1034,9 +1043,12 @@ void MainWindow::closeEvent(QCloseEvent *event) { void MainWindow::trayIconActivated(QSystemTrayIcon::ActivationReason reason) { switch (reason) { case QSystemTrayIcon::Trigger: - this->compose(); - this->showNormal(); - this->activateWindow(); + if (!this->isVisible() && !recentlyClosed) { + log_to_file("Recently Closed: %d \n", recentlyClosed); + this->compose(); + this->showNormal(); + this->activateWindow(); + } break; default: break; diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index adcb8b1..48dd06b 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -261,6 +261,9 @@ private: QTimer *ewsUpdateTimer; static constexpr uint64_t ewsUpdateTimerFrequency = 500; double widthRatio = 0.28; + bool recentlyClosed = false; + uint8_t recentlyClosedTimerFrequency = 1000; + QTimer *recentlyClosedTimer; QScrollArea *scrollArea; HeaderWidget* hw; From cb320da8cda45ae86ba9878708842d0b42d6abd4 Mon Sep 17 00:00:00 2001 From: Hane Date: Wed, 8 May 2024 20:46:17 +0200 Subject: [PATCH 38/78] fixed visual artifact when add/remove sessions while window open --- src/qt/qtclasses.cpp | 14 ++++++++++---- src/qt/qtclasses.h | 2 ++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 3449c0f..b44f401 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -628,18 +628,21 @@ EndpointWidget::EndpointWidget(EndpointHandler* eph, QWidget *parent, uint64_t i } - void EndpointWidget::addSessionWidget(CustomWidgetEvent* ev){ + this->setUpdatesEnabled(false); uint64_t index = this->sessionWidgets.size(); SessionWidget* sw = new SessionWidget(index, ev->payload, this); ev->payload->setFrontIndex(index); + sw->calculateSize(currentWidth, currentHeight); this->widgetLayout->addWidget(sw, row, 0, 1, 4); row++; sessionWidgets.push_back(sw); + this->setUpdatesEnabled(true); return; } void EndpointWidget::removeSessionWidget(CustomWidgetEvent* ev){ + this->setUpdatesEnabled(false); uint64_t i = ev->payload->getFrontIndex(); SessionWidget* deceased = sessionWidgets.at(i); deceased->setParent(nullptr); @@ -649,6 +652,7 @@ void EndpointWidget::removeSessionWidget(CustomWidgetEvent* ev) sessionWidgets.at(i) = nullptr; //row--; ev->payload->setFrontIndex(INT_MAX); + this->setUpdatesEnabled(true); //this->sessionWidgetsUpdateTimer->start(); return; } @@ -736,6 +740,8 @@ void MainWindow::reorderEndpointWidgetCollection() { void EndpointWidget::calculateSize(uint64_t width, uint64_t height) { /* og 1080p 120% testing values */ + this->currentWidth = width; + this->currentHeight = height; log_to_file("[EndpointWidget %s sizes]\n", converter.to_bytes(this->getEndpointHandler()->getName()).c_str()); log_to_file("Params: {Width: %u Height: %u}\n", width, height); this->mainLabel->setMaximumWidth((int)(width * 0.50) /* 1080p 120%*/); @@ -1068,11 +1074,11 @@ void MainWindow::reloadEndpointWidgets() { log_wdebugcpp(L"epw name: " + epw->getEndpointHandler()->getName()); if ((epw->getEndpointHandler()->getRoles() == Roles::ROLE_ALL) || (epw->getEndpointHandler()->getRoles() & Roles::ROLE_MULTIMEDIA)) - ews[0] = epw; + { ews[0] = epw; epw->setIndex(0); } else if (epw->getEndpointHandler()->getRoles() & Roles::ROLE_COMMUNICATIONS) - ews[1] = epw; + { ews[1] = epw; epw->setIndex(1); } else - ews.push_back(epw); + { ews.push_back(epw); epw->setIndex(ews.size() - 1); } } } //todo: ya no está aquí tirao como tal, y de hecho, no ha fallado de la manera arriba descrita diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index 48dd06b..854e7d8 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -201,6 +201,8 @@ private: ChannelWidget* cw; std::vector sessionWidgets; QSize minimum; + uint64_t currentWidth = 1; + uint64_t currentHeight = 1; //std::vector *ephs; //std::vector *sliders; From 33f3d8216fce7d75f7688ed03258cc986fbc3135 Mon Sep 17 00:00:00 2001 From: Hane Date: Fri, 10 May 2024 19:51:48 +0200 Subject: [PATCH 39/78] change defaults poc --- qtest.pro | 2 +- src/back/backlasses.cpp | 14 ++- src/back/backlasses.h | 1 + src/back/ipolicyconfig.h | 241 +++++++++++++++++++++++++++++---------- src/back/msinclude.h | 2 +- 5 files changed, 195 insertions(+), 65 deletions(-) diff --git a/qtest.pro b/qtest.pro index 707ff2d..4677fd8 100644 --- a/qtest.pro +++ b/qtest.pro @@ -1,6 +1,6 @@ QMAKE_CXXFLAGS += --target=x86_64-w64-mingw32 -g -gcodeview QMAKE_LFLAGS += --target=x86_64-w64-mingw32 -g -Wl,-pdb= -v -LIBS += -LC:/capybara/libclang/x86_64-w64-mingw32/lib -lWinmm -lodbc32 -lodbccp32 -luuid -loleaut32 -lole32 -lshell32 -ladvapi32 -lcomdlg32 -lwinspool -lgdi32 -luser32 -lkernel32 -lpropsys +LIBS += -LC:/capybara/libclang/x86_64-w64-mingw32/lib -lWinmm -lodbc32 -lodbccp32 -luuid -loleaut32 -lole32 -lshell32 -ladvapi32 -lcomdlg32 -lwinspool -lgdi32 -luser32 -lkernel32 -lpropsys #"kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" -luuid -loleaut32 -lole32 -lshell32 -ladvapi32 -lcomdlg32 -lwinspool -lgdi32 -luser32 -lkernel32 DEFINES += DEBUG QT_LOGGING_TO_CONSOLE=1 WIN32_LEAN_AND_MEAN CONFIG += debug diff --git a/src/back/backlasses.cpp b/src/back/backlasses.cpp index f5b4fa7..cf53962 100644 --- a/src/back/backlasses.cpp +++ b/src/back/backlasses.cpp @@ -569,8 +569,10 @@ void Overseer::initCOMLibrary() { //todo: wtf? why is it working? floats are ptrs... this->guid = GUIDToNGuid(&tempGuid); - //if(FAILED(CoCreateInstance(__uuidof(CPolicyConfigClient), - // NULL, CLSCTX_ALL, __uuidof(IPolicyConfig10), (LPVOID *)&policyConfig))) {exit(-1);} + HRESULT hre = CoCreateInstance(__uuidof(CPolicyConfigClient), + NULL, CLSCTX_ALL, + __uuidof(IPolicyConfig7), (LPVOID *)&policyConfig); + if (hre != S_OK) exit(-1); //TODO: Release lpguid? //TODO: Uninitialize COM @@ -606,6 +608,14 @@ void Overseer::reloadEndpoints(Flows flow) { } deviceCollection->Release(); + //IPolicyConfig7 test + deviceEnumerator->GetDefaultAudioEndpoint(MSflow, ERole::eCommunications, &temp); + LPWSTR tempString = nullptr; + temp->GetId(&tempString); + HRESULT hre = policyConfig->SetDefaultEndpoint( + tempString, + ERole::eMultimedia + ); /* * Discerning default endpoints per role diff --git a/src/back/backlasses.h b/src/back/backlasses.h index f2c0199..0cee06e 100644 --- a/src/back/backlasses.h +++ b/src/back/backlasses.h @@ -136,6 +136,7 @@ class Overseer { std::vector playbackDevices; std::vector captureDevices; void initCOMLibrary(); + IPolicyConfig7* policyConfig; //IMMDeviceCollection *deviceCollection; //int numCaptureEndpoints; //std::vector *captureDevices; diff --git a/src/back/ipolicyconfig.h b/src/back/ipolicyconfig.h index 2e10aab..7b474bb 100644 --- a/src/back/ipolicyconfig.h +++ b/src/back/ipolicyconfig.h @@ -1,47 +1,42 @@ -// ---------------------------------------------------------------------------- -// PolicyConfig.h -// Undocumented COM-interface IPolicyConfig. -// Use for set default audio render endpoint -// @author EreTIk -// ---------------------------------------------------------------------------- +#pragma once + +#ifndef __IPolicyConfig7_FWD_DEFINED__ +#define __IPolicyConfig7_FWD_DEFINED__ +typedef interface IPolicyConfig7 IPolicyConfig7; +#ifdef __cplusplus +interface IPolicyConfig7; +#endif +#endif /* __IPolicyConfig7_FWD_DEFINED__ */ + +#ifndef __CPolicyConfigClient_FWD_DEFINED__ +#define __CPolicyConfigClient_FWD_DEFINED__ +typedef class CPolicyConfigClient CPolicyConfigClient; +#endif /* __CPolicyConfigClient_FWD_DEFINED__ */ + +/***************************************************************************** + * CPolicyConfigClient coclass + */ + +DEFINE_GUID(CLSID_CPolicyConfigClient, 0x870af99c, 0x171d, 0x4f9e, 0xaf,0x0d, 0xe6,0x3d,0xf4,0x0c,0x2b,0xc9); +#ifdef __cplusplus +class DECLSPEC_UUID("870af99c-171d-4f9e-af0d-e63df40c2bc9") CPolicyConfigClient; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(CPolicyConfigClient, 0x870af99c, 0x171d, 0x4f9e, 0xaf,0x0d, 0xe6,0x3d,0xf4,0x0c,0x2b,0xc9) +#endif +#endif -#pragma once - - -interface DECLSPEC_UUID("CA286FC3-91FD-42C3-8E9B-CAAFA66242E3") -IPolicyConfig10; - -interface DECLSPEC_UUID("00000000-0000-0000-C000-000000000046") -IPolicyConfig10_1; - -interface DECLSPEC_UUID("F8679F50-850A-41CF-9C72-430F290290C8") -IPolicyConfig7; - -/* interface DECLSPEC_UUID("568B9108-44BF-40B4-9006-86AFE5B5A620") */ -/* IPolicyConfigVista; */ - -interface DECLSPEC_UUID("f8679f50-850a-41cf-9c72-430f290290c8") -IPolicyConfig; -class DECLSPEC_UUID("870af99c-171d-4f9e-af0d-e63df40c2bc9") -CPolicyConfigClient; -// ---------------------------------------------------------------------------- -// class CPolicyConfigClient -// {870af99c-171d-4f9e-af0d-e63df40c2bc9} -// -// interface IPolicyConfig -// {f8679f50-850a-41cf-9c72-430f290290c8} -// -// Query interface: -// CComPtr PolicyConfig; -// PolicyConfig.CoCreateInstance(__uuidof(CPolicyConfigClient)); -// -// @compatible: Windows 7 and Later -// ---------------------------------------------------------------------------- -interface IPolicyConfig : public IUnknown -{ +/***************************************************************************** + * IPolicyConfig7 interface + */ +#ifndef __IPolicyConfig7_INTERFACE_DEFINED__ +#define __IPolicyConfig7_INTERFACE_DEFINED__ +DEFINE_GUID(IID_IPolicyConfig7, 0xf8679f50, 0x850a, 0x41cf, 0x9c,0x72, 0x43,0x0f,0x29,0x02,0x90,0xc8); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("f8679f50-850a-41cf-9c72-430f290290c8") +IPolicyConfig7 : public IUnknown { + public: - virtual HRESULT GetMixFormat( PCWSTR, WAVEFORMATEX ** @@ -107,32 +102,69 @@ public: INT ); }; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IPolicyConfig7, 0xf8679f50, 0x850a, 0x41cf, 0x9c,0x72, 0x43,0x0f,0x29,0x02,0x90,0xc8) +#endif + +#endif + + +#endif /* __IPolicyConfig7_INTERFACE_DEFINED__ */ + + + + +/* __CRT_UUID_DECL(IPolicyConfig10, 0xca286fc3, 0x91fd, 0x42c3, 0x8e,0x9b, 0xca,0xaf,0xa6,0x62,0x42,0xe3) */ +/* __CRT_UUID_DECL(CPolicyConfigClient, 0x870af99c, 0x171d, 0x4f9e, 0xaf,0x0d, 0xe6,0x3d,0xf4,0x0c,0x2b,0xc9) */ + -/* interface DECLSPEC_UUID("568b9108-44bf-40b4-9006-86afe5b5a620") */ -/* IPolicyConfigVista; */ -/* class DECLSPEC_UUID("294935CE-F637-4E7C-A41B-AB255460B862") */ -/* CPolicyConfigVistaClient; */ /* // ---------------------------------------------------------------------------- */ -/* // class CPolicyConfigVistaClient */ -/* // {294935CE-F637-4E7C-A41B-AB255460B862} */ +/* // PolicyConfig.h */ +/* // Undocumented COM-interface IPolicyConfig. */ +/* // Use for set default audio render endpoint */ +/* // @author EreTIk */ +/* // ---------------------------------------------------------------------------- */ + +/* #pragma once */ + +/* interface DECLSPEC_UUID("CA286FC3-91FD-42C3-8E9B-CAAFA66242E3") */ +/* IPolicyConfig10; */ + +/* interface DECLSPEC_UUID("00000000-0000-0000-C000-000000000046") */ +/* IPolicyConfig10_1; */ + +/* interface DECLSPEC_UUID("F8679F50-850A-41CF-9C72-430F290290C8") */ +/* IPolicyConfig7; */ + +/* /\* interface DECLSPEC_UUID("568B9108-44BF-40B4-9006-86AFE5B5A620") *\/ */ +/* /\* IPolicyConfigVista; *\/ */ + +/* interface DECLSPEC_UUID("f8679f50-850a-41cf-9c72-430f290290c8") */ +/* IPolicyConfig; */ + +/* class DECLSPEC_UUID("870af99c-171d-4f9e-af0d-e63df40c2bc9") */ +/* CPolicyConfigClient; */ +/* // ---------------------------------------------------------------------------- */ +/* // class CPolicyConfigClient */ +/* // {870af99c-171d-4f9e-af0d-e63df40c2bc9} */ /* // */ -/* // interface IPolicyConfigVista */ -/* // {568b9108-44bf-40b4-9006-86afe5b5a620} */ +/* // interface IPolicyConfig */ +/* // {f8679f50-850a-41cf-9c72-430f290290c8} */ /* // */ /* // Query interface: */ -/* // CComPtr PolicyConfig; */ -/* // PolicyConfig.CoCreateInstance(__uuidof(CPolicyConfigVistaClient)); */ +/* // CComPtr PolicyConfig; */ +/* // PolicyConfig.CoCreateInstance(__uuidof(CPolicyConfigClient)); */ /* // */ -/* // @compatible: Windows Vista and Later */ +/* // @compatible: Windows 7 and Later */ /* // ---------------------------------------------------------------------------- */ -/* interface IPolicyConfigVista : public IUnknown */ +/* interface IPolicyConfig : public IUnknown */ /* { */ /* public: */ /* virtual HRESULT GetMixFormat( */ /* PCWSTR, */ /* WAVEFORMATEX ** */ -/* ); // not available on Windows 7, use method from IPolicyConfig */ +/* ); */ /* virtual HRESULT STDMETHODCALLTYPE GetDeviceFormat( */ /* PCWSTR, */ @@ -140,6 +172,10 @@ public: /* WAVEFORMATEX ** */ /* ); */ +/* virtual HRESULT STDMETHODCALLTYPE ResetDeviceFormat( */ +/* PCWSTR */ +/* ); */ + /* virtual HRESULT STDMETHODCALLTYPE SetDeviceFormat( */ /* PCWSTR, */ /* WAVEFORMATEX *, */ @@ -151,22 +187,22 @@ public: /* INT, */ /* PINT64, */ /* PINT64 */ -/* ); // not available on Windows 7, use method from IPolicyConfig */ +/* ); */ /* virtual HRESULT STDMETHODCALLTYPE SetProcessingPeriod( */ /* PCWSTR, */ /* PINT64 */ -/* ); // not available on Windows 7, use method from IPolicyConfig */ +/* ); */ /* virtual HRESULT STDMETHODCALLTYPE GetShareMode( */ /* PCWSTR, */ /* struct DeviceShareMode * */ -/* ); // not available on Windows 7, use method from IPolicyConfig */ +/* ); */ /* virtual HRESULT STDMETHODCALLTYPE SetShareMode( */ /* PCWSTR, */ /* struct DeviceShareMode * */ -/* ); // not available on Windows 7, use method from IPolicyConfig */ +/* ); */ /* virtual HRESULT STDMETHODCALLTYPE GetPropertyValue( */ /* PCWSTR, */ @@ -181,12 +217,95 @@ public: /* ); */ /* virtual HRESULT STDMETHODCALLTYPE SetDefaultEndpoint( */ -/* __in PCWSTR wszDeviceId, */ -/* __in ERole eRole */ +/* PCWSTR wszDeviceId, */ +/* ERole eRole */ /* ); */ /* virtual HRESULT STDMETHODCALLTYPE SetEndpointVisibility( */ /* PCWSTR, */ /* INT */ -/* ); // not available on Windows 7, use method from IPolicyConfig */ +/* ); */ /* }; */ + +/* /\* interface DECLSPEC_UUID("568b9108-44bf-40b4-9006-86afe5b5a620") *\/ */ +/* /\* IPolicyConfigVista; *\/ */ +/* /\* class DECLSPEC_UUID("294935CE-F637-4E7C-A41B-AB255460B862") *\/ */ +/* /\* CPolicyConfigVistaClient; *\/ */ +/* /\* // ---------------------------------------------------------------------------- *\/ */ +/* /\* // class CPolicyConfigVistaClient *\/ */ +/* /\* // {294935CE-F637-4E7C-A41B-AB255460B862} *\/ */ +/* /\* // *\/ */ +/* /\* // interface IPolicyConfigVista *\/ */ +/* /\* // {568b9108-44bf-40b4-9006-86afe5b5a620} *\/ */ +/* /\* // *\/ */ +/* /\* // Query interface: *\/ */ +/* /\* // CComPtr PolicyConfig; *\/ */ +/* /\* // PolicyConfig.CoCreateInstance(__uuidof(CPolicyConfigVistaClient)); *\/ */ +/* /\* // *\/ */ +/* /\* // @compatible: Windows Vista and Later *\/ */ +/* /\* // ---------------------------------------------------------------------------- *\/ */ +/* /\* interface IPolicyConfigVista : public IUnknown *\/ */ +/* /\* { *\/ */ +/* /\* public: *\/ */ + +/* /\* virtual HRESULT GetMixFormat( *\/ */ +/* /\* PCWSTR, *\/ */ +/* /\* WAVEFORMATEX ** *\/ */ +/* /\* ); // not available on Windows 7, use method from IPolicyConfig *\/ */ + +/* /\* virtual HRESULT STDMETHODCALLTYPE GetDeviceFormat( *\/ */ +/* /\* PCWSTR, *\/ */ +/* /\* INT, *\/ */ +/* /\* WAVEFORMATEX ** *\/ */ +/* /\* ); *\/ */ + +/* /\* virtual HRESULT STDMETHODCALLTYPE SetDeviceFormat( *\/ */ +/* /\* PCWSTR, *\/ */ +/* /\* WAVEFORMATEX *, *\/ */ +/* /\* WAVEFORMATEX * *\/ */ +/* /\* ); *\/ */ + +/* /\* virtual HRESULT STDMETHODCALLTYPE GetProcessingPeriod( *\/ */ +/* /\* PCWSTR, *\/ */ +/* /\* INT, *\/ */ +/* /\* PINT64, *\/ */ +/* /\* PINT64 *\/ */ +/* /\* ); // not available on Windows 7, use method from IPolicyConfig *\/ */ + +/* /\* virtual HRESULT STDMETHODCALLTYPE SetProcessingPeriod( *\/ */ +/* /\* PCWSTR, *\/ */ +/* /\* PINT64 *\/ */ +/* /\* ); // not available on Windows 7, use method from IPolicyConfig *\/ */ + +/* /\* virtual HRESULT STDMETHODCALLTYPE GetShareMode( *\/ */ +/* /\* PCWSTR, *\/ */ +/* /\* struct DeviceShareMode * *\/ */ +/* /\* ); // not available on Windows 7, use method from IPolicyConfig *\/ */ + +/* /\* virtual HRESULT STDMETHODCALLTYPE SetShareMode( *\/ */ +/* /\* PCWSTR, *\/ */ +/* /\* struct DeviceShareMode * *\/ */ +/* /\* ); // not available on Windows 7, use method from IPolicyConfig *\/ */ + +/* /\* virtual HRESULT STDMETHODCALLTYPE GetPropertyValue( *\/ */ +/* /\* PCWSTR, *\/ */ +/* /\* const PROPERTYKEY &, *\/ */ +/* /\* PROPVARIANT * *\/ */ +/* /\* ); *\/ */ + +/* /\* virtual HRESULT STDMETHODCALLTYPE SetPropertyValue( *\/ */ +/* /\* PCWSTR, *\/ */ +/* /\* const PROPERTYKEY &, *\/ */ +/* /\* PROPVARIANT * *\/ */ +/* /\* ); *\/ */ + +/* /\* virtual HRESULT STDMETHODCALLTYPE SetDefaultEndpoint( *\/ */ +/* /\* __in PCWSTR wszDeviceId, *\/ */ +/* /\* __in ERole eRole *\/ */ +/* /\* ); *\/ */ + +/* /\* virtual HRESULT STDMETHODCALLTYPE SetEndpointVisibility( *\/ */ +/* /\* PCWSTR, *\/ */ +/* /\* INT *\/ */ +/* /\* ); // not available on Windows 7, use method from IPolicyConfig *\/ */ +/* /\* }; *\/ */ diff --git a/src/back/msinclude.h b/src/back/msinclude.h index 9911165..16216f6 100644 --- a/src/back/msinclude.h +++ b/src/back/msinclude.h @@ -23,10 +23,10 @@ //#include #include #include -#include "ipolicyconfig.h" #include #include +#include "ipolicyconfig.h" #include "audiometerinfo.h" // IAudioMeterInformation From 6ebe2604e70603612e72cf2fd457f691c9d8b67b Mon Sep 17 00:00:00 2001 From: Hane Date: Sat, 11 May 2024 22:39:49 +0200 Subject: [PATCH 40/78] base for cruft removal --- src/back/backlasses.cpp | 16 +++++++++------- src/qt/qtclasses.cpp | 3 ++- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/back/backlasses.cpp b/src/back/backlasses.cpp index cf53962..e398b21 100644 --- a/src/back/backlasses.cpp +++ b/src/back/backlasses.cpp @@ -609,13 +609,15 @@ void Overseer::reloadEndpoints(Flows flow) { deviceCollection->Release(); //IPolicyConfig7 test - deviceEnumerator->GetDefaultAudioEndpoint(MSflow, ERole::eCommunications, &temp); - LPWSTR tempString = nullptr; - temp->GetId(&tempString); - HRESULT hre = policyConfig->SetDefaultEndpoint( - tempString, - ERole::eMultimedia - ); + /* + * deviceEnumerator->GetDefaultAudioEndpoint(MSflow, ERole::eCommunications, &temp); + * LPWSTR tempString = nullptr; + * temp->GetId(&tempString); + * HRESULT hre = policyConfig->SetDefaultEndpoint( + * tempString, + * ERole::eMultimedia + * ); + */ /* * Discerning default endpoints per role diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index b44f401..5e0bd3a 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -867,7 +867,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { setWindowFlags(Qt::Window | Qt::MSWindowsFixedSizeDialogHint); setWindowFlags(Qt::FramelessWindowHint | Qt::ToolTip); //setAttribute(Qt::WA_TranslucentBackground); - //setStyleSheet("background-color: rgba(255,182,193,60%);"); + //setStyleSheet("background: transparent; "); + //setStyleSheet("background-color: rgba(255,182,193);"); setWindowTitle(STRING_TITLE); /* * Registering needed custom events From c1665b33e2e0c216d7a287ae41c62195e5037269 Mon Sep 17 00:00:00 2001 From: Hane Date: Mon, 13 May 2024 16:27:35 +0200 Subject: [PATCH 41/78] removed SVV.exe --- assets/SoundVolumeView.exe | Bin 203592 -> 0 bytes bueno.bat | 2 - src/back/backlasses.cpp | 90 +++++++++---------------------------- src/back/backlasses.h | 11 +++-- 4 files changed, 27 insertions(+), 76 deletions(-) delete mode 100644 assets/SoundVolumeView.exe diff --git a/assets/SoundVolumeView.exe b/assets/SoundVolumeView.exe deleted file mode 100644 index 4c7a7d2fe0e11b39fec51ffb8152ecd41e19e9c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 203592 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
R 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 literal 0 HcmV?d00001 diff --git a/assets/installer.xcf b/assets/installer.xcf new file mode 100644 index 0000000000000000000000000000000000000000..96669f1efd409adaa5b5e92494b1dbc41ee422a4 GIT binary patch literal 103610 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

ik;N8#zJ;L|4<*un-#IspWrbcsn(_ijw`|D%G1P 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 literal 0 HcmV?d00001 diff --git a/assets/uninstaller.ico b/assets/uninstaller.ico new file mode 100644 index 0000000000000000000000000000000000000000..24278c229b7a3a797df4fbe921b3e0ad42a09309 GIT binary patch literal 196273 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 literal 0 HcmV?d00001 diff --git a/bueno.bat b/bueno.bat index 6aa7e4e..3e2ab0d 100644 --- a/bueno.bat +++ b/bueno.bat @@ -1,3 +1,4 @@ taskkill /F /IM "qtest.exe" -qmake -o build\Makefile .\qtest.pro -mingw32-make.exe -C .\build -f Makefile +qmake -o build\Makefile .\qtest.pro +mingw32-make.exe -C .\build -f Makefile.Release +mingw32-make.exe -C .\build -f Makefile.Debug diff --git a/install/installer.nsi b/install/installer.nsi index b2e29eb..9181444 100644 --- a/install/installer.nsi +++ b/install/installer.nsi @@ -1,61 +1,121 @@ ;Auto versioning------------------------------- -!makensis "version.nsi" -!system "GetVersion.exe" -!include "Version.txt" -; optional cleanup -!delfile "GetVersion.exe" -!delfile "Version.txt" - -;Defines--------------- -;!define MULTIUSER_EXECUTIONLEVEL Highest - + !makensis "/DBUILDTYPE=$BUILDTYPE version.nsi" + !system "GetVersion.exe" + !include "Version.txt" + ;optional cleanup + !delfile "GetVersion.exe" + !delfile "Version.txt" ;Includes-------------------------------- !include "MUI2.nsh" !include "nsDialogs.nsh" !include "LogicLib.nsh" - ;!include "MultiUser.nsh" + !include "${NSISDIR}\Contrib\Language files\English.nsh" + !include "${NSISDIR}\Contrib\Language files\Spanish.nsh" + ;!include "${NSISDIR}\Contrib\Language files\English.nsh" + + +;Defines---------------------------------- + !define MUI_LANGDLL_ALLLANGUAGES + !define MUI_UNICON "..\assets\uninstaller.ico" + !define MUI_ICON "..\assets\installer.ico" + ;-------------------------------- ;General ;Name and file - Name "MixerQ" - OutFile "mixerq-installer-${version}.exe" - Unicode True + !if ${BUILDTYPE} == "release" + Name "MixerQ" + !else + Name "MixerQd" + !endif + OutFile "..\build\bin\MixerQ-installer-${version}.exe" - ;Default installation folder - ;InstallDir "$LOCALAPPDATA\Modern UI Test" - ;Get installation folder from registry if available ;InstallDirRegKey HKCU "Software\Modern UI Test" "" - + + Unicode True Var Is_Admin Var Install_Type ;Request application privileges for UAC. If admin is not available, only user-level install will be available RequestExecutionLevel highest +;-------------------------------- +;Interface Settings + + !define MUI_ABORTWARNING + +;-------------------------------- +;Pages + !insertmacro MUI_PAGE_WELCOME + !insertmacro MUI_PAGE_LICENSE "..\LICENSE.txt" + ;!insertmacro MULTIUSER_PAGE_INSTALLMODE + Page Custom InstallTargetPage + ;!insertmacro MUI_PAGE_COMPONENTS + !define MUI_PAGE_CUSTOMFUNCTION_PRE Skip_Directory_Func + !insertmacro MUI_PAGE_DIRECTORY + !insertmacro MUI_PAGE_INSTFILES + !insertmacro MUI_PAGE_FINISH + + !insertmacro MUI_UNPAGE_WELCOME + !insertmacro MUI_UNPAGE_CONFIRM + !insertmacro MUI_UNPAGE_INSTFILES + !insertmacro MUI_UNPAGE_FINISH + +;-------------------------------- +;Languages + + !insertmacro MUI_LANGUAGE "English" + !insertmacro MUI_LANGUAGE "Spanish" + !insertmacro MUI_LANGUAGE "SpanishInternational" + + ;English---------------------------- + LangString Header_Title ${LANG_ENGLISH} "Configure Install" + LangString Header_Subtitle ${LANG_ENGLISH} "Customize install settings" + LangString Option_Scope ${LANG_ENGLISH} "Select for whom will $(^Name) be installed: " + LangString Scope_Machine ${LANG_ENGLISH} "All users" + LangString Scope_User ${LANG_ENGLISH} "Current user" + + ;Spanish/SpanishInternational---------------------------- + LangString Header_Title ${LANG_SPANISH} "Configurar instalación" + LangString Header_Subtitle ${LANG_SPANISH} "Elija los ajustes de la instalación" + LangString Option_Scope ${LANG_SPANISH} "$(^Name) será instalado para: " + LangString Scope_Machine ${LANG_SPANISH} "Todos los usuarios" + LangString Scope_User ${LANG_SPANISH} "Usuario actual" + + LangString Header_Title ${LANG_SPANISHINTERNATIONAL} "Configurar instalación" + LangString Header_Subtitle ${LANG_SPANISHINTERNATIONAL} "Elija los ajustes de la instalación" + LangString Option_Scope ${LANG_SPANISHINTERNATIONAL} "$(^Name) será instalado para: " + LangString Scope_Machine ${LANG_SPANISHINTERNATIONAL} "Todos los usuarios" + LangString Scope_User ${LANG_SPANISHINTERNATIONAL} "Usuario actual" + +;Functions------------------------------ + Function Skip_Directory_Func + ;StrCmp $Install_Type "user" dontSkip + Abort # skip the page + ;dontSkip: + FunctionEnd !macro ONINIT un Function ${un}.onInit ; The value of SetShellVarContext detetmines whether SHCTX is HKLM or HKCU - ; and whether SMPROGRAMS refers to all users or just the current user + ; and whether SMPROGRAMS refers to all users or just the current + !insertmacro MUI_LANGDLL_DISPLAY UserInfo::GetAccountType Pop $0 ${If} $0 == "Admin" ; If we're an admin, default to installing to C:\Program Files - ;SetShellVarContext all - ;StrCpy $INSTDIR_BASE "$PROGRAMFILES64" + SetShellVarContext all + ; StrCpy $INSTDIR "$PROGRAMFILES64\$(^Name)" StrCpy $Is_Admin "true" - StrCpy $Install_Type "machine" ${Else} ; If we're just a user, default to installing to ~\AppData\Local - ;SetShellVarContext current - ;StrCpy $INSTDIR_BASE "$LOCALAPPDATA" + SetShellVarContext current + ; StrCpy $INSTDIR "$LOCALAPPDATA\$(^Name)" StrCpy $Is_Admin "false" - StrCpy $Install_Type "user" ${EndIf} ; ${If} $INSTDIR == "" @@ -75,56 +135,35 @@ !insertmacro ONINIT "" !insertmacro ONINIT "un" -;-------------------------------- -;Interface Settings - - !define MUI_ABORTWARNING - -;-------------------------------- -;Pages - - !insertmacro MUI_PAGE_WELCOME - !insertmacro MUI_PAGE_LICENSE "..\LICENSE.txt" - ;!insertmacro MULTIUSER_PAGE_INSTALLMODE - Page Custom InstallTargetPage - ;!insertmacro MUI_PAGE_COMPONENTS - !insertmacro MUI_PAGE_DIRECTORY - !insertmacro MUI_PAGE_INSTFILES - !insertmacro MUI_PAGE_FINISH - - !insertmacro MUI_UNPAGE_WELCOME - !insertmacro MUI_UNPAGE_CONFIRM - !insertmacro MUI_UNPAGE_INSTFILES - !insertmacro MUI_UNPAGE_FINISH - -;-------------------------------- -;Languages - - !insertmacro MUI_LANGUAGE "English" - ;NSDialog InstallTarget Page definition--------------------------------- Function InstallTargetPage - !insertmacro MUI_HEADER_TEXT "Configure Install" "Customize install settings" - + !insertmacro MUI_HEADER_TEXT $(Header_Title) $(Header_Subtitle) + ;MessageBox MB_OK "Install type $Install_Type" + ;MessageBox MB_OK "Build type ${BUILDTYPE}" nsDialogs::Create 1018 Pop $0 - ${NSD_CreateLabel} 0 0 100% 10% "Select for whom will $(^Name) be installed: " + FindWindow $0 "#32770" + GetDlgItem $1 $0 1 ;next/install button + SendMessage $1 ${WM_SETTEXT} 1 "STR:$(^InstallBtn)" + Pop $0 + + ${NSD_CreateLabel} 0 0 100% 10% $(Option_Scope) Pop $3 - ${NSD_CreateFirstRadioButton} 0 12% 40% 6% "All users" + ${NSD_CreateFirstRadioButton} 0 12% 40% 6% $(Scope_Machine) Pop $1 ${If} $Is_Admin == "false" EnableWindow $1 0 StrCpy $INSTDIR "$LOCALAPPDATA\$(^Name)" ${Else} SendMessage $1 ${BM_CLICK} "" "" ;Set default - StrCpy $INSTDIR "$PROGRAMFILES64\$(^Name)" + StrCpy $INSTDIR "$PROGRAMFILES64\$(^Name)" ${EndIf} ${NSD_OnClick} $1 All_Users_Click - ${NSD_CreateAdditionalRadioButton} 0 24% 40% 6% "Current user" + ${NSD_CreateAdditionalRadioButton} 0 24% 40% 6% $(Scope_User) Pop $2 ${IfThen} $Is_Admin == "false" ${|} SendMessage $2 ${BM_CLICK} "" "" ${|} ${NSD_OnClick} $2 Current_User_Click @@ -134,16 +173,24 @@ FunctionEnd Function All_Users_Click Pop $0 - StrCpy $INSTDIR "$PROGRAMFILES64\$(^Name)" - StrCpy $Install_Type "machine" + SetShellVarContext all + StrCpy $INSTDIR "$PROGRAMFILES64\$(^Name)" + StrCpy $Install_Type "machine" ;${NSD_SetText} $0 "machine" + ;FindWindow $0 "#32770" + ;GetDlgItem $1 $0 1 ;next/install button + ;SendMessage $1 ${WM_SETTEXT} 1 "STR:$(^InstallBtn)" FunctionEnd Function Current_User_Click Pop $0 + SetShellVarContext current StrCpy $INSTDIR "$LOCALAPPDATA\$(^Name)" - StrCpy $Install_Type "user" + StrCpy $Install_Type "user" ;${NSD_SetText} $0 "user" + ;FindWindow $0 "#32770" + ;GetDlgItem $1 $0 1 ;next/install button + ;SendMessage $1 ${WM_SETTEXT} 1 "STR:$(^NextBtn)" FunctionEnd ;Default section---------------------- @@ -151,27 +198,27 @@ Section SetRegView 64 SetOutPath $INSTDIR - ;File "..\build\debug\qtest.exe" + !if ${BUILDTYPE} == "release" + File "..\build\bin\MixerQ.exe" + !else + File "..\build\bin\MixerQd.exe" + !endif File "..\LICENSE.txt" + ;Start menu shortcut + createDirectory "$SMPROGRAMS\$(^Name)" + createShortCut "$SMPROGRAMS\$(^Name)\$(^Name).lnk" "$INSTDIR\$(^Name).exe" + createShortCut "$SMPROGRAMS\$(^Name)\Uninstall$(^Name).lnk" "$INSTDIR\Uninstall$(^Name).exe" + ;Store installation folder - - ${If} $Install_Type == "user" - WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" "DisplayName" "$(^Name)" - WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" "UninstallString" '"$INSTDIR\Uninstall.exe"' - WriteRegDWORD HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" "NoModify" 1 - WriteRegDWORD HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" "NoRepair" 1 - WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Run" "$(^Name)" "$INSTDIR\$(^Name)" - ${Else} - WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" "DisplayName" "$(^Name)" - WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" "UninstallString" '"$INSTDIR\Uninstall.exe"' - WriteRegDWORD HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" "NoModify" 1 - WriteRegDWORD HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" "NoRepair" 1 - WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Run" "$(^Name)" "$INSTDIR\$(^Name)" - ${EndIf} + WriteRegStr SHCTX "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" "DisplayName" "$(^Name)" + WriteRegStr SHCTX "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" "UninstallString" '"$INSTDIR\Uninstall$(^Name).exe"' + WriteRegDWORD SHCTX "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" "NoModify" 1 + WriteRegDWORD SHCTX "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" "NoRepair" 1 + WriteRegStr SHCTX "SOFTWARE\Microsoft\Windows\CurrentVersion\Run" "$(^Name)" "$INSTDIR\$(^Name)" ;Create uninstaller - WriteUninstaller "$INSTDIR\Uninstall.exe" + WriteUninstaller "$INSTDIR\Uninstall$(^Name).exe" SectionEnd @@ -191,20 +238,28 @@ SectionEnd ;Uninstaller Section Section "Uninstall" - SetRegView 64 - ;ADD YOUR OWN FILES HERE... - Delete "$INSTDIR\qtest.exe" - Delete "$INSTDIR\LICENSE.txt" - Delete "$INSTDIR\Uninstall.exe" -;!define PRODUCT_UNINST_ROOT_KEY "HKLM" - RMDir "$INSTDIR" + SetRegView 64 + + SetShellVarContext current + DeleteRegValue HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Run" "$(^Name)" + DeleteRegKey HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" + Delete "$SMPROGRAMS\$(^Name)\$(^Name).lnk" + Delete "$SMPROGRAMS\$(^Name)\Uninstall$(^Name).lnk" + RMDir "$SMPROGRAMS\$(^Name)" - ${If} $Install_Type == "user" - DeleteRegValue HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Run" "$(^Name)" - DeleteRegKey HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" - ${Else} + ${If} $Is_Admin == "true" + SetShellVarContext all DeleteRegValue HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Run" "$(^Name)" DeleteRegKey HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" + Delete "$SMPROGRAMS\$(^Name)\$(^Name).lnk" + Delete "$SMPROGRAMS\$(^Name)\Uninstall$(^Name).lnk" + RMDir "$SMPROGRAMS\$(^Name)" ${EndIf} - + + Delete "$INSTDIR\$(^Name).exe" + Delete "$INSTDIR\LICENSE.txt" + Delete "$INSTDIR\Uninstall$(^Name).exe" + ;!define PRODUCT_UNINST_ROOT_KEY "HKLM" + RMDir "$INSTDIR" + SectionEnd diff --git a/install/version.nsi b/install/version.nsi index 6f5ac83..5e85c8c 100644 --- a/install/version.nsi +++ b/install/version.nsi @@ -1,4 +1,8 @@ -!define File "..\build\debug\qtest.exe" +!if ${BUILDTYPE} == "release" + !define File "..\build\bin\MixerQ.exe" +!else + !define File "..\build\bin\MixerQd.exe" +!endif OutFile "GetVersion.exe" SilentInstall silent diff --git a/qtest.pro b/qtest.pro index be04829..0568bca 100644 --- a/qtest.pro +++ b/qtest.pro @@ -1,18 +1,28 @@ -QMAKE_CXXFLAGS += --target=x86_64-w64-mingw32 -g -gcodeview -O0 -Werror=return-type -QMAKE_LFLAGS += --target=x86_64-w64-mingw32 -g -Wl,-pdb= -v -LIBS += -LC:/capybara/libclang/x86_64-w64-mingw32/lib -lWinmm -lodbc32 -lodbccp32 -luuid -loleaut32 -lole32 -lshell32 -ladvapi32 -lcomdlg32 -lwinspool -lgdi32 -luser32 -lkernel32 -lpropsys -static -stdlib=libc++ -lunwind -#"kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" -luuid -loleaut32 -lole32 -lshell32 -ladvapi32 -lcomdlg32 -lwinspool -lgdi32 -luser32 -lkernel32 -DEFINES += DEBUG QT_LOGGING_TO_CONSOLE=1 WIN32_LEAN_AND_MEAN _WIN32_WINNT=0x0602 -CONFIG += debug +TEMPLATE = app +QMAKE_CXXFLAGS += --target=x86_64-w64-mingw32 -Werror=return-type +QMAKE_LFLAGS += --target=x86_64-w64-mingw32 -v +CONFIG(release, debug|release) { + TARGET = MixerQ + DESTDIR = bin + VERSION = 0.9.0.0 + #QMAKE_CXXFLAGS += -O2 < Default. Modifying requires removing. +} else { + TARGET = MixerQd + DESTDIR = bin + VERSION = 0.9.0.1 + QMAKE_CXXFLAGS += -g -gcodeview -O0 + QMAKE_LFLAGS += -g -Wl,-pdb= +} + +LIBS += -lWinmm -lodbc32 -lodbccp32 -luuid -loleaut32 -lole32 -lshell32 -ladvapi32 -lcomdlg32 -lwinspool -lgdi32 -luser32 -lkernel32 -lpropsys -static -stdlib=libc++ -lunwind +DEFINES += QT_LOGGING_TO_CONSOLE=1 WIN32_LEAN_AND_MEAN _WIN32_WINNT=0x0602 +DEFINES_DEBUG += DEBUG QT += widgets network INCLUDEPATH += "$$PWD\src" "$$PWD\src\qt" "$$PWD\src\back" "$$PWD\src\back\reimpl" "$$PWD\src\cont" -DESTPATH += "$$PWD\src" "$$PWD\src\qt" "$$PWD\src\back" "$$PWD\src\back\reimpl" "$$PWD\src\cont" -VPATH += "$$PWD\src" "$$PWD\src\qt" "$$PWD\src\back" "$$PWD\src\back\reimpl" "$$PWD\src\cont" +VPATH += "$$PWD\src" "$$PWD\src\qt" "$$PWD\src\back" "$$PWD\src\back\reimpl" "$$PWD\src\cont" SOURCES += qtestmain.cpp qtclasses.cpp backlasses.cpp backsessionclasses.cpp contclasses.cpp contsessionclasses.cpp settings.cpp HEADERS += qtclasses.h backlasses.h backsessionclasses.h contclasses.h contsessionclasses.h global.h debug.h backfuncs.h ipolicyconfig.h msinclude.h meterslider.h qtvisuals.h settings.h RESOURCES = assets.qrc RC_ICONS += assets/logo.ico - -#DESTDIR += "build" From 667482cfeaa18fc00038192387fd5d1035764963 Mon Sep 17 00:00:00 2001 From: Hane Date: Tue, 14 Jan 2025 19:19:49 +0100 Subject: [PATCH 68/78] marshalling on sessions, missing override tag --- src/back/backlasses.cpp | 19 +++++++++++++------ src/back/backlasses.h | 11 +++++++++++ src/back/backsessionclasses.cpp | 2 ++ src/cont/contclasses.cpp | 26 +++++++++++++++++++++----- src/cont/contclasses.h | 3 +++ src/cont/contsessionclasses.cpp | 2 +- src/debug.h | 4 ++-- src/global.h | 1 + src/qt/qtclasses.cpp | 2 ++ src/qt/qtclasses.h | 2 +- 10 files changed, 57 insertions(+), 15 deletions(-) diff --git a/src/back/backlasses.cpp b/src/back/backlasses.cpp index a0f0e06..517e10d 100644 --- a/src/back/backlasses.cpp +++ b/src/back/backlasses.cpp @@ -45,15 +45,20 @@ HRESULT EndpointNewSessionCallback::OnSessionCreated(IAudioSessionControl *NewSe //ISimmpleAudioVolume* sessionVolume; if (FAILED(NewSession->QueryInterface(__uuidof(IAudioSessionControl2), (void**)&sessionControl))) { log_wdebugcpp(L"no nueva sesion......"); }; if (sessionControl) { - sessionControl->AddRef(); - //sessionControl->QueryInterface(__uuidof(ISimpleAudioVolume), (void**)&sessionVolume); Session* newSession = new Session(this->eph->getEndpoint(), sessionControl); - eph->addSessionSendFront(newSession); + + SessionThreadParams tp = { .eph = this->eph, .session = newSession, .isDelete = false }; + std::thread sessionThread(EndpointNewSessionCallback::createSessionThread, tp); + sessionThread.detach(); } return S_OK; } +void EndpointNewSessionCallback::createSessionThread(SessionThreadParams params) { + params.eph->addSessionSendFront(params.session); +} + EndpointVolumeCallback::EndpointVolumeCallback(Endpoint* ep){ this->ep = ep; } @@ -224,6 +229,7 @@ HRESULT EndpointSituationCallback::OnDeviceStateChanged(LPCWSTR pwstrDeviceId, D } HRESULT EndpointSituationCallback::OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key) { + //TODO: New thread + Mutex os->updateEndpointInfo(std::wstring(pwstrDeviceId)); return S_OK; } @@ -236,7 +242,6 @@ Endpoint::Endpoint(IMMDevice* ep, IPolicyConfig7* policyConfig, uint64_t idx){ * It can't multiflag, it's that stupid. MS momento. * Only shows most relevant flag according to MS, i.e. 0110 sends 0010 */ - //todo: preguntitas owindows dword no es uint32_t even tho mingw mingas if(FAILED(endpoint->GetState(&this->endpointState))) {exit(-2);}; if(this->endpointState == EndpointState::ENDPOINT_ACTIVE) { @@ -288,7 +293,6 @@ void Endpoint::activateEndpointSessions() { log_debugcpp("Couldn't open session manager2, huh"); return; } - IAudioSessionEnumerator* sessionEnumerator = nullptr; if (FAILED(sessionManager->GetSessionEnumerator(&sessionEnumerator))) { log_wdebugcpp(L"sesEnumeratorBros..."); return; } @@ -488,7 +492,9 @@ std::vector Endpoint::getSessions() { } size_t Endpoint::getSessionCount() { - return endpointSessions.size(); + size_t sessionCount; + sessionCount = endpointSessions.size(); + return sessionCount; } void Endpoint::registerNewSessionNotification(EndpointNewSessionCallback* ensc){ @@ -507,6 +513,7 @@ void Endpoint::deleteSessions() { } Endpoint::~Endpoint(){ + //EPs are never deleted. log_wdebugcpp(L"murio endpoint-san uwu"); properties->Release(); endpointVolume->Release(); diff --git a/src/back/backlasses.h b/src/back/backlasses.h index 071e0e1..511774b 100644 --- a/src/back/backlasses.h +++ b/src/back/backlasses.h @@ -82,6 +82,7 @@ class Endpoint { void unregisterNewSessionNotification(EndpointNewSessionCallback* ensc); void deleteSessions(); void activateEndpointSessions(); + std::mutex endpointSessionsMutex; ~Endpoint(); private: @@ -185,16 +186,26 @@ class Overseer { }; class EndpointNewSessionCallback : public IAudioSessionNotification { + private: + struct SessionThreadParams; + public: EndpointNewSessionCallback(EndpointHandler *eph); ULONG AddRef(); ULONG Release(); HRESULT QueryInterface(REFIID riid, VOID **ppvInterface); HRESULT OnSessionCreated(IAudioSessionControl *NewSession); + static void createSessionThread(SessionThreadParams params); private: ULONG ref = 1; EndpointHandler *eph; + + struct SessionThreadParams { + EndpointHandler *eph; + Session *session; + bool isDelete; + }; }; namespace Environment { diff --git a/src/back/backsessionclasses.cpp b/src/back/backsessionclasses.cpp index aafe147..f98369f 100644 --- a/src/back/backsessionclasses.cpp +++ b/src/back/backsessionclasses.cpp @@ -37,6 +37,8 @@ HRESULT SessionStateCallback::QueryInterface(REFIID riid, VOID **ppvInterface) { } HRESULT SessionStateCallback::OnDisplayNameChanged(LPCWSTR NewDisplayName, LPCGUID EventContext) { + //TODO: Preguntar + while(sh->getVolumeInfo()->isNameChanged == true); sh->setName(std::wstring(NewDisplayName)); sh->getVolumeInfo()->isNameChanged = true; return S_OK; diff --git a/src/cont/contclasses.cpp b/src/cont/contclasses.cpp index e9561f0..ad85647 100644 --- a/src/cont/contclasses.cpp +++ b/src/cont/contclasses.cpp @@ -191,10 +191,15 @@ Endpoint* EndpointHandler::getEndpoint() { } void EndpointHandler::addSessionSendFront(Session* session) { - ep->addSession(session); - + if(!ep->endpointSessionsMutex.try_lock()) return; + + this->ep->addSession(session); SessionHandler* sessionHandler = new SessionHandler(this, session, (getSessionCount() - 1)); + ep->endpointSessionsMutex.unlock(); + + sessionHandlersMutex.lock(); sessionHandlers.push_back(sessionHandler); + sessionHandlersMutex.unlock(); this->addSessionWidget(sessionHandler); } @@ -218,14 +223,24 @@ void EndpointHandler::deleteSessions() { void EndpointHandler::createSessionHandlers() { ep->activateEndpointSessions(); - ensc = new EndpointNewSessionCallback(this); - ep->registerNewSessionNotification(ensc); if (this->flow == Flows::FLOW_PLAYBACK) { for (int i = 0; i < this->getSessionCount(); i++) { SessionHandler* sessionHandler = new SessionHandler(this, this->getSessions().at(i),i); sessionHandlers.push_back(sessionHandler); } - } + } + ensc = new EndpointNewSessionCallback(this); + ep->registerNewSessionNotification(ensc); +} + +void EndpointHandler::lockSessionCollections() { + this->sessionHandlersMutex.lock(); + this->ep->endpointSessionsMutex.lock(); +} + +void EndpointHandler::unlockSessionCollections() { + this->sessionHandlersMutex.unlock(); + this->ep->endpointSessionsMutex.unlock(); } EndpointHandler::~EndpointHandler() { @@ -386,6 +401,7 @@ void OverseerHandler::updateFrontEndpointName(Endpoint* ep) { } void OverseerHandler::reviseEndpointShowing(std::wstring endpointId, EndpointState state) { + //TODO: Race condition!!!!! std::vector allHandlers; allHandlers.insert(allHandlers.end(), this->captureEndpointHandlers.begin(), this->captureEndpointHandlers.end()); allHandlers.insert(allHandlers.end(), this->playbackEndpointHandlers.begin(), this->playbackEndpointHandlers.end()); diff --git a/src/cont/contclasses.h b/src/cont/contclasses.h index ac4ecdc..9d43088 100644 --- a/src/cont/contclasses.h +++ b/src/cont/contclasses.h @@ -76,6 +76,9 @@ public: void removeSessionFromFront(SessionHandler* sh); void deleteSessions(); void createSessionHandlers(); + std::mutex sessionHandlersMutex; + void lockSessionCollections(); + void unlockSessionCollections(); ~EndpointHandler(); private: diff --git a/src/cont/contsessionclasses.cpp b/src/cont/contsessionclasses.cpp index b63631e..fd53e62 100644 --- a/src/cont/contsessionclasses.cpp +++ b/src/cont/contsessionclasses.cpp @@ -5,7 +5,7 @@ SessionHandler::SessionHandler(EndpointHandler* eph, Session* session, size_t id this->eph = eph; this->idx = idx; this->session = session; - + this->svi.mainVolume = session->getVolume(AudioChannel::CHANNEL_MAIN); this->svi.muted = session->getMute(); this->svi.caller = osh->getGuid(); diff --git a/src/debug.h b/src/debug.h index d4b9195..61855c1 100644 --- a/src/debug.h +++ b/src/debug.h @@ -58,7 +58,7 @@ std::bitset varToBitset(T info) { OutputDebugStringW(std::wstring(L"[DEBUG] (" + std::wstring(WFILE) + L":" + std::to_wstring(__LINE__) + L"): " + std::wstring(str) +L"\n").c_str()); \ } while (0) -#endif +#endif //_WIN32 #define log_to_file(fmt, cnt...) do { \ if(writable) fprintf_s(fileLog, fmt,##cnt); \ @@ -76,7 +76,7 @@ std::bitset varToBitset(T info) { #define initialize_file_log() false #define close_file_log_buffer() #define PIPE_NAME "Mixerq" -#endif +#endif //DEBUG /* Here as a quick reference, in case smthn similar is needed again */ /* typedef void (EndpointWidget::*epwMuteFunc)(bool muted); */ diff --git a/src/global.h b/src/global.h index ba5678b..88f2dae 100644 --- a/src/global.h +++ b/src/global.h @@ -11,6 +11,7 @@ #include #include #include +#include #include "debug.h" //#include "settings.h" diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 376cdcd..07adb81 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -750,7 +750,9 @@ EndpointWidget::~EndpointWidget() { timer->stop(); delete timer; this->eph->setFrontVisibilityInfo(EndpointState::ENDPOINT_ALL, INT_MAX); + this->eph->lockSessionCollections(); this->eph->deleteSessions(); + this->eph->unlockSessionCollections(); for(auto sw : sessionWidgets) { delete sw; } diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index 1ca937d..6d1ec61 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -203,7 +203,7 @@ private slots: private: //std::vector *ephs; - bool eventFilter(QObject *object, QEvent *event); + bool eventFilter(QObject *object, QEvent *event) override; void flushRoleChanges(); void changeFrontDefaults(Roles role, EndpointWidget* newDef, EndpointWidget* oldDef); From 59a92fa34b23200e5e964080d10996ebc3b5bf52 Mon Sep 17 00:00:00 2001 From: Hane Date: Wed, 15 Jan 2025 21:47:35 +0100 Subject: [PATCH 69/78] fixed race cons ep/eph coll, unwanted session state update crash + some minor code cleanup --- src/back/backlasses.cpp | 173 ++++++++++++++++++-------------- src/back/backlasses.h | 17 ++-- src/back/backsessionclasses.cpp | 6 +- src/cont/contclasses.cpp | 53 ++++------ src/cont/contclasses.h | 9 +- src/qt/qtclasses.cpp | 9 +- src/qtestmain.cpp | 2 +- 7 files changed, 143 insertions(+), 126 deletions(-) diff --git a/src/back/backlasses.cpp b/src/back/backlasses.cpp index 517e10d..a2271ea 100644 --- a/src/back/backlasses.cpp +++ b/src/back/backlasses.cpp @@ -48,8 +48,8 @@ HRESULT EndpointNewSessionCallback::OnSessionCreated(IAudioSessionControl *NewSe Session* newSession = new Session(this->eph->getEndpoint(), sessionControl); SessionThreadParams tp = { .eph = this->eph, .session = newSession, .isDelete = false }; - std::thread sessionThread(EndpointNewSessionCallback::createSessionThread, tp); - sessionThread.detach(); + std::thread newSessionThread(EndpointNewSessionCallback::createSessionThread, tp); + newSessionThread.detach(); } return S_OK; @@ -97,43 +97,55 @@ HRESULT EndpointVolumeCallback::QueryInterface(REFIID riid, VOID **ppvInterface) HRESULT EndpointVolumeCallback::OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA pNotify) { if (pNotify == NULL) return E_INVALIDARG; + AUDIO_VOLUME_NOTIFICATION_DATA paramCopy; + memcpy(¶mCopy, pNotify, sizeof(AUDIO_VOLUME_NOTIFICATION_DATA)); + float* channelVolumes = (float*)malloc(pNotify->nChannels * sizeof(float)); + for (int i = 0; i < pNotify->nChannels; i++) { + channelVolumes[i] = pNotify->afChannelVolumes[i]; + } + std::thread updateVolumeThread(&EndpointVolumeCallback::updateVolumeInfo, this, paramCopy, channelVolumes); + updateVolumeThread.detach(); + return S_OK; +} + +void EndpointVolumeCallback::updateVolumeInfo(AUDIO_VOLUME_NOTIFICATION_DATA newVolume, float* channelVolumes) { //delete osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller; //osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller.freeData4(); //Could've made a function or = override to hide this within Nguid, but back in cont = bad. osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller.data1 \ - = pNotify->guidEventContext.Data1; + = newVolume.guidEventContext.Data1; osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller.data2 \ - = pNotify->guidEventContext.Data2; + = newVolume.guidEventContext.Data2; osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller.data3 \ - = pNotify->guidEventContext.Data3; + = newVolume.guidEventContext.Data3; for(int i = 0; i < 8 /* Data4 size */; i++){ - osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller.data4[i] = pNotify->guidEventContext.Data4[i]; + osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller.data4[i] = newVolume.guidEventContext.Data4[i]; } - //memcpy(&osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller, &pNotify->guidEventContext,sizeof(NGuid) ); - Flows flow = this->ep->getFlow(); - EndpointHandler* eph = nullptr; - if (flow & Flows::FLOW_PLAYBACK) { - eph = osh->getPlaybackEndpointHandlers().at(this->ep->getIndex()); - } else { - eph = osh->getCaptureEndpointHandlers().at(this->ep->getIndex()); - } + //memcpy(&osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller, &newVolume.guidEventContext,sizeof(NGuid) ); + Flows flow = this->ep->getFlow(); + EndpointHandler* eph = nullptr; + if (flow & Flows::FLOW_PLAYBACK) { + eph = osh->getPlaybackEndpointHandlers().at(this->ep->getIndex()); + } else { + eph = osh->getCaptureEndpointHandlers().at(this->ep->getIndex()); + } - eph->getCallbackInfo()->muted = pNotify->bMuted; - eph->getCallbackInfo()->mainVolume = pNotify->fMasterVolume; - eph->getCallbackInfo()->channels = pNotify->nChannels; + eph->getCallbackInfo()->muted = newVolume.bMuted; + eph->getCallbackInfo()->mainVolume = newVolume.fMasterVolume; + eph->getCallbackInfo()->channels = newVolume.nChannels; - UINT j = 0; - //todo: do while here caused stack corruption; sus - while(j < pNotify->nChannels) { - if (flow & Flows::FLOW_PLAYBACK) - eph->getCallbackInfo()->channelVolumes[j] = pNotify->afChannelVolumes[j]; - else - eph->getCallbackInfo()->channelVolumes[j] = pNotify->afChannelVolumes[j]; - j++; - } - return S_OK; + UINT j = 0; + //todo: do while here caused stack corruption; sus + while(j < newVolume.nChannels) { + if (flow & Flows::FLOW_PLAYBACK) + eph->getCallbackInfo()->channelVolumes[j] = channelVolumes[j]; + else + eph->getCallbackInfo()->channelVolumes[j] = channelVolumes[j]; + j++; + } + free(channelVolumes); } @@ -210,31 +222,34 @@ HRESULT EndpointSituationCallback::OnDeviceRemoved(LPCWSTR pwstrDeviceId) { HRESULT EndpointSituationCallback::OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState) { std::wstring endpointId = std::wstring(pwstrDeviceId); + EndpointState newState; switch (dwNewState) { case DEVICE_STATE_ACTIVE: - osh->reviseEndpointShowing(endpointId, EndpointState::ENDPOINT_ACTIVE); + newState = EndpointState::ENDPOINT_ACTIVE; break; case DEVICE_STATE_DISABLED: - osh->reviseEndpointShowing(endpointId, EndpointState::ENDPOINT_DISABLED); + newState = EndpointState::ENDPOINT_DISABLED; break; case DEVICE_STATE_NOTPRESENT: - osh->reviseEndpointShowing(endpointId, EndpointState::ENDPOINT_NOTPRESENT); + newState = EndpointState::ENDPOINT_NOTPRESENT; break; case DEVICE_STATE_UNPLUGGED: - osh->reviseEndpointShowing(endpointId, EndpointState::ENDPOINT_UNPLUGGED); + newState = EndpointState::ENDPOINT_UNPLUGGED; break; } - + std::thread newEndpointThread(&OverseerHandler::reviseEndpointShowing, osh, + endpointId, newState); + newEndpointThread.detach(); return S_OK; } HRESULT EndpointSituationCallback::OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key) { - //TODO: New thread + Mutex - os->updateEndpointInfo(std::wstring(pwstrDeviceId)); + std::thread propertyThread(&Overseer::updateEndpointInfo, os, std::wstring(pwstrDeviceId)); + propertyThread.detach(); return S_OK; } -Endpoint::Endpoint(IMMDevice* ep, IPolicyConfig7* policyConfig, uint64_t idx){ +Endpoint::Endpoint(IMMDevice* ep, IPolicyConfig7* policyConfig, uint64_t idx) { this->endpoint = ep; this->idx = idx; this->policyConfig = policyConfig; @@ -242,7 +257,9 @@ Endpoint::Endpoint(IMMDevice* ep, IPolicyConfig7* policyConfig, uint64_t idx){ * It can't multiflag, it's that stupid. MS momento. * Only shows most relevant flag according to MS, i.e. 0110 sends 0010 */ - if(FAILED(endpoint->GetState(&this->endpointState))) {exit(-2);}; + DWORD state; + if(FAILED(endpoint->GetState(&state))) {exit(-2);}; + this->endpointState = (EndpointState)state; if(this->endpointState == EndpointState::ENDPOINT_ACTIVE) { activateEndpointVolume(); @@ -253,11 +270,9 @@ Endpoint::Endpoint(IMMDevice* ep, IPolicyConfig7* policyConfig, uint64_t idx){ } - reloadEndpointChannels(); - //todo: atexit into exit Gather ID LPWSTR tempString = nullptr; - if (FAILED(endpoint->GetId(&tempString))) {exit(-1);}; + if (FAILED(endpoint->GetId(&tempString))) { exit(-1); }; endpointId = std::wstring(tempString); log_wdebugcpp(endpointId); CoTaskMemFree(tempString); @@ -265,6 +280,8 @@ Endpoint::Endpoint(IMMDevice* ep, IPolicyConfig7* policyConfig, uint64_t idx){ endpoint->OpenPropertyStore(STGM_READ, &properties); this->updateName(); this->setFlow(); + + reloadEndpointChannels(); } void Endpoint::updateName() { @@ -319,28 +336,19 @@ void Endpoint::addSession(Session* session) { } void Endpoint::activateEndpointVolume() { - //bool extraThread = false; - /* - * Forgive me, for MS has sinned, and now I must too. - */ - if (this->endpointVolume == nullptr){ - HRESULT result = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); + //If this EP is created after init, COM won't be initialized on the executing thread. + HRESULT result = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); + if (this->endpointVolume == nullptr) { endpoint->Activate(IID_IAudioEndpointVolume, CLSCTX_ALL, NULL, (void**)&this->endpointVolume); //todo: check header if(FAILED(endpoint->Activate(__uuidof(IAudioMeterInformation), - CLSCTX_ALL, NULL, (void**)&endpointPeakMeter))) { log_debugcpp("peakbros..."); } - // - - //if (endpointVolume == nullptr) { //why they returning 0 after dealing with the error jfc CO_E_NOTINITIALIZED) { - //CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE); - //extraThread = true; - //goto initialized; - //} - //log_debugcpp(std::string("no endpointVolume (IAudioEndpointVolume)")); - if (result == S_OK) - CoUninitialize(); + CLSCTX_ALL, NULL, (void**)&endpointPeakMeter))) { + log_debugcpp("peakbros..."); + } } + if (result == S_OK) + CoUninitialize(); } void Endpoint::reloadEndpointChannels() { @@ -393,7 +401,7 @@ bool Endpoint::getMute(){ return mute; } -void Endpoint::setState(uint8_t state){ +void Endpoint::setState(EndpointState state){ this->endpointState = state; if(state == EndpointState::ENDPOINT_ACTIVE) { this->activateEndpointVolume(); @@ -401,7 +409,7 @@ void Endpoint::setState(uint8_t state){ } } -size_t Endpoint::getState(){ +EndpointState Endpoint::getState(){ return this->endpointState; } @@ -409,9 +417,13 @@ void Endpoint::setVolume(NGuid guid, int channel, float volume) { //TIP: There used to be log messages here. Now, it's a ghost town. GUID tempMsGuid = NGuidToGUID(guid); if (channel == AudioChannel::CHANNEL_MAIN) { - if(FAILED(endpointVolume->SetMasterVolumeLevelScalar(volume, &tempMsGuid))) {}; + if(FAILED(endpointVolume->SetMasterVolumeLevelScalar(volume, &tempMsGuid))) { + log_wdebugcpp(L"Master volume failed for endpoint: " + friendlyName); + }; } else { - if(FAILED(endpointVolume->SetChannelVolumeLevelScalar(channel, volume, &tempMsGuid))) {}; + if(FAILED(endpointVolume->SetChannelVolumeLevelScalar(channel, volume, &tempMsGuid))) { + log_wdebugcpp(L"Channel " + std::to_wstring(channel) + L" volume failed for endpoint: " + friendlyName); + }; } } @@ -525,9 +537,8 @@ Endpoint::~Endpoint(){ } void Overseer::initCOMLibrary() { - OutputDebugStringW(L"EPWidget creation\n"); if(FAILED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE))) { - log_debugcpp("si"); }; + log_debugcpp("Not even COM?"); }; //Retrieving endpoint enumerator @@ -535,7 +546,7 @@ void Overseer::initCOMLibrary() { if(FAILED(CoCreateInstance( __uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&deviceEnumerator)) ) - { log_debugcpp("si"); }; + { log_debugcpp("No MMDeviceEnum. Weird."); }; GUID tempGuid; if(FAILED(CoCreateGuid(&tempGuid))) { log_debugcpp("Failed to obtain GUID: " ); }; @@ -551,7 +562,7 @@ void Overseer::initCOMLibrary() { //TODO: Uninitialize COM } -void Overseer::reloadEndpoints(Flows flow) { +void Overseer::createEndpoints(Flows flow) { IMMDeviceCollection *deviceCollection; unsigned int numEndpoints; EDataFlow MSflow = (flow == Flows::FLOW_PLAYBACK ? EDataFlow::eRender : EDataFlow::eCapture); @@ -631,9 +642,15 @@ void Overseer::reloadEndpoints(Flows flow) { } Endpoint* Overseer::addEndpoint(std::wstring endpointId, /* out */Flows* flow = nullptr) { + //This method is only called from the new endpoint callback and its subsequent thread, + //so another STA can be safely instantiated + if(FAILED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE))) { + log_debugcpp("EP Callback Thread failed. Sad!"); + return nullptr; + } IMMDevice* newep; if(FAILED(deviceEnumerator->GetDevice((LPCWSTR)endpointId.c_str(), &newep))) - log_debugcpp("ay caramba con la hot metida."); + log_debugcpp("ay caramba con la hot metida. Sad!"); Endpoint *endpoint = new Endpoint(newep, policyConfig); @@ -646,10 +663,11 @@ Endpoint* Overseer::addEndpoint(std::wstring endpointId, /* out */Flows* flow = this->captureDevices.push_back(endpoint); } if (flow != nullptr) *flow = getFlow; + CoUninitialize(); return endpoint; } -Overseer::Overseer() : epsc(this){ +Overseer::Overseer() : epsc(this) { log_debugcpp("Initializing Overseer"); //Storing exe path for later use (mainly "Run on startup") @@ -669,20 +687,19 @@ Overseer::Overseer() : epsc(this){ log_debugcpp("-Initializing COM"); initCOMLibrary(); - //Obtaining playback endpoint collection on this point in time - reloadEndpoints(Flows::FLOW_PLAYBACK); + //Obtaining playback endpoint collection + createEndpoints(Flows::FLOW_PLAYBACK); //reloadEndpoints(Flows::FLOW_CAPTURE); - - //Registering for endpoint information callback - //this->epsc.fill(deviceEnumerator, playbackDevices, captureDevices); - //this->epsc.fill(deviceEnumerator); - if(FAILED(deviceEnumerator->RegisterEndpointNotificationCallback(((IMMNotificationClient*)&epsc)))) { log_debugcpp("when no enchufas......"); } } NGuid Overseer::getGuid() { return guid; } +void Overseer::registerEndpointSituationCallback() { + if(FAILED(deviceEnumerator->RegisterEndpointNotificationCallback(((IMMNotificationClient*)&epsc)))) { log_debugcpp("when no enchufas......"); } +} + std::vector Overseer::getPlaybackEndpoints() { return playbackDevices; } @@ -692,19 +709,22 @@ std::vector Overseer::getCaptureEndpoints() { } void Overseer::updateEndpointInfo(std::wstring endpointId) { - log_wdebugcpp(L"new name Endpoint id: " + endpointId); //todo: reintroduce capture devices + playbackMutex.lock(); + log_wdebugcpp(L"new name Endpoint id: " + endpointId); for(auto ep : playbackDevices) { - if (ep->getId() == endpointId) { + if (ep->getId() == endpointId && ep->getState() == EndpointState::ENDPOINT_ACTIVE) { ep->updateName(); osh->updateFrontEndpointName(ep); break; } } + playbackMutex.unlock(); } Overseer::~Overseer(){ - log_debugcpp("cum"); + //Overseer is never deleted. This is to annotate what would need to be taken care of. + log_debugcpp("jej"); deviceEnumerator->Release(); for(unsigned long long i = 0; i < playbackDevices.size(); i++){ delete(playbackDevices.at(i)); @@ -885,7 +905,6 @@ bool Environment::checkStartup(HKEY rootKeyFlags) { } void Environment::updateStartupConfig(bool onStartup) { - DWORD typeReturned; wchar_t regSubKey[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\Run\\"; if(!onStartup) { diff --git a/src/back/backlasses.h b/src/back/backlasses.h index 511774b..5876b4b 100644 --- a/src/back/backlasses.h +++ b/src/back/backlasses.h @@ -58,8 +58,8 @@ class Endpoint { float getVolume(int channel); void setMute(NGuid guid, bool muted); bool getMute(); - void setState(uint8_t state); - size_t getState(); + void setState(EndpointState state); + EndpointState getState(); Roles getRoles(); void setRoles(Roles role); void assignRoles(Roles role); @@ -101,7 +101,7 @@ class Endpoint { std::wstring descriptionName; std::wstring deviceName; std::wstring endpointId; - unsigned long endpointState; + EndpointState endpointState; Roles endpointRoles = (Roles)0; uint64_t idx; //Not implemented in llvm-mingw. Sad! todo: mingw patch @@ -118,6 +118,7 @@ class EndpointVolumeCallback : public IAudioEndpointVolumeCallback { ULONG Release(); HRESULT QueryInterface(REFIID riid, VOID **ppvInterface); HRESULT OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA update); + void updateVolumeInfo(AUDIO_VOLUME_NOTIFICATION_DATA newVolume, float* channelVolumes); //~EndpointVolumeCallback(); private: @@ -146,15 +147,18 @@ class Overseer { public: Overseer(); + void registerEndpointSituationCallback(); NGuid getGuid(); std::vector getPlaybackEndpoints(); std::vector getCaptureEndpoints(); void updateEndpointInfo(std::wstring endpointId); - void reloadEndpoints(Flows flow); + void createEndpoints(Flows flow); Endpoint* addEndpoint(std::wstring endpointId, /* out */ Flows* flow); + std::mutex playbackMutex; + std::mutex captureMutex; //void setEndpointStatusCallback(); //void setEndpointStatusCallback(); @@ -166,8 +170,6 @@ class Overseer { ~Overseer(); private: - std::wstring exeAbsPath; - void initCOMLibrary(); NGuid guid; @@ -177,7 +179,6 @@ class Overseer { std::vector playbackDevices; std::vector captureDevices; - IPolicyConfig7* policyConfig; friend class Endpoint; //IMMDeviceCollection *deviceCollection; @@ -222,7 +223,9 @@ namespace Environment { bool isToRunAtStartup(); uint32_t getAccentColor(); + //todo: binary path cache unused static std::wstring exeAbsPath; + static uint32_t exeAbsPathLen; static bool lightMode; static bool startup = false; static HKEY scope; diff --git a/src/back/backsessionclasses.cpp b/src/back/backsessionclasses.cpp index f98369f..5816ac5 100644 --- a/src/back/backsessionclasses.cpp +++ b/src/back/backsessionclasses.cpp @@ -93,8 +93,10 @@ HRESULT SessionStateCallback::OnStateChanged(AudioSessionState NewState) { } HRESULT SessionStateCallback::OnSessionDisconnected(AudioSessionDisconnectReason DisconnectReason) { - sh->setState(SessionState::DISCONNECTED); - sh->reviseSessionShowing(SessionState::DISCONNECTED); + if (DisconnectReason != DisconnectReasonDeviceRemoval) { + sh->setState(SessionState::DISCONNECTED); + sh->reviseSessionShowing(SessionState::DISCONNECTED); + } return S_OK; } diff --git a/src/cont/contclasses.cpp b/src/cont/contclasses.cpp index ad85647..54a349c 100644 --- a/src/cont/contclasses.cpp +++ b/src/cont/contclasses.cpp @@ -24,7 +24,6 @@ void setConfigDirToDefaults() { } EndpointHandler::EndpointHandler(uint64_t idx, Flows flow) { - //std::vector endpoints = osh->getPlaybackEndpoints().at(idx); this->idx = idx; this->flow = flow; this->ep = (flow == Flows::FLOW_PLAYBACK ? osh->getPlaybackEndpoints().at(idx) : osh->getCaptureEndpoints().at(idx)); @@ -62,16 +61,6 @@ Flows EndpointHandler::getFlow(){ return ep->getFlow(); } -/* these two, currently unused. If I use them, I should feel bad. - * Endpoint* EndpointHandler::getEndpoint() { - * return this->ep; - * } - * - * EndpointVolumeCallback* EndpointHandler::getEndpointVolumeCallback() { - * return this->epc; - * } - */ - BackEndpointVolumeCallbackInfo* EndpointHandler::getCallbackInfo(){ return &this->callbackInfo; } @@ -134,12 +123,12 @@ void EndpointHandler::setBackEndpointVolumeCallbackInfoContent(uint8_t state) { } } -void EndpointHandler::setState(uint8_t state){ +void EndpointHandler::setState(EndpointState state){ ep->setState(state); this->setBackEndpointVolumeCallbackInfoContent(state); } -void EndpointHandler::setState(uint8_t state, uint64_t index){ +void EndpointHandler::setState(EndpointState state, uint64_t index){ ep->setState(state); this->setFrontVisibilityInfo((EndpointState)state, index); this->setBackEndpointVolumeCallbackInfoContent(state); @@ -318,7 +307,7 @@ uint64_t OverseerHandler::getCaptureEndpointsCount(){ return this->os->getCaptureEndpoints().size(); } -void OverseerHandler::reloadEndpointHandlers(){ +void OverseerHandler::createEndpointHandlers(){ //todo: add capture //std::vector* ephs = new std::vector; @@ -326,9 +315,7 @@ void OverseerHandler::reloadEndpointHandlers(){ for(uint64_t i = 0; i < this->getPlaybackEndpointsCount(); i++){ log_debugcpp("Creating Playback handler " + std::to_string(i)); - - EndpointHandler* ephexx = new EndpointHandler(i, Flows::FLOW_PLAYBACK); - //this->playbackEndpointHandlers.push_back(ephexx); + new EndpointHandler(i, Flows::FLOW_PLAYBACK); log_debugcpp("Created Playback handler " + std::to_string(i) + ", adding to vector. " + " VSize: " + std::to_string(this->playbackEndpointHandlers.size())); } @@ -336,37 +323,27 @@ void OverseerHandler::reloadEndpointHandlers(){ log_debugcpp("Capture VSize: " + std::to_string(this->getCaptureEndpointsCount())); - for(uint64_t i = 0; i < this->getCaptureEndpointsCount(); i++){ + for(uint64_t i = 0; i < this->getCaptureEndpointsCount(); i++) { log_debugcpp("Creating Capture handler " + std::to_string(i)); - - /* - * if(i < (this->captureEndpointHandlers.size()) && - * this->captureEndpointHandlers.at(i) != nullptr) - * delete captureEndpointHandlers.at(i); - */ - - EndpointHandler* ephoo = new EndpointHandler(i, Flows::FLOW_CAPTURE); - //this->captureEndpointHandlers.push_back(ephoo); + new EndpointHandler(i, Flows::FLOW_CAPTURE); log_debugcpp("Created Capture handler " + std::to_string(i) + ", adding to vector. " + " VSize: " + std::to_string(this->captureEndpointHandlers.size())); - /* * if (i >= this->captureEndpointHandlers.size()) * captureEndpointHandlers.push_back(eph); * else captureEndpointHandlers.at(i) = eph; */ } - //setEndpointHandlers(ephs); + os->registerEndpointSituationCallback(); } -EndpointHandler* OverseerHandler::addEndpoint(std::wstring endpointId, /* out */ Flows *flow = nullptr){ +EndpointHandler* OverseerHandler::addEndpoint(std::wstring endpointId, /* out */ Flows *flow = nullptr) { + //This method is only called from the new endpoint callback and its subsequent thread Flows localFlow; Endpoint* newEp = this->os->addEndpoint(endpointId, &localFlow); uint64_t ephIdx = (localFlow == Flows::FLOW_PLAYBACK ? this->getPlaybackEndpointsCount() : this->getCaptureEndpointsCount()) - 1; EndpointHandler* newEph = new EndpointHandler(ephIdx, localFlow); - // std::vector getPlaybackEndpointHandlers(); - //std::vector getCaptureEndpointHandlers(); if (flow != nullptr) *flow = localFlow; return newEph; } @@ -403,6 +380,10 @@ void OverseerHandler::updateFrontEndpointName(Endpoint* ep) { void OverseerHandler::reviseEndpointShowing(std::wstring endpointId, EndpointState state) { //TODO: Race condition!!!!! std::vector allHandlers; + handlersPlaybackMutex.lock(); + handlersCaptureMutex.lock(); + os->playbackMutex.lock(); + os->captureMutex.lock(); allHandlers.insert(allHandlers.end(), this->captureEndpointHandlers.begin(), this->captureEndpointHandlers.end()); allHandlers.insert(allHandlers.end(), this->playbackEndpointHandlers.begin(), this->playbackEndpointHandlers.end()); EndpointHandler* eph = nullptr; @@ -425,11 +406,15 @@ void OverseerHandler::reviseEndpointShowing(std::wstring endpointId, EndpointSta //todo: mic done but disabled. Tab-kun will come... if (flow == Flows::FLOW_CAPTURE) return; - if(eph && EndpointState::ENDPOINT_ACTIVE & state) { + if (eph && EndpointState::ENDPOINT_ACTIVE & state) { this->addEndpointWidget(eph); - } else if (eph && eph->getFrontVisibilityState() == EndpointState::ENDPOINT_ACTIVE){ + } else if (eph && eph->getFrontVisibilityState() == EndpointState::ENDPOINT_ACTIVE) { this->removeEndpointWidget(eph->getFrontVisibilityIndex()); } + handlersPlaybackMutex.unlock(); + handlersCaptureMutex.unlock(); + os->playbackMutex.unlock(); + os->captureMutex.unlock(); return; } diff --git a/src/cont/contclasses.h b/src/cont/contclasses.h index 9d43088..91d225b 100644 --- a/src/cont/contclasses.h +++ b/src/cont/contclasses.h @@ -58,8 +58,8 @@ public: void setVolume(NGuid guid, int channel, int value); void setMute(NGuid guid, bool muted); - void setState(uint8_t state); - void setState(uint8_t state, uint64_t idx); + void setState(EndpointState state); + void setState(EndpointState state, uint64_t idx); float getPeakVolume(); /* sessions */ @@ -137,10 +137,12 @@ public: void pushBackEndpointHandler(EndpointHandler* eph, Flows flow); uint64_t getPlaybackEndpointsCount(); uint64_t getCaptureEndpointsCount(); - void reloadEndpointHandlers(); + void createEndpointHandlers(); EndpointHandler* addEndpoint(std::wstring endpointId, Flows *flow); NGuid getGuid(); + std::mutex handlersPlaybackMutex; + std::mutex handlersCaptureMutex; /* * void setSessionVolumeCallback(std::function changeSessionVolume); * void setSessionVolume(float newValue, ); @@ -150,6 +152,7 @@ private: Overseer *os; std::vector playbackEndpointHandlers; std::vector captureEndpointHandlers; + std::function changeFrontDefaults; std::function removeEndpointWidget; std::function addEndpointWidget; diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 07adb81..667ce99 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -1088,7 +1088,10 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { mainMenuBar->addWidget(hw); mainMenuBar->setMovable(false); this->addToolBar(Qt::BottomToolBarArea, mainMenuBar); - + + /* + * Create initial endpoint widgets batch + */ reloadEndpointWidgets(); /* @@ -1319,7 +1322,8 @@ void MainWindow::trayIconActivated(QSystemTrayIcon::ActivationReason reason) { void MainWindow::reloadEndpointWidgets() { size_t i = 0; ews.resize(2); - //todo: -log flag + + osh->handlersPlaybackMutex.lock(); for (size_t epwIndex = 2; i < (osh->getPlaybackEndpointHandlers().size()); i++) { if (osh->getPlaybackEndpointHandlers().at(i)->getState() == EndpointState::ENDPOINT_ACTIVE){ log_debugcpp("EPWidget creation"); @@ -1336,6 +1340,7 @@ void MainWindow::reloadEndpointWidgets() { { ews.push_back(epw); epw->setIndex(ews.size() - 1); } } } + osh->handlersPlaybackMutex.unlock(); //todo: ya no está aquí tirao como tal, y de hecho, no ha fallado de la manera arriba descrita //pero ahora lo añado porque sí solo para garantizar que está y poder reusarlo luego lmao widgetLayout->addItem(lastRowSpacer, i, 0); diff --git a/src/qtestmain.cpp b/src/qtestmain.cpp index b036c72..d264d01 100644 --- a/src/qtestmain.cpp +++ b/src/qtestmain.cpp @@ -121,7 +121,7 @@ int main (int argc, char* argv[]) { //INIT CONT log_debugcpp("main init"); - osh->reloadEndpointHandlers(); + osh->createEndpointHandlers(); log_debugcpp("Reloaded endpoint handlers"); //INIT FRONT From f0d263d96e611aba980e23edcdbf8c7f54100eb9 Mon Sep 17 00:00:00 2001 From: Hane Date: Wed, 15 Jan 2025 21:49:41 +0100 Subject: [PATCH 70/78] changed misleading log message --- src/settings.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/settings.cpp b/src/settings.cpp index 6845254..f76e73c 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -193,7 +193,11 @@ namespace ini { (create ? OPEN_ALWAYS : OPEN_EXISTING), NULL); if(settingsHandle == INVALID_HANDLE_VALUE) { + std::string createString = std::string(create ? (char*)"create" : (char*)"open"); log_debugcpp("[SET] Can't create settings file: " + std::to_string(GetLastError())); + log_debugcpp("[SET] Can't " + createString + \ + " settings file on: " + std::string(path) + \ + ", error: " + std::to_string(GetLastError())); releaseBeforeReturn(); return nullptr; } From c60b6f71ed7417ed07867c279636d80d2cec2c81 Mon Sep 17 00:00:00 2001 From: Hane Date: Wed, 15 Jan 2025 21:50:28 +0100 Subject: [PATCH 71/78] updated build script & small installer name fix --- bueno.bat | 5 ++++- install/installer.nsi | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/bueno.bat b/bueno.bat index 3e2ab0d..37b5dec 100644 --- a/bueno.bat +++ b/bueno.bat @@ -1,4 +1,7 @@ -taskkill /F /IM "qtest.exe" +taskkill /F /IM "MixerQ.exe" +taskkill /F /IM "MixerQd.exe" qmake -o build\Makefile .\qtest.pro mingw32-make.exe -C .\build -f Makefile.Release mingw32-make.exe -C .\build -f Makefile.Debug +makensis /DBUILDTYPE=release install\installer.nsi +makensis /DBUILDTYPE=debug install\installer.nsi diff --git a/install/installer.nsi b/install/installer.nsi index 9181444..7e62045 100644 --- a/install/installer.nsi +++ b/install/installer.nsi @@ -1,6 +1,6 @@ ;Auto versioning------------------------------- - !makensis "/DBUILDTYPE=$BUILDTYPE version.nsi" + !makensis "/DBUILDTYPE=${BUILDTYPE} version.nsi" !system "GetVersion.exe" !include "Version.txt" ;optional cleanup From a3d00be3fe489437f064de72e7761b188345e43a Mon Sep 17 00:00:00 2001 From: Hane Date: Thu, 16 Jan 2025 18:09:02 +0100 Subject: [PATCH 72/78] fixed callback not released + unneded env func param --- src/back/backlasses.cpp | 2 +- src/back/backlasses.h | 2 +- src/cont/contclasses.cpp | 2 +- src/cont/contsessionclasses.cpp | 4 ++++ src/cont/contsessionclasses.h | 2 +- 5 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/back/backlasses.cpp b/src/back/backlasses.cpp index a2271ea..7219279 100644 --- a/src/back/backlasses.cpp +++ b/src/back/backlasses.cpp @@ -741,7 +741,7 @@ wchar_t* Environment::getExeAbsPath(uint32_t *exeAbsPathLength) { return exeAbsPath; } -std::string Environment::createSettingsPath(SettingsTargetDirectory target, bool create) { +std::string Environment::createSettingsPath(SettingsTargetDirectory target) { wchar_t* settingsPath = nullptr; wchar_t settingsFile[] = L"\\settings.ini"; uint32_t settingsFileLen = (sizeof(settingsFile) / sizeof(wchar_t)) - 1; diff --git a/src/back/backlasses.h b/src/back/backlasses.h index 5876b4b..4fdf888 100644 --- a/src/back/backlasses.h +++ b/src/back/backlasses.h @@ -211,7 +211,7 @@ class EndpointNewSessionCallback : public IAudioSessionNotification { namespace Environment { wchar_t* getExeAbsPath(uint32_t *exeAbsPathLength); - std::string createSettingsPath(SettingsTargetDirectory target, bool create); + std::string createSettingsPath(SettingsTargetDirectory target); void populateSystemValues(); void openControlPanel(); ProcessedNativeEvent processTopLevelWindowMessage(void* msg); diff --git a/src/cont/contclasses.cpp b/src/cont/contclasses.cpp index 54a349c..3cfa3da 100644 --- a/src/cont/contclasses.cpp +++ b/src/cont/contclasses.cpp @@ -3,7 +3,7 @@ void setConfigDirToDefaults() { #define tryFileDir(dir, create) do { \ - OverseerHandler::settingsPath = Environment::createSettingsPath(dir, create); \ + OverseerHandler::settingsPath = Environment::createSettingsPath(dir); \ set = ini::UserSettings::createSettings(OverseerHandler::settingsPath.c_str(), create); \ if(set) { \ return; \ diff --git a/src/cont/contsessionclasses.cpp b/src/cont/contsessionclasses.cpp index fd53e62..0f6caaf 100644 --- a/src/cont/contsessionclasses.cpp +++ b/src/cont/contsessionclasses.cpp @@ -85,3 +85,7 @@ void SessionHandler::reviseSessionShowing(SessionState state) { } } +SessionHandler::~SessionHandler() { + session->removeStateCallback(ssc); + ssc->Release(); +} diff --git a/src/cont/contsessionclasses.h b/src/cont/contsessionclasses.h index 488d3ea..455bbe1 100644 --- a/src/cont/contsessionclasses.h +++ b/src/cont/contsessionclasses.h @@ -33,7 +33,7 @@ class SessionHandler { void setName(std::wstring newName); void reviseSessionShowing(SessionState state); SessionVolumeInfo* getVolumeInfo(); - + ~SessionHandler(); private: SessionVolumeInfo svi; EndpointHandler* eph; From 0a301fb9bf815ab819737ec5d85f879109ee417c Mon Sep 17 00:00:00 2001 From: Hane Date: Thu, 16 Jan 2025 20:29:02 +0100 Subject: [PATCH 73/78] fixed new endpoints not appering if UI open + no roles / deque --- src/back/backlasses.cpp | 11 +++++++++-- src/global.h | 1 + src/qt/qtclasses.cpp | 11 +++++++---- src/qt/qtclasses.h | 3 ++- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/back/backlasses.cpp b/src/back/backlasses.cpp index 7219279..8e1a152 100644 --- a/src/back/backlasses.cpp +++ b/src/back/backlasses.cpp @@ -204,9 +204,16 @@ HRESULT EndpointSituationCallback::OnDefaultDeviceChanged(EDataFlow flow, ERole } std::wstring wstringEndpointId = pwstrDeviceId; log_wdebugcpp(L"we got za defol 4 " + wstringEndpointId); + //APTTYPE aptType; + //APTTYPEQUALIFIER aptQualifier; + //CoGetApartmentType( + // &aptType, + // &aptQualifier + // ); + //std::thread roleChangeThread(&OverseerHandler::roleBucketEntryCallback, this, + // nRole, wstringEndpointId); + //roleChangeThread.detach(); osh->roleBucketEntryCallback(nRole, wstringEndpointId); - //osh->changeFrontDefaultsCallback(nRole, wstringEndpointId); - return S_OK; } diff --git a/src/global.h b/src/global.h index 88f2dae..8799892 100644 --- a/src/global.h +++ b/src/global.h @@ -12,6 +12,7 @@ #include #include #include +#include #include "debug.h" //#include "settings.h" diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 667ce99..1712d3c 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -792,7 +792,9 @@ void MainWindow::removeEndpointWidget(CustomWidgetEvent* ev){ void MainWindow::addEndpointWidget(CustomWidgetEvent* ev){ EndpointWidget* epw = new EndpointWidget(ev->payload, containerWidget, this->ews.size()); //epw->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); - //this->widgetLayout->addWidget(epw); + epw->setParent(this); + if(this->widgetLayout) + this->widgetLayout->addWidget(epw); ews.push_back(epw); return; } @@ -984,7 +986,9 @@ HeaderWidget::HeaderWidget(QWidget *parent) : QWidget(parent) { void MainWindow::createLayout(QGridLayout *newLayout) { log_debugcpp("createLayout"); widgetLayout->removeItem(lastRowSpacer); - delete this->widgetLayout; + QGridLayout *tempStore = this->widgetLayout; + this->widgetLayout = 0; + delete tempStore; containerWidget->setLayout(newLayout); this->widgetLayout = newLayout; @@ -1191,9 +1195,8 @@ bool MainWindow::eventFilter(QObject *object, QEvent *event) { } void MainWindow::flushRoleChanges() { - //TODO: bucket list deque std::pair change = roleBucketList.front(); - roleBucketList.erase(roleBucketList.begin()); + roleBucketList.pop_front(); EndpointWidget *newDef = nullptr, *oldDef = nullptr; for (uint64_t i = 0; i < ews.size(); i++) { diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index 6d1ec61..73a6b2c 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -208,7 +208,8 @@ private: void changeFrontDefaults(Roles role, EndpointWidget* newDef, EndpointWidget* oldDef); std::vector ews; - std::vector> roleBucketList; + std::deque> roleBucketList; + std::mutex roleBucketMutex; QWidget *containerWidget; QGridLayout *widgetLayout; From 109330dbbdcb873c3e3d21f7ecd82312dbc44cfe Mon Sep 17 00:00:00 2001 From: Hane Date: Thu, 16 Jan 2025 22:11:10 +0100 Subject: [PATCH 74/78] fixed lots of edge cases revolving role changes --- src/back/backlasses.cpp | 9 --------- src/qt/qtclasses.cpp | 41 ++++++++++++++++++++++++++++------------- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/src/back/backlasses.cpp b/src/back/backlasses.cpp index 8e1a152..4b64b8e 100644 --- a/src/back/backlasses.cpp +++ b/src/back/backlasses.cpp @@ -204,15 +204,6 @@ HRESULT EndpointSituationCallback::OnDefaultDeviceChanged(EDataFlow flow, ERole } std::wstring wstringEndpointId = pwstrDeviceId; log_wdebugcpp(L"we got za defol 4 " + wstringEndpointId); - //APTTYPE aptType; - //APTTYPEQUALIFIER aptQualifier; - //CoGetApartmentType( - // &aptType, - // &aptQualifier - // ); - //std::thread roleChangeThread(&OverseerHandler::roleBucketEntryCallback, this, - // nRole, wstringEndpointId); - //roleChangeThread.detach(); osh->roleBucketEntryCallback(nRole, wstringEndpointId); return S_OK; } diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 1712d3c..ff5cb51 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -626,6 +626,7 @@ EndpointWidget::EndpointWidget(EndpointHandler* eph, QWidget *parent, uint64_t i defaultRolesCheckBoxes.at(role)->setDisabled(assignedRoles optor role ? true : false); \ defaultRolesCheckBoxes.at(role)->setText(STRING_##role); \ connect(defaultRolesCheckBoxes.at(role), &QCheckBox::stateChanged,[this] { \ + defaultRolesCheckBoxes.at(role)->setChecked(!(defaultRolesCheckBoxes.at(role)->isChecked())); \ this->eph->setRoles(role); \ }); \ widgetLayout->addWidget(defaultRolesCheckBoxes.at(role), row, col++); \ @@ -1200,6 +1201,7 @@ void MainWindow::flushRoleChanges() { EndpointWidget *newDef = nullptr, *oldDef = nullptr; for (uint64_t i = 0; i < ews.size(); i++) { + if(newDef && oldDef) break; auto epw = this->ews.at(i); if (!epw) continue; if (epw->getEndpointHandler()->getId() == change.second) { @@ -1218,20 +1220,28 @@ void MainWindow::flushRoleChanges() { } void MainWindow::changeFrontDefaults(Roles role, EndpointWidget* newDef, EndpointWidget* oldDef) { - //Sigh... I hope to get this right when librole is done... - //todo: STILL BORKED. THEY'RE NOT FORFEITING THEIR OLD POSITION - //debug if (role != Roles::ROLE_COMMUNICATIONS) return; + //Sigh... MS's naive (non)API didn't help, but... + //Since widgets are removed previously, they must be added back. + //This produces unneeded vector size increases. + //Also, there's no freaking way this must be this illegible. + //TODO: Rewrite this method. Seriously. You'll have to get to, someday. if (newDef && !oldDef) { newDef->getDefaultRolesWidgets().at(role)->blockSignals(true); - newDef->getEndpointHandler()->assignRoles(role); - QCoreApplication::instance()->postEvent(newDef->getDefaultRolesWidgets().at(role), new QEvent((QEvent::Type)CustomQEvent::EndpointDefaultChange)); + uint8_t newDefCurRolesReversed = ~(newDef->getEndpointHandler()->getRoles()); + if(newDefCurRolesReversed & role) { + newDef->getEndpointHandler()->assignRoles(role); + QCoreApplication::instance()->sendEvent(newDef->getDefaultRolesWidgets().at(role), new QEvent((QEvent::Type)CustomQEvent::EndpointDefaultChange)); + } uint8_t newDefIdx = newDef->getIndex(); uint8_t newDefRoles = newDef->getEndpointHandler()->getRoles(); if (newDefRoles == Roles::ROLE_ALL) { newDef->setIndex(0); this->ews[0] = newDef; - //if (this->ews[1] && newDef->getEndpointHandler()->getId() == endpointId) this->ews[1] = nullptr; - QCoreApplication::instance()->postEvent(newDef->getDefaultRolesWidgets().at(Roles::ROLE_ALL), new QEvent((QEvent::Type)CustomQEvent::EndpointDefaultChange)); + if(newDefCurRolesReversed & role) { + newDef->getDefaultRolesWidgets().at(Roles::ROLE_ALL)->blockSignals(true); + QCoreApplication::instance()->sendEvent(newDef->getDefaultRolesWidgets().at(Roles::ROLE_ALL), new QEvent((QEvent::Type)CustomQEvent::EndpointDefaultChange)); + newDef->getDefaultRolesWidgets().at(Roles::ROLE_ALL)->blockSignals(false); + } } else if ((newDefRoles & Roles::ROLE_MULTIMEDIA) || (newDefRoles & Roles::ROLE_CONSOLE)){ newDef->setIndex(0); @@ -1246,14 +1256,15 @@ void MainWindow::changeFrontDefaults(Roles role, EndpointWidget* newDef, Endpoin else if (oldDef && newDef) { newDef->getDefaultRolesWidgets().at(role)->blockSignals(true); newDef->getEndpointHandler()->assignRoles(role); - QCoreApplication::instance()->postEvent(newDef->getDefaultRolesWidgets().at(role), new QEvent((QEvent::Type)CustomQEvent::EndpointDefaultChange)); + QCoreApplication::instance()->sendEvent(newDef->getDefaultRolesWidgets().at(role), new QEvent((QEvent::Type)CustomQEvent::EndpointDefaultChange)); uint8_t newDefIdx = newDef->getIndex(); uint8_t newDefRoles = newDef->getEndpointHandler()->getRoles(); if (newDefRoles == Roles::ROLE_ALL) { newDef->setIndex(0); this->ews[0] = newDef; - //if (this->ews[1] && newDef->getEndpointHandler()->getId() == endpointId) this->ews[1] = nullptr; - QCoreApplication::instance()->postEvent(newDef->getDefaultRolesWidgets().at(Roles::ROLE_ALL), new QEvent((QEvent::Type)CustomQEvent::EndpointDefaultChange)); + newDef->getDefaultRolesWidgets().at(Roles::ROLE_ALL)->blockSignals(true); + QCoreApplication::instance()->sendEvent(newDef->getDefaultRolesWidgets().at(Roles::ROLE_ALL), new QEvent((QEvent::Type)CustomQEvent::EndpointDefaultChange)); + newDef->getDefaultRolesWidgets().at(Roles::ROLE_ALL)->blockSignals(false); } else if ((newDefRoles & Roles::ROLE_MULTIMEDIA) || (newDefRoles & Roles::ROLE_CONSOLE)){ newDef->setIndex(0); @@ -1267,9 +1278,13 @@ void MainWindow::changeFrontDefaults(Roles role, EndpointWidget* newDef, Endpoin oldDef->getDefaultRolesWidgets().at(role)->blockSignals(true); uint8_t oldDefRoles = oldDef->getEndpointHandler()->getRoles(); - if (oldDefRoles == Roles::ROLE_ALL) QCoreApplication::instance()->postEvent(oldDef->getDefaultRolesWidgets().at(Roles::ROLE_ALL), new QEvent((QEvent::Type)CustomQEvent::EndpointDefaultChange)); + if (oldDefRoles == Roles::ROLE_ALL) { + oldDef->getDefaultRolesWidgets().at(Roles::ROLE_ALL)->blockSignals(true); + QCoreApplication::instance()->sendEvent(oldDef->getDefaultRolesWidgets().at(Roles::ROLE_ALL), new QEvent((QEvent::Type)CustomQEvent::EndpointDefaultChange)); + oldDef->getDefaultRolesWidgets().at(Roles::ROLE_ALL)->blockSignals(false); + } oldDef->getEndpointHandler()->removeRoles(role); - QCoreApplication::instance()->postEvent(oldDef->getDefaultRolesWidgets().at(role), new QEvent((QEvent::Type)CustomQEvent::EndpointDefaultChange)); + QCoreApplication::instance()->sendEvent(oldDef->getDefaultRolesWidgets().at(role), new QEvent((QEvent::Type)CustomQEvent::EndpointDefaultChange)); oldDefRoles = oldDef->getEndpointHandler()->getRoles(); //this->ews[oldDef->getIndex()] = nullptr; if ((oldDefRoles & Roles::ROLE_MULTIMEDIA) && @@ -1291,7 +1306,7 @@ void MainWindow::changeFrontDefaults(Roles role, EndpointWidget* newDef, Endpoin log_debugcpp("oldDef new idx: " + std::to_string(oldDef->getIndex())); oldDef->getDefaultRolesWidgets().at(role)->blockSignals(false); } - QCoreApplication::instance()->postEvent + QCoreApplication::instance()->sendEvent (this, new QEvent((QEvent::Type)CustomQEvent::RecomposeMainWindow)); } From 53f9506115b2b7f1df5122b8bc07eb3013059274 Mon Sep 17 00:00:00 2001 From: Hane Date: Sat, 18 Jan 2025 18:01:35 +0100 Subject: [PATCH 75/78] fixed newly arisen race cons (busywaits) --- src/back/backlasses.cpp | 31 +++++++++++++++++++++++++++++-- src/back/backlasses.h | 10 ++++++++-- src/cont/contclasses.cpp | 20 ++++++++++++++++++-- src/cont/contclasses.h | 9 ++++----- 4 files changed, 59 insertions(+), 11 deletions(-) diff --git a/src/back/backlasses.cpp b/src/back/backlasses.cpp index 4b64b8e..cead61f 100644 --- a/src/back/backlasses.cpp +++ b/src/back/backlasses.cpp @@ -48,8 +48,10 @@ HRESULT EndpointNewSessionCallback::OnSessionCreated(IAudioSessionControl *NewSe Session* newSession = new Session(this->eph->getEndpoint(), sessionControl); SessionThreadParams tp = { .eph = this->eph, .session = newSession, .isDelete = false }; - std::thread newSessionThread(EndpointNewSessionCallback::createSessionThread, tp); + wait = true; + std::thread newSessionThread(&EndpointNewSessionCallback::createSessionThread, this, tp); newSessionThread.detach(); + while(wait); } return S_OK; @@ -57,6 +59,7 @@ HRESULT EndpointNewSessionCallback::OnSessionCreated(IAudioSessionControl *NewSe void EndpointNewSessionCallback::createSessionThread(SessionThreadParams params) { params.eph->addSessionSendFront(params.session); + this->wait = false; } EndpointVolumeCallback::EndpointVolumeCallback(Endpoint* ep){ @@ -103,8 +106,10 @@ HRESULT EndpointVolumeCallback::OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA pNotify for (int i = 0; i < pNotify->nChannels; i++) { channelVolumes[i] = pNotify->afChannelVolumes[i]; } + wait = true; std::thread updateVolumeThread(&EndpointVolumeCallback::updateVolumeInfo, this, paramCopy, channelVolumes); updateVolumeThread.detach(); + while(wait); return S_OK; } @@ -112,6 +117,9 @@ void EndpointVolumeCallback::updateVolumeInfo(AUDIO_VOLUME_NOTIFICATION_DATA new //delete osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller; //osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller.freeData4(); //Could've made a function or = override to hide this within Nguid, but back in cont = bad. + osh->handlersPlaybackMutex.lock(); + osh->handlersCaptureMutex.lock(); + osh->lockEndpoints(); osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller.data1 \ = newVolume.guidEventContext.Data1; osh->getPlaybackEndpointHandlers().at(this->ep->getIndex())->getCallbackInfo()->caller.data2 \ @@ -146,9 +154,15 @@ void EndpointVolumeCallback::updateVolumeInfo(AUDIO_VOLUME_NOTIFICATION_DATA new j++; } free(channelVolumes); + osh->unlockEndpoints(); + osh->handlersPlaybackMutex.unlock(); + osh->handlersCaptureMutex.unlock(); + wait = false; } - +void EndpointVolumeCallback::reportFinished() { + this->wait = false; +} EndpointSituationCallback::EndpointSituationCallback(Overseer* os){ this->os = os; @@ -235,18 +249,26 @@ HRESULT EndpointSituationCallback::OnDeviceStateChanged(LPCWSTR pwstrDeviceId, D newState = EndpointState::ENDPOINT_UNPLUGGED; break; } + isEpStateChanging = true; std::thread newEndpointThread(&OverseerHandler::reviseEndpointShowing, osh, endpointId, newState); newEndpointThread.detach(); + while(isEpStateChanging); return S_OK; } HRESULT EndpointSituationCallback::OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key) { + isEpStateChanging = true; std::thread propertyThread(&Overseer::updateEndpointInfo, os, std::wstring(pwstrDeviceId)); propertyThread.detach(); + while(isEpStateChanging); return S_OK; } +void EndpointSituationCallback::reportFinishedStateChange() { + this->isEpStateChanging = false; +} + Endpoint::Endpoint(IMMDevice* ep, IPolicyConfig7* policyConfig, uint64_t idx) { this->endpoint = ep; this->idx = idx; @@ -665,6 +687,10 @@ Endpoint* Overseer::addEndpoint(std::wstring endpointId, /* out */Flows* flow = return endpoint; } +void Overseer::reportFinishedStateChange() { + epsc.reportFinishedStateChange(); +} + Overseer::Overseer() : epsc(this) { log_debugcpp("Initializing Overseer"); @@ -718,6 +744,7 @@ void Overseer::updateEndpointInfo(std::wstring endpointId) { } } playbackMutex.unlock(); + epsc.reportFinishedStateChange(); } Overseer::~Overseer(){ diff --git a/src/back/backlasses.h b/src/back/backlasses.h index 4fdf888..9f79924 100644 --- a/src/back/backlasses.h +++ b/src/back/backlasses.h @@ -119,11 +119,13 @@ class EndpointVolumeCallback : public IAudioEndpointVolumeCallback { HRESULT QueryInterface(REFIID riid, VOID **ppvInterface); HRESULT OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA update); void updateVolumeInfo(AUDIO_VOLUME_NOTIFICATION_DATA newVolume, float* channelVolumes); + void reportFinished(); //~EndpointVolumeCallback(); private: ULONG ref = 1; Endpoint* ep; + bool wait = false; }; class EndpointSituationCallback : public IMMNotificationClient { @@ -137,10 +139,11 @@ class EndpointSituationCallback : public IMMNotificationClient { HRESULT OnDeviceRemoved(LPCWSTR pwstrDeviceId); HRESULT OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState); HRESULT OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key); - + void reportFinishedStateChange(); private: ULONG ref = 1; Overseer* os; + bool isEpStateChanging = false; }; class Overseer { @@ -156,6 +159,8 @@ class Overseer { void createEndpoints(Flows flow); Endpoint* addEndpoint(std::wstring endpointId, /* out */ Flows* flow); + + void reportFinishedStateChange(); std::mutex playbackMutex; std::mutex captureMutex; @@ -196,9 +201,10 @@ class EndpointNewSessionCallback : public IAudioSessionNotification { ULONG Release(); HRESULT QueryInterface(REFIID riid, VOID **ppvInterface); HRESULT OnSessionCreated(IAudioSessionControl *NewSession); - static void createSessionThread(SessionThreadParams params); + void createSessionThread(SessionThreadParams params); private: + bool wait = false; ULONG ref = 1; EndpointHandler *eph; diff --git a/src/cont/contclasses.cpp b/src/cont/contclasses.cpp index 3cfa3da..27ae4a6 100644 --- a/src/cont/contclasses.cpp +++ b/src/cont/contclasses.cpp @@ -348,6 +348,10 @@ EndpointHandler* OverseerHandler::addEndpoint(std::wstring endpointId, /* out */ return newEph; } +void OverseerHandler::reportFinishedStateChange() { + os->reportFinishedStateChange(); +} + NGuid OverseerHandler::getGuid() { return this->os->getGuid(); } @@ -397,24 +401,26 @@ void OverseerHandler::reviseEndpointShowing(std::wstring endpointId, EndpointSta //debug Flows flow; if (!eph) { - if (state ^ EndpointState::ENDPOINT_ACTIVE) return; + if (state ^ EndpointState::ENDPOINT_ACTIVE) goto end; //flow = Flows::FLOW_CAPTURE; eph = osh->addEndpoint(endpointId, &flow); } else flow = eph->getFlow(); //todo: mic done but disabled. Tab-kun will come... - if (flow == Flows::FLOW_CAPTURE) return; + if (flow == Flows::FLOW_CAPTURE) goto end; if (eph && EndpointState::ENDPOINT_ACTIVE & state) { this->addEndpointWidget(eph); } else if (eph && eph->getFrontVisibilityState() == EndpointState::ENDPOINT_ACTIVE) { this->removeEndpointWidget(eph->getFrontVisibilityIndex()); } + end: handlersPlaybackMutex.unlock(); handlersCaptureMutex.unlock(); os->playbackMutex.unlock(); os->captureMutex.unlock(); + os->reportFinishedStateChange(); return; } @@ -430,3 +436,13 @@ void OverseerHandler::setEndpointHandlers(std::vector ephs){ this->playbackEndpointHandlers = ephs; } +void OverseerHandler::lockEndpoints() { + os->playbackMutex.lock(); + os->captureMutex.lock(); +} + +void OverseerHandler::unlockEndpoints() { + os->playbackMutex.unlock(); + os->captureMutex.unlock(); +} + diff --git a/src/cont/contclasses.h b/src/cont/contclasses.h index 91d225b..2034986 100644 --- a/src/cont/contclasses.h +++ b/src/cont/contclasses.h @@ -138,15 +138,14 @@ public: uint64_t getPlaybackEndpointsCount(); uint64_t getCaptureEndpointsCount(); void createEndpointHandlers(); - EndpointHandler* addEndpoint(std::wstring endpointId, Flows *flow); + EndpointHandler* addEndpoint(std::wstring endpointId, Flows *flow); + void reportFinishedStateChange(); NGuid getGuid(); std::mutex handlersPlaybackMutex; std::mutex handlersCaptureMutex; - /* - * void setSessionVolumeCallback(std::function changeSessionVolume); - * void setSessionVolume(float newValue, ); - */ + void lockEndpoints(); + void unlockEndpoints(); private: Overseer *os; From f1b734bea6397153cdf742166717a4a4c54d350c Mon Sep 17 00:00:00 2001 From: Hane Date: Sat, 18 Jan 2025 18:51:32 +0100 Subject: [PATCH 76/78] fixed visuals when adding epw/sw while window is visible --- src/qt/qtclasses.cpp | 52 +++++++++++++++++++++++--------------------- src/qt/qtclasses.h | 2 +- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index ff5cb51..2e2a4b0 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -266,9 +266,12 @@ void MainWindow::updateColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { scrollArea->verticalScrollBar()->setPalette(pal); } -void MainWindow::compose() { - //todo: invalidate layout when adding sessions with window open +void MainWindow::compose(bool isVisible) { //We need dynamically added child widgets to expand so that we know their height + this->setUpdatesEnabled(false); + uint64_t windowWidth; + uint64_t screenHeight; + uint64_t windowHeight; /* * Setting correct widget widths and heights */ @@ -281,24 +284,22 @@ void MainWindow::compose() { screenRes.getCoords(&srx1, &sry1, &srx2, &sry2); log_debugcpp("(for Percentage) Screen Res: " + std::to_string(srx1) + " " + std::to_string(srx2)+" " + std::to_string(sry1) + " " + std::to_string(sry2)); - - uint64_t windowWidth = (((uint64_t)std::abs(screenRes.width())) * widthRatio); - uint64_t screenHeight = (uint64_t)std::abs(screenRes.height()); + windowWidth = (((uint64_t)std::abs(screenRes.width())) * widthRatio); + screenHeight = (uint64_t)std::abs(screenRes.height()); log_debugcpp("Window Width: " + std::to_string(windowWidth)); log_to_file("Window Width: %d \n", windowWidth); - - - this->createLayout(new QGridLayout()); this->scrollArea->setMaximumWidth((int)windowWidth); this->scrollArea->setMinimumWidth((int)windowWidth); this->mainMenuBar->setMinimumWidth((int)windowWidth); this->mainMenuBar->setMaximumWidth((int)windowWidth); + + this->createLayout(new QGridLayout()); for (auto *epw : ews) { if (!epw) continue; epw->updateChannelsVisibility(); epw->calculateSize(windowWidth - scrollArea->verticalScrollBar()->sizeHint().width() - - widgetLayout->contentsMargins().left() - , screenHeight); + - widgetLayout->contentsMargins().left() + , screenHeight); log_debugcpp("epw loop"); log_debugcpp("epw roles: " + print_as_binary((epw->getEndpointHandler()->getRoles()))); //std::bitset content = @@ -309,7 +310,7 @@ void MainWindow::compose() { /* * Calculating window height */ - uint64_t windowHeight = 0; + windowHeight = 0; int left = 0, top = 0, right = 0, bottom = 0; for (int i = 0; i < 2; i++) { if (!ews[i]) continue; @@ -321,13 +322,17 @@ void MainWindow::compose() { windowHeight += mainMenuBar->sizeHint().height(); log_debugcpp("windowHeight final value: " + std::to_string(windowHeight)); - //Undoing scrolling - scrollArea->verticalScrollBar()->setValue(0); - - /* - * Establishing initial window size and position - */ - setGeometry(setSizePosition(screen, windowWidth, windowHeight)); + this->setUpdatesEnabled(true); + if (!isVisible) { + /* + * Undoing scrolling + */ + scrollArea->verticalScrollBar()->setValue(0); + /* + * Establishing initial window size and position + */ + setGeometry(setSizePosition(screen, windowWidth, windowHeight)); + } } QScreen* MainWindow::getCurrentScreen() { @@ -683,7 +688,6 @@ EndpointWidget::EndpointWidget(EndpointHandler* eph, QWidget *parent, uint64_t i eph->getSessionHandlers().at(i)->setFrontIndex(i); } - /* Add/Remove SessionWidget callback */ eph->setAddSessionWidgetFunction([this](SessionHandler* sessionHandler) { QCoreApplication::instance()->postEvent(this, new CustomWidgetEvent((QEvent::Type)CustomQEvent::SessionWidgetCreated, sessionHandler)); @@ -707,10 +711,8 @@ void EndpointWidget::addSessionWidget(CustomWidgetEvent* ev){ const QWidgetList topLevelWidgets = QApplication::topLevelWidgets(); for (QWidget *widget : topLevelWidgets) { if (qobject_cast(widget)) { - double widthRatio = ((MainWindow*)widget)->widthRatio; - double dpr = ((MainWindow*)widget)->dpr; - sw->calculateSize((std::abs(this->screen()->geometry().width()) * widthRatio) * dpr, - std::abs(this->screen()->geometry().height())); + QCoreApplication::instance()->postEvent + (widget, new QEvent((QEvent::Type)CustomQEvent::RecomposeMainWindow)); } } this->widgetLayout->addWidget(sw, row, 0, 1, 4); @@ -768,7 +770,7 @@ void MainWindow::customEvent(QEvent* ev) { this->addEndpointWidget((CustomWidgetEvent*)ev); } else if (ev->type() == (QEvent::Type)CustomQEvent::RecomposeMainWindow) { ev->setAccepted(true); - if (this->isVisible()) this->compose(); + if (this->isVisible()) this->compose(true); } else if (ev->type() == (QEvent::Type)CustomQEvent::EndpointRoleChange) { ev->setAccepted(true); this->flushRoleChanges(); @@ -1327,7 +1329,7 @@ void MainWindow::trayIconActivated(QSystemTrayIcon::ActivationReason reason) { case QSystemTrayIcon::Trigger: if (!this->isVisible() && !recentlyClosed) { log_to_file("Recently Closed: %d \n", recentlyClosed); - this->compose(); + this->compose(false); this->showNormal(); this->activateWindow(); } diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index 73a6b2c..0d5733f 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -183,7 +183,7 @@ class MainWindow : public QMainWindow { public: MainWindow(QWidget *parent = nullptr); void reloadEndpointWidgets(); - void compose(); + void compose(bool isVisible); void updateColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a); protected: From 3239e60471f71aca363293e73e74d26a1d311dfe Mon Sep 17 00:00:00 2001 From: Hane Date: Sat, 25 Jan 2025 17:27:36 +0100 Subject: [PATCH 77/78] fixed synch issues + memleak @ name fetching --- src/back/backlasses.cpp | 51 +++++++++++++++++++++++---------- src/back/backlasses.h | 15 +++++----- src/back/backsessionclasses.cpp | 15 ++++++---- src/cont/contclasses.cpp | 24 +++++++++++++--- src/cont/contclasses.h | 2 ++ src/cont/contsessionclasses.h | 8 +++--- src/qt/qtclasses.cpp | 15 +++++----- 7 files changed, 87 insertions(+), 43 deletions(-) diff --git a/src/back/backlasses.cpp b/src/back/backlasses.cpp index cead61f..74af6a0 100644 --- a/src/back/backlasses.cpp +++ b/src/back/backlasses.cpp @@ -234,6 +234,7 @@ HRESULT EndpointSituationCallback::OnDeviceRemoved(LPCWSTR pwstrDeviceId) { HRESULT EndpointSituationCallback::OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState) { std::wstring endpointId = std::wstring(pwstrDeviceId); + log_wdebugcpp(L"Endpoint state change for " + endpointId); EndpointState newState; switch (dwNewState) { case DEVICE_STATE_ACTIVE: @@ -249,7 +250,7 @@ HRESULT EndpointSituationCallback::OnDeviceStateChanged(LPCWSTR pwstrDeviceId, D newState = EndpointState::ENDPOINT_UNPLUGGED; break; } - isEpStateChanging = true; + isEpStateChanging.exchange(true); std::thread newEndpointThread(&OverseerHandler::reviseEndpointShowing, osh, endpointId, newState); newEndpointThread.detach(); @@ -258,7 +259,7 @@ HRESULT EndpointSituationCallback::OnDeviceStateChanged(LPCWSTR pwstrDeviceId, D } HRESULT EndpointSituationCallback::OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key) { - isEpStateChanging = true; + isEpStateChanging.exchange(true); std::thread propertyThread(&Overseer::updateEndpointInfo, os, std::wstring(pwstrDeviceId)); propertyThread.detach(); while(isEpStateChanging); @@ -266,7 +267,8 @@ HRESULT EndpointSituationCallback::OnPropertyValueChanged(LPCWSTR pwstrDeviceId, } void EndpointSituationCallback::reportFinishedStateChange() { - this->isEpStateChanging = false; + this->isEpStateChanging.exchange(false); + return; } Endpoint::Endpoint(IMMDevice* ep, IPolicyConfig7* policyConfig, uint64_t idx) { @@ -325,23 +327,29 @@ void Endpoint::activateEndpointSessions() { return; } - if (FAILED(endpoint->Activate(__uuidof(IAudioSessionManager2), - CLSCTX_ALL, NULL, (void**) &sessionManager))) { - log_debugcpp("Couldn't open session manager2, huh"); - return; + if (!sessionManager) { + if (FAILED(endpoint->Activate(__uuidof(IAudioSessionManager2), + CLSCTX_ALL, NULL, (void**) &sessionManager))) { + log_debugcpp("Couldn't open session manager2, huh"); + return; + } } + IAudioSessionEnumerator* sessionEnumerator = nullptr; - if (FAILED(sessionManager->GetSessionEnumerator(&sessionEnumerator))) { log_wdebugcpp(L"sesEnumeratorBros..."); return; } + if (FAILED(sessionManager->GetSessionEnumerator(&sessionEnumerator))) { log_wdebugcpp(L"sesEnumeratorBros..."); exit(-5); return; } endpointSessions.resize(1, nullptr); int sessionCount; sessionEnumerator->GetCount(&sessionCount); for (int i = 0; i < sessionCount; i++) { IAudioSessionControl* sessionControlTmp; - sessionEnumerator->GetSession(i, (IAudioSessionControl**)&sessionControlTmp); + if (FAILED(sessionEnumerator->GetSession(i, (IAudioSessionControl**)&sessionControlTmp))) { + exit(-6); + } IAudioSessionControl2* sessionControl; - sessionControlTmp->QueryInterface(__uuidof(IAudioSessionControl2), (void**)&sessionControl); - sessionControl->AddRef(); + if(FAILED(sessionControlTmp->QueryInterface(__uuidof(IAudioSessionControl2), (void**)&sessionControl))){ + exit(-7); + } sessionControlTmp->Release(); Session* session = new Session(this, sessionControl, (size_t)i); if (sessionControl->IsSystemSoundsSession() == S_OK) endpointSessions[0] = session; @@ -350,6 +358,13 @@ void Endpoint::activateEndpointSessions() { sessionEnumerator->Release(); } +/* + * void Endpoint::deleteSessionManager() { + * sessionManager->Release(); + * sessionManager = nullptr; + * } + */ + void Endpoint::addSession(Session* session) { session->setIndex(this->getSessionCount()); endpointSessions.push_back(session); @@ -357,9 +372,15 @@ void Endpoint::addSession(Session* session) { void Endpoint::activateEndpointVolume() { //If this EP is created after init, COM won't be initialized on the executing thread. - HRESULT result = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); + HRESULT result = CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE); if (this->endpointVolume == nullptr) { - endpoint->Activate(IID_IAudioEndpointVolume, CLSCTX_ALL, NULL, (void**)&this->endpointVolume); + if(FAILED(endpoint->Activate( + IID_IAudioEndpointVolume, + CLSCTX_ALL, + NULL, + (void**)&this->endpointVolume))) { + log_debugcpp("No volume, huh"); + } //todo: check header if(FAILED(endpoint->Activate(__uuidof(IAudioMeterInformation), @@ -594,8 +615,8 @@ void Overseer::createEndpoints(Flows flow) { /* * Counting them */ - if(FAILED(deviceCollection->GetCount(&numEndpoints))) { log_debugcpp("si");}; - if(numEndpoints == 0) { log_debugcpp("si"); }; + if(FAILED(deviceCollection->GetCount(&numEndpoints))) { log_debugcpp("si");}; + if(numEndpoints == 0) { log_debugcpp("si"); }; /* * Retrieving actual endpoints and storing them on their own collection diff --git a/src/back/backlasses.h b/src/back/backlasses.h index 9f79924..5acedba 100644 --- a/src/back/backlasses.h +++ b/src/back/backlasses.h @@ -82,6 +82,7 @@ class Endpoint { void unregisterNewSessionNotification(EndpointNewSessionCallback* ensc); void deleteSessions(); void activateEndpointSessions(); + //void deleteSessionManager(); std::mutex endpointSessionsMutex; ~Endpoint(); @@ -91,12 +92,13 @@ class Endpoint { std::vector endpointSessions; uint32_t channelCount = 0; IMMDevice *endpoint; - IAudioClient *audioClient; + IAudioEndpointVolume *endpointVolume = nullptr; + IPropertyStore *properties; + IAudioMeterInformation *endpointPeakMeter = nullptr; + //IAudioClient *audioClient; int64_t defTime, minTime; IAudioSessionManager2 *sessionManager = nullptr; Flows flow; - IAudioEndpointVolume *endpointVolume = nullptr; - IPropertyStore *properties; std::wstring friendlyName; std::wstring descriptionName; std::wstring deviceName; @@ -105,7 +107,6 @@ class Endpoint { Roles endpointRoles = (Roles)0; uint64_t idx; //Not implemented in llvm-mingw. Sad! todo: mingw patch - IAudioMeterInformation *endpointPeakMeter = nullptr; IPolicyConfig7* policyConfig; }; @@ -125,7 +126,7 @@ class EndpointVolumeCallback : public IAudioEndpointVolumeCallback { private: ULONG ref = 1; Endpoint* ep; - bool wait = false; + std::atomic wait = false; }; class EndpointSituationCallback : public IMMNotificationClient { @@ -143,7 +144,7 @@ class EndpointSituationCallback : public IMMNotificationClient { private: ULONG ref = 1; Overseer* os; - bool isEpStateChanging = false; + std::atomic isEpStateChanging = false; }; class Overseer { @@ -204,7 +205,7 @@ class EndpointNewSessionCallback : public IAudioSessionNotification { void createSessionThread(SessionThreadParams params); private: - bool wait = false; + std::atomic wait = false; ULONG ref = 1; EndpointHandler *eph; diff --git a/src/back/backsessionclasses.cpp b/src/back/backsessionclasses.cpp index 5816ac5..c9aaa85 100644 --- a/src/back/backsessionclasses.cpp +++ b/src/back/backsessionclasses.cpp @@ -39,6 +39,7 @@ HRESULT SessionStateCallback::QueryInterface(REFIID riid, VOID **ppvInterface) { HRESULT SessionStateCallback::OnDisplayNameChanged(LPCWSTR NewDisplayName, LPCGUID EventContext) { //TODO: Preguntar while(sh->getVolumeInfo()->isNameChanged == true); + sh->setName(std::wstring(NewDisplayName)); sh->getVolumeInfo()->isNameChanged = true; return S_OK; @@ -238,13 +239,15 @@ bool Session::fetchNameViaFD(std::wstring exePath, DWORD pid, std::wstring *sess void* fileVersionInfo = malloc(fileVersionInfoSize); if(!GetFileVersionInfoExW(FILE_VER_GET_LOCALISED | FILE_VER_GET_NEUTRAL, - exePath.c_str(),0,fileVersionInfoSize, fileVersionInfo)) + exePath.c_str(),0,fileVersionInfoSize, fileVersionInfo)) { return false; + } UINT translationArrayLen = 0; - if (!VerQueryValueW(fileVersionInfo, L"\\VarFileInfo\\Translation", (LPVOID*)&translationArray, &translationArrayLen)) + if (!VerQueryValueW(fileVersionInfo, L"\\VarFileInfo\\Translation", (LPVOID*)&translationArray, &translationArrayLen)) { + free(fileVersionInfo); return false; - + } //File descriptor parsing //TODO: https://learn.microsoft.com/en-us/windows/win32/api/winnls/nf-winnls-getuserpreferreduilanguages /* It is possible to retrieve user languages and try to use one of those before falling back to whatever @@ -257,7 +260,6 @@ bool Session::fetchNameViaFD(std::wstring exePath, DWORD pid, std::wstring *sess int8_t syslangIdx = -1; wchar_t metadataStringKey[256]; wchar_t* metadataString = NULL; - //std::wstring name; for (UINT i = 0; i < availableLangs; i++) { LANGID defaultUILanguage = GetUserDefaultUILanguage(); if (defaultUILanguage != translationArray[i].wLanguage) @@ -340,8 +342,6 @@ bool Session::fetchNameViaMSIX(std::wstring exePath, DWORD pid, std::wstring *se if (sessionName->length() > 0) return true; else return false; - - } @@ -419,4 +419,7 @@ Session::~Session() { meterInformation->Release(); sessionControl->Release(); sessionVolume->Release(); + meterInformation = nullptr; + sessionControl = nullptr; + sessionVolume = nullptr; } diff --git a/src/cont/contclasses.cpp b/src/cont/contclasses.cpp index 27ae4a6..d2188f0 100644 --- a/src/cont/contclasses.cpp +++ b/src/cont/contclasses.cpp @@ -28,7 +28,6 @@ EndpointHandler::EndpointHandler(uint64_t idx, Flows flow) { this->flow = flow; this->ep = (flow == Flows::FLOW_PLAYBACK ? osh->getPlaybackEndpoints().at(idx) : osh->getCaptureEndpoints().at(idx)); - epc = new EndpointVolumeCallback(ep); this->callbackInfo.caller = osh->getGuid(); //epName = ep->getName(); this->setBackEndpointVolumeCallbackInfoContent(this->getState()); @@ -115,7 +114,10 @@ void EndpointHandler::setBackEndpointVolumeCallbackInfoContent(uint8_t state) { callbackInfo.muted = this->getMute(); callbackInfo.mainVolume = this->getVolume(AudioChannel::CHANNEL_MAIN); callbackInfo.channels = this->getChannelCount(); - ep->setVolumeCallback(epc); + if (!epc) { + epc = new EndpointVolumeCallback(ep); + ep->setVolumeCallback(epc); + } callbackInfo.channelVolumes.resize(this->callbackInfo.channels); for(uint32_t i = 0; i < this->getChannelCount(); i++){ callbackInfo.channelVolumes.at(i) = this->getVolume(i); @@ -181,13 +183,13 @@ Endpoint* EndpointHandler::getEndpoint() { void EndpointHandler::addSessionSendFront(Session* session) { if(!ep->endpointSessionsMutex.try_lock()) return; + sessionHandlersMutex.lock(); this->ep->addSession(session); SessionHandler* sessionHandler = new SessionHandler(this, session, (getSessionCount() - 1)); - ep->endpointSessionsMutex.unlock(); - sessionHandlersMutex.lock(); sessionHandlers.push_back(sessionHandler); + ep->endpointSessionsMutex.unlock(); sessionHandlersMutex.unlock(); this->addSessionWidget(sessionHandler); } @@ -203,11 +205,13 @@ void EndpointHandler::removeSessionFromFront(SessionHandler* sh) { void EndpointHandler::deleteSessions() { ep->unregisterNewSessionNotification(ensc); ensc->Release(); + ensc = nullptr; for (auto sh : sessionHandlers) { delete sh; } sessionHandlers.resize(0); ep->deleteSessions(); + //ep->deleteSessionManager(); } void EndpointHandler::createSessionHandlers() { @@ -218,6 +222,9 @@ void EndpointHandler::createSessionHandlers() { sessionHandlers.push_back(sessionHandler); } } +} + +void EndpointHandler::createSessionHandlersCallback() { ensc = new EndpointNewSessionCallback(this); ep->registerNewSessionNotification(ensc); } @@ -232,6 +239,12 @@ void EndpointHandler::unlockSessionCollections() { this->ep->endpointSessionsMutex.unlock(); } +void EndpointHandler::removeVolumeCallback() { + ep->removeVolumeCallback(epc); + epc->Release(); + epc = nullptr; +} + EndpointHandler::~EndpointHandler() { ep->removeVolumeCallback(epc); ep->unregisterNewSessionNotification(ensc); @@ -411,10 +424,13 @@ void OverseerHandler::reviseEndpointShowing(std::wstring endpointId, EndpointSta if (flow == Flows::FLOW_CAPTURE) goto end; if (eph && EndpointState::ENDPOINT_ACTIVE & state) { + eph->setState(EndpointState::ENDPOINT_ACTIVE); this->addEndpointWidget(eph); } else if (eph && eph->getFrontVisibilityState() == EndpointState::ENDPOINT_ACTIVE) { + eph->removeVolumeCallback(); this->removeEndpointWidget(eph->getFrontVisibilityIndex()); } + end: handlersPlaybackMutex.unlock(); handlersCaptureMutex.unlock(); diff --git a/src/cont/contclasses.h b/src/cont/contclasses.h index 2034986..cd0e03e 100644 --- a/src/cont/contclasses.h +++ b/src/cont/contclasses.h @@ -76,9 +76,11 @@ public: void removeSessionFromFront(SessionHandler* sh); void deleteSessions(); void createSessionHandlers(); + void createSessionHandlersCallback(); std::mutex sessionHandlersMutex; void lockSessionCollections(); void unlockSessionCollections(); + void removeVolumeCallback(); ~EndpointHandler(); private: diff --git a/src/cont/contsessionclasses.h b/src/cont/contsessionclasses.h index 455bbe1..24c0ba7 100644 --- a/src/cont/contsessionclasses.h +++ b/src/cont/contsessionclasses.h @@ -9,10 +9,10 @@ class SessionStateCallback; struct SessionVolumeInfo { //SessionVolumeInfo(bool muted, float mainVolume); - bool muted; - float mainVolume; - NGuid caller; - bool isNameChanged = false; + bool muted; + float mainVolume; + NGuid caller; + std::atomic isNameChanged = false; //size_t channels; //std::vector channelVolumes; }; diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 2e2a4b0..a5c391d 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -411,7 +411,8 @@ SessionWidget::SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent) if (sh->getVolumeInfo()->isNameChanged) { mainLabel->setText(QString::fromStdWString(sh->getName())); mainLabel->setToolTip(QString::fromStdWString(sh->getName())); - } + sh->getVolumeInfo()->isNameChanged = false; + } const float roundingFactor = 0.005; mainSlider->blockSignals(true); muteButton->blockSignals(true); @@ -538,7 +539,7 @@ EndpointWidget::EndpointWidget(EndpointHandler* eph, QWidget *parent, uint64_t i eph->createSessionHandlers(); //todo: sussy - this->eph->setState(EndpointState::ENDPOINT_ACTIVE, idx); + this->eph->setFrontVisibilityInfo(EndpointState::ENDPOINT_ACTIVE, idx); this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); widgetLayout = new QGridLayout(this); //this->setContentsMargins(0, 0, 0, 0); @@ -698,9 +699,9 @@ EndpointWidget::EndpointWidget(EndpointHandler* eph, QWidget *parent, uint64_t i }); log_debugcpp("ENDPOINT_WIDGETED"); + eph->createSessionHandlersCallback(); } - void EndpointWidget::addSessionWidget(CustomWidgetEvent* ev){ this->setUpdatesEnabled(false); uint64_t index = this->sessionWidgets.size(); @@ -779,7 +780,7 @@ void MainWindow::customEvent(QEvent* ev) { } //__attribute__((optimize("O0", "unroll-loops"))) -void MainWindow::removeEndpointWidget(CustomWidgetEvent* ev){ +void MainWindow::removeEndpointWidget(CustomWidgetEvent* ev) { uint64_t i = ev->payload; this->ews.at(i)->setParent(nullptr); this->widgetLayout->removeWidget(ews.at(i)); @@ -792,7 +793,7 @@ void MainWindow::removeEndpointWidget(CustomWidgetEvent* ev){ return; } -void MainWindow::addEndpointWidget(CustomWidgetEvent* ev){ +void MainWindow::addEndpointWidget(CustomWidgetEvent* ev) { EndpointWidget* epw = new EndpointWidget(ev->payload, containerWidget, this->ews.size()); //epw->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); epw->setParent(this); @@ -924,9 +925,9 @@ EndpointHandler* EndpointWidget::getEndpointHandler(){ * } */ -void EndpointWidget::setIndex(uint64_t idx){ +void EndpointWidget::setIndex(uint64_t idx) { this->idx = idx; - this->eph->setState(EndpointState::ENDPOINT_ACTIVE, this->idx); + this->eph->setFrontVisibilityInfo(EndpointState::ENDPOINT_ACTIVE, this->idx); } uint64_t EndpointWidget::getIndex(){ From 8f864f33941dcfcb835a97771f5e7f803cf0141b Mon Sep 17 00:00:00 2001 From: Hane Date: Sat, 31 Jan 2026 18:09:02 +0100 Subject: [PATCH 78/78] wip: icons --- assets.qrc | 2 ++ bueno.bat | 6 +++--- qtest.pro | 2 +- src/qt/qtclasses.cpp | 38 ++++++++++++++++++++++++++++++--- src/qt/qtclasses.h | 7 +++++- src/qt/qtcommon.h | 32 +++++++++++++++++++++++++++ src/qt/qtvisuals.h | 51 ++++++++++++++++++++++++++++++++++++++++++-- 7 files changed, 128 insertions(+), 10 deletions(-) diff --git a/assets.qrc b/assets.qrc index 4ef5956..2206a8a 100644 --- a/assets.qrc +++ b/assets.qrc @@ -4,5 +4,7 @@ assets/notificationAreaIcon.png assets/style.qss assets/logo.ico + assets/mute.svg + assets/unmute.svg diff --git a/bueno.bat b/bueno.bat index 37b5dec..540be60 100644 --- a/bueno.bat +++ b/bueno.bat @@ -1,7 +1,7 @@ taskkill /F /IM "MixerQ.exe" taskkill /F /IM "MixerQd.exe" qmake -o build\Makefile .\qtest.pro -mingw32-make.exe -C .\build -f Makefile.Release +REM mingw32-make.exe -C .\build -f Makefile.Release mingw32-make.exe -C .\build -f Makefile.Debug -makensis /DBUILDTYPE=release install\installer.nsi -makensis /DBUILDTYPE=debug install\installer.nsi +REM makensis /DBUILDTYPE=release install\installer.nsi +REM makensis /DBUILDTYPE=debug install\installer.nsi diff --git a/qtest.pro b/qtest.pro index 0568bca..42a1165 100644 --- a/qtest.pro +++ b/qtest.pro @@ -18,7 +18,7 @@ LIBS += -lWinmm -lodbc32 -lodbccp32 -luuid -loleaut32 -lole32 -lshell32 -ladvapi DEFINES += QT_LOGGING_TO_CONSOLE=1 WIN32_LEAN_AND_MEAN _WIN32_WINNT=0x0602 DEFINES_DEBUG += DEBUG -QT += widgets network +QT += widgets network svg INCLUDEPATH += "$$PWD\src" "$$PWD\src\qt" "$$PWD\src\back" "$$PWD\src\back\reimpl" "$$PWD\src\cont" VPATH += "$$PWD\src" "$$PWD\src\qt" "$$PWD\src\back" "$$PWD\src\back\reimpl" "$$PWD\src\cont" diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index a5c391d..a9148b9 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -207,6 +207,36 @@ void ExtendedCheckBox::customEvent(QEvent* ev) { QCheckBox::customEvent(ev); } +void ExtendedCheckBox::paintEvent(QPaintEvent *event) { + QStylePainter p(this); + QStyleOptionButton opt; + initStyleOption(&opt); + opt.icon = this->icons; + p.drawControl((QStyle::ControlElement)CustomControlElement::CE_ExtendedCheckBox, opt); + //QStyle* style = QApplication::style(); + //style->drawComplexControl((QStyle::ComplexControl)CC_MeterSlider, &sliderComplex2, &painter, this); +} + +void ExtendedCheckBox::addIcon(char* const path, QIcon::State state) { + QString str(path); + QSvgRenderer rr(str); + QPixmap pixmap(64, 64); + pixmap.fill(Qt::transparent); + QPainter painter(&pixmap); + rr.render(&painter); + painter.setCompositionMode(QPainter::CompositionMode_SourceIn); + uint8_t a, r, g, b; + if (StylingHelper::argbToDiscreteValues(osh->getAccentColor(), &r, &g, &b, &a)) { + QColor color(r, g, b, a); + painter.fillRect(pixmap.rect(), color); + } + painter.end(); + icons.addPixmap(pixmap, QIcon::Normal, state); + //this->setIcon(icons); + //icons.addFile(":/Icons/images/second.svg",QSize(32,32),QIcon::Normal,QIcon::Off); +} + + QRect MainWindow::setSizePosition(QScreen* screen, int width, int height) { //setGeometry ignores decoration size, theres others for that QRect trayIconPos = this->trayIcon->geometry(); @@ -276,7 +306,7 @@ void MainWindow::compose(bool isVisible) { * Setting correct widget widths and heights */ log_to_file("[Compose]\n"); - screen = this->getCurrentScreen(); + screen = StylingHelper::getCurrentScreen(); log_debugcpp("Screen: " + screen->model().toStdString() + " " + screen->name().toStdString()); QRect screenRes = screen->geometry(); @@ -336,7 +366,7 @@ void MainWindow::compose(bool isVisible) { } QScreen* MainWindow::getCurrentScreen() { - //todo: Using cursor pos as screen detector. Flawed. + //note: Using cursor pos as screen detector. Flawed. QPoint cursorPos = QCursor::pos(); log_debugcpp("Cursor pos: " + std::to_string(cursorPos.ry()) + " " + std::to_string(cursorPos.rx())); @@ -363,7 +393,9 @@ SessionWidget::SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent) widgetLayout->getContentsMargins(&left, &top, &right, &bottom); widgetLayout->setContentsMargins(0, top, 0, bottom); - muteButton = new QCheckBox(this); + muteButton = new ExtendedCheckBox(this); + muteButton->addIcon(":/assets/mute.svg", QIcon::On); + muteButton->addIcon(":/assets/unmute.svg", QIcon::Off); mainLabel = new QLabel(QString::fromStdWString(sh->getName()), this); mainLabel->setToolTip(QString::fromStdWString(sh->getName())); mainSlider = new MeterSlider(Qt::Horizontal, this); diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index 0d5733f..1b2658b 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -40,12 +40,17 @@ public: class ExtendedCheckBox : public QCheckBox { Q_OBJECT +private: + QIcon icons; + protected: void customEvent(QEvent* ev) override; + void paintEvent(QPaintEvent* event) override; public: //c++11: this inherits all parent's constructors unconditionally using QCheckBox::QCheckBox; + void addIcon(char* const path, QIcon::State state); //alternative being calling parent ctor directly after declaring child ctor: //B(int x) : A(x) { } }; @@ -68,7 +73,7 @@ private: MeterSlider *mainSlider = nullptr; uint64_t idx; QHBoxLayout *widgetLayout = nullptr; - QCheckBox *muteButton = nullptr; + ExtendedCheckBox *muteButton = nullptr; SessionHandler* sh; QTimer* volumePoller = nullptr; QSpacerItem* widthSpacer; diff --git a/src/qt/qtcommon.h b/src/qt/qtcommon.h index b059504..61873eb 100644 --- a/src/qt/qtcommon.h +++ b/src/qt/qtcommon.h @@ -44,6 +44,7 @@ #include #include #include +#include //#include //#include /* @@ -63,6 +64,10 @@ enum CustomComplexControl { CC_MeterSlider = 0xf0000001 }; +enum CustomControlElement { + CE_ExtendedCheckBox = 0xf0000001 +}; + namespace StylingHelper { static inline void setBackgroundColor(bool lightMode) { @@ -218,5 +223,32 @@ namespace StylingHelper { return pal.color(QPalette::Base); } + static inline QPixmap svg2Pixmap(const QString& svgContent, + const QSize& size, + QPainter::CompositionMode mode = QPainter::CompositionMode_SourceOver) + { + QSvgRenderer rr(svgContent); + QImage image(size.width(), size.height(), QImage::Format_ARGB32); + QPainter painter(&image); + painter.setCompositionMode(mode); + image.fill(Qt::transparent); + rr.render(&painter); + return QPixmap::fromImage(image); + } + + static inline QScreen* getCurrentScreen() { + //note: Using cursor pos as screen detector. Flawed. + QPoint cursorPos = QCursor::pos(); + + for (QScreen *screen : QGuiApplication::screens()) { + QRect screenRect = screen->geometry(); + if (screenRect.contains(cursorPos)) { + return screen; + } + } + + return QGuiApplication::primaryScreen(); + } + } diff --git a/src/qt/qtvisuals.h b/src/qt/qtvisuals.h index 0db58e8..8902ed1 100644 --- a/src/qt/qtvisuals.h +++ b/src/qt/qtvisuals.h @@ -20,8 +20,10 @@ public: return baseStyle()->styleHint(hint, option, widget, returnData); } - QRect subControlRect(ComplexControl control, const QStyleOptionComplex *option, - SubControl subControl, const QWidget *widget) const { + QRect subControlRect(ComplexControl control, + const QStyleOptionComplex *option, + SubControl subControl, + const QWidget *widget) const { QRect rect = QCommonStyle::subControlRect(CC_Slider, option, subControl, widget); switch (control) { @@ -186,6 +188,51 @@ public: } + void drawControl(ControlElement element, const QStyleOption *opt, + QPainter *p, const QWidget *widget) const + { + switch(element) { + case CE_ExtendedCheckBox: + if (const QStyleOptionButton *btn = qstyleoption_cast(opt)) { + QStyleOptionButton subopt = *btn; + subopt.rect = subElementRect(SE_CheckBoxIndicator, btn, widget); + //proxy()->drawPrimitive(PE_IndicatorCheckBox, &subopt, p, widget); + subopt.rect = subElementRect(SE_CheckBoxContents, btn, widget); + + //proxy()->drawControl(CE_CheckBoxLabel, &subopt, p, widget); + int alignment = visualAlignment(btn->direction, Qt::AlignLeft | Qt::AlignVCenter); + + if (!proxy()->styleHint(SH_UnderlineShortcut, btn, widget)) + alignment |= Qt::TextHideMnemonic; + QPixmap pix; + QRect textRect = btn->rect; + if (!btn->icon.isNull()) { + pix = btn->icon.pixmap(btn->iconSize, StylingHelper::getCurrentScreen()->devicePixelRatio(), + QIcon::Mode::Normal, btn->state & State_On ? QIcon::On : QIcon::Off); + proxy()->drawItemPixmap(p, btn->rect, alignment, pix); + if (btn->direction == Qt::RightToLeft) + textRect.setRight(textRect.right() - btn->iconSize.width() - 4); + else + textRect.setLeft(textRect.left() + btn->iconSize.width() + 4); + } + if (!btn->text.isEmpty()){ + proxy()->drawItemText(p, textRect, alignment | Qt::TextShowMnemonic, + btn->palette, btn->state & State_Enabled, btn->text, QPalette::WindowText); + } + // + if (btn->state & State_HasFocus) { + QStyleOptionFocusRect fropt; + fropt.QStyleOption::operator=(*btn); + fropt.rect = subElementRect(SE_CheckBoxFocusRect, btn, widget); + proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, p, widget); + } + } + default: + baseStyle()->drawControl(element, opt, p, widget); + break; + } + } + void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget) const { QColor outline;

~>seLx`ep|j5*LqYWqW(|Ths?p)KLc^ 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 XHi6$MX1&XX;<+8{CO*bT;E4YNbQI2I diff --git a/bueno.bat b/bueno.bat index edb534d..6aa7e4e 100644 --- a/bueno.bat +++ b/bueno.bat @@ -1,5 +1,3 @@ taskkill /F /IM "qtest.exe" qmake -o build\Makefile .\qtest.pro -copy /Y /B .\assets\SoundVolumeView.exe .\build\debug -copy /Y /B .\assets\SoundVolumeView.exe .\build\release mingw32-make.exe -C .\build -f Makefile diff --git a/src/back/backlasses.cpp b/src/back/backlasses.cpp index e398b21..e6e691d 100644 --- a/src/back/backlasses.cpp +++ b/src/back/backlasses.cpp @@ -228,10 +228,10 @@ HRESULT EndpointSituationCallback::OnPropertyValueChanged(LPCWSTR pwstrDeviceId, return S_OK; } -Endpoint::Endpoint(IMMDevice* ep, uint64_t idx){ +Endpoint::Endpoint(IMMDevice* ep, IPolicyConfig7* policyConfig, uint64_t idx){ this->endpoint = ep; this->idx = idx; - + this->policyConfig = policyConfig; /* * It can't multiflag, it's that stupid. MS momento. * Only shows most relevant flag according to MS, i.e. 0110 sends 0010 @@ -434,70 +434,30 @@ Roles Endpoint::getRoles(){ } void Endpoint::setRoles(Roles role){ - //todo: otro exe momento - STARTUPINFOEXW startupConfig; - PROCESS_INFORMATION processInfo; - SecureZeroMemory(&startupConfig, sizeof(STARTUPINFOEXW)); - SecureZeroMemory(&startupConfig.StartupInfo, sizeof(STARTUPINFOW)); - startupConfig.StartupInfo.cb = sizeof(STARTUPINFOEXW); - SecureZeroMemory(&processInfo, sizeof(PROCESS_INFORMATION)); + if (!policyConfig) return; - std::wstring command = L"SoundVolumeView.exe /SetDefault " + endpointId + L" "; - std::wstring troublePair = L"1"; - switch (role) { - case Roles::ROLE_ALL: - /* - * console or multimedia, one sends both, at least for now; - * either cos of ms or dis guy; - * no choice but to treat them as one for now. - * command += L"all"; and nothing else would've been nice... - */ - troublePair = command + troublePair; - if(CreateProcessW( - NULL, - (wchar_t*)troublePair.c_str(), - NULL, - NULL, - false, - CREATE_UNICODE_ENVIRONMENT, - NULL, - NULL, - (LPSTARTUPINFOW)&startupConfig, - &processInfo - ) == true) { - WaitForSingleObject(processInfo.hProcess, INFINITE ); - CloseHandle(processInfo.hProcess); - CloseHandle(processInfo.hThread); - } - command += L"2"; - break; + bool allRoles = false; + ERole val; + switch(role) { case Roles::ROLE_CONSOLE: - command += std::to_wstring(0); + val = eConsole; break; case Roles::ROLE_MULTIMEDIA: - command += std::to_wstring(1); + val = eMultimedia; break; case Roles::ROLE_COMMUNICATIONS: - command += std::to_wstring(2); + val = eCommunications; + break; + default: + allRoles = true; break; } - - if(CreateProcessW( - NULL, - (wchar_t*)command.c_str(), - NULL, - NULL, - false, - CREATE_UNICODE_ENVIRONMENT, - NULL, - NULL, - (LPSTARTUPINFOW)&startupConfig, - &processInfo - ) == true) { - WaitForSingleObject(processInfo.hProcess, INFINITE ); - CloseHandle(processInfo.hProcess); - CloseHandle(processInfo.hThread); - } + if (allRoles) { + policyConfig->SetDefaultEndpoint(endpointId.c_str(), eMultimedia); + //policyConfig->SetDefaultEndpoint(endpointId.c_str(), eConsole); + policyConfig->SetDefaultEndpoint(endpointId.c_str(), eCommunications); + } + policyConfig->SetDefaultEndpoint(endpointId.c_str(), val); } void Endpoint::assignRoles(Roles role){ @@ -599,7 +559,7 @@ void Overseer::reloadEndpoints(Flows flow) { IMMDevice *temp; for (unsigned int i = 0; i < numEndpoints; i++){ if(deviceCollection->Item(i, &temp) != 0) { log_debugcpp("si"); }; - Endpoint *endpoint = new Endpoint(temp, i); + Endpoint *endpoint = new Endpoint(temp, policyConfig, i); if (flow == Flows::FLOW_PLAYBACK) this->playbackDevices.push_back(endpoint); else @@ -608,16 +568,6 @@ void Overseer::reloadEndpoints(Flows flow) { } deviceCollection->Release(); - //IPolicyConfig7 test - /* - * deviceEnumerator->GetDefaultAudioEndpoint(MSflow, ERole::eCommunications, &temp); - * LPWSTR tempString = nullptr; - * temp->GetId(&tempString); - * HRESULT hre = policyConfig->SetDefaultEndpoint( - * tempString, - * ERole::eMultimedia - * ); - */ /* * Discerning default endpoints per role @@ -670,7 +620,7 @@ Endpoint* Overseer::addEndpoint(std::wstring endpointId, /* out */Flows* flow = if(FAILED(deviceEnumerator->GetDevice((LPCWSTR)endpointId.c_str(), &newep))) log_debugcpp("ay caramba con la hot metida."); - Endpoint *endpoint = new Endpoint(newep); + Endpoint *endpoint = new Endpoint(newep, policyConfig); Flows getFlow = endpoint->getFlow(); if (getFlow == Flows::FLOW_PLAYBACK) { diff --git a/src/back/backlasses.h b/src/back/backlasses.h index 0cee06e..bcc3306 100644 --- a/src/back/backlasses.h +++ b/src/back/backlasses.h @@ -11,7 +11,7 @@ class Session; class Endpoint { public: - Endpoint(IMMDevice* endpoint, uint64_t idx = 0); + Endpoint(IMMDevice* endpoint, IPolicyConfig7* policyConfig, uint64_t idx = 0); //todo: how to forward declare delegate constructors? //Endpoint(IMMDevice* endpoint) : Endpoint(endpoint, 0) {}; void reloadEndpointChannels(); @@ -69,7 +69,7 @@ class Endpoint { uint64_t idx; //Not implemented in llvm-mingw. Sad! todo: mingw patch IAudioMeterInformation *endpointPeakMeter = nullptr; - + IPolicyConfig7* policyConfig; }; class EndpointVolumeCallback : public IAudioEndpointVolumeCallback { @@ -128,15 +128,18 @@ class Overseer { ~Overseer(); private: + void initCOMLibrary(); + NGuid guid; IMMDeviceEnumerator *deviceEnumerator; EndpointSituationCallback epsc; - //IPolicyConfig *policyConfig; + std::vector playbackDevices; std::vector captureDevices; - void initCOMLibrary(); + IPolicyConfig7* policyConfig; + friend class Endpoint; //IMMDeviceCollection *deviceCollection; //int numCaptureEndpoints; //std::vector *captureDevices; From 5d179bb91c3f4ecdc66eb69ca87feeccef068ba3 Mon Sep 17 00:00:00 2001 From: Hane Date: Mon, 13 May 2024 17:00:54 +0200 Subject: [PATCH 42/78] changed qmake recipe (static c++/unwind link) --- qtest.pro | 2 +- src/back/backlasses.cpp | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/qtest.pro b/qtest.pro index 4677fd8..42b246a 100644 --- a/qtest.pro +++ b/qtest.pro @@ -1,6 +1,6 @@ QMAKE_CXXFLAGS += --target=x86_64-w64-mingw32 -g -gcodeview QMAKE_LFLAGS += --target=x86_64-w64-mingw32 -g -Wl,-pdb= -v -LIBS += -LC:/capybara/libclang/x86_64-w64-mingw32/lib -lWinmm -lodbc32 -lodbccp32 -luuid -loleaut32 -lole32 -lshell32 -ladvapi32 -lcomdlg32 -lwinspool -lgdi32 -luser32 -lkernel32 -lpropsys +LIBS += -LC:/capybara/libclang/x86_64-w64-mingw32/lib -lWinmm -lodbc32 -lodbccp32 -luuid -loleaut32 -lole32 -lshell32 -ladvapi32 -lcomdlg32 -lwinspool -lgdi32 -luser32 -lkernel32 -lpropsys -static -stdlib=libc++ -lunwind #"kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" -luuid -loleaut32 -lole32 -lshell32 -ladvapi32 -lcomdlg32 -lwinspool -lgdi32 -luser32 -lkernel32 DEFINES += DEBUG QT_LOGGING_TO_CONSOLE=1 WIN32_LEAN_AND_MEAN CONFIG += debug diff --git a/src/back/backlasses.cpp b/src/back/backlasses.cpp index e6e691d..1f7b442 100644 --- a/src/back/backlasses.cpp +++ b/src/back/backlasses.cpp @@ -456,8 +456,7 @@ void Endpoint::setRoles(Roles role){ policyConfig->SetDefaultEndpoint(endpointId.c_str(), eMultimedia); //policyConfig->SetDefaultEndpoint(endpointId.c_str(), eConsole); policyConfig->SetDefaultEndpoint(endpointId.c_str(), eCommunications); - } - policyConfig->SetDefaultEndpoint(endpointId.c_str(), val); + } else policyConfig->SetDefaultEndpoint(endpointId.c_str(), val); } void Endpoint::assignRoles(Roles role){ From b6b7e1c577b379e2435e34d5ec9b743b14b39962 Mon Sep 17 00:00:00 2001 From: Hane Date: Tue, 14 May 2024 19:48:11 +0200 Subject: [PATCH 43/78] code cleanup: refactored ugly window width/scrheight cache --- src/qt/qtclasses.cpp | 13 ++++++++++--- src/qt/qtclasses.h | 4 +--- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 5e0bd3a..efac234 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -633,7 +633,16 @@ void EndpointWidget::addSessionWidget(CustomWidgetEvent* ev){ uint64_t index = this->sessionWidgets.size(); SessionWidget* sw = new SessionWidget(index, ev->payload, this); ev->payload->setFrontIndex(index); - sw->calculateSize(currentWidth, currentHeight); + //MainWindow* mw = dynamic_cast(parent()); + //TODO: change mainwindow's widget name and subclass qwidget + const QWidgetList topLevelWidgets = QApplication::topLevelWidgets(); + for (QWidget *widget : topLevelWidgets) { + if (dynamic_cast(widget)) { + double widthRatio = ((MainWindow*)widget)->widthRatio; + sw->calculateSize(std::abs(this->screen()->geometry().width()) * widthRatio, + std::abs(this->screen()->geometry().height())); + } + } this->widgetLayout->addWidget(sw, row, 0, 1, 4); row++; sessionWidgets.push_back(sw); @@ -740,8 +749,6 @@ void MainWindow::reorderEndpointWidgetCollection() { void EndpointWidget::calculateSize(uint64_t width, uint64_t height) { /* og 1080p 120% testing values */ - this->currentWidth = width; - this->currentHeight = height; log_to_file("[EndpointWidget %s sizes]\n", converter.to_bytes(this->getEndpointHandler()->getName()).c_str()); log_to_file("Params: {Width: %u Height: %u}\n", width, height); this->mainLabel->setMaximumWidth((int)(width * 0.50) /* 1080p 120%*/); diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index 854e7d8..c85b9ad 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -201,8 +201,6 @@ private: ChannelWidget* cw; std::vector sessionWidgets; QSize minimum; - uint64_t currentWidth = 1; - uint64_t currentHeight = 1; //std::vector *ephs; //std::vector *sliders; @@ -272,7 +270,7 @@ private: QToolBar *mainMenuBar; QScreen *screen; QSpacerItem* lastRowSpacer; - + friend class EndpointWidget; //public slots: // void setEndpointHandlers(std::vector *ephs); From 0e123f886dc8874c1e0988620fe9d496f26e44b3 Mon Sep 17 00:00:00 2001 From: Hane Date: Thu, 16 May 2024 17:23:54 +0200 Subject: [PATCH 44/78] sliders value set to where clicked --- qtest.pro | 2 +- src/back/backlasses.cpp | 4 +- src/qt/qtclasses.cpp | 25 +++++++++--- src/qt/qtclasses.h | 11 +++--- src/qt/qtvisuals.h | 84 +++++++++++++++++++++++++++++++++++++++++ src/qtestmain.cpp | 4 +- 6 files changed, 115 insertions(+), 15 deletions(-) create mode 100644 src/qt/qtvisuals.h diff --git a/qtest.pro b/qtest.pro index 42b246a..97274fa 100644 --- a/qtest.pro +++ b/qtest.pro @@ -11,7 +11,7 @@ DESTPATH += "$$PWD\src" "$$PWD\src\qt" "$$PWD\src\back" "$$PWD\src\back\reimpl" VPATH += "$$PWD\src" "$$PWD\src\qt" "$$PWD\src\back" "$$PWD\src\back\reimpl" "$$PWD\src\cont" SOURCES += qtestmain.cpp qtclasses.cpp backlasses.cpp backsessionclasses.cpp contclasses.cpp contsessionclasses.cpp -HEADERS += qtclasses.h backlasses.h backsessionclasses.h contclasses.h contsessionclasses.h global.h debug.h backfuncs.h ipolicyconfig.h msinclude.h +HEADERS += qtclasses.h backlasses.h backsessionclasses.h contclasses.h contsessionclasses.h global.h debug.h backfuncs.h ipolicyconfig.h msinclude.h qtvisuals.h RESOURCES = assets.qrc RC_ICONS += assets/logo.ico diff --git a/src/back/backlasses.cpp b/src/back/backlasses.cpp index 1f7b442..bfc0e83 100644 --- a/src/back/backlasses.cpp +++ b/src/back/backlasses.cpp @@ -1,5 +1,5 @@ -#include -#include +#include "backlasses.h" +#include "backfuncs.h" EndpointNewSessionCallback::EndpointNewSessionCallback(EndpointHandler* eph){ this->eph = eph; diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index efac234..2174d7d 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -6,6 +6,19 @@ CustomWidgetEvent::CustomWidgetEvent(QEvent::Type type, T payload) : QEvent(t this->payload = payload; } + +/* + * MeterSlider::MeterSlider(Qt::Orientation orientation, QWidget* parent) { + * //style = new MixerStyle(); + * //this->setStyle(style); + * } + */ + + +MeterSlider::~MeterSlider() { + //delete style; +} + void MeterSlider::setPeakValue(float peakValue) { this->peakValue = peakValue; } @@ -99,7 +112,7 @@ void MeterSlider::paintEvent(QPaintEvent *event) { ((QWidget*)parent())->layout()->getContentsMargins(&left, &top, &right, &bottom); QStyle *style = QApplication::style(); - int lol = style->pixelMetric(QStyle::PM_SliderSpaceAvailable); + //int lol = style->pixelMetric(QStyle::PM_SliderSpaceAvailable); QPainter painter(this); //painter.setPen(Qt::blue); painter.setOpacity(1.0); @@ -125,8 +138,7 @@ void MeterSlider::paintEvent(QPaintEvent *event) { // - ((this->maximum() - this->value()) * stepWidth)) //double ratio = ; double handleShift = (double)((sliderSize.width() * ((double)(this->maximum() - this->value()) / 100))); - painter.fillRect((this->width() - ((this->maximum() - this->value()) * stepWidth)) - (sliderSize.width()) + handleShift, - top / 2, sliderSize.width(), sliderSize.height() - bottom, Qt::magenta); + painter.fillRect((this->width() - ((this->maximum() - this->value()) * stepWidth)) - (sliderSize.width()) + handleShift, top / 2, sliderSize.width(), sliderSize.height() - bottom, Qt::magenta); //sliderComplex.subControls = QStyle::SC_SliderHandle; //p.drawComplexControl(QStyle::CC_Slider, sliderComplex); } @@ -637,7 +649,7 @@ void EndpointWidget::addSessionWidget(CustomWidgetEvent* ev){ //TODO: change mainwindow's widget name and subclass qwidget const QWidgetList topLevelWidgets = QApplication::topLevelWidgets(); for (QWidget *widget : topLevelWidgets) { - if (dynamic_cast(widget)) { + if (qobject_cast(widget)) { double widthRatio = ((MainWindow*)widget)->widthRatio; sw->calculateSize(std::abs(this->screen()->geometry().width()) * widthRatio, std::abs(this->screen()->geometry().height())); @@ -917,8 +929,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); //scrollArea->verticalScrollBar()->setSingleStep(1); - - scrollArea->setStyleSheet("QScrollBar:vertical { width: 4px; }"); + + //custom style = no qss + //scrollArea->setStyleSheet("QScrollBar:vertical { width: 4px; }"); //scrollArea->setMinimumWidth(500); setCentralWidget(scrollArea); diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index c85b9ad..fd9f936 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -1,8 +1,5 @@ #pragma once -//#ifndef MAINWINDOW_H -//#define MAINWINDOW_H - #include #include #include @@ -47,6 +44,7 @@ #include "global.h" #include "contclasses.h" +#include "qtvisuals.h" enum SpawnPos { LEFT = (1 << 1), @@ -76,12 +74,17 @@ public: class MeterSlider : public QSlider { Q_OBJECT private: + ~MeterSlider(); float peakValue; + MixerStyle* style; + protected: bool event(QEvent* ev) override; void paintEvent(QPaintEvent *event) override; public: + //MeterSlider(Qt::Orientation orientation, QWidget *parent = nullptr); + //MeterSlider(QWidget* parent = nullptr) : MeterSlider(Qt::Vertical, parent){}; void setPeakValue(float peakValue); using QSlider::QSlider; }; @@ -278,5 +281,3 @@ private: //void valueChanged(int value); }; - -//#endif diff --git a/src/qt/qtvisuals.h b/src/qt/qtvisuals.h new file mode 100644 index 0000000..5aba5ca --- /dev/null +++ b/src/qt/qtvisuals.h @@ -0,0 +1,84 @@ +#pragma once + +#include + +class MixerStyle : public QProxyStyle { + Q_OBJECT + +public: + using QProxyStyle::QProxyStyle; + + //void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, + //QPainter *p, const QWidget *widget) const override; + + int styleHint(QStyle::StyleHint hint, const QStyleOption* option = 0, const QWidget* widget = 0, + QStyleHintReturn* returnData = 0) const { + if (hint == QStyle::SH_Slider_AbsoluteSetButtons) + return (Qt::LeftButton | Qt::MiddleButton | Qt::RightButton); + return QProxyStyle::styleHint(hint, option, widget, returnData); + } +}; + + +/* + * void MixerStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, + * QPainter *p, const QWidget *widget) const { + * #if QT_CONFIG(slider) + * case CC_Slider: + * if (const QStyleOptionSlider *slider = qstyleoption_cast(opt)) { + * if (slider->subControls == SC_SliderTickmarks) { + * int tickOffset = proxy()->pixelMetric(PM_SliderTickmarkOffset, slider, widget); + * int ticks = slider->tickPosition; + * int thickness = proxy()->pixelMetric(PM_SliderControlThickness, slider, widget); + * int len = proxy()->pixelMetric(PM_SliderLength, slider, widget); + * int available = proxy()->pixelMetric(PM_SliderSpaceAvailable, slider, widget); + * int interval = slider->tickInterval; + * if (interval <= 0) { + * interval = slider->singleStep; + * if (QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, interval, + * available) + * - QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, + * 0, available) < 3) + * interval = slider->pageStep; + * } + * if (!interval) + * interval = 1; + * int fudge = len / 2; + * int pos; + * // Since there is no subrect for tickmarks do a translation here. + * QPainterStateSaver pss(p); + * p->translate(slider->rect.x(), slider->rect.y()); + * p->setPen(slider->palette.windowText().color()); + * int v = slider->minimum; + * while (v <= slider->maximum + 1) { + * if (v == slider->maximum + 1 && interval == 1) + * break; + * const int v_ = qMin(v, slider->maximum); + * pos = QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, + * v_, available) + fudge; + ** + * if (slider->orientation == Qt::Horizontal) { + * if (ticks & QSlider::TicksAbove) + * p->drawLine(pos, 0, pos, tickOffset - 2); + * if (ticks & QSlider::TicksBelow) + * p->drawLine(pos, tickOffset + thickness + 1, pos, + * slider->rect.height()-1); + * } else { + * if (ticks & QSlider::TicksAbove) + * p->drawLine(0, pos, tickOffset - 2, pos); + * if (ticks & QSlider::TicksBelow) + * p->drawLine(tickOffset + thickness + 1, pos, + * slider->rect.width()-1, pos); + * } + * // in the case where maximum is max int + * int nextInterval = v + interval; + * if (nextInterval < v) + * break; + * v = nextInterval; + * } + * } + * } + * break; +*#endif // QT_CONFIG(slider) +*/ +//} diff --git a/src/qtestmain.cpp b/src/qtestmain.cpp index 1d421cc..e4a6aec 100644 --- a/src/qtestmain.cpp +++ b/src/qtestmain.cpp @@ -46,13 +46,15 @@ void closeDebugFileLog() { */ int main (int argc, char* argv[]) { + /* * QStringList styles = QStyleFactory::keys(); * for(QString a : styles) { * log_debugcpp(a.toStdString()); * } */ - //QApplication::setStyle("Fusion"); + + QApplication::setStyle(new MixerStyle(QStyleFactory::create("windowsvista"))); //Check if running //https://stackoverflow.com/questions/48060989/qt-show-application-if-currently-running initialize_file_log(); From f8171f12f310938b1de65f27d67d92c41358754f Mon Sep 17 00:00:00 2001 From: Hane Date: Tue, 21 May 2024 19:35:12 +0200 Subject: [PATCH 45/78] wip: bad build config, unnecesary reimpl. Step by step --- qtest.pro | 2 +- src/qt/qtclasses.cpp | 145 +++++----- src/qt/qtclasses.h | 8 +- src/qt/qtvisuals.h | 613 ++++++++++++++++++++++++++++++++++++++----- src/qtestmain.cpp | 7 +- 5 files changed, 640 insertions(+), 135 deletions(-) diff --git a/qtest.pro b/qtest.pro index 97274fa..63ef85f 100644 --- a/qtest.pro +++ b/qtest.pro @@ -11,7 +11,7 @@ DESTPATH += "$$PWD\src" "$$PWD\src\qt" "$$PWD\src\back" "$$PWD\src\back\reimpl" VPATH += "$$PWD\src" "$$PWD\src\qt" "$$PWD\src\back" "$$PWD\src\back\reimpl" "$$PWD\src\cont" SOURCES += qtestmain.cpp qtclasses.cpp backlasses.cpp backsessionclasses.cpp contclasses.cpp contsessionclasses.cpp -HEADERS += qtclasses.h backlasses.h backsessionclasses.h contclasses.h contsessionclasses.h global.h debug.h backfuncs.h ipolicyconfig.h msinclude.h qtvisuals.h +HEADERS += qtclasses.h backlasses.h backsessionclasses.h contclasses.h contsessionclasses.h global.h debug.h backfuncs.h ipolicyconfig.h msinclude.h RESOURCES = assets.qrc RC_ICONS += assets/logo.ico diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 2174d7d..6e90375 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -34,6 +34,7 @@ bool MeterSlider::event(QEvent* ev) { void MeterSlider::paintEvent(QPaintEvent *event) { QStyleOptionSlider sliderComplex = QStyleOptionSlider(); sliderComplex.initFrom(this); + /* * sliderComplex.initFrom(this); * sliderComplex.subControls = QStyle::SC_None; @@ -64,36 +65,46 @@ void MeterSlider::paintEvent(QPaintEvent *event) { * //sliderComplex.subControls = QStyle::SC_SliderGroove; * if (this->tickPosition() != NoTicks) sliderComplex.subControls |= QStyle::SC_SliderTickmarks; * QStylePainter p(this); - * p.drawComplexControl(QStyle::CC_Slider, sliderComplex); */ + //p.drawComplexControl(QStyle::CC_Slider, sliderComplex); + - /* - * QStyleOptionSlider sliderComplex2 = QStyleOptionSlider(); - * sliderComplex2.initFrom(this); - * sliderComplex2.orientation = this->orientation(); - * sliderComplex2.maximum = this->maximum(); - * sliderComplex2.minimum = this->minimum(); - * sliderComplex2.tickPosition = (QSlider::TickPosition)this->tickPosition(); - * sliderComplex2.tickInterval = this->tickInterval(); - * sliderComplex2.upsideDown = (this->orientation() == Qt::Horizontal) ? - * (this->invertedAppearance() != (sliderComplex2.direction == Qt::RightToLeft)) - * : (!this->invertedAppearance()); - * sliderComplex2.subControls = QStyle::SC_SliderHandle; - * sliderComplex2.direction = Qt::LeftToRight; // we use the upsideDown option instead - * sliderComplex2.sliderPosition = this->sliderPosition(); - * sliderComplex2.sliderValue = this->value(); - * sliderComplex2.singleStep = this->singleStep(); - * sliderComplex2.pageStep = this->pageStep(); - * if (this->orientation() == Qt::Horizontal) - * sliderComplex2.state |= QStyle::State_Horizontal; - * - * if (this->isSliderDown()) { - * sliderComplex2.activeSubControls = QStyle::SC_SliderHandle; - * sliderComplex2.state |= QStyle::State_Sunken; - * } else { - * sliderComplex2.activeSubControls = QStyle::SC_SliderHandle; - * } - */ + + QStyleOptionSlider sliderComplex2 = QStyleOptionSlider(); + sliderComplex2.initFrom(this); + sliderComplex2.orientation = this->orientation(); + sliderComplex2.maximum = this->maximum(); + sliderComplex2.minimum = this->minimum(); + sliderComplex2.tickPosition = (QSlider::TickPosition)this->tickPosition(); + sliderComplex2.tickInterval = this->tickInterval(); + sliderComplex2.upsideDown = (this->orientation() == Qt::Horizontal) ? + (this->invertedAppearance() != (sliderComplex2.direction == Qt::RightToLeft)) + : (!this->invertedAppearance()); + sliderComplex2.subControls = QStyle::SC_SliderHandle; + sliderComplex2.direction = Qt::LeftToRight; // we use the upsideDown option instead + sliderComplex2.sliderPosition = this->sliderPosition(); + sliderComplex2.sliderValue = this->value(); + sliderComplex2.singleStep = this->singleStep(); + sliderComplex2.pageStep = this->pageStep(); + if (this->orientation() == Qt::Horizontal) + sliderComplex2.state |= QStyle::State_Horizontal; + + if (this->isSliderDown()) { + sliderComplex2.activeSubControls = QStyle::SC_SliderHandle; + sliderComplex2.state |= QStyle::State_Sunken; + } else { + sliderComplex2.activeSubControls = QStyle::SC_SliderHandle; + } + + sliderComplex2.subControls = QStyle::SC_SliderGroove | QStyle::SC_SliderHandle; + if ((QSlider::TickPosition)this->tickPosition() != NoTicks) + sliderComplex2.subControls |= QStyle::SC_SliderTickmarks; + + QPainter painter(this); + QStyle* stle = QApplication::style(); + stle->drawComplexControl((QStyle::ComplexControl)CC_MeterSlider, &sliderComplex2, &painter, this); + + //Q_D(QSlider); /* @@ -108,39 +119,43 @@ void MeterSlider::paintEvent(QPaintEvent *event) { * //p.drawComplexControl(QStyle::CC_Slider, opt); */ //QSlider::paintEvent(event); - int left = 0, top = 0, right = 0, bottom = 0; - ((QWidget*)parent())->layout()->getContentsMargins(&left, &top, &right, &bottom); - QStyle *style = QApplication::style(); - //int lol = style->pixelMetric(QStyle::PM_SliderSpaceAvailable); - QPainter painter(this); - //painter.setPen(Qt::blue); - painter.setOpacity(1.0); - painter.setClipping(false); - painter.setCompositionMode(QPainter::CompositionMode::CompositionMode_Source); - float peakLength = (this->width() * (this->peakValue)); - double stepWidth = (double)this->width() * ((double)this->singleStep() / (this->maximum() - this->minimum())); - //Fusion seems to fuck around with bar's height and width - //const qreal dpr = painter->device()->devicePixelRatio(); - //QStyleOptionSlider sliderComplex = QStyleOptionSlider(); //slider.initFrom(this); - QRect sliderSize = style->subControlRect(QStyle::CC_Slider, (QStyleOptionComplex*)&sliderComplex, QStyle::SC_SliderHandle); - int handleCenterPos = QStyle::sliderPositionFromValue(this->minimum(), this->maximum(), this->value(), this->width()); - int unattenuatedPeakMeter = ((this->width() * this->peakValue) >= handleCenterPos - (sliderSize.width() / 2)) - ? this->width() - (sliderSize.width() / 2) - : this->width() * this->peakValue; - //QApplication::style()->subControlRect(QStyle::CC_Slider, (QStyleOptionComplex*)&slider, QStyle::SC_SliderHandle); - painter.fillRect(0, (this->height() / 2) - 3, this->width(), - 4, Qt::white); - painter.fillRect(0, (this->height() / 2) - 3, this->width() * this->peakValue, - 4, Qt::gray); - painter.fillRect(0, (this->height() / 2) - 3, (this->width() - ((this->maximum() - this->value()) * stepWidth)) * this->peakValue, - 4, Qt::green); - // - ((this->maximum() - this->value()) * stepWidth)) - //double ratio = ; - double handleShift = (double)((sliderSize.width() * ((double)(this->maximum() - this->value()) / 100))); - painter.fillRect((this->width() - ((this->maximum() - this->value()) * stepWidth)) - (sliderSize.width()) + handleShift, top / 2, sliderSize.width(), sliderSize.height() - bottom, Qt::magenta); - //sliderComplex.subControls = QStyle::SC_SliderHandle; - //p.drawComplexControl(QStyle::CC_Slider, sliderComplex); + //IL CODE WENO lbockomet + /* + * int left = 0, top = 0, right = 0, bottom = 0; + * ((QWidget*)parent())->layout()->getContentsMargins(&left, &top, &right, &bottom); + * + * QStyle *style = QApplication::style(); + * //int lol = style->pixelMetric(QStyle::PM_SliderSpaceAvailable); + * QPainter painter(this); + * //painter.setPen(Qt::blue); + * painter.setOpacity(1.0); + * painter.setClipping(false); + * painter.setCompositionMode(QPainter::CompositionMode::CompositionMode_Source); + * float peakLength = (this->width() * (this->peakValue)); + * double stepWidth = (double)this->width() * ((double)this->singleStep() / (this->maximum() - this->minimum())); + * //Fusion seems to fuck around with bar's height and width + * //const qreal dpr = painter->device()->devicePixelRatio(); + * //QStyleOptionSlider sliderComplex = QStyleOptionSlider(); //slider.initFrom(this); + * QRect sliderSize = style->subControlRect(QStyle::CC_Slider, (QStyleOptionComplex*)&sliderComplex, QStyle::SC_SliderHandle); + * int handleCenterPos = QStyle::sliderPositionFromValue(this->minimum(), this->maximum(), this->value(), this->width()); + * int unattenuatedPeakMeter = ((this->width() * this->peakValue) >= handleCenterPos - (sliderSize.width() / 2)) + * ? this->width() - (sliderSize.width() / 2) + * : this->width() * this->peakValue; + * //QApplication::style()->subControlRect(QStyle::CC_Slider, (QStyleOptionComplex*)&slider, QStyle::SC_SliderHandle); + * painter.fillRect(0, (this->height() / 2) - 3, this->width(), + * 4, Qt::white); + * painter.fillRect(0, (this->height() / 2) - 3, this->width() * this->peakValue, + * 4, Qt::gray); + * painter.fillRect(0, (this->height() / 2) - 3, (this->width() - ((this->maximum() - this->value()) * stepWidth)) * this->peakValue, + * 4, Qt::green); + * // - ((this->maximum() - this->value()) * stepWidth)) + * //double ratio = ; + * double handleShift = (double)((sliderSize.width() * ((double)(this->maximum() - this->value()) / 100))); + * painter.fillRect((this->width() - ((this->maximum() - this->value()) * stepWidth)) - (sliderSize.width()) + handleShift, top / 2, sliderSize.width(), sliderSize.height() - bottom, Qt::magenta); + * //sliderComplex.subControls = QStyle::SC_SliderHandle; + * //p.drawComplexControl(QStyle::CC_Slider, sliderComplex); + */ } void ExtendedCheckBox::customEvent(QEvent* ev) { @@ -317,8 +332,8 @@ SessionWidget::SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent) mainSlider->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); //mainSlider->setMinimumWidth(120 /*1/16 1080p*/); mainSlider->setFocusPolicy(Qt::StrongFocus); - mainSlider->setTickPosition(QSlider::TicksBothSides); - mainSlider->setTickInterval(5); + //mainSlider->setTickPosition(QSlider::TicksBothSides); + //mainSlider->setTickInterval(5); mainSlider->setSingleStep(1); mainSlider->setRange(0,100); @@ -428,7 +443,7 @@ ChannelWidget::ChannelWidget(uint32_t channelCount, EndpointHandler* eph, QWidge for(uint64_t channel = 0, col = 0, row = 0; channel < channelCount && channelCount > 1; channel++){ MeterSlider* tmp = new MeterSlider(Qt::Horizontal); QLabel* tmpLb = new QLabel(""); - tmp->setTickInterval(5); + //tmp->setTickInterval(5); tmp->setSingleStep(1); tmp->setRange(0,100); @@ -520,8 +535,8 @@ EndpointWidget::EndpointWidget(EndpointHandler* eph, QWidget *parent, uint64_t i //muteButton->setStyleSheet("background-color: #A3C1DA; color: red"); mainSlider->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); mainSlider->setFocusPolicy(Qt::StrongFocus); - mainSlider->setTickPosition(QSlider::TicksBothSides); - mainSlider->setTickInterval(5); + //mainSlider->setTickPosition(QSlider::TicksBothSides); + //mainSlider->setTickInterval(5); mainSlider->setSingleStep(1); mainSlider->setRange(0,100); diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index fd9f936..47216be 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -29,7 +29,7 @@ #include #include #include - +#include //#include /* * #else @@ -44,7 +44,6 @@ #include "global.h" #include "contclasses.h" -#include "qtvisuals.h" enum SpawnPos { LEFT = (1 << 1), @@ -76,8 +75,8 @@ class MeterSlider : public QSlider { private: ~MeterSlider(); float peakValue; - MixerStyle* style; + friend class MixerStyle; protected: bool event(QEvent* ev) override; void paintEvent(QPaintEvent *event) override; @@ -89,6 +88,9 @@ public: using QSlider::QSlider; }; +//todo: TEST. TEST. +#include "qtvisuals.h" + class ExtendedCheckBox : public QCheckBox { Q_OBJECT protected: diff --git a/src/qt/qtvisuals.h b/src/qt/qtvisuals.h index 5aba5ca..bad98bb 100644 --- a/src/qt/qtvisuals.h +++ b/src/qt/qtvisuals.h @@ -1,84 +1,567 @@ #pragma once #include +#include +#include +#include +//#include "qstylehelper.cpp" + +//repeats. bruh. +//#include +//#include + +enum CustomComplexControl { + CC_MeterSlider = 0xf0000001 +}; + + +namespace qt64 { + static inline QLatin1String operator""_L1(const char* ch, uint64_t) { + return QLatin1String(ch); + } +} + +using namespace qt64; + class MixerStyle : public QProxyStyle { - Q_OBJECT - public: using QProxyStyle::QProxyStyle; - //void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, - //QPainter *p, const QWidget *widget) const override; - + //void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *option, + // QPainter *painter, const QWidget *widget) const override; + //Click on slider = move handle to click pos int styleHint(QStyle::StyleHint hint, const QStyleOption* option = 0, const QWidget* widget = 0, QStyleHintReturn* returnData = 0) const { if (hint == QStyle::SH_Slider_AbsoluteSetButtons) return (Qt::LeftButton | Qt::MiddleButton | Qt::RightButton); return QProxyStyle::styleHint(hint, option, widget, returnData); } + + /* + * #ifdef Q_OS_DARWIN + * static const qreal qstyleBaseDpi = 72; + * #else + * static const qreal qstyleBaseDpi = 96; + * #endif + */ + + //Q_GUI_EXPORT int qt_defaultDpiX() const; + + qreal dpi(const QStyleOption *option) const { + #ifndef Q_OS_DARWIN + // Prioritize the application override, except for on macOS where + // we have historically not supported the AA_Use96Dpi flag. + if (QCoreApplication::testAttribute(Qt::AA_Use96Dpi)) + return 96; + #endif + + // Expect that QStyleOption::QFontMetrics::QFont has the correct DPI set + if (option) + return option->fontMetrics.fontDpi(); + + // Fall back to historical Qt behavior: hardocded 72 DPI on mac, + // primary screen DPI on other platforms. + #ifdef Q_OS_DARWIN + return qstyleBaseDpi; + #else + return QGuiApplication::primaryScreen()->physicalDotsPerInch(); + #endif + } + + qreal dpiScaled(qreal value, qreal dpi) const { + static const qreal qstyleBaseDpi = 96; + return value * dpi / qstyleBaseDpi; + } + + qreal dpiScaled(qreal value, const QPaintDevice *device) const { + return dpiScaled(value, device->logicalDpiX()); + } + + qreal dpiScaled(qreal value, const QStyleOption *option) const { + return dpiScaled(value, dpi(option)); + } + + QRect subControlRect(ComplexControl control, const QStyleOptionComplex *option, + SubControl subControl, const QWidget *widget) const { + QRect rect = QCommonStyle::subControlRect(CC_Slider, option, subControl, widget); + + switch (control) { + #if QT_CONFIG(slider) + case CC_MeterSlider: + if (const QStyleOptionSlider *slider = qstyleoption_cast(option)) { + int tickSize = proxy()->pixelMetric(PM_SliderTickmarkOffset, option, widget); + switch (subControl) { + case SC_SliderHandle: { + const bool bothTicks = (slider->tickPosition & QSlider::TicksBothSides) == QSlider::TicksBothSides; + if (slider->orientation == Qt::Horizontal) { + rect.setHeight(baseStyle()->pixelMetric(PM_SliderThickness, option, widget)); + rect.setWidth(baseStyle()->pixelMetric(PM_SliderLength, option, widget)); + int centerY = slider->rect.center().y() - rect.height() / 2; + if (!bothTicks) { + if (slider->tickPosition & QSlider::TicksAbove) + centerY += tickSize; + if (slider->tickPosition & QSlider::TicksBelow) + centerY -= tickSize - 1; + } + rect.moveTop(centerY); + } else { + rect.setWidth(baseStyle()->pixelMetric(PM_SliderThickness, option, widget)); + rect.setHeight(baseStyle()->pixelMetric(PM_SliderLength, option, widget)); + int centerX = slider->rect.center().x() - rect.width() / 2; + if (!bothTicks) { + if (slider->tickPosition & QSlider::TicksAbove) + centerX += tickSize; + if (slider->tickPosition & QSlider::TicksBelow) + centerX -= tickSize - 1; + } + rect.moveLeft(centerX); + } + } + break; + case SC_SliderGroove: { + QPoint grooveCenter = slider->rect.center(); + //rect.setWidth(std::abs(grooveCenter.x()) * 2); + const int grooveThickness = this->dpiScaled(7, option); //QStyleHelper::dpiScaled(7, option); + const bool bothTicks = (slider->tickPosition & QSlider::TicksBothSides) == QSlider::TicksBothSides; + if (slider->orientation == Qt::Horizontal) { + rect.setHeight(grooveThickness); + if (!bothTicks) { + if (slider->tickPosition & QSlider::TicksAbove) + grooveCenter.ry() += tickSize; + if (slider->tickPosition & QSlider::TicksBelow) + grooveCenter.ry() -= tickSize - 1; + } + } else { + rect.setWidth(grooveThickness); + if (!bothTicks) { + if (slider->tickPosition & QSlider::TicksAbove) + grooveCenter.rx() += tickSize; + if (slider->tickPosition & QSlider::TicksBelow) + grooveCenter.rx() -= tickSize - 1; + } + } + rect.moveCenter(grooveCenter); + break; + } + default: + break; + } + } + return rect; + break; +#endif // QT_CONFIG(slider) + default: + return baseStyle()->subControlRect(control, option, subControl, widget); + break; + } + + } + + void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *option, + QPainter *painter, const QWidget *widget) const { + switch (cc) { + #if QT_CONFIG(slider) + case CC_MeterSlider: + if (const QStyleOptionSlider *slider = qstyleoption_cast(option)) { + const qreal dpr = painter->device()->devicePixelRatio(); + const QColor buttonColor = this->buttonColor(option->palette); + QRect groove = this->subControlRect(static_cast(CC_MeterSlider), option, SC_SliderGroove, widget); + QRect handle = this->subControlRect(static_cast(CC_MeterSlider), option, SC_SliderHandle, widget); + + bool horizontal = slider->orientation == Qt::Horizontal; + bool ticksAbove = slider->tickPosition & QSlider::TicksAbove; + bool ticksBelow = slider->tickPosition & QSlider::TicksBelow; + QColor activeHighlight = this->highlight(option->palette); + QPixmap cache; + QBrush oldBrush = painter->brush(); + QPen oldPen = painter->pen(); + QColor shadowAlpha(Qt::black); + shadowAlpha.setAlpha(10); + QColor outline; + if (option->state & State_HasFocus && option->state & State_KeyboardFocusChange) + outline = this->highlightedOutline(option->palette); + + if ((option->subControls & SC_SliderGroove) && groove.isValid()) { + QColor grooveColor; + grooveColor.setHsv(buttonColor.hue(), + qMin(255, (int)(buttonColor.saturation())), + qMin(255, (int)(buttonColor.value()*0.9))); + QString groovePixmapName = "slider_groove" + QString::number(groove.width());//QStyleHelper::uniqueName("slider_groove"_L1, option, groove.size(), dpr); + QRect pixmapRect(0, 0, groove.width(), groove.height()); + + // draw background groove + if (!QPixmapCache::find(groovePixmapName, &cache)) { + cache = this->styleCachePixmap(pixmapRect.size(), dpr); + QPainter groovePainter(&cache); + groovePainter.setRenderHint(QPainter::Antialiasing, true); + groovePainter.translate(0.5, 0.5); + QLinearGradient gradient; + if (horizontal) { + gradient.setStart(pixmapRect.center().x(), pixmapRect.top()); + gradient.setFinalStop(pixmapRect.center().x(), pixmapRect.bottom()); + } + else { + gradient.setStart(pixmapRect.left(), pixmapRect.center().y()); + gradient.setFinalStop(pixmapRect.right(), pixmapRect.center().y()); + } + groovePainter.setPen(QPen(outline)); + gradient.setColorAt(0, grooveColor.darker(110)); + gradient.setColorAt(1, grooveColor.lighter(110));//palette.button().color().darker(115)); + groovePainter.setBrush(gradient); + groovePainter.drawRoundedRect(pixmapRect.adjusted(1, 1, -2, -2), 1, 1); + groovePainter.end(); + QPixmapCache::insert(groovePixmapName, cache); + } + painter->drawPixmap(groove.topLeft(), cache); + + // draw groove lines (vol + unattenuated vol) + const MeterSlider* widgetCast = qobject_cast(widget); + if (widgetCast) { + QRect clipRect; + //if (!groovePixmapName.isEmpty()) groovePixmapName += QLatin1String("_unattenuated"); + //groovePixmapName += "_unattenuated"_L1; + //lf (!QPixmapCache::find(groovePixmapName, &cache)) { + cache = styleCachePixmap(pixmapRect.size(), dpr); + QPainter groovePainter(&cache); + QLinearGradient gradient; + if (horizontal) { + gradient.setStart(pixmapRect.center().x(), pixmapRect.top()); + gradient.setFinalStop(pixmapRect.center().x(), pixmapRect.bottom()); + } + else { + gradient.setStart(pixmapRect.left(), pixmapRect.center().y()); + gradient.setFinalStop(pixmapRect.right(), pixmapRect.center().y()); + } + QColor highlight = QColor(30,30,30,100); //this->highlight(option->palette); + QColor highlightedoutline = highlight.darker(140); + QColor grooveOutline = outline; + if (qGray(grooveOutline.rgb()) > qGray(highlightedoutline.rgb())) + grooveOutline = highlightedoutline; + + float peakValue = ((MeterSlider*)widget)->peakValue; + groovePainter.setRenderHint(QPainter::Antialiasing, true); + groovePainter.translate(0.5, 0.5); + groovePainter.setPen(QPen(grooveOutline)); + gradient.setColorAt(0, highlight); + gradient.setColorAt(1, highlight.lighter(130)); + groovePainter.setBrush(gradient); + groovePainter.drawRoundedRect(pixmapRect.adjusted( + 1, 1, + -pixmapRect.width() + (pixmapRect.width() * peakValue), + -2), 1, 1); + groovePainter.setPen(this->innerContrastLine()); + groovePainter.setBrush(Qt::green); + double stepWidth = (double)widgetCast->width() * ((double)widgetCast->singleStep() / (widgetCast->maximum() - widgetCast->minimum())); + groovePainter.drawRoundedRect(pixmapRect.adjusted( + 1, 1, + -pixmapRect.width() + ((widgetCast->width() - ((widgetCast->maximum() - widgetCast->value()) * stepWidth)) * peakValue), + -2), 1, 1); + + groovePainter.end(); + //QPixmapCache::insert(groovePixmapName, cache); + //} + //} + if (horizontal) { + if (slider->upsideDown) + clipRect = QRect(handle.right(), groove.top(), groove.right() - handle.right(), groove.height()); + else + clipRect = QRect(groove.left() + 2, groove.top() + 2, + groove.right() - 2, 2); + } else { + if (slider->upsideDown) + clipRect = QRect(groove.left(), handle.bottom(), groove.width(), groove.height() - (handle.bottom() - slider->rect.top())); + else + clipRect = QRect(groove.left(), groove.top(), groove.width(), handle.top() - groove.top()); + } + painter->save(); + painter->setClipRect(clipRect.adjusted(0, 0, 1, 1), Qt::IntersectClip); + painter->drawPixmap(groove.topLeft(), cache); + painter->restore(); + } + } + if (option->subControls & SC_SliderTickmarks) { + painter->save(); + painter->translate(slider->rect.x(), slider->rect.y()); + painter->setPen(outline); + int tickSize = proxy()->pixelMetric(PM_SliderTickmarkOffset, option, widget); + int available = proxy()->pixelMetric(PM_SliderSpaceAvailable, slider, widget); + int interval = slider->tickInterval; + if (interval <= 0) { + interval = slider->singleStep; + if (QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, interval, + available) + - QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, + 0, available) < 3) + interval = slider->pageStep; + } + if (interval <= 0) + interval = 1; + + int v = slider->minimum; + int len = proxy()->pixelMetric(PM_SliderLength, slider, widget); + QVector lines; + while (v <= slider->maximum + 1) { + if (v == slider->maximum + 1 && interval == 1) + break; + const int v_ = qMin(v, slider->maximum); + int pos = sliderPositionFromValue(slider->minimum, slider->maximum, + v_, (horizontal + ? slider->rect.width() + : slider->rect.height()) - len, + slider->upsideDown) + len / 2; + int extra = 2 - ((v_ == slider->minimum || v_ == slider->maximum) ? 1 : 0); + + if (horizontal) { + if (ticksAbove) { + lines += QLine(pos, slider->rect.top() + extra, + pos, slider->rect.top() + tickSize); + } + if (ticksBelow) { + lines += QLine(pos, slider->rect.bottom() - extra, + pos, slider->rect.bottom() - tickSize); + } + } else { + if (ticksAbove) { + lines += QLine(slider->rect.left() + extra, pos, + slider->rect.left() + tickSize, pos); + } + if (ticksBelow) { + lines += QLine(slider->rect.right() - extra, pos, + slider->rect.right() - tickSize, pos); + } + } + // in the case where maximum is max int + int nextInterval = v + interval; + if (nextInterval < v) + break; + v = nextInterval; + } + painter->drawLines(lines); + painter->restore(); + } + // draw handle + if ((option->subControls & SC_SliderHandle) ) { + QString handlePixmapName = "slider_handle" + QString::number(handle.height());//QStyleHelper::uniqueName("slider_handle"_L1, option,//handle.size(), dpr); + if (!QPixmapCache::find(handlePixmapName, &cache)) { + cache = styleCachePixmap(handle.size(), dpr); + QRect pixmapRect(0, 0, handle.width(), handle.height()); + QPainter handlePainter(&cache); + QRect gradRect = pixmapRect.adjusted(2, 2, -2, -2); + + // gradient fill + QRect r = pixmapRect.adjusted(1, 1, -2, -2); + QLinearGradient gradient = qt_fusion_gradient(gradRect, this->buttonColor(option->palette),horizontal ? TopDown : FromLeft); + + handlePainter.setRenderHint(QPainter::Antialiasing, true); + handlePainter.translate(0.5, 0.5); + + handlePainter.setPen(Qt::NoPen); + handlePainter.setBrush(QColor(0, 0, 0, 40)); + handlePainter.drawRect(horizontal ? r.adjusted(-1, 2, 1, -2) : r.adjusted(2, -1, -2, 1)); + + handlePainter.setPen(QPen(this->outline(option->palette))); + if (option->state & State_HasFocus && option->state & State_KeyboardFocusChange) + handlePainter.setPen(QPen(this->highlightedOutline(option->palette))); + + handlePainter.setBrush(gradient); + handlePainter.drawRoundedRect(r, 2, 2); + handlePainter.setBrush(Qt::NoBrush); + handlePainter.setPen(this->innerContrastLine()); + handlePainter.drawRoundedRect(r.adjusted(1, 1, -1, -1), 2, 2); + + QColor cornerAlpha = outline.darker(120); + cornerAlpha.setAlpha(80); + + //handle shadow + handlePainter.setPen(shadowAlpha); + handlePainter.drawLine(QPoint(r.left() + 2, r.bottom() + 1), QPoint(r.right() - 2, r.bottom() + 1)); + handlePainter.drawLine(QPoint(r.right() + 1, r.bottom() - 3), QPoint(r.right() + 1, r.top() + 4)); + handlePainter.drawLine(QPoint(r.right() - 1, r.bottom()), QPoint(r.right() + 1, r.bottom() - 2)); + + handlePainter.end(); + QPixmapCache::insert(handlePixmapName, cache); + } + + painter->drawPixmap(handle.topLeft(), cache); + + } + painter->setBrush(oldBrush); + painter->setPen(oldPen); + } + break; + default: + baseStyle()->drawComplexControl(cc, option, painter, widget); +#endif // QT_CONFIG(slider) +} +} + +private: + enum Direction { + TopDown, + FromLeft, + BottomUp, + FromRight + }; + + QColor highlightedOutline(const QPalette &pal) const { + QColor highlightedOutline = highlight(pal).darker(125); + if (highlightedOutline.value() > 160) + highlightedOutline.setHsl(highlightedOutline.hue(), highlightedOutline.saturation(), 160); + return highlightedOutline; + } + + QColor outline(const QPalette &pal) const { + if (pal.window().style() == Qt::TexturePattern) + return QColor(0, 0, 0, 160); + return pal.window().color().darker(140); + } + + QColor buttonColor(const QPalette &pal) const { + QColor buttonColor = pal.button().color(); + int val = qGray(buttonColor.rgb()); + buttonColor = buttonColor.lighter(100 + qMax(1, (180 - val)/6)); + buttonColor.setHsv(buttonColor.hue(), buttonColor.saturation() * 0.75, buttonColor.value()); + return buttonColor; + } + + QColor highlight(const QPalette &pal) const { + if (isMacSystemPalette(pal)) + return QColor(60, 140, 230); + return pal.color(QPalette::Highlight); + } + + QColor innerContrastLine() const { + return QColor(255, 255, 255, 30); + } + + bool isMacSystemPalette(const QPalette &pal) const { + Q_UNUSED(pal); +#if defined(Q_OS_MACOS) + const QPalette *themePalette = QGuiApplicationPrivate::platformTheme()->palette(); + if (themePalette && themePalette->color(QPalette::Normal, QPalette::Highlight) == + pal.color(QPalette::Normal, QPalette::Highlight) && + themePalette->color(QPalette::Normal, QPalette::HighlightedText) == + pal.color(QPalette::Normal, QPalette::HighlightedText)) + return true; +#endif + return false; + } + + inline QPixmap styleCachePixmap(const QSize &size, qreal pixelRatio) const { + //not api + QPixmap cachePixmap = QPixmap(size * pixelRatio); + cachePixmap.setDevicePixelRatio(pixelRatio); + cachePixmap.fill(Qt::transparent); + return cachePixmap; + } + + static QLinearGradient qt_fusion_gradient(const QRect &rect, const QBrush &baseColor, Direction direction = TopDown) +{ + int x = rect.center().x(); + int y = rect.center().y(); + QLinearGradient gradient; + switch (direction) { + case FromLeft: + gradient = QLinearGradient(rect.left(), y, rect.right(), y); + break; + case FromRight: + gradient = QLinearGradient(rect.right(), y, rect.left(), y); + break; + case BottomUp: + gradient = QLinearGradient(x, rect.bottom(), x, rect.top()); + break; + case TopDown: + default: + gradient = QLinearGradient(x, rect.top(), x, rect.bottom()); + break; + } + if (baseColor.gradient()) + gradient.setStops(baseColor.gradient()->stops()); + else { + QColor gradientStartColor = baseColor.color().lighter(124); + QColor gradientStopColor = baseColor.color().lighter(102); + gradient.setColorAt(0, gradientStartColor); + gradient.setColorAt(1, gradientStopColor); + // Uncomment for adding shiny shading + // QColor midColor1 = mergedColors(gradientStartColor, gradientStopColor, 55); + // QColor midColor2 = mergedColors(gradientStartColor, gradientStopColor, 45); + // gradient.setColorAt(0.5, midColor1); + // gradient.setColorAt(0.501, midColor2); + } + return gradient; +} + }; + /* * void MixerStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, - * QPainter *p, const QWidget *widget) const { - * #if QT_CONFIG(slider) - * case CC_Slider: - * if (const QStyleOptionSlider *slider = qstyleoption_cast(opt)) { - * if (slider->subControls == SC_SliderTickmarks) { - * int tickOffset = proxy()->pixelMetric(PM_SliderTickmarkOffset, slider, widget); - * int ticks = slider->tickPosition; - * int thickness = proxy()->pixelMetric(PM_SliderControlThickness, slider, widget); - * int len = proxy()->pixelMetric(PM_SliderLength, slider, widget); - * int available = proxy()->pixelMetric(PM_SliderSpaceAvailable, slider, widget); - * int interval = slider->tickInterval; - * if (interval <= 0) { - * interval = slider->singleStep; - * if (QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, interval, - * available) - * - QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, - * 0, available) < 3) - * interval = slider->pageStep; - * } - * if (!interval) - * interval = 1; - * int fudge = len / 2; - * int pos; - * // Since there is no subrect for tickmarks do a translation here. - * QPainterStateSaver pss(p); - * p->translate(slider->rect.x(), slider->rect.y()); - * p->setPen(slider->palette.windowText().color()); - * int v = slider->minimum; - * while (v <= slider->maximum + 1) { - * if (v == slider->maximum + 1 && interval == 1) - * break; - * const int v_ = qMin(v, slider->maximum); - * pos = QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, - * v_, available) + fudge; - ** - * if (slider->orientation == Qt::Horizontal) { - * if (ticks & QSlider::TicksAbove) - * p->drawLine(pos, 0, pos, tickOffset - 2); - * if (ticks & QSlider::TicksBelow) - * p->drawLine(pos, tickOffset + thickness + 1, pos, - * slider->rect.height()-1); - * } else { - * if (ticks & QSlider::TicksAbove) - * p->drawLine(0, pos, tickOffset - 2, pos); - * if (ticks & QSlider::TicksBelow) - * p->drawLine(tickOffset + thickness + 1, pos, - * slider->rect.width()-1, pos); - * } - * // in the case where maximum is max int - * int nextInterval = v + interval; - * if (nextInterval < v) - * break; - * v = nextInterval; - * } - * } - * } - * break; -*#endif // QT_CONFIG(slider) -*/ -//} + * QPainter *p, const QWidget *widget) const { + * switch (cc) { + * #if QT_CONFIG(slider) + * case CC_Slider: + * if (const QStyleOptionSlider *slider = qstyleoption_cast(opt)) { + * if (slider->subControls == SC_SliderTickmarks) { + * int tickOffset = proxy()->pixelMetric(PM_SliderTickmarkOffset, slider, widget); + * int ticks = slider->tickPosition; + * int thickness = proxy()->pixelMetric(PM_SliderControlThickness, slider, widget); + * int len = proxy()->pixelMetric(PM_SliderLength, slider, widget); + * int available = proxy()->pixelMetric(PM_SliderSpaceAvailable, slider, widget); + * int interval = slider->tickInterval; + * if (interval <= 0) { + * interval = slider->singleStep; + * if (QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, interval, + * available) + * - QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, + * 0, available) < 3) + * interval = slider->pageStep; + * } + * if (!interval) + * interval = 1; + * int fudge = len / 2; + * int pos; + * // Since there is no subrect for tickmarks do a translation here. + * p->save(); + * p->translate(slider->rect.x(), slider->rect.y()); + * p->setPen(slider->palette.windowText().color()); + * int v = slider->minimum; + * while (v <= slider->maximum + 1) { + * if (v == slider->maximum + 1 && interval == 1) + * break; + * const int v_ = qMin(v, slider->maximum); + * pos = QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, + * v_, available) + fudge; + * if (slider->orientation == Qt::Horizontal) { + * if (ticks & QSlider::TicksAbove) + * p->drawLine(pos, 0, pos, tickOffset - 2); + * if (ticks & QSlider::TicksBelow) + * p->drawLine(pos, tickOffset + thickness + 1, pos, + * slider->rect.height()-1); + * } else { + * if (ticks & QSlider::TicksAbove) + * p->drawLine(0, pos, tickOffset - 2, pos); + * if (ticks & QSlider::TicksBelow) + * p->drawLine(tickOffset + thickness + 1, pos, + * slider->rect.width()-1, pos); + * } + * // in the case where maximum is max int + * int nextInterval = v + interval; + * if (nextInterval < v) + * break; + * v = nextInterval; + * } + * } + * } + * p->restore(); + * break; + * default: + * baseStyle()->drawComplexControl(cc, opt, p, widget); + * break; + * } + * #endif // QT_CONFIG(slider) + * } + */ + +//void MixerStyle:: diff --git a/src/qtestmain.cpp b/src/qtestmain.cpp index e4a6aec..f37e337 100644 --- a/src/qtestmain.cpp +++ b/src/qtestmain.cpp @@ -7,6 +7,7 @@ #include #include #include +#include //#include "contclasses.h" #define INIT_FILELOG #include "qtclasses.h" @@ -54,7 +55,11 @@ int main (int argc, char* argv[]) { * } */ - QApplication::setStyle(new MixerStyle(QStyleFactory::create("windowsvista"))); + QApplication::setStyle(new MixerStyle(QStyleFactory::create("Fusion"))); + QPalette palette = QGuiApplication::palette(); + //todo: ez full apply os accent colorw + palette.setColor(QPalette::Active, QPalette::Highlight, QColor(255, 192, 203, 200));//QColor(30,30,30,100)); + QGuiApplication::setPalette(palette); //Check if running //https://stackoverflow.com/questions/48060989/qt-show-application-if-currently-running initialize_file_log(); From f7de5ef80351f4244acc12afd002153413013fea Mon Sep 17 00:00:00 2001 From: Hane Date: Thu, 30 May 2024 19:23:09 +0200 Subject: [PATCH 46/78] Qt code refactor w/ functional style --- qtest.pro | 2 +- src/qt/meterslider.h | 19 +++++ src/qt/qtclasses.cpp | 2 +- src/qt/qtclasses.h | 65 ++--------------- src/qt/qtcommon.h | 165 +++++++++++++++++++++++++++++++++++++++++++ src/qt/qtvisuals.h | 141 ++++-------------------------------- src/qtestmain.cpp | 14 +--- 7 files changed, 207 insertions(+), 201 deletions(-) create mode 100644 src/qt/meterslider.h create mode 100644 src/qt/qtcommon.h diff --git a/qtest.pro b/qtest.pro index 63ef85f..303f059 100644 --- a/qtest.pro +++ b/qtest.pro @@ -11,7 +11,7 @@ DESTPATH += "$$PWD\src" "$$PWD\src\qt" "$$PWD\src\back" "$$PWD\src\back\reimpl" VPATH += "$$PWD\src" "$$PWD\src\qt" "$$PWD\src\back" "$$PWD\src\back\reimpl" "$$PWD\src\cont" SOURCES += qtestmain.cpp qtclasses.cpp backlasses.cpp backsessionclasses.cpp contclasses.cpp contsessionclasses.cpp -HEADERS += qtclasses.h backlasses.h backsessionclasses.h contclasses.h contsessionclasses.h global.h debug.h backfuncs.h ipolicyconfig.h msinclude.h +HEADERS += qtclasses.h backlasses.h backsessionclasses.h contclasses.h contsessionclasses.h global.h debug.h backfuncs.h ipolicyconfig.h msinclude.h meterslider.h qtvisuals.h RESOURCES = assets.qrc RC_ICONS += assets/logo.ico diff --git a/src/qt/meterslider.h b/src/qt/meterslider.h new file mode 100644 index 0000000..a59e16e --- /dev/null +++ b/src/qt/meterslider.h @@ -0,0 +1,19 @@ +#include + +class MeterSlider : public QSlider { + Q_OBJECT +private: + ~MeterSlider(); + float peakValue; + + friend class MixerStyle; +protected: + bool event(QEvent* ev) override; + void paintEvent(QPaintEvent *event) override; + +public: + //MeterSlider(Qt::Orientation orientation, QWidget *parent = nullptr); + //MeterSlider(QWidget* parent = nullptr) : MeterSlider(Qt::Vertical, parent){}; + void setPeakValue(float peakValue); + using QSlider::QSlider; +}; diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 6e90375..d0141ea 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -1,4 +1,5 @@ #include "qtclasses.h" +#include "meterslider.h" #define POLLING_RATE 2 template @@ -103,7 +104,6 @@ void MeterSlider::paintEvent(QPaintEvent *event) { QPainter painter(this); QStyle* stle = QApplication::style(); stle->drawComplexControl((QStyle::ComplexControl)CC_MeterSlider, &sliderComplex2, &painter, this); - //Q_D(QSlider); diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index 47216be..6a5e9aa 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -1,50 +1,10 @@ #pragma once -#include -#include -#include - -#include -#include -#include -//#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -//#include -/* - * #else - * class QSlider; - * class QLabel; - * class QGridLayout; - * class QPushButton; - * class QWidget; - * class QMainWindow; - * #endif - */ - -#include "global.h" +#include "qtcommon.h" #include "contclasses.h" +class MeterSlider; + enum SpawnPos { LEFT = (1 << 1), RIGHT = (0 << 1), @@ -70,26 +30,9 @@ public: }; //Q_DECLARE_METATYPE(EndpointWidgetEvent) -class MeterSlider : public QSlider { - Q_OBJECT -private: - ~MeterSlider(); - float peakValue; - - friend class MixerStyle; -protected: - bool event(QEvent* ev) override; - void paintEvent(QPaintEvent *event) override; - -public: - //MeterSlider(Qt::Orientation orientation, QWidget *parent = nullptr); - //MeterSlider(QWidget* parent = nullptr) : MeterSlider(Qt::Vertical, parent){}; - void setPeakValue(float peakValue); - using QSlider::QSlider; -}; //todo: TEST. TEST. -#include "qtvisuals.h" +//#include "qtvisuals.h" class ExtendedCheckBox : public QCheckBox { Q_OBJECT diff --git a/src/qt/qtcommon.h b/src/qt/qtcommon.h new file mode 100644 index 0000000..8c9119c --- /dev/null +++ b/src/qt/qtcommon.h @@ -0,0 +1,165 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +//#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#include +/* + * #else + * class QSlider; + * class QLabel; + * class QGridLayout; + * class QPushButton; + * class QWidget; + * class QMainWindow; + * #endif + */ + +#include "global.h" + +enum CustomComplexControl { + CC_MeterSlider = 0xf0000001 +}; + +namespace StylingHelper { + static inline QLatin1String operator""_L1(const char* ch, uint64_t) { + return QLatin1String(ch); + } + + static inline qreal dpi(const QStyleOption *option) { + #ifndef Q_OS_DARWIN + // Prioritize the application override, except for on macOS where + // we have historically not supported the AA_Use96Dpi flag. + if (QCoreApplication::testAttribute(Qt::AA_Use96Dpi)) + return 96; + #endif + + // Expect that QStyleOption::QFontMetrics::QFont has the correct DPI set + if (option) + return option->fontMetrics.fontDpi(); + + // Fall back to historical Qt behavior: hardocded 72 DPI on mac, + // primary screen DPI on other platforms. + #ifdef Q_OS_DARWIN + return qstyleBaseDpi; + #else + return QGuiApplication::primaryScreen()->physicalDotsPerInch(); + #endif + } + + /* + * #ifdef Q_OS_DARWIN + * static const qreal qstyleBaseDpi = 72; + * #else + * static const qreal qstyleBaseDpi = 96; + * #endif + */ + + //Q_GUI_EXPORT int qt_defaultDpiX() const; + + + static inline qreal dpiScaled(qreal value, qreal dpi) { + static const qreal qstyleBaseDpi = 96; + return value * dpi / qstyleBaseDpi; + } + + static inline qreal dpiScaled(qreal value, const QPaintDevice *device) { + return dpiScaled(value, device->logicalDpiX()); + } + + static inline qreal dpiScaled(qreal value, const QStyleOption *option) { + return dpiScaled(value, dpi(option)); + } + + static inline bool isMacSystemPalette(const QPalette &pal) { + Q_UNUSED(pal); +#if defined(Q_OS_MACOS) + const QPalette *themePalette = QGuiApplicationPrivate::platformTheme()->palette(); + if (themePalette && themePalette->color(QPalette::Normal, QPalette::Highlight) == + pal.color(QPalette::Normal, QPalette::Highlight) && + themePalette->color(QPalette::Normal, QPalette::HighlightedText) == + pal.color(QPalette::Normal, QPalette::HighlightedText)) + return true; +#endif + return false; + } + + static inline QColor highlight(const QPalette &pal) { + if (isMacSystemPalette(pal)) + return QColor(60, 140, 230); + return pal.color(QPalette::Highlight); + } + + static inline QColor highlightedOutline(const QPalette &pal) { + QColor highlightedOutline = highlight(pal).darker(125); + if (highlightedOutline.value() > 160) + highlightedOutline.setHsl(highlightedOutline.hue(), highlightedOutline.saturation(), 160); + return highlightedOutline; + } + + static inline QColor getOutline(const QPalette &pal) { + if (pal.window().style() == Qt::TexturePattern) + return QColor(0, 0, 0, 160); + return pal.window().color().darker(140); + } + + static inline QColor getButtonColor(const QPalette &pal) { + QColor buttonColor = pal.button().color(); + int val = qGray(buttonColor.rgb()); + buttonColor = buttonColor.lighter(100 + qMax(1, (180 - val)/6)); + buttonColor.setHsv(buttonColor.hue(), buttonColor.saturation() * 0.75, buttonColor.value()); + return buttonColor; + } + + static inline QColor innerContrastLine() { + return QColor(255, 255, 255, 30); + } + + static inline QPixmap styleCachePixmap(const QSize &size, qreal pixelRatio) { + //not api + QPixmap cachePixmap = QPixmap(size * pixelRatio); + cachePixmap.setDevicePixelRatio(pixelRatio); + cachePixmap.fill(Qt::transparent); + return cachePixmap; + } +} + diff --git a/src/qt/qtvisuals.h b/src/qt/qtvisuals.h index bad98bb..f88c025 100644 --- a/src/qt/qtvisuals.h +++ b/src/qt/qtvisuals.h @@ -1,28 +1,9 @@ #pragma once -#include -#include -#include -#include -//#include "qstylehelper.cpp" - -//repeats. bruh. -//#include -//#include - -enum CustomComplexControl { - CC_MeterSlider = 0xf0000001 -}; - - -namespace qt64 { - static inline QLatin1String operator""_L1(const char* ch, uint64_t) { - return QLatin1String(ch); - } -} - -using namespace qt64; +#include "qtcommon.h" +#include "meterslider.h" +using namespace StylingHelper; class MixerStyle : public QProxyStyle { public: @@ -38,50 +19,6 @@ public: return QProxyStyle::styleHint(hint, option, widget, returnData); } - /* - * #ifdef Q_OS_DARWIN - * static const qreal qstyleBaseDpi = 72; - * #else - * static const qreal qstyleBaseDpi = 96; - * #endif - */ - - //Q_GUI_EXPORT int qt_defaultDpiX() const; - - qreal dpi(const QStyleOption *option) const { - #ifndef Q_OS_DARWIN - // Prioritize the application override, except for on macOS where - // we have historically not supported the AA_Use96Dpi flag. - if (QCoreApplication::testAttribute(Qt::AA_Use96Dpi)) - return 96; - #endif - - // Expect that QStyleOption::QFontMetrics::QFont has the correct DPI set - if (option) - return option->fontMetrics.fontDpi(); - - // Fall back to historical Qt behavior: hardocded 72 DPI on mac, - // primary screen DPI on other platforms. - #ifdef Q_OS_DARWIN - return qstyleBaseDpi; - #else - return QGuiApplication::primaryScreen()->physicalDotsPerInch(); - #endif - } - - qreal dpiScaled(qreal value, qreal dpi) const { - static const qreal qstyleBaseDpi = 96; - return value * dpi / qstyleBaseDpi; - } - - qreal dpiScaled(qreal value, const QPaintDevice *device) const { - return dpiScaled(value, device->logicalDpiX()); - } - - qreal dpiScaled(qreal value, const QStyleOption *option) const { - return dpiScaled(value, dpi(option)); - } - QRect subControlRect(ComplexControl control, const QStyleOptionComplex *option, SubControl subControl, const QWidget *widget) const { QRect rect = QCommonStyle::subControlRect(CC_Slider, option, subControl, widget); @@ -122,7 +59,7 @@ public: case SC_SliderGroove: { QPoint grooveCenter = slider->rect.center(); //rect.setWidth(std::abs(grooveCenter.x()) * 2); - const int grooveThickness = this->dpiScaled(7, option); //QStyleHelper::dpiScaled(7, option); + const int grooveThickness = dpiScaled(7, option); //QStyleHelper::dpiScaled(7, option); const bool bothTicks = (slider->tickPosition & QSlider::TicksBothSides) == QSlider::TicksBothSides; if (slider->orientation == Qt::Horizontal) { rect.setHeight(grooveThickness); @@ -165,14 +102,14 @@ public: case CC_MeterSlider: if (const QStyleOptionSlider *slider = qstyleoption_cast(option)) { const qreal dpr = painter->device()->devicePixelRatio(); - const QColor buttonColor = this->buttonColor(option->palette); + const QColor buttonColor = getButtonColor(option->palette); QRect groove = this->subControlRect(static_cast(CC_MeterSlider), option, SC_SliderGroove, widget); QRect handle = this->subControlRect(static_cast(CC_MeterSlider), option, SC_SliderHandle, widget); bool horizontal = slider->orientation == Qt::Horizontal; bool ticksAbove = slider->tickPosition & QSlider::TicksAbove; bool ticksBelow = slider->tickPosition & QSlider::TicksBelow; - QColor activeHighlight = this->highlight(option->palette); + QColor activeHighlight = highlight(option->palette); QPixmap cache; QBrush oldBrush = painter->brush(); QPen oldPen = painter->pen(); @@ -180,7 +117,7 @@ public: shadowAlpha.setAlpha(10); QColor outline; if (option->state & State_HasFocus && option->state & State_KeyboardFocusChange) - outline = this->highlightedOutline(option->palette); + outline = highlightedOutline(option->palette); if ((option->subControls & SC_SliderGroove) && groove.isValid()) { QColor grooveColor; @@ -192,7 +129,7 @@ public: // draw background groove if (!QPixmapCache::find(groovePixmapName, &cache)) { - cache = this->styleCachePixmap(pixmapRect.size(), dpr); + cache = styleCachePixmap(pixmapRect.size(), dpr); QPainter groovePainter(&cache); groovePainter.setRenderHint(QPainter::Antialiasing, true); groovePainter.translate(0.5, 0.5); @@ -250,7 +187,7 @@ public: 1, 1, -pixmapRect.width() + (pixmapRect.width() * peakValue), -2), 1, 1); - groovePainter.setPen(this->innerContrastLine()); + groovePainter.setPen(innerContrastLine()); groovePainter.setBrush(Qt::green); double stepWidth = (double)widgetCast->width() * ((double)widgetCast->singleStep() / (widgetCast->maximum() - widgetCast->minimum())); groovePainter.drawRoundedRect(pixmapRect.adjusted( @@ -351,7 +288,7 @@ public: // gradient fill QRect r = pixmapRect.adjusted(1, 1, -2, -2); - QLinearGradient gradient = qt_fusion_gradient(gradRect, this->buttonColor(option->palette),horizontal ? TopDown : FromLeft); + QLinearGradient gradient = qt_fusion_gradient(gradRect, getButtonColor(option->palette),horizontal ? TopDown : FromLeft); handlePainter.setRenderHint(QPainter::Antialiasing, true); handlePainter.translate(0.5, 0.5); @@ -360,14 +297,14 @@ public: handlePainter.setBrush(QColor(0, 0, 0, 40)); handlePainter.drawRect(horizontal ? r.adjusted(-1, 2, 1, -2) : r.adjusted(2, -1, -2, 1)); - handlePainter.setPen(QPen(this->outline(option->palette))); + handlePainter.setPen(QPen(getOutline(option->palette))); if (option->state & State_HasFocus && option->state & State_KeyboardFocusChange) - handlePainter.setPen(QPen(this->highlightedOutline(option->palette))); + handlePainter.setPen(QPen(highlightedOutline(option->palette))); handlePainter.setBrush(gradient); handlePainter.drawRoundedRect(r, 2, 2); handlePainter.setBrush(Qt::NoBrush); - handlePainter.setPen(this->innerContrastLine()); + handlePainter.setPen(innerContrastLine()); handlePainter.drawRoundedRect(r.adjusted(1, 1, -1, -1), 2, 2); QColor cornerAlpha = outline.darker(120); @@ -404,57 +341,7 @@ private: FromRight }; - QColor highlightedOutline(const QPalette &pal) const { - QColor highlightedOutline = highlight(pal).darker(125); - if (highlightedOutline.value() > 160) - highlightedOutline.setHsl(highlightedOutline.hue(), highlightedOutline.saturation(), 160); - return highlightedOutline; - } - - QColor outline(const QPalette &pal) const { - if (pal.window().style() == Qt::TexturePattern) - return QColor(0, 0, 0, 160); - return pal.window().color().darker(140); - } - - QColor buttonColor(const QPalette &pal) const { - QColor buttonColor = pal.button().color(); - int val = qGray(buttonColor.rgb()); - buttonColor = buttonColor.lighter(100 + qMax(1, (180 - val)/6)); - buttonColor.setHsv(buttonColor.hue(), buttonColor.saturation() * 0.75, buttonColor.value()); - return buttonColor; - } - - QColor highlight(const QPalette &pal) const { - if (isMacSystemPalette(pal)) - return QColor(60, 140, 230); - return pal.color(QPalette::Highlight); - } - - QColor innerContrastLine() const { - return QColor(255, 255, 255, 30); - } - - bool isMacSystemPalette(const QPalette &pal) const { - Q_UNUSED(pal); -#if defined(Q_OS_MACOS) - const QPalette *themePalette = QGuiApplicationPrivate::platformTheme()->palette(); - if (themePalette && themePalette->color(QPalette::Normal, QPalette::Highlight) == - pal.color(QPalette::Normal, QPalette::Highlight) && - themePalette->color(QPalette::Normal, QPalette::HighlightedText) == - pal.color(QPalette::Normal, QPalette::HighlightedText)) - return true; -#endif - return false; - } - - inline QPixmap styleCachePixmap(const QSize &size, qreal pixelRatio) const { - //not api - QPixmap cachePixmap = QPixmap(size * pixelRatio); - cachePixmap.setDevicePixelRatio(pixelRatio); - cachePixmap.fill(Qt::transparent); - return cachePixmap; - } + static QLinearGradient qt_fusion_gradient(const QRect &rect, const QBrush &baseColor, Direction direction = TopDown) { diff --git a/src/qtestmain.cpp b/src/qtestmain.cpp index f37e337..6c435c3 100644 --- a/src/qtestmain.cpp +++ b/src/qtestmain.cpp @@ -1,17 +1,9 @@ -#include -#include - -#include -#include -#include -#include -#include -#include -#include //#include "contclasses.h" #define INIT_FILELOG +#include "qtcommon.h" #include "qtclasses.h" -#include "global.h" +#include "qtvisuals.h" +//#include "global.h" OverseerHandler *osh = nullptr; From 313a26c8e3f6361b86548e032071043ffb64f84e Mon Sep 17 00:00:00 2001 From: Hane Date: Thu, 30 May 2024 22:36:35 +0200 Subject: [PATCH 47/78] wip: customize scroll bar --- src/qt/qtclasses.cpp | 18 +- src/qt/qtcommon.h | 25 ++- src/qt/qtvisuals.h | 455 +++++++++++++++++++++++++++++++++++++++---- 3 files changed, 449 insertions(+), 49 deletions(-) diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index d0141ea..d6b4c48 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -250,6 +250,8 @@ void MainWindow::compose() { log_debugcpp("Window Width: " + std::to_string(windowWidth)); this->createLayout(new QGridLayout()); + //scrollArea->verticalScrollBar()->setMinimumWidth(windowWidth * (widthRatio)); + //scrollArea->verticalScrollBar()->setMaximumWidth(windowWidth * (widthRatio)); /* * this->setAttribute(Qt::WA_DontShowOnScreen, true); * this->show(); @@ -257,10 +259,11 @@ void MainWindow::compose() { * this->hide(); * this->setAttribute(Qt::WA_DontShowOnScreen, false); */ - + const qreal dpr = screen->devicePixelRatio(); + log_to_file("dpr: %f \n", dpr); for (auto *epw : ews) { if (!epw) continue; - epw->calculateSize(windowWidth, screenHeight); + epw->calculateSize(windowWidth * dpr,screenHeight * screen->devicePixelRatio()); log_debugcpp("epw loop"); log_debugcpp("epw roles: " + print_as_binary((epw->getEndpointHandler()->getRoles()))); //std::bitset content = @@ -277,8 +280,8 @@ void MainWindow::compose() { if (!ews[i]) continue; ews[i]->layout()->getContentsMargins(&left, &top, &right, &bottom); windowHeight += ews[i]->sizeHint().height(); - windowHeight += top; - windowHeight += bottom; + windowHeight += top * 3; + //windowHeight += bottom; log_debugcpp("windowHeight loop: " + std::to_string(windowHeight)); } windowHeight += mainMenuBar->sizeHint().height(); @@ -941,13 +944,12 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { scrollArea = new QScrollArea(this); scrollArea->setWidget(widget); scrollArea->setWidgetResizable(true); - scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + scrollArea->setContentsMargins(QMargins(0, 0, 0, 0)); + //scrollArea->verticalScrollBar()->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); //scrollArea->verticalScrollBar()->setSingleStep(1); - //custom style = no qss - //scrollArea->setStyleSheet("QScrollBar:vertical { width: 4px; }"); - //scrollArea->setMinimumWidth(500); setCentralWidget(scrollArea); /* diff --git a/src/qt/qtcommon.h b/src/qt/qtcommon.h index 8c9119c..dc2797b 100644 --- a/src/qt/qtcommon.h +++ b/src/qt/qtcommon.h @@ -41,7 +41,7 @@ #include #include #include - +//#include //#include /* * #else @@ -65,7 +65,7 @@ namespace StylingHelper { return QLatin1String(ch); } - static inline qreal dpi(const QStyleOption *option) { + static inline qreal calculateDpi(const QStyleOption *option) { #ifndef Q_OS_DARWIN // Prioritize the application override, except for on macOS where // we have historically not supported the AA_Use96Dpi flag. @@ -77,7 +77,7 @@ namespace StylingHelper { if (option) return option->fontMetrics.fontDpi(); - // Fall back to historical Qt behavior: hardocded 72 DPI on mac, + // Fall back to historical Qt behavior: hardcoded 72 DPI on mac, // primary screen DPI on other platforms. #ifdef Q_OS_DARWIN return qstyleBaseDpi; @@ -86,6 +86,10 @@ namespace StylingHelper { #endif } + static inline qreal calculateDpi(const QScreen screen) { + return QFontMetrics(QApplication::font()).fontDpi(); + } + /* * #ifdef Q_OS_DARWIN * static const qreal qstyleBaseDpi = 72; @@ -107,7 +111,7 @@ namespace StylingHelper { } static inline qreal dpiScaled(qreal value, const QStyleOption *option) { - return dpiScaled(value, dpi(option)); + return dpiScaled(value, calculateDpi(option)); } static inline bool isMacSystemPalette(const QPalette &pal) { @@ -161,5 +165,18 @@ namespace StylingHelper { cachePixmap.fill(Qt::transparent); return cachePixmap; } + + static inline QColor backgroundColor(const QPalette &pal, const QWidget* widget) + { + #if QT_CONFIG(scrollarea) + if (qobject_cast(widget) && widget->parent() && + qobject_cast(widget->parent()->parent())) + return widget->parentWidget()->parentWidget()->palette().color(QPalette::Base); + #else + Q_UNUSED(widget); + #endif + return pal.color(QPalette::Base); + } + } diff --git a/src/qt/qtvisuals.h b/src/qt/qtvisuals.h index f88c025..4c7ac4f 100644 --- a/src/qt/qtvisuals.h +++ b/src/qt/qtvisuals.h @@ -16,15 +16,15 @@ public: QStyleHintReturn* returnData = 0) const { if (hint == QStyle::SH_Slider_AbsoluteSetButtons) return (Qt::LeftButton | Qt::MiddleButton | Qt::RightButton); - return QProxyStyle::styleHint(hint, option, widget, returnData); + return baseStyle()->styleHint(hint, option, widget, returnData); } QRect subControlRect(ComplexControl control, const QStyleOptionComplex *option, - SubControl subControl, const QWidget *widget) const { + SubControl subControl, const QWidget *widget) const { QRect rect = QCommonStyle::subControlRect(CC_Slider, option, subControl, widget); switch (control) { - #if QT_CONFIG(slider) +#if QT_CONFIG(slider) case CC_MeterSlider: if (const QStyleOptionSlider *slider = qstyleoption_cast(option)) { int tickSize = proxy()->pixelMetric(PM_SliderTickmarkOffset, option, widget); @@ -88,6 +88,94 @@ public: return rect; break; #endif // QT_CONFIG(slider) +#if QT_CONFIG(scrollbar) + case CC_ScrollBar: + if (const QStyleOptionSlider *scrollbar = qstyleoption_cast(option)) { + QRect scrollBarRect = scrollbar->rect; + const uint64_t offset = 7; + scrollBarRect.setWidth(scrollBarRect.width() / 2); + scrollBarRect.setHeight(scrollBarRect.height() - 15); + int maxlen = ((scrollbar->orientation == Qt::Horizontal) ? + scrollBarRect.width() : scrollBarRect.height()); + int sliderlen; + + // calculate slider length + if (scrollbar->maximum != scrollbar->minimum) { + uint range = scrollbar->maximum - scrollbar->minimum; + sliderlen = (qint64(scrollbar->pageStep) * maxlen) / (range + scrollbar->pageStep); + + int slidermin = baseStyle()->pixelMetric(PM_ScrollBarSliderMin, scrollbar, widget); + if (sliderlen < slidermin || range > INT_MAX / 2) + sliderlen = slidermin; + if (sliderlen > maxlen) + sliderlen = maxlen; + } else { + sliderlen = maxlen; + } + + int sliderstart = sliderPositionFromValue(scrollbar->minimum, + scrollbar->maximum, + scrollbar->sliderPosition, + maxlen - sliderlen, + scrollbar->upsideDown); + + switch (subControl) { + /* + * case SC_ScrollBarSubLine: // top/left button + * if (scrollbar->orientation == Qt::Horizontal) { + * int buttonWidth = qMin(scrollBarRect.width() / 2, sbextent); + * rect.setRect(0, 0, buttonWidth, scrollBarRect.height()); + * } else { + * int buttonHeight = qMin(scrollBarRect.height() / 2, sbextent); + * rect.setRect(0, 0, scrollBarRect.width(), buttonHeight); + * } + * break; + * case SC_ScrollBarAddLine: // bottom/right button + * if (scrollbar->orientation == Qt::Horizontal) { + * int buttonWidth = qMin(scrollBarRect.width()/2, sbextent); + * rect.setRect(scrollBarRect.width() - buttonWidth, 0, buttonWidth, scrollBarRect.height()); + * } else { + * int buttonHeight = qMin(scrollBarRect.height()/2, sbextent); + * rect.setRect(0, scrollBarRect.height() - buttonHeight, scrollBarRect.width(), buttonHeight); + * } + * break; + */ + case SC_ScrollBarSubPage: // between top/left button and slider + if (scrollbar->orientation == Qt::Horizontal) + rect.setRect(0, 0, sliderstart, scrollBarRect.height()); + else + rect.setRect(0, 0, scrollBarRect.width(), sliderstart); + break; + case SC_ScrollBarAddPage: // between bottom/right button and slider + if (scrollbar->orientation == Qt::Horizontal) + rect.setRect(sliderstart + sliderlen, 0, + maxlen - sliderstart - sliderlen, scrollBarRect.height()); + else + rect.setRect(0, sliderstart + sliderlen, scrollBarRect.width(), + maxlen - sliderstart - sliderlen); + break; + case SC_ScrollBarGroove: + if (scrollbar->orientation == Qt::Horizontal) + rect.setRect(offset, 0, scrollBarRect.width(), + scrollBarRect.height()); + else + rect.setRect(0, offset, scrollBarRect.width(), + scrollBarRect.height() ); + break; + case SC_ScrollBarSlider: + if (scrollbar->orientation == Qt::Horizontal) + rect.setRect(sliderstart + offset, 0, sliderlen, scrollBarRect.height()); + else + rect.setRect(0, sliderstart + offset, scrollBarRect.width(), sliderlen); + break; + default: + break; + } + rect = visualRect(scrollbar->direction, scrollBarRect, rect); + return rect; + } + break; +#endif // QT_CONFIG(scrollbar) default: return baseStyle()->subControlRect(control, option, subControl, widget); break; @@ -97,6 +185,7 @@ public: void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget) const { + QColor outline; switch (cc) { #if QT_CONFIG(slider) case CC_MeterSlider: @@ -115,7 +204,7 @@ public: QPen oldPen = painter->pen(); QColor shadowAlpha(Qt::black); shadowAlpha.setAlpha(10); - QColor outline; + if (option->state & State_HasFocus && option->state & State_KeyboardFocusChange) outline = highlightedOutline(option->palette); @@ -327,6 +416,213 @@ public: painter->setPen(oldPen); } break; + case CC_ScrollBar: + painter->save(); + if (const QStyleOptionSlider *scrollBar = qstyleoption_cast(option)) { + bool wasActive = false; + qreal expandScale = 1.0; + qreal expandOffset = -1.0; + QObject *styleObject = option->styleObject; + + QColor buttonColor = calculateButtonColor(option->palette); + QColor gradientStartColor = buttonColor.lighter(104); + QColor gradientStopColor = buttonColor.darker(102); + + bool transient = styleHint(SH_ScrollBar_Transient, option, widget); + bool horizontal = scrollBar->orientation == Qt::Horizontal; + bool sunken = scrollBar->state & State_Sunken; + + QRect scrollBarSubLine = subControlRect(cc, scrollBar, SC_ScrollBarSubLine, widget); + QRect scrollBarAddLine = subControlRect(cc, scrollBar, SC_ScrollBarAddLine, widget); + QRect scrollBarSlider = subControlRect(cc, scrollBar, SC_ScrollBarSlider, widget); + QRect scrollBarGroove = subControlRect(cc, scrollBar, SC_ScrollBarGroove, widget); + + QRect rect = option->rect; + outline = highlightedOutline(option->palette); + QColor alphaOutline = outline; + alphaOutline.setAlpha(180); + + QColor arrowColor = option->palette.windowText().color(); + //QColor arrowColor = QColor(188,143,143,100); + arrowColor.setAlpha(160); + + const QColor bgColor = backgroundColor(option->palette, widget); + const bool isDarkBg = bgColor.red() < 128 && bgColor.green() < 128 && bgColor.blue() < 128; + + if (transient) { + if (horizontal) { + rect.setY(rect.y() + 4.5 - expandOffset); + scrollBarSlider.setY(scrollBarSlider.y() + 4.5 - expandOffset); + scrollBarGroove.setY(scrollBarGroove.y() + 4.5 - expandOffset); + + rect.setHeight(rect.height() * expandScale); + scrollBarGroove.setHeight(scrollBarGroove.height() * expandScale); + } else { + rect.setX(rect.x() + 4.5 - expandOffset); + scrollBarSlider.setX(scrollBarSlider.x() + 4.5 - expandOffset); + scrollBarGroove.setX(scrollBarGroove.x() + 4.5 - expandOffset); + + rect.setWidth(rect.width() * expandScale); + scrollBarGroove.setWidth(scrollBarGroove.width() * expandScale); + } + } + + // Paint groove + if ((!transient || scrollBar->activeSubControls || wasActive) && scrollBar->subControls & SC_ScrollBarGroove) { + QLinearGradient gradient(rect.center().x(), rect.top(), + rect.center().x(), rect.bottom()); + if (!horizontal) + gradient = QLinearGradient(rect.left(), rect.center().y(), + rect.right(), rect.center().y()); + if (!transient || !isDarkBg) { + gradient.setColorAt(0, buttonColor.darker(107)); + gradient.setColorAt(0.1, buttonColor.darker(105)); + gradient.setColorAt(0.9, buttonColor.darker(105)); + gradient.setColorAt(1, buttonColor.darker(107)); + } else { + gradient.setColorAt(0, bgColor.lighter(157)); + gradient.setColorAt(0.1, bgColor.lighter(155)); + gradient.setColorAt(0.9, bgColor.lighter(155)); + gradient.setColorAt(1, bgColor.lighter(157)); + } + + painter->save(); + //painter->fillRect(rect, gradient); + painter->setPen(Qt::NoPen); + painter->setPen(alphaOutline); + + QColor subtleEdge = alphaOutline; + subtleEdge.setAlpha(40); + painter->setPen(subtleEdge); + //painter->setBrush(Qt::NoBrush); + painter->setOpacity(0.3); + painter->setBrush(Qt::gray); + painter->drawRoundedRect(scrollBarGroove, 5, 5); + //painter->drawRoundedRect(scrollBarGroove, 5, 5); + painter->restore(); + } + + QRect pixmapRect = scrollBarSlider; + QLinearGradient gradient(pixmapRect.center().x(), pixmapRect.top(), + pixmapRect.center().x(), pixmapRect.bottom()); + if (!horizontal) + gradient = QLinearGradient(pixmapRect.left(), pixmapRect.center().y(), + pixmapRect.right(), pixmapRect.center().y()); + + QLinearGradient highlightedGradient = gradient; + + QColor midColor2 = mergedColors(gradientStartColor, gradientStopColor, 40); + gradient.setColorAt(0, calculateButtonColor(option->palette).lighter(108)); + gradient.setColorAt(1, calculateButtonColor(option->palette)); + + highlightedGradient.setColorAt(0, gradientStartColor.darker(102)); + highlightedGradient.setColorAt(1, gradientStopColor.lighter(102)); + + // Paint slider + if (scrollBar->subControls & SC_ScrollBarSlider) { + if (transient) { + QRect rect = scrollBarSlider.adjusted(horizontal ? 1 : 2, horizontal ? 2 : 1, -1, -1); + painter->setPen(Qt::NoPen); + painter->setBrush(isDarkBg ? lightShade() : darkShade()); + int r = qMin(rect.width(), rect.height()) / 2; + + painter->save(); + painter->setRenderHint(QPainter::Antialiasing, true); + painter->drawRoundedRect(rect, r, r); + painter->restore(); + } else { + QRect pixmapRect = scrollBarSlider; + painter->setPen(QPen(alphaOutline)); + if (option->state & State_Sunken && scrollBar->activeSubControls & SC_ScrollBarSlider) + painter->setBrush(midColor2); + else if (option->state & State_MouseOver && scrollBar->activeSubControls & SC_ScrollBarSlider) + painter->setBrush(highlightedGradient); + else if (!isDarkBg) + painter->setBrush(gradient); + else + painter->setBrush(midColor2); + + painter->drawRoundedRect(pixmapRect.adjusted(horizontal ? -1 : 0, horizontal ? 0 : -1, horizontal ? 0 : -1, horizontal ? -1 : 0), 5, 5); + + painter->setPen(innerContrastLine()); + painter->drawRoundedRect(scrollBarSlider.adjusted(horizontal ? 0 : 1, horizontal ? 1 : 0, -1, -1), 5, 5); + + // Outer shadow + // painter->setPen(subtleEdge); + // if (horizontal) { + //// painter->drawLine(scrollBarSlider.topLeft() + QPoint(-2, 0), scrollBarSlider.bottomLeft() + QPoint(2, 0)); + //// painter->drawLine(scrollBarSlider.topRight() + QPoint(-2, 0), scrollBarSlider.bottomRight() + QPoint(2, 0)); + // } else { + //// painter->drawLine(pixmapRect.topLeft() + QPoint(0, -2), pixmapRect.bottomLeft() + QPoint(0, -2)); + //// painter->drawLine(pixmapRect.topRight() + QPoint(0, 2), pixmapRect.bottomRight() + QPoint(0, 2)); + // } + } + } + + /* + * // The SubLine (up/left) buttons + * if (!transient && scrollBar->subControls & SC_ScrollBarSubLine) { + * if ((scrollBar->activeSubControls & SC_ScrollBarSubLine) && sunken) + * painter->setBrush(gradientStopColor); + * else if ((scrollBar->activeSubControls & SC_ScrollBarSubLine)) + * painter->setBrush(highlightedGradient); + * else + * painter->setBrush(gradient); + * + * painter->setPen(Qt::NoPen); + * painter->drawRoundedRect(scrollBarSubLine.adjusted(horizontal ? 0 : 1, horizontal ? 1 : 0, 0, 0), 1, 1); + * painter->setPen(QPen(alphaOutline)); + * if (option->state & State_Horizontal) { + * painter->drawRect(scrollBarSubLine.adjusted(horizontal ? 0 : 1, 0, horizontal ? 1 : 0, horizontal ? -1 : 0)); + * } else { + * painter->drawRoundedRect(scrollBarSubLine.adjusted(0, 0, horizontal ? 0 : -1, 0), 1, 1); + * } + * + * QRect upRect = scrollBarSubLine.adjusted(horizontal ? 0 : 1, horizontal ? 1 : 0, horizontal ? -2 : -1, horizontal ? -1 : -2); + * painter->setBrush(Qt::NoBrush); + * painter->setPen(innerContrastLine()); + * painter->drawRect(upRect); + * + * // Arrows + * Qt::ArrowType arrowType = Qt::UpArrow; + * if (option->state & State_Horizontal) + * arrowType = option->direction == Qt::LeftToRight ? Qt::LeftArrow : Qt::RightArrow; + * qt_fusion_draw_arrow(arrowType, painter, option, upRect, arrowColor); + * } + * + * // The AddLine (down/right) button + * if (!transient && scrollBar->subControls & SC_ScrollBarAddLine) { + * if ((scrollBar->activeSubControls & SC_ScrollBarAddLine) && sunken) + * painter->setBrush(gradientStopColor); + * else if ((scrollBar->activeSubControls & SC_ScrollBarAddLine)) + * painter->setBrush(midColor2); + * else + * painter->setBrush(gradient); + * + * painter->setPen(Qt::NoPen); + * painter->drawRect(scrollBarAddLine.adjusted(horizontal ? 0 : 1, horizontal ? 1 : 0, 0, 0)); + * painter->setPen(QPen(alphaOutline, 1)); + * if (option->state & State_Horizontal) { + * painter->drawRect(scrollBarAddLine.adjusted(horizontal ? -1 : 0, 0, horizontal ? -1 : 0, horizontal ? -1 : 0)); + * } else { + * painter->drawRect(scrollBarAddLine.adjusted(0, horizontal ? 0 : -1, horizontal ? 0 : -1, horizontal ? 0 : -1)); + * } + * + * QRect downRect = scrollBarAddLine.adjusted(1, 1, -1, -1); + * painter->setPen(innerContrastLine()); + * painter->setBrush(Qt::NoBrush); + * painter->drawRect(downRect); + * + * Qt::ArrowType arrowType = Qt::DownArrow; + * if (option->state & State_Horizontal) + * arrowType = option->direction == Qt::LeftToRight ? Qt::RightArrow : Qt::LeftArrow; + * qt_fusion_draw_arrow(arrowType, painter, option, downRect, arrowColor); + * } + */ + + } + painter->restore(); + break; default: baseStyle()->drawComplexControl(cc, option, painter, widget); #endif // QT_CONFIG(slider) @@ -341,42 +637,127 @@ private: FromRight }; - - static QLinearGradient qt_fusion_gradient(const QRect &rect, const QBrush &baseColor, Direction direction = TopDown) + { + int x = rect.center().x(); + int y = rect.center().y(); + QLinearGradient gradient; + switch (direction) { + case FromLeft: + gradient = QLinearGradient(rect.left(), y, rect.right(), y); + break; + case FromRight: + gradient = QLinearGradient(rect.right(), y, rect.left(), y); + break; + case BottomUp: + gradient = QLinearGradient(x, rect.bottom(), x, rect.top()); + break; + case TopDown: + default: + gradient = QLinearGradient(x, rect.top(), x, rect.bottom()); + break; + } + if (baseColor.gradient()) + gradient.setStops(baseColor.gradient()->stops()); + else { + QColor gradientStartColor = baseColor.color().lighter(124); + QColor gradientStopColor = baseColor.color().lighter(102); + gradient.setColorAt(0, gradientStartColor); + gradient.setColorAt(1, gradientStopColor); + // Uncomment for adding shiny shading + // QColor midColor1 = mergedColors(gradientStartColor, gradientStopColor, 55); + // QColor midColor2 = mergedColors(gradientStartColor, gradientStopColor, 45); + // gradient.setColorAt(0.5, midColor1); + // gradient.setColorAt(0.501, midColor2); + } + return gradient; + } + + QColor calculateButtonColor(const QPalette &pal) const { + QColor buttonColor = pal.button().color(); + int val = qGray(buttonColor.rgb()); + buttonColor = buttonColor.lighter(100 + qMax(1, (180 - val)/6)); + buttonColor.setHsv(buttonColor.hue(), buttonColor.saturation() * 0.75, buttonColor.value()); + return buttonColor; + } + + // Used for grip handles + QColor lightShade() const { + return QColor(255, 255, 255, 90); + } + + QColor darkShade() const { + return QColor(0, 0, 0, 60); + } + + QColor innerContrastLine() const { + return QColor(255, 255, 255, 30); + } + + static QColor mergedColors(const QColor &colorA, const QColor &colorB, int factor = 50) { + const int maxFactor = 100; + QColor tmp = colorA; + tmp.setRed((tmp.red() * factor) / maxFactor + (colorB.red() * (maxFactor - factor)) / maxFactor); + tmp.setGreen((tmp.green() * factor) / maxFactor + (colorB.green() * (maxFactor - factor)) / maxFactor); + tmp.setBlue((tmp.blue() * factor) / maxFactor + (colorB.blue() * (maxFactor - factor)) / maxFactor); + return tmp; + } + + + static void qt_fusion_draw_arrow(Qt::ArrowType type, QPainter *painter, const QStyleOption *option, const QRect &rect, const QColor &color) { - int x = rect.center().x(); - int y = rect.center().y(); - QLinearGradient gradient; - switch (direction) { - case FromLeft: - gradient = QLinearGradient(rect.left(), y, rect.right(), y); - break; - case FromRight: - gradient = QLinearGradient(rect.right(), y, rect.left(), y); - break; - case BottomUp: - gradient = QLinearGradient(x, rect.bottom(), x, rect.top()); - break; - case TopDown: - default: - gradient = QLinearGradient(x, rect.top(), x, rect.bottom()); - break; + if (rect.isEmpty()) + return; + + const qreal dpi = calculateDpi(option); + const qreal dpr = painter->device()->devicePixelRatio(); + const int arrowWidth = int(dpiScaled(14, dpi)); + const int arrowHeight = int(dpiScaled(8, dpi)); + + const int arrowMax = qMin(arrowHeight, arrowWidth); + const int rectMax = qMin(rect.height(), rect.width()); + const int size = qMin(arrowMax, rectMax); + + QPixmap cachePixmap; + const QString cacheKey = "fusion-arrow"_L1 + QString::number(rect.size().width()) + QString::number(rect.size().height()) + QString::number(type); + + if (!QPixmapCache::find(cacheKey, &cachePixmap)) { + cachePixmap = styleCachePixmap(rect.size(), dpr); + QPainter cachePainter(&cachePixmap); + + QRectF arrowRect; + arrowRect.setWidth(size); + arrowRect.setHeight(arrowHeight * size / arrowWidth); + if (type == Qt::LeftArrow || type == Qt::RightArrow) + arrowRect = arrowRect.transposed(); + arrowRect.moveTo((rect.width() - arrowRect.width()) / 2.0, + (rect.height() - arrowRect.height()) / 2.0); + + std::array triangle; + switch (type) { + case Qt::DownArrow: + triangle = {arrowRect.topLeft(), arrowRect.topRight(), QPointF(arrowRect.center().x(), arrowRect.bottom())}; + break; + case Qt::RightArrow: + triangle = {arrowRect.topLeft(), arrowRect.bottomLeft(), QPointF(arrowRect.right(), arrowRect.center().y())}; + break; + case Qt::LeftArrow: + triangle = {arrowRect.topRight(), arrowRect.bottomRight(), QPointF(arrowRect.left(), arrowRect.center().y())}; + break; + default: + triangle = {arrowRect.bottomLeft(), arrowRect.bottomRight(), QPointF(arrowRect.center().x(), arrowRect.top())}; + break; + } + + cachePainter.setPen(Qt::NoPen); + cachePainter.setBrush(color); + cachePainter.setRenderHint(QPainter::Antialiasing); + cachePainter.drawPolygon(triangle.data(), int(triangle.size())); + + QPixmapCache::insert(cacheKey, cachePixmap); } - if (baseColor.gradient()) - gradient.setStops(baseColor.gradient()->stops()); - else { - QColor gradientStartColor = baseColor.color().lighter(124); - QColor gradientStopColor = baseColor.color().lighter(102); - gradient.setColorAt(0, gradientStartColor); - gradient.setColorAt(1, gradientStopColor); - // Uncomment for adding shiny shading - // QColor midColor1 = mergedColors(gradientStartColor, gradientStopColor, 55); - // QColor midColor2 = mergedColors(gradientStartColor, gradientStopColor, 45); - // gradient.setColorAt(0.5, midColor1); - // gradient.setColorAt(0.501, midColor2); - } - return gradient; + + painter->drawPixmap(rect, cachePixmap); } }; From 544da49e32f98e3f76d673d1e72540a02a0481c5 Mon Sep 17 00:00:00 2001 From: Hane Date: Thu, 6 Jun 2024 20:08:36 +0200 Subject: [PATCH 48/78] test: fixed unnamed sessions #6 --- src/back/backlasses.cpp | 2 +- src/back/backsessionclasses.cpp | 113 ++++++++++++++++++++++++-------- src/back/msinclude.h | 2 + src/qt/qtclasses.cpp | 2 +- src/qt/qtcommon.h | 2 +- 5 files changed, 91 insertions(+), 30 deletions(-) diff --git a/src/back/backlasses.cpp b/src/back/backlasses.cpp index bfc0e83..60e6a93 100644 --- a/src/back/backlasses.cpp +++ b/src/back/backlasses.cpp @@ -475,7 +475,7 @@ void Endpoint::setFlow() { if(FAILED(this->endpoint->QueryInterface(__uuidof(IMMEndpoint), (void**)&flowGetter))) { log_debugcpp("no flow..."); } EDataFlow MSflow; - HRESULT vafllar = flowGetter->GetDataFlow(&MSflow); + flowGetter->GetDataFlow(&MSflow); this->flow = (MSflow == EDataFlow::eRender ? Flows::FLOW_PLAYBACK : Flows::FLOW_CAPTURE); log_debugcpp("Endpoint flow: " + std::to_string(flow)); flowGetter->Release(); diff --git a/src/back/backsessionclasses.cpp b/src/back/backsessionclasses.cpp index 80eeac8..ba0eaa6 100644 --- a/src/back/backsessionclasses.cpp +++ b/src/back/backsessionclasses.cpp @@ -196,11 +196,14 @@ std::wstring Session::fetchProcessName(DWORD pid) { /* * https://learn.microsoft.com/en-us/windows/win32/api/tlhelp32/nf-tlhelp32-createtoolhelp32snapshot * https://stackoverflow.com/questions/11843368/how-to-get-process-description + * https://notes.indezine.com/2018/05/microsoft-locale-ids.html#:~:text=Wait%2C%201033%20is%20the%20decimal,ID%20for%20English%20%E2%80%93%20United%20States. + * https://stackoverflow.com/questions/64321036/c-win32-getting-app-name-using-pid-and-executable-path */ /* Executable path retrieval */ std::wstring exePath = L""; - + std::wstring msixName; + HANDLE processList = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, pid); if (processList == INVALID_HANDLE_VALUE) { log_wdebugcpp(L"aye no procname."); @@ -224,55 +227,111 @@ std::wstring Session::fetchProcessName(DWORD pid) { } CloseHandle(processList); - /* File description retrieval */ + /* File description retrieval: size and available lang-codepages */ struct LANGANDCODEPAGE { WORD wLanguage; WORD wCodePage; } *translationArray; DWORD filler; - DWORD fileVersionInfoSize = GetFileVersionInfoSizeW(exePath.c_str(), &filler); + DWORD fileVersionInfoSize = GetFileVersionInfoSizeExW + (FILE_VER_GET_LOCALISED | FILE_VER_GET_NEUTRAL, exePath.c_str(), &filler); if (!fileVersionInfoSize) return exePath; void* fileVersionInfo = malloc(fileVersionInfoSize); - if(!GetFileVersionInfoW(exePath.c_str(),0,fileVersionInfoSize, fileVersionInfo)) + if(!GetFileVersionInfoExW(FILE_VER_GET_LOCALISED | FILE_VER_GET_NEUTRAL, + exePath.c_str(),0,fileVersionInfoSize, fileVersionInfo)) return exePath; UINT translationArrayLen = 0; if (!VerQueryValueW(fileVersionInfo, L"\\VarFileInfo\\Translation", (LPVOID*)&translationArray, &translationArrayLen)) return exePath; + + //File descriptor parsing + //TODO: https://learn.microsoft.com/en-us/windows/win32/api/winnls/nf-winnls-getuserpreferreduilanguages + /* It is possible to retrieve user languages and try to use one of those before falling back to whatever + * is available. Also possible to hardcode en-US or any other lang-codepage combo. When an actual translation + * sysem is put in place, I'll come finish this up. + */ + uint64_t availableLangs = (translationArrayLen / sizeof(LANGANDCODEPAGE)); + if (!availableLangs) return exePath; - bool match = false; - for (UINT i = 0; i < (translationArrayLen / sizeof(LANGANDCODEPAGE)); i++) { - wchar_t fileDescriptionKey[256]; + int8_t syslangIdx = -1; + wchar_t metadataStringKey[256]; + wchar_t* metadataString = NULL; + //std::wstring name; + for (UINT i = 0; i < availableLangs; i++) { LANGID defaultUILanguage = GetUserDefaultUILanguage(); if (defaultUILanguage != translationArray[i].wLanguage) continue; - match = true; - wchar_t* fileDescription = NULL; - UINT fileDescriptionSize = 0; - swprintf(fileDescriptionKey, L"\\StringFileInfo\\%04x%04x\\FileDescription", - translationArray[i].wLanguage, translationArray[i].wCodePage); - if (VerQueryValueW(fileVersionInfo, fileDescriptionKey, (LPVOID*)&fileDescription, &fileDescriptionSize)) { - exePath = std::wstring(fileDescription); - } + syslangIdx = i; + break; } - if (!match && 1 <= (translationArrayLen / sizeof(LANGANDCODEPAGE))) { - wchar_t fileDescriptionKey[256]; + UINT metadataStringSize = 0; + swprintf(metadataStringKey, L"\\StringFileInfo\\%04x%04x\\FileDescription", + translationArray[(syslangIdx < 0 ? 0 : syslangIdx)].wLanguage, + translationArray[(syslangIdx < 0 ? 0 : syslangIdx)].wCodePage); + if (VerQueryValueW(fileVersionInfo, metadataStringKey, (LPVOID*)&metadataString, &metadataStringSize) + && metadataString[0] != '\0') { + return std::wstring(metadataString); + } + swprintf(metadataStringKey, L"\\StringFileInfo\\%04x%04x\\ProductName", + translationArray[(syslangIdx < 0 ? 0 : syslangIdx)].wLanguage, + translationArray[(syslangIdx < 0 ? 0 : syslangIdx)].wCodePage); + if (VerQueryValueW(fileVersionInfo, metadataStringKey, (LPVOID*)&metadataString, &metadataStringSize) + && metadataString[0] != '\0') { + return std::wstring(metadataString); + } -wchar_t* fileDescription = NULL; - UINT fileDescriptionSize = 0; - swprintf(fileDescriptionKey, L"\\StringFileInfo\\%04x%04x\\FileDescription", - translationArray[0].wLanguage, translationArray[0].wCodePage); - if (VerQueryValueW(fileVersionInfo, fileDescriptionKey, (LPVOID*)&fileDescription, &fileDescriptionSize)) { - exePath = std::wstring(fileDescription); - } - } + //MSIX? + HANDLE process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid); + if(!process) return exePath; - free(fileVersionInfo); - return exePath; + //constant missing in mingw64. TB removed when I upgrade to a mingw64 ver that has it + #define APPLICATION_USER_MODEL_ID_MAX_LENGTH 130 + uint32_t length = APPLICATION_USER_MODEL_ID_MAX_LENGTH; + PWSTR userModelId = (PWSTR)malloc(length * sizeof(wchar_t)); + if(GetApplicationUserModelId(process, &length, userModelId) != ERROR_SUCCESS) { + CloseHandle(process); + return exePath; + } + CloseHandle(process); + + static constexpr wchar_t* prefix = L"shell:appsfolder\\"; + uint32_t prefixLen = wcslen(prefix); + uint32_t userModelIdLen = wcslen(userModelId); + wchar_t* fullName; + fullName = (prefixLen + userModelIdLen < length) + ? (wchar_t*)malloc(length * sizeof(wchar_t)) + : (wchar_t*)malloc((length * 2) * sizeof(wchar_t)); + for (int32_t i = prefixLen - 1; i >= 0; i--) { + fullName[i] = prefix[i]; + } + for (uint32_t i = 0; i < userModelIdLen + 1; i++) { + fullName[prefixLen + i] = userModelId[i]; + } + + IShellItem* si; + HRESULT hr = SHCreateItemFromParsingName(fullName, + nullptr, + IID_IShellItem, + (void**)&si + ); + free(fullName); + LPWSTR humanName = nullptr; + si->GetDisplayName(SIGDN_NORMALDISPLAY, &humanName); + if(humanName && humanName[0] != '\0') { + msixName = std::wstring(humanName); + CoTaskMemFree(humanName); + } + if(si) si->Release(); + + if (msixName.length() > 0) + return msixName; + else return exePath; + //free(fileVersionInfo); } //todo: conflicting names. change callback name diff --git a/src/back/msinclude.h b/src/back/msinclude.h index 16216f6..6f21ae1 100644 --- a/src/back/msinclude.h +++ b/src/back/msinclude.h @@ -7,6 +7,8 @@ #include #include +#include +#include #include #include #include diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index d6b4c48..48b248c 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -263,7 +263,7 @@ void MainWindow::compose() { log_to_file("dpr: %f \n", dpr); for (auto *epw : ews) { if (!epw) continue; - epw->calculateSize(windowWidth * dpr,screenHeight * screen->devicePixelRatio()); + epw->calculateSize(windowWidth, screenHeight); log_debugcpp("epw loop"); log_debugcpp("epw roles: " + print_as_binary((epw->getEndpointHandler()->getRoles()))); //std::bitset content = diff --git a/src/qt/qtcommon.h b/src/qt/qtcommon.h index dc2797b..414e236 100644 --- a/src/qt/qtcommon.h +++ b/src/qt/qtcommon.h @@ -86,7 +86,7 @@ namespace StylingHelper { #endif } - static inline qreal calculateDpi(const QScreen screen) { + static inline qreal calculateDpi() { return QFontMetrics(QApplication::font()).fontDpi(); } From 42b30b1bf8622ddb06979d669f1b43e25377c312 Mon Sep 17 00:00:00 2001 From: Hane Date: Tue, 6 Aug 2024 09:45:25 +0200 Subject: [PATCH 49/78] Og & wip: names --- qtest.pro | 2 +- src/back/backsessionclasses.cpp | 236 ++++++++++++++++++++++++++++---- src/back/backsessionclasses.h | 10 +- src/global.h | 2 + src/qt/qtclasses.cpp | 5 +- 5 files changed, 227 insertions(+), 28 deletions(-) diff --git a/qtest.pro b/qtest.pro index 303f059..b85fa53 100644 --- a/qtest.pro +++ b/qtest.pro @@ -1,4 +1,4 @@ -QMAKE_CXXFLAGS += --target=x86_64-w64-mingw32 -g -gcodeview +QMAKE_CXXFLAGS += --target=x86_64-w64-mingw32 -g -gcodeview -Og QMAKE_LFLAGS += --target=x86_64-w64-mingw32 -g -Wl,-pdb= -v LIBS += -LC:/capybara/libclang/x86_64-w64-mingw32/lib -lWinmm -lodbc32 -lodbccp32 -luuid -loleaut32 -lole32 -lshell32 -ladvapi32 -lcomdlg32 -lwinspool -lgdi32 -luser32 -lkernel32 -lpropsys -static -stdlib=libc++ -lunwind #"kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" -luuid -loleaut32 -lole32 -lshell32 -ladvapi32 -lcomdlg32 -lwinspool -lgdi32 -luser32 -lkernel32 diff --git a/src/back/backsessionclasses.cpp b/src/back/backsessionclasses.cpp index ba0eaa6..41bf733 100644 --- a/src/back/backsessionclasses.cpp +++ b/src/back/backsessionclasses.cpp @@ -126,9 +126,12 @@ Session::Session(Endpoint* ep, IAudioSessionControl2* sessionControl, size_t idx else { LPWSTR sessionDisplayName; this->sessionControl->GetDisplayName(&sessionDisplayName); - if (!wcscmp(sessionDisplayName, L"")) - this->sessionName = this->fetchProcessName(pid); - else + if (!wcscmp(sessionDisplayName, L"")) { + std::wstring exePath; + if (getExePath(pid, &exePath)) + fetchName(exePath, pid); + else this->sessionName = std::wstring(LSTRING_UNNAMED_SESSION); + } else this->sessionName = std::wstring(sessionDisplayName); CoTaskMemFree(sessionDisplayName); } @@ -192,22 +195,20 @@ void Session::setMute(NGuid guid, bool muted) { if(FAILED(sessionVolume->SetMute(muted, &tempMsGuid))) { log_wdebugcpp(std::wstring(L"SessionVolume null?")); }; } -std::wstring Session::fetchProcessName(DWORD pid) { +bool Session::getExePath(DWORD pid, std::wstring *exePath) { /* * https://learn.microsoft.com/en-us/windows/win32/api/tlhelp32/nf-tlhelp32-createtoolhelp32snapshot * https://stackoverflow.com/questions/11843368/how-to-get-process-description * https://notes.indezine.com/2018/05/microsoft-locale-ids.html#:~:text=Wait%2C%201033%20is%20the%20decimal,ID%20for%20English%20%E2%80%93%20United%20States. * https://stackoverflow.com/questions/64321036/c-win32-getting-app-name-using-pid-and-executable-path */ - - /* Executable path retrieval */ - std::wstring exePath = L""; - std::wstring msixName; + + //std::wstring msixName; HANDLE processList = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, pid); if (processList == INVALID_HANDLE_VALUE) { log_wdebugcpp(L"aye no procname."); - return exePath; + return false; } MODULEENTRY32W me32w; @@ -215,7 +216,7 @@ std::wstring Session::fetchProcessName(DWORD pid) { if(Module32FirstW(processList, &me32w)) { do { if (me32w.th32ProcessID == pid) { - exePath = std::wstring(me32w.szExePath); + *exePath = std::wstring(me32w.szExePath); break; /* * However, if the calling process is a 32-bit process, you must call the @@ -226,7 +227,10 @@ std::wstring Session::fetchProcessName(DWORD pid) { } while(Module32NextW(processList, &me32w)); } CloseHandle(processList); + return true; +} +bool Session::fetchNameViaFD(std::wstring exePath, DWORD pid, std::wstring *sessionName) { /* File description retrieval: size and available lang-codepages */ struct LANGANDCODEPAGE { WORD wLanguage; @@ -236,16 +240,16 @@ std::wstring Session::fetchProcessName(DWORD pid) { DWORD filler; DWORD fileVersionInfoSize = GetFileVersionInfoSizeExW (FILE_VER_GET_LOCALISED | FILE_VER_GET_NEUTRAL, exePath.c_str(), &filler); - if (!fileVersionInfoSize) return exePath; + if (!fileVersionInfoSize) return false; void* fileVersionInfo = malloc(fileVersionInfoSize); if(!GetFileVersionInfoExW(FILE_VER_GET_LOCALISED | FILE_VER_GET_NEUTRAL, exePath.c_str(),0,fileVersionInfoSize, fileVersionInfo)) - return exePath; + return false; UINT translationArrayLen = 0; if (!VerQueryValueW(fileVersionInfo, L"\\VarFileInfo\\Translation", (LPVOID*)&translationArray, &translationArrayLen)) - return exePath; + return false; //File descriptor parsing //TODO: https://learn.microsoft.com/en-us/windows/win32/api/winnls/nf-winnls-getuserpreferreduilanguages @@ -254,7 +258,7 @@ std::wstring Session::fetchProcessName(DWORD pid) { * sysem is put in place, I'll come finish this up. */ uint64_t availableLangs = (translationArrayLen / sizeof(LANGANDCODEPAGE)); - if (!availableLangs) return exePath; + if (!availableLangs) { free(fileVersionInfo); return false; } int8_t syslangIdx = -1; wchar_t metadataStringKey[256]; @@ -275,19 +279,30 @@ std::wstring Session::fetchProcessName(DWORD pid) { translationArray[(syslangIdx < 0 ? 0 : syslangIdx)].wCodePage); if (VerQueryValueW(fileVersionInfo, metadataStringKey, (LPVOID*)&metadataString, &metadataStringSize) && metadataString[0] != '\0') { - return std::wstring(metadataString); + free(fileVersionInfo); + *sessionName = std::wstring(metadataString); + return true; } swprintf(metadataStringKey, L"\\StringFileInfo\\%04x%04x\\ProductName", translationArray[(syslangIdx < 0 ? 0 : syslangIdx)].wLanguage, translationArray[(syslangIdx < 0 ? 0 : syslangIdx)].wCodePage); if (VerQueryValueW(fileVersionInfo, metadataStringKey, (LPVOID*)&metadataString, &metadataStringSize) && metadataString[0] != '\0') { - return std::wstring(metadataString); + free(fileVersionInfo); + *sessionName = std::wstring(metadataString); + return true; } - //MSIX? + if(fileVersionInfo) + free(fileVersionInfo); + + return false; +} + + +bool Session::fetchNameViaMSIX(std::wstring exePath, DWORD pid, std::wstring *sessionName) { HANDLE process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid); - if(!process) return exePath; + if(!process) return false; //constant missing in mingw64. TB removed when I upgrade to a mingw64 ver that has it #define APPLICATION_USER_MODEL_ID_MAX_LENGTH 130 @@ -295,7 +310,7 @@ std::wstring Session::fetchProcessName(DWORD pid) { PWSTR userModelId = (PWSTR)malloc(length * sizeof(wchar_t)); if(GetApplicationUserModelId(process, &length, userModelId) != ERROR_SUCCESS) { CloseHandle(process); - return exePath; + return false; } CloseHandle(process); @@ -323,17 +338,190 @@ std::wstring Session::fetchProcessName(DWORD pid) { LPWSTR humanName = nullptr; si->GetDisplayName(SIGDN_NORMALDISPLAY, &humanName); if(humanName && humanName[0] != '\0') { - msixName = std::wstring(humanName); + *sessionName = std::wstring(humanName); CoTaskMemFree(humanName); } if(si) si->Release(); - if (msixName.length() > 0) - return msixName; - else return exePath; - //free(fileVersionInfo); + if (sessionName->length() > 0) + return true; + else return false; + + } +BOOL test(HWND hwnd, LPARAM lParam) { + int length = GetWindowTextLength(hwnd); + wchar_t* buffer = new wchar_t[length + 1]; + GetWindowTextW(hwnd, buffer, length + 1); + std::wstring windowTitle(buffer); + delete[] buffer; + + // List visible windows with a non-empty title + if (IsWindowVisible(hwnd) && length != 0) { + //std::wcout << hwnd << ": " << windowTitle << std::endl; + log_wdebugcpp(windowTitle); + return FALSE; + } + return TRUE; + + /* + * auto pParams = (DWORD)(lParam); + * + * DWORD processId; + * if (GetWindowThreadProcessId(hwnd, &processId) && processId == pParams) { + * // Stop enumerating + * //SetLastError(-1); + * //pParams->first = hwnd; + * //return FALSE; + * } + * + * // Continue enumerating + * return TRUE; + */ +} + + +bool Session::fetchNameViaWindowName(std::wstring exePath, DWORD pid, std::wstring *sessionName) { + std::pair params = { 0, pid }; + + BOOL result = EnumWindows(test, NULL); + + /* + * if (!params.first) goto msix; + */ + + + /* + * if (!result && GetLastError() == -1 && params.first) { + * return params.first; + * } + */ +} + +void Session::fetchName(std::wstring exePath, DWORD pid) { + if(fetchNameViaWindowName(exePath, pid, &this->sessionName)) + return; + else if(fetchNameViaMSIX(exePath, pid, &this->sessionName)) + return; + else if(!fetchNameViaFD(exePath, pid, &this->sessionName)) + this->sessionName = exePath; + + /* + * std::thread ttest(&Session::fetchNameViaWindowName, this, exePath, pid, &this->sessionName); + * ttest.join(); + */ +} + +/* + * std::wstring Session::fetchProcessName(DWORD pid) { + * /\* Executable path retrieval *\/ + * std::wstring exePath = L""; + * std::wstring msixName; + * + * HANDLE processList = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, pid); + * if (processList == INVALID_HANDLE_VALUE) { + * log_wdebugcpp(L"aye no procname."); + * return exePath; + * } + * + * MODULEENTRY32W me32w; + * me32w.dwSize = sizeof(MODULEENTRY32W); + * if(Module32FirstW(processList, &me32w)) { + * do { + * if (me32w.th32ProcessID == pid) { + * exePath = std::wstring(me32w.szExePath); + * break; + * /\* + * * However, if the calling process is a 32-bit process, you must call the + * * QueryFullProcessImageName function to retrieve the full path of the + * * executable file for a 64-bit process. + * *\/ + * } + * } while(Module32NextW(processList, &me32w)); + * } + * CloseHandle(processList); + * + * + * //No FD info available. Window name? + * nofdinfo: + * if(fileVersionInfo) + * free(fileVersionInfo); + * + * std::pair params = { 0, pid }; + * BOOL result = EnumWindows([](HWND hwnd, LPARAM lParam) -> BOOL { + * auto pParams = (std::pair*)(lParam); + * + * DWORD processId; + * if (GetWindowThreadProcessId(hwnd, &processId) && processId == pParams->second) { + * // Stop enumerating + * SetLastError(-1); + * pParams->first = hwnd; + * return FALSE; + * } + * + * // Continue enumerating + * return TRUE; + * }, (LPARAM)¶ms); + * + * if (!params.first) goto msix; + * + * + * /\* + * * if (!result && GetLastError() == -1 && params.first) { + * * return params.first; + * * } + * *\/ + * + * //No window info. MSIX? + * HANDLE process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid); + * if(!process) return exePath; + * + * //constant missing in mingw64. TB removed when I upgrade to a mingw64 ver that has it + * #define APPLICATION_USER_MODEL_ID_MAX_LENGTH 130 + * uint32_t length = APPLICATION_USER_MODEL_ID_MAX_LENGTH; + * PWSTR userModelId = (PWSTR)malloc(length * sizeof(wchar_t)); + * if(GetApplicationUserModelId(process, &length, userModelId) != ERROR_SUCCESS) { + * CloseHandle(process); + * return exePath; + * } + * CloseHandle(process); + * + * static constexpr wchar_t* prefix = L"shell:appsfolder\\"; + * uint32_t prefixLen = wcslen(prefix); + * uint32_t userModelIdLen = wcslen(userModelId); + * wchar_t* fullName; + * fullName = (prefixLen + userModelIdLen < length) + * ? (wchar_t*)malloc(length * sizeof(wchar_t)) + * : (wchar_t*)malloc((length * 2) * sizeof(wchar_t)); + * for (int32_t i = prefixLen - 1; i >= 0; i--) { + * fullName[i] = prefix[i]; + * } + * for (uint32_t i = 0; i < userModelIdLen + 1; i++) { + * fullName[prefixLen + i] = userModelId[i]; + * } + * + * IShellItem* si; + * HRESULT hr = SHCreateItemFromParsingName(fullName, + * nullptr, + * IID_IShellItem, + * (void**)&si + * ); + * free(fullName); + * LPWSTR humanName = nullptr; + * si->GetDisplayName(SIGDN_NORMALDISPLAY, &humanName); + * if(humanName && humanName[0] != '\0') { + * msixName = std::wstring(humanName); + * CoTaskMemFree(humanName); + * } + * if(si) si->Release(); + * + * if (msixName.length() > 0) + * return msixName; + * else return exePath; + * } + */ + //todo: conflicting names. change callback name void Session::setState(SessionState state) { sessionState = state; diff --git a/src/back/backsessionclasses.h b/src/back/backsessionclasses.h index 2b5db99..9e0f7a8 100644 --- a/src/back/backsessionclasses.h +++ b/src/back/backsessionclasses.h @@ -4,6 +4,7 @@ #include "global.h" #include "contclasses.h" +#include class Endpoint; class SessionStateCallback : public IAudioSessionEvents { @@ -47,13 +48,18 @@ class Session { //uint32_t getChannelCount(); private: - std::wstring fetchProcessName(DWORD pid); + bool getExePath(DWORD pid, /*out*/ std::wstring *exePath); + void fetchName(std::wstring exePath, DWORD pid); + bool fetchNameViaFD(std::wstring exePath, DWORD pid, /*out*/ std::wstring *sessionName); + bool fetchNameViaMSIX(std::wstring exePath, DWORD pid, /*out*/ std::wstring *sessionName); + bool fetchNameViaWindowName(std::wstring exePath, DWORD pid, /*out*/ std::wstring *sessionName); + /* std::wstring fetchProcessName(DWORD pid); */ std::wstring sessionName; SessionState sessionState; Endpoint* ep; IAudioSessionControl2* sessionControl = nullptr; IAudioMeterInformation* meterInformation = nullptr; ISimpleAudioVolume* sessionVolume = nullptr; - size_t idx; + size_t idx; }; diff --git a/src/global.h b/src/global.h index b8b9bcb..5d327dc 100644 --- a/src/global.h +++ b/src/global.h @@ -30,6 +30,8 @@ #define STRING_CP "Open Control Panel" #define STRING_ABOUT "About" #define STRING_STARTUP "Run at startup" + +#define LSTRING_UNNAMED_SESSION L"Unnamed session" //INIT BACK enum AudioChannel { diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 48b248c..cbe8106 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -326,6 +326,7 @@ SessionWidget::SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent) muteButton = new QCheckBox(this); mainLabel = new QLabel(QString::fromStdWString(sh->getName()), this); + mainLabel->setToolTip(QString::fromStdWString(sh->getName())); mainSlider = new MeterSlider(Qt::Horizontal, this); //mainLabel->setMaximumWidth(150 /*1/16ish 1080p*/); @@ -368,8 +369,10 @@ SessionWidget::SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent) connect(volumePoller, &QTimer::timeout, [this, sh](){ //if (memcmp(osh->callbackInfo[idx]->caller, osh->getGuid(), sizeof(NGuid)) == 0) return; CHECK IF THIS PROGRAM GENERATED THE FUNSIES IS NO LONGER IN USE FOR NOW. //todo: global + constexpr + ratio - if (sh->getVolumeInfo()->isNameChanged) + if (sh->getVolumeInfo()->isNameChanged) { mainLabel->setText(QString::fromStdWString(sh->getName())); + mainLabel->setToolTip(QString::fromStdWString(sh->getName())); + } const float roundingFactor = 0.005; mainSlider->blockSignals(true); muteButton->blockSignals(true); From 517f117575b57c4fa360efa70dce06c8f133454a Mon Sep 17 00:00:00 2001 From: Hane Date: Tue, 6 Aug 2024 14:49:52 +0200 Subject: [PATCH 50/78] o0 & windownames --- qtest.pro | 2 +- src/back/backsessionclasses.cpp | 184 +++++--------------------------- 2 files changed, 27 insertions(+), 159 deletions(-) diff --git a/qtest.pro b/qtest.pro index b85fa53..cc46052 100644 --- a/qtest.pro +++ b/qtest.pro @@ -1,4 +1,4 @@ -QMAKE_CXXFLAGS += --target=x86_64-w64-mingw32 -g -gcodeview -Og +QMAKE_CXXFLAGS += --target=x86_64-w64-mingw32 -g -gcodeview -O0 -Werror=return-type QMAKE_LFLAGS += --target=x86_64-w64-mingw32 -g -Wl,-pdb= -v LIBS += -LC:/capybara/libclang/x86_64-w64-mingw32/lib -lWinmm -lodbc32 -lodbccp32 -luuid -loleaut32 -lole32 -lshell32 -ladvapi32 -lcomdlg32 -lwinspool -lgdi32 -luser32 -lkernel32 -lpropsys -static -stdlib=libc++ -lunwind #"kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" -luuid -loleaut32 -lole32 -lshell32 -ladvapi32 -lcomdlg32 -lwinspool -lgdi32 -luser32 -lkernel32 diff --git a/src/back/backsessionclasses.cpp b/src/back/backsessionclasses.cpp index 41bf733..40352bf 100644 --- a/src/back/backsessionclasses.cpp +++ b/src/back/backsessionclasses.cpp @@ -350,178 +350,46 @@ bool Session::fetchNameViaMSIX(std::wstring exePath, DWORD pid, std::wstring *se } -BOOL test(HWND hwnd, LPARAM lParam) { - int length = GetWindowTextLength(hwnd); - wchar_t* buffer = new wchar_t[length + 1]; - GetWindowTextW(hwnd, buffer, length + 1); - std::wstring windowTitle(buffer); - delete[] buffer; - - // List visible windows with a non-empty title - if (IsWindowVisible(hwnd) && length != 0) { - //std::wcout << hwnd << ": " << windowTitle << std::endl; - log_wdebugcpp(windowTitle); - return FALSE; - } - return TRUE; - - /* - * auto pParams = (DWORD)(lParam); - * - * DWORD processId; - * if (GetWindowThreadProcessId(hwnd, &processId) && processId == pParams) { - * // Stop enumerating - * //SetLastError(-1); - * //pParams->first = hwnd; - * //return FALSE; - * } - * - * // Continue enumerating - * return TRUE; - */ -} - bool Session::fetchNameViaWindowName(std::wstring exePath, DWORD pid, std::wstring *sessionName) { + //lParam is documented as in, so... Beware of future explosions, ig? std::pair params = { 0, pid }; - BOOL result = EnumWindows(test, NULL); - - /* - * if (!params.first) goto msix; - */ - + BOOL result = EnumWindows([](HWND hwnd, LPARAM lParam) -> BOOL { + auto pParams = (std::pair*)(lParam); - /* - * if (!result && GetLastError() == -1 && params.first) { - * return params.first; - * } - */ + DWORD processId; + if (GetWindowThreadProcessId(hwnd, &processId) && processId == pParams->second) { + // Stop enumerating + SetLastError(-1); + pParams->first = hwnd; + return FALSE; + } + + // Continue enumerating + return TRUE; + } , (LPARAM)¶ms); + + if(!result && GetLastError() == -1 && params.first) { + int length = GetWindowTextLength(params.first); + wchar_t* buffer = new wchar_t[length + 1]; + GetWindowTextW(params.first, buffer, length + 1); + *sessionName = buffer; + delete[] buffer; + return true; + } + return false; } void Session::fetchName(std::wstring exePath, DWORD pid) { - if(fetchNameViaWindowName(exePath, pid, &this->sessionName)) + if(fetchNameViaFD(exePath, pid, &this->sessionName)) return; else if(fetchNameViaMSIX(exePath, pid, &this->sessionName)) return; - else if(!fetchNameViaFD(exePath, pid, &this->sessionName)) + else if(!fetchNameViaWindowName(exePath, pid, &this->sessionName)) this->sessionName = exePath; - - /* - * std::thread ttest(&Session::fetchNameViaWindowName, this, exePath, pid, &this->sessionName); - * ttest.join(); - */ } -/* - * std::wstring Session::fetchProcessName(DWORD pid) { - * /\* Executable path retrieval *\/ - * std::wstring exePath = L""; - * std::wstring msixName; - * - * HANDLE processList = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, pid); - * if (processList == INVALID_HANDLE_VALUE) { - * log_wdebugcpp(L"aye no procname."); - * return exePath; - * } - * - * MODULEENTRY32W me32w; - * me32w.dwSize = sizeof(MODULEENTRY32W); - * if(Module32FirstW(processList, &me32w)) { - * do { - * if (me32w.th32ProcessID == pid) { - * exePath = std::wstring(me32w.szExePath); - * break; - * /\* - * * However, if the calling process is a 32-bit process, you must call the - * * QueryFullProcessImageName function to retrieve the full path of the - * * executable file for a 64-bit process. - * *\/ - * } - * } while(Module32NextW(processList, &me32w)); - * } - * CloseHandle(processList); - * - * - * //No FD info available. Window name? - * nofdinfo: - * if(fileVersionInfo) - * free(fileVersionInfo); - * - * std::pair params = { 0, pid }; - * BOOL result = EnumWindows([](HWND hwnd, LPARAM lParam) -> BOOL { - * auto pParams = (std::pair*)(lParam); - * - * DWORD processId; - * if (GetWindowThreadProcessId(hwnd, &processId) && processId == pParams->second) { - * // Stop enumerating - * SetLastError(-1); - * pParams->first = hwnd; - * return FALSE; - * } - * - * // Continue enumerating - * return TRUE; - * }, (LPARAM)¶ms); - * - * if (!params.first) goto msix; - * - * - * /\* - * * if (!result && GetLastError() == -1 && params.first) { - * * return params.first; - * * } - * *\/ - * - * //No window info. MSIX? - * HANDLE process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid); - * if(!process) return exePath; - * - * //constant missing in mingw64. TB removed when I upgrade to a mingw64 ver that has it - * #define APPLICATION_USER_MODEL_ID_MAX_LENGTH 130 - * uint32_t length = APPLICATION_USER_MODEL_ID_MAX_LENGTH; - * PWSTR userModelId = (PWSTR)malloc(length * sizeof(wchar_t)); - * if(GetApplicationUserModelId(process, &length, userModelId) != ERROR_SUCCESS) { - * CloseHandle(process); - * return exePath; - * } - * CloseHandle(process); - * - * static constexpr wchar_t* prefix = L"shell:appsfolder\\"; - * uint32_t prefixLen = wcslen(prefix); - * uint32_t userModelIdLen = wcslen(userModelId); - * wchar_t* fullName; - * fullName = (prefixLen + userModelIdLen < length) - * ? (wchar_t*)malloc(length * sizeof(wchar_t)) - * : (wchar_t*)malloc((length * 2) * sizeof(wchar_t)); - * for (int32_t i = prefixLen - 1; i >= 0; i--) { - * fullName[i] = prefix[i]; - * } - * for (uint32_t i = 0; i < userModelIdLen + 1; i++) { - * fullName[prefixLen + i] = userModelId[i]; - * } - * - * IShellItem* si; - * HRESULT hr = SHCreateItemFromParsingName(fullName, - * nullptr, - * IID_IShellItem, - * (void**)&si - * ); - * free(fullName); - * LPWSTR humanName = nullptr; - * si->GetDisplayName(SIGDN_NORMALDISPLAY, &humanName); - * if(humanName && humanName[0] != '\0') { - * msixName = std::wstring(humanName); - * CoTaskMemFree(humanName); - * } - * if(si) si->Release(); - * - * if (msixName.length() > 0) - * return msixName; - * else return exePath; - * } - */ - //todo: conflicting names. change callback name void Session::setState(SessionState state) { sessionState = state; From e42dbaa19438780980f813e5b0cca504b4764b27 Mon Sep 17 00:00:00 2001 From: Hane Date: Thu, 8 Aug 2024 15:04:17 +0200 Subject: [PATCH 51/78] session shown name parsing, pending refactor --- src/back/backsessionclasses.cpp | 41 +++++++++++++++++++++++---------- src/back/backsessionclasses.h | 4 ++-- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/src/back/backsessionclasses.cpp b/src/back/backsessionclasses.cpp index 40352bf..1bfc37f 100644 --- a/src/back/backsessionclasses.cpp +++ b/src/back/backsessionclasses.cpp @@ -128,11 +128,14 @@ Session::Session(Endpoint* ep, IAudioSessionControl2* sessionControl, size_t idx this->sessionControl->GetDisplayName(&sessionDisplayName); if (!wcscmp(sessionDisplayName, L"")) { std::wstring exePath; - if (getExePath(pid, &exePath)) - fetchName(exePath, pid); - else this->sessionName = std::wstring(LSTRING_UNNAMED_SESSION); + if (getExePath(pid, &exePath)) { + if (fetchName(exePath, pid)) goto nameFound; + } + if (fetchNameViaWindowName(pid, &this->sessionName)) goto nameFound; } else this->sessionName = std::wstring(sessionDisplayName); + + nameFound: CoTaskMemFree(sessionDisplayName); } } @@ -207,7 +210,7 @@ bool Session::getExePath(DWORD pid, std::wstring *exePath) { HANDLE processList = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, pid); if (processList == INVALID_HANDLE_VALUE) { - log_wdebugcpp(L"aye no procname."); + log_wdebugcpp(L"aye no procname. -> " + std::to_wstring(GetLastError())); return false; } @@ -351,7 +354,7 @@ bool Session::fetchNameViaMSIX(std::wstring exePath, DWORD pid, std::wstring *se } -bool Session::fetchNameViaWindowName(std::wstring exePath, DWORD pid, std::wstring *sessionName) { +bool Session::fetchNameViaWindowName(DWORD pid, std::wstring *sessionName) { //lParam is documented as in, so... Beware of future explosions, ig? std::pair params = { 0, pid }; @@ -359,7 +362,11 @@ bool Session::fetchNameViaWindowName(std::wstring exePath, DWORD pid, std::wstri auto pParams = (std::pair*)(lParam); DWORD processId; - if (GetWindowThreadProcessId(hwnd, &processId) && processId == pParams->second) { + //&& GetWindow(hwnd, GW_OWNER) == 0 + if (IsWindowVisible(hwnd) && GetWindowThreadProcessId(hwnd, &processId) && processId == pParams->second) { + int length = GetWindowTextLength(hwnd); + if (!length) return TRUE; + // Stop enumerating SetLastError(-1); pParams->first = hwnd; @@ -371,6 +378,7 @@ bool Session::fetchNameViaWindowName(std::wstring exePath, DWORD pid, std::wstri } , (LPARAM)¶ms); if(!result && GetLastError() == -1 && params.first) { + //todo: double-dipping length... Not a fan int length = GetWindowTextLength(params.first); wchar_t* buffer = new wchar_t[length + 1]; GetWindowTextW(params.first, buffer, length + 1); @@ -381,13 +389,22 @@ bool Session::fetchNameViaWindowName(std::wstring exePath, DWORD pid, std::wstri return false; } -void Session::fetchName(std::wstring exePath, DWORD pid) { +bool Session::fetchName(std::wstring exePath, DWORD pid) { + /* + * if(fetchNameViaWindowName(exePath, pid, &this->sessionName)) + * return; + * else if(fetchNameViaMSIX(exePath, pid, &this->sessionName)) + * return; + * else if(!fetchNameViaFD(exePath, pid, &this->sessionName)) + * this->sessionName = exePath; + */ + if(fetchNameViaFD(exePath, pid, &this->sessionName)) - return; - else if(fetchNameViaMSIX(exePath, pid, &this->sessionName)) - return; - else if(!fetchNameViaWindowName(exePath, pid, &this->sessionName)) - this->sessionName = exePath; + return true; + return fetchNameViaMSIX(exePath, pid, &this->sessionName); + //else if(!fetchNameViaWindowName(exePath, pid, &this->sessionName)) + /// this->sessionName = exePath; + } //todo: conflicting names. change callback name diff --git a/src/back/backsessionclasses.h b/src/back/backsessionclasses.h index 9e0f7a8..3f73f3d 100644 --- a/src/back/backsessionclasses.h +++ b/src/back/backsessionclasses.h @@ -49,10 +49,10 @@ class Session { private: bool getExePath(DWORD pid, /*out*/ std::wstring *exePath); - void fetchName(std::wstring exePath, DWORD pid); + bool fetchName(std::wstring exePath, DWORD pid); bool fetchNameViaFD(std::wstring exePath, DWORD pid, /*out*/ std::wstring *sessionName); bool fetchNameViaMSIX(std::wstring exePath, DWORD pid, /*out*/ std::wstring *sessionName); - bool fetchNameViaWindowName(std::wstring exePath, DWORD pid, /*out*/ std::wstring *sessionName); + bool fetchNameViaWindowName(DWORD pid, /*out*/ std::wstring *sessionName); /* std::wstring fetchProcessName(DWORD pid); */ std::wstring sessionName; SessionState sessionState; From d4db24ed7dc664a89f93674829774df0702ab748 Mon Sep 17 00:00:00 2001 From: Hane Date: Wed, 14 Aug 2024 17:02:57 +0200 Subject: [PATCH 52/78] wip: no endpoint, role rework, visual ratio --- assets.qrc | 1 + assets/selawk.ttf | Bin 0 -> 44224 bytes qtest.pro | 2 +- src/back/backlasses.cpp | 6 +- src/cont/contclasses.cpp | 18 +++- src/cont/contclasses.h | 10 ++- src/global.h | 2 + src/qt/qtclasses.cpp | 177 +++++++++++++++++++++++++++++---------- src/qt/qtclasses.h | 11 ++- src/qt/qtcommon.h | 1 + src/qt/qtvisuals.h | 2 + src/qtestmain.cpp | 11 ++- 12 files changed, 184 insertions(+), 57 deletions(-) create mode 100644 assets/selawk.ttf diff --git a/assets.qrc b/assets.qrc index a9cbd38..4ef5956 100644 --- a/assets.qrc +++ b/assets.qrc @@ -1,5 +1,6 @@ + assets/selawk.ttf assets/notificationAreaIcon.png assets/style.qss assets/logo.ico diff --git a/assets/selawk.ttf b/assets/selawk.ttf new file mode 100644 index 0000000000000000000000000000000000000000..736bac3c210abb56f305c0c2307da8e002cd6c91 GIT binary patch literal 44224 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, wstringEndpointId); + osh->roleBucketEntryCallback(nRole, wstringEndpointId); + //osh->changeFrontDefaultsCallback(nRole, wstringEndpointId); return S_OK; } @@ -586,6 +588,8 @@ void Overseer::reloadEndpoints(Flows flow) { break; } deviceEnumerator->GetDefaultAudioEndpoint(MSflow, val, &temp); + if (!temp) continue; + LPWSTR id = nullptr; if (flow == Flows::FLOW_PLAYBACK) { diff --git a/src/cont/contclasses.cpp b/src/cont/contclasses.cpp index f46571c..fb0e22a 100644 --- a/src/cont/contclasses.cpp +++ b/src/cont/contclasses.cpp @@ -290,12 +290,22 @@ NGuid OverseerHandler::getGuid() { return this->os->getGuid(); } -void OverseerHandler::setChangeFrontDefaultsFunction(std::function changeFrontDefaults){ - this->changeFrontDefaults = changeFrontDefaults; +/* + * void OverseerHandler::setChangeFrontDefaultsFunction(std::function changeFrontDefaults){ + * this->changeFrontDefaults = changeFrontDefaults; + * } + * + * void OverseerHandler::changeFrontDefaultsCallback(Roles role, std::wstring endpointId) { + * this->changeFrontDefaults(role, endpointId); + * } + */ + +void OverseerHandler::roleBucketEntryCallback(Roles role, std::wstring endpointId){ + this->roleBucketEntry(role, endpointId); } -void OverseerHandler::changeFrontDefaultsCallback(Roles role, std::wstring endpointId) { - this->changeFrontDefaults(role, endpointId); +void OverseerHandler::setRoleBucketEntryFunction(std::function roleBucketEntry) { + this->roleBucketEntry = roleBucketEntry; } void OverseerHandler::updateFrontEndpointName(Endpoint* ep) { diff --git a/src/cont/contclasses.h b/src/cont/contclasses.h index b0cea92..e147394 100644 --- a/src/cont/contclasses.h +++ b/src/cont/contclasses.h @@ -99,9 +99,12 @@ public: OverseerHandler(); void openControlPanel(); - void setChangeFrontDefaultsFunction(std::function changeFrontDefaults); - void changeFrontDefaultsCallback(Roles role, std::wstring endpointId); + //void setChangeFrontDefaultsFunction(std::function changeFrontDefaults); + //void changeFrontDefaultsCallback(Roles role, std::wstring endpointId); + void roleBucketEntryCallback(Roles role, std::wstring endpointId); + void setRoleBucketEntryFunction(std::function roleBucketEntry); + void updateFrontEndpointName(Endpoint* ep); //void setReviseEndpointShowingFunction(std::function reviseEndpointShowing); void reviseEndpointShowing(std::wstring endpointId, EndpointState state); @@ -132,7 +135,8 @@ private: std::function changeFrontDefaults; std::function removeEndpointWidget; std::function addEndpointWidget; - + std::function roleBucketEntry; + /* Session's */ std::function changeSessionVolume; //std::function updateFrontVolumeCallback; diff --git a/src/global.h b/src/global.h index 5d327dc..4c22755 100644 --- a/src/global.h +++ b/src/global.h @@ -31,6 +31,8 @@ #define STRING_ABOUT "About" #define STRING_STARTUP "Run at startup" +#define STRING_NOENDPOINT "No active endpoints" + #define LSTRING_UNNAMED_SESSION L"Unnamed session" //INIT BACK diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index cbe8106..50a046a 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -245,10 +245,17 @@ void MainWindow::compose() { screenRes.getCoords(&srx1, &sry1, &srx2, &sry2); log_debugcpp("(for Percentage) Screen Res: " + std::to_string(srx1) + " " + std::to_string(srx2)+" " + std::to_string(sry1) + " " + std::to_string(sry2)); - uint64_t windowWidth = (uint64_t)std::abs(screenRes.width()) * this->widthRatio; + dpr = screen->devicePixelRatio(); + log_to_file("dpr: %f \n", dpr); + uint64_t windowWidth = ((uint64_t)std::abs(screenRes.width()) * widthRatio) * dpr; uint64_t screenHeight = (uint64_t)std::abs(screenRes.height()); log_debugcpp("Window Width: " + std::to_string(windowWidth)); + QFontMetrics fontMetrics = this->fontMetrics(); + int maxCharWidth = fontMetrics.maxWidth(); + int charHeight = fontMetrics.height(); + //QSize QFontMetrics::size(int flags, const QString &text, int tabStops = 0, int *tabArray = nullptr) const + this->createLayout(new QGridLayout()); //scrollArea->verticalScrollBar()->setMinimumWidth(windowWidth * (widthRatio)); //scrollArea->verticalScrollBar()->setMaximumWidth(windowWidth * (widthRatio)); @@ -259,8 +266,7 @@ void MainWindow::compose() { * this->hide(); * this->setAttribute(Qt::WA_DontShowOnScreen, false); */ - const qreal dpr = screen->devicePixelRatio(); - log_to_file("dpr: %f \n", dpr); + for (auto *epw : ews) { if (!epw) continue; epw->calculateSize(windowWidth, screenHeight); @@ -396,7 +402,7 @@ void SessionWidget::calculateSize(uint64_t width, uint64_t height) { /* og 1080p 120% testing values */ this->mainLabel->setMaximumWidth((int)(width * 0.30) /*1/16ish 1080p*/); this->mainLabel->setMinimumWidth((int)(width * 0.30) /*1/16ish 1080p*/); - this->mainLabel->setMaximumHeight((int)(height * 0.02)); + //this->mainLabel->setMaximumHeight((int)(height * 0.02)); this->mainLabel->setMinimumHeight((int)(height * 0.02)); this->muteButton->setMaximumWidth((int)(width * 0.10) /*1/32th 1080p*/); this->muteButton->setMinimumWidth((int)(width * 0.10) /*1/32th 1080p*/); @@ -410,7 +416,9 @@ void SessionWidget::calculateSize(uint64_t width, uint64_t height) { log_to_file("\tMute btn Maximum width: %d \n", muteButton->maximumWidth()); log_to_file("\tMute btn Minimum width: %d \n", muteButton->minimumWidth()); log_to_file("\tSlider Minimum width: %d \n", mainSlider->minimumWidth()); - log_to_file("\tSpacer Minimum width: %d \n\n", widthSpacer->minimumSize().width()); + log_to_file("\tSlider Maximum width: %d \n", mainSlider->maximumWidth()); + log_to_file("\tSpacer Minimum width: %d \n", widthSpacer->minimumSize().width()); + log_to_file("\tSpacer Maximum width: %d \n\n", widthSpacer->maximumSize().width()); } std::wstring SessionWidget::getName() { @@ -457,8 +465,10 @@ ChannelWidget::ChannelWidget(uint32_t channelCount, EndpointHandler* eph, QWidge tmp->setValue((int) volume); tmpLb->setText(QString::number(volume)); //tmpLb->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); - tmp->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - tmpLb->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + //tmp->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + tmp->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + //mainSlider->setFocusPolicy(Qt::StrongFocus); + tmpLb->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); this->channelSliders.push_back(tmp); this->channelLabels.push_back(tmpLb); widgetLayout->addWidget(tmp, row , col); @@ -505,7 +515,6 @@ EndpointWidget::EndpointWidget(EndpointHandler* eph, QWidget *parent, uint64_t i //todo: sussy this->eph->setState(EndpointState::ENDPOINT_ACTIVE, idx); this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); - //setAttribute(Qt::WA_TranslucentBackground); widgetLayout = new QGridLayout(this); //this->setLayout(widgetLayout); log_debugcpp("epw main layout parent: " + std::to_string((intptr_t)(widgetLayout->parent()))); @@ -575,6 +584,7 @@ EndpointWidget::EndpointWidget(EndpointHandler* eph, QWidget *parent, uint64_t i uint32_t epChannelCount = eph->getChannelCount(); if(epChannelCount > 1) { cw = new ChannelWidget(epChannelCount, eph, nullptr); + cw->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); //cw->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); widgetLayout->addWidget(cw, row++, 0, 1, 4 /*colmax*/, Qt::AlignTop); } @@ -672,7 +682,8 @@ void EndpointWidget::addSessionWidget(CustomWidgetEvent* ev){ for (QWidget *widget : topLevelWidgets) { if (qobject_cast(widget)) { double widthRatio = ((MainWindow*)widget)->widthRatio; - sw->calculateSize(std::abs(this->screen()->geometry().width()) * widthRatio, + double dpr = ((MainWindow*)widget)->dpr; + sw->calculateSize((std::abs(this->screen()->geometry().width()) * widthRatio) * dpr, std::abs(this->screen()->geometry().height())); } } @@ -726,6 +737,9 @@ void MainWindow::customEvent(QEvent* ev) { } else if (ev->type() == (QEvent::Type)CustomQEvent::RecomposeMainWindow) { ev->setAccepted(true); if (this->isVisible()) this->compose(); + } else if (ev->type() == (QEvent::Type)CustomQEvent::EndpointRoleChange) { + ev->setAccepted(true); + this->flushRoleChanges(); } QMainWindow::customEvent(ev); } @@ -783,14 +797,27 @@ void MainWindow::reorderEndpointWidgetCollection() { void EndpointWidget::calculateSize(uint64_t width, uint64_t height) { /* og 1080p 120% testing values */ log_to_file("[EndpointWidget %s sizes]\n", converter.to_bytes(this->getEndpointHandler()->getName()).c_str()); - log_to_file("Params: {Width: %u Height: %u}\n", width, height); - this->mainLabel->setMaximumWidth((int)(width * 0.50) /* 1080p 120%*/); - this->mainLabel->setMinimumWidth((int)(width * 0.50) /* 1080p 120%*/); + log_to_file("Params: {WWidth: %u SHeight: %u}\n", width, height); + //this->setMaximumWidth(width); + /* + * this->mainLabel->setMaximumWidth((int)(width * 0.50) /\* 1080p 120%*\/); + * this->mainLabel->setMinimumWidth((int)(width * 0.50) /\* 1080p 120%*\/); + */ + this->mainLabel->setMaximumSize((int)(width * 0.50), height /* 1080p 120%*/); + this->mainLabel->setMinimumSize((int)(width * 0.50), 1 /* 1080p 120%*/); + this->muteButton->setMaximumSize((int)(width * 0.10), height /* 1080p 120%*/); + this->mainVolumeLabel->setMaximumSize((int)(width * 0.10), height /* 1080p 120%*/); + for (auto roleCheckbox : defaultRolesCheckBoxes) { + roleCheckbox.second->setMaximumWidth((int)(width * 0.20) /* 1080p 120%*/); + log_to_file("Role %d width: %d \n", roleCheckbox.first, roleCheckbox.second->maximumWidth()); + } log_to_file("Main label width: %d \n", this->mainLabel->maximumWidth()); + log_to_file("Mute button width: %d \n", this->muteButton->maximumWidth()); + log_to_file("Volume label width: %d \n", this->mainVolumeLabel->maximumWidth()); if (cw) { this->cw->setMinimumSize(QSize(1, (height * 0.06) * (int)((cw->getChannelCount() / 2) + 0.5))); - this->cw->setMaximumSize(QSize(QWIDGETSIZE_MAX, (height * 0.06) * (int)((cw->getChannelCount() / 2) + 0.5))); + //this->cw->setMaximumSize(QSize(width, (height * 0.06) * (int)((cw->getChannelCount() / 2) + 0.5))); log_to_file("Channels Maximum size: %d, %d \n", cw->maximumWidth(), cw->maximumHeight()); log_to_file("Channels Minimum size: %d, %d \n", cw->minimumWidth(), cw->minimumHeight()); } @@ -889,15 +916,23 @@ void MainWindow::createLayout(QGridLayout *newLayout) { widget->setLayout(newLayout); this->widgetLayout = newLayout; + bool areEndpoints = false; uint64_t i = 0; for (EndpointWidget *epw : ews) { if (!epw) continue; + else areEndpoints = true; log_debugcpp("EPWidget positioning"); log_wdebugcpp(L"epw name: " + epw->getEndpointHandler()->getName()); epw->setIndex(i); this->widgetLayout->addWidget(epw, i++, 0); } - widgetLayout->addItem(lastRowSpacer, i, 0); + if(areEndpoints) + widgetLayout->addItem(lastRowSpacer, i, 0); + else { + if (noEndpoints) delete noEndpoints; + noEndpoints = new QLabel(STRING_NOENDPOINT, this); + widgetLayout->addWidget(noEndpoints, i, 0); + } } MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { @@ -910,6 +945,17 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { //setStyleSheet("background: transparent; "); //setStyleSheet("background-color: rgba(255,182,193);"); setWindowTitle(STRING_TITLE); + /* + * Font setup + */ + int id = QFontDatabase::addApplicationFont(":/assets/selawk.ttf"); + QString family = QFontDatabase::applicationFontFamilies(id).at(0); + font = QFont(family); + font.setKerning(true); + font.setPointSize(12); + this->setFont(font); + //qputenv("QT_AUTO_SCREEN_SCALE_FACTOR", "1"); + /* * Registering needed custom events */ @@ -920,9 +966,11 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { QEvent::registerEventType(CustomQEvent::SessionWidgetObsolete); QEvent::registerEventType(CustomQEvent::SessionWidgetCreated); QEvent::registerEventType(CustomQEvent::RecomposeMainWindow); + QEvent::registerEventType(CustomQEvent::EndpointRoleChange); /* This spacer provides proper spacing when window vertically > widgets. */ lastRowSpacer = new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding); + ewsUpdateTimer = new QTimer(this); recentlyClosedTimer = new QTimer(this); widget = new QWidget(); @@ -945,6 +993,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { * Scroll bar code */ scrollArea = new QScrollArea(this); + //widget->setAttribute(Qt::WA_TranslucentBackground); scrollArea->setWidget(widget); scrollArea->setWidgetResizable(true); scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); @@ -959,6 +1008,11 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { * Menu bar code */ mainMenuBar = new QToolBar(this); + /* + * QPalette pal; + * pal.setColor(QPalette::Window, Qt::transparent); + * mainMenuBar->setPalette(pal); + */ hw = new HeaderWidget(this); mainMenuBar->addWidget(hw); mainMenuBar->setMovable(false); @@ -993,26 +1047,72 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { this->recentlyClosedTimer->start(); } }); + /* * Set of function callback definitons for EndpointSituationCallback */ - osh->setChangeFrontDefaultsFunction([this](Roles role, std::wstring endpointId) { - //Sigh... I hope to get this right when librole is done... - //todo: STILL BORKED. THEY'RE NOT FORFEITING THEIR OLD POSITION - EndpointWidget *newDef = nullptr, *oldDef = nullptr; - for (uint64_t i = 0; i < ews.size(); i++) { - auto epw = this->ews.at(i); - if (!epw) continue; - if (epw->getEndpointHandler()->getId() == endpointId) { - newDef = epw; - continue; } - if (epw->getEndpointHandler()->getRoles() & role) { - oldDef = epw; - continue; } - } + osh->setRoleBucketEntryFunction([this](Roles role, std::wstring endpointId) { + std::pair entry = { role, endpointId }; + this->roleBucketList.push_back(entry); + QCoreApplication::instance()->postEvent(this, new QEvent((QEvent::Type)CustomQEvent::EndpointRoleChange),Qt::LowEventPriority); + }); - //debug if (role != Roles::ROLE_COMMUNICATIONS) return; - if (oldDef && newDef) { + osh->setRemoveEndpointWidgetFunction([this](uint64_t index) { + QCoreApplication::instance()->postEvent(this, new CustomWidgetEvent((QEvent::Type)CustomQEvent::EndpointWidgetObsolete, index)); + }); + + osh->setAddEndpointWidgetFunction([this](EndpointHandler* eph) { + QCoreApplication::instance()->postEvent(this, new CustomWidgetEvent((QEvent::Type)CustomQEvent::EndpointWidgetCreated, eph)); + }); + +} + +void MainWindow::flushRoleChanges() { + std::pair change = roleBucketList.back(); + roleBucketList.pop_back(); + this->changeFrontDefaults(change.first, change.second); +} + +void MainWindow::changeFrontDefaults(Roles role, std::wstring endpointId) { + //Sigh... I hope to get this right when librole is done... + //todo: STILL BORKED. THEY'RE NOT FORFEITING THEIR OLD POSITION + EndpointWidget *newDef = nullptr, *oldDef = nullptr; + for (uint64_t i = 0; i < ews.size(); i++) { + auto epw = this->ews.at(i); + if (!epw) continue; + if (epw->getEndpointHandler()->getId() == endpointId) { + newDef = epw; + continue; } + if (epw->getEndpointHandler()->getRoles() & role) { + oldDef = epw; + continue; } + } + + + //debug if (role != Roles::ROLE_COMMUNICATIONS) return; + if (newDef && !oldDef) { + newDef->getDefaultRolesWidgets().at(role)->blockSignals(true); + newDef->getEndpointHandler()->assignRoles(role); + QCoreApplication::instance()->postEvent(newDef->getDefaultRolesWidgets().at(role), new QEvent((QEvent::Type)CustomQEvent::EndpointDefaultChange)); + uint8_t newDefIdx = newDef->getIndex(); + uint8_t newDefRoles = newDef->getEndpointHandler()->getRoles(); + if (newDefRoles == Roles::ROLE_ALL) { + newDef->setIndex(0); + this->ews[0] = newDef; + if (this->ews[1] && newDef->getEndpointHandler()->getId() == endpointId) this->ews[1] = nullptr; + QCoreApplication::instance()->postEvent(newDef->getDefaultRolesWidgets().at(Roles::ROLE_ALL), new QEvent((QEvent::Type)CustomQEvent::EndpointDefaultChange)); + } else if ((newDefRoles & Roles::ROLE_MULTIMEDIA) || + (newDefRoles & Roles::ROLE_CONSOLE)){ + newDef->setIndex(0); + this->ews[0] = newDef; + } else if (newDefRoles & Roles::ROLE_COMMUNICATIONS) { + newDef->setIndex(1); + this->ews[1] = newDef; + } + log_debugcpp("newDef new idx: " + std::to_string(newDef->getIndex())); + newDef->getDefaultRolesWidgets().at(role)->blockSignals(false); + } + else if (oldDef && newDef) { this->ews.at(oldDef->getIndex()) = nullptr; this->ews.at(newDef->getIndex()) = nullptr; newDef->getDefaultRolesWidgets().at(role)->blockSignals(true); @@ -1023,6 +1123,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { if (newDefRoles == Roles::ROLE_ALL) { newDef->setIndex(0); this->ews[0] = newDef; + if (this->ews[1] && newDef->getEndpointHandler()->getId() == endpointId) this->ews[1] = nullptr; QCoreApplication::instance()->postEvent(newDef->getDefaultRolesWidgets().at(Roles::ROLE_ALL), new QEvent((QEvent::Type)CustomQEvent::EndpointDefaultChange)); } else if ((newDefRoles & Roles::ROLE_MULTIMEDIA) || (newDefRoles & Roles::ROLE_CONSOLE)){ @@ -1060,19 +1161,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { } log_debugcpp("oldDef new idx: " + std::to_string(oldDef->getIndex())); oldDef->getDefaultRolesWidgets().at(role)->blockSignals(false); - } - QCoreApplication::instance()->postEvent - (this, new QEvent((QEvent::Type)CustomQEvent::RecomposeMainWindow)); - }); - - osh->setRemoveEndpointWidgetFunction([this](uint64_t index) { - QCoreApplication::instance()->postEvent(this, new CustomWidgetEvent((QEvent::Type)CustomQEvent::EndpointWidgetObsolete, index)); - }); - - osh->setAddEndpointWidgetFunction([this](EndpointHandler* eph) { - QCoreApplication::instance()->postEvent(this, new CustomWidgetEvent((QEvent::Type)CustomQEvent::EndpointWidgetCreated, eph)); - }); - + } + QCoreApplication::instance()->postEvent + (this, new QEvent((QEvent::Type)CustomQEvent::RecomposeMainWindow)); } void MainWindow::closeEvent(QCloseEvent *event) { diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index 6a5e9aa..c1b6c6e 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -18,7 +18,8 @@ enum CustomQEvent { EndpointDefaultChange = 1003, SessionWidgetCreated = 1004, SessionWidgetObsolete = 1005, - RecomposeMainWindow = 1006 + RecomposeMainWindow = 1006, + EndpointRoleChange = 1007 }; template @@ -198,7 +199,11 @@ private slots: private: //std::vector *ephs; + void flushRoleChanges(); + void changeFrontDefaults(Roles role, std::wstring endpointId); + std::vector ews; + std::vector> roleBucketList; QWidget *widget; QGridLayout *widgetLayout; @@ -209,6 +214,7 @@ private: QTimer *ewsUpdateTimer; static constexpr uint64_t ewsUpdateTimerFrequency = 500; double widthRatio = 0.28; + double dpr = 1.0; bool recentlyClosed = false; uint8_t recentlyClosedTimerFrequency = 1000; QTimer *recentlyClosedTimer; @@ -218,6 +224,9 @@ private: QToolBar *mainMenuBar; QScreen *screen; QSpacerItem* lastRowSpacer; + QLabel* noEndpoints = nullptr; + + QFont font; friend class EndpointWidget; //public slots: // void setEndpointHandlers(std::vector *ephs); diff --git a/src/qt/qtcommon.h b/src/qt/qtcommon.h index 414e236..b7a7245 100644 --- a/src/qt/qtcommon.h +++ b/src/qt/qtcommon.h @@ -41,6 +41,7 @@ #include #include #include +#include //#include //#include /* diff --git a/src/qt/qtvisuals.h b/src/qt/qtvisuals.h index 4c7ac4f..3095c45 100644 --- a/src/qt/qtvisuals.h +++ b/src/qt/qtvisuals.h @@ -181,6 +181,8 @@ public: break; } + return QRect(); + } void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *option, diff --git a/src/qtestmain.cpp b/src/qtestmain.cpp index 6c435c3..490608d 100644 --- a/src/qtestmain.cpp +++ b/src/qtestmain.cpp @@ -46,16 +46,19 @@ int main (int argc, char* argv[]) { * log_debugcpp(a.toStdString()); * } */ - + QApplication::setStyle(new MixerStyle(QStyleFactory::create("Fusion"))); + //QApplication::setFont(font); QPalette palette = QGuiApplication::palette(); //todo: ez full apply os accent colorw palette.setColor(QPalette::Active, QPalette::Highlight, QColor(255, 192, 203, 200));//QColor(30,30,30,100)); QGuiApplication::setPalette(palette); - //Check if running - //https://stackoverflow.com/questions/48060989/qt-show-application-if-currently-running + initialize_file_log(); atexit(closeDebugFileLog); + + //Check if running + //https://stackoverflow.com/questions/48060989/qt-show-application-if-currently-running //std::set_terminate(closeDebugFileLog2); if (!isSingleInstanceRunning("Mixer")) startSingleInstanceServer("Mixer"); @@ -71,7 +74,7 @@ int main (int argc, char* argv[]) { //INIT FRONT QScopedPointer app(createApplication(argc, argv)); - + MainWindow window = MainWindow(); //window.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::QSizePolicy::MinimumExpanding) QApplication::setQuitOnLastWindowClosed(false); From 0bf8b321ae0c2d920919f7af1331270a815efaa2 Mon Sep 17 00:00:00 2001 From: Hane Date: Thu, 15 Aug 2024 18:07:13 +0200 Subject: [PATCH 53/78] wip: endpoint add/remove/role change bugfixes --- src/qt/qtclasses.cpp | 54 ++++++++++++++++++++++++-------------------- src/qt/qtclasses.h | 2 +- 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 50a046a..8692897 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -753,14 +753,15 @@ void MainWindow::removeEndpointWidget(CustomWidgetEvent* ev){ //delete ews.at(index); delete ews.at(i); ews.at(i) = nullptr; - this->ewsUpdateTimer->start(); + //TODO: is a flattener really necessary? + //this->ewsUpdateTimer->start(); return; } void MainWindow::addEndpointWidget(CustomWidgetEvent* ev){ EndpointWidget* epw = new EndpointWidget(ev->payload, widget, this->ews.size()); //epw->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); - this->widgetLayout->addWidget(epw); + //this->widgetLayout->addWidget(epw); ews.push_back(epw); return; } @@ -911,6 +912,7 @@ HeaderWidget::HeaderWidget(QWidget *parent) : QWidget(parent) { } void MainWindow::createLayout(QGridLayout *newLayout) { + log_debugcpp("createLayout"); widgetLayout->removeItem(lastRowSpacer); delete this->widgetLayout; widget->setLayout(newLayout); @@ -921,16 +923,15 @@ void MainWindow::createLayout(QGridLayout *newLayout) { for (EndpointWidget *epw : ews) { if (!epw) continue; else areEndpoints = true; - log_debugcpp("EPWidget positioning"); log_wdebugcpp(L"epw name: " + epw->getEndpointHandler()->getName()); - epw->setIndex(i); + //epw->setIndex(i); this->widgetLayout->addWidget(epw, i++, 0); } - if(areEndpoints) + if(areEndpoints) { + if (noEndpoints) { delete noEndpoints; noEndpoints = nullptr; } widgetLayout->addItem(lastRowSpacer, i, 0); - else { - if (noEndpoints) delete noEndpoints; - noEndpoints = new QLabel(STRING_NOENDPOINT, this); + } else { + if (!noEndpoints) noEndpoints = new QLabel(STRING_NOENDPOINT, this); widgetLayout->addWidget(noEndpoints, i, 0); } } @@ -1068,27 +1069,32 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { } void MainWindow::flushRoleChanges() { - std::pair change = roleBucketList.back(); - roleBucketList.pop_back(); - this->changeFrontDefaults(change.first, change.second); -} - -void MainWindow::changeFrontDefaults(Roles role, std::wstring endpointId) { - //Sigh... I hope to get this right when librole is done... - //todo: STILL BORKED. THEY'RE NOT FORFEITING THEIR OLD POSITION + //TODO: bucket list deque + std::pair change = roleBucketList.front(); + roleBucketList.erase(roleBucketList.begin()); + EndpointWidget *newDef = nullptr, *oldDef = nullptr; for (uint64_t i = 0; i < ews.size(); i++) { auto epw = this->ews.at(i); if (!epw) continue; - if (epw->getEndpointHandler()->getId() == endpointId) { + if (epw->getEndpointHandler()->getId() == change.second) { newDef = epw; - continue; } - if (epw->getEndpointHandler()->getRoles() & role) { + ews.at(i) = nullptr; + continue; + } + if (epw->getEndpointHandler()->getRoles() & change.first) { oldDef = epw; - continue; } + ews.at(i) = nullptr; + continue; + } } - + this->changeFrontDefaults(change.first, newDef, oldDef); +} + +void MainWindow::changeFrontDefaults(Roles role, EndpointWidget* newDef, EndpointWidget* oldDef) { + //Sigh... I hope to get this right when librole is done... + //todo: STILL BORKED. THEY'RE NOT FORFEITING THEIR OLD POSITION //debug if (role != Roles::ROLE_COMMUNICATIONS) return; if (newDef && !oldDef) { newDef->getDefaultRolesWidgets().at(role)->blockSignals(true); @@ -1099,7 +1105,7 @@ void MainWindow::changeFrontDefaults(Roles role, std::wstring endpointId) { if (newDefRoles == Roles::ROLE_ALL) { newDef->setIndex(0); this->ews[0] = newDef; - if (this->ews[1] && newDef->getEndpointHandler()->getId() == endpointId) this->ews[1] = nullptr; + //if (this->ews[1] && newDef->getEndpointHandler()->getId() == endpointId) this->ews[1] = nullptr; QCoreApplication::instance()->postEvent(newDef->getDefaultRolesWidgets().at(Roles::ROLE_ALL), new QEvent((QEvent::Type)CustomQEvent::EndpointDefaultChange)); } else if ((newDefRoles & Roles::ROLE_MULTIMEDIA) || (newDefRoles & Roles::ROLE_CONSOLE)){ @@ -1113,8 +1119,6 @@ void MainWindow::changeFrontDefaults(Roles role, std::wstring endpointId) { newDef->getDefaultRolesWidgets().at(role)->blockSignals(false); } else if (oldDef && newDef) { - this->ews.at(oldDef->getIndex()) = nullptr; - this->ews.at(newDef->getIndex()) = nullptr; newDef->getDefaultRolesWidgets().at(role)->blockSignals(true); newDef->getEndpointHandler()->assignRoles(role); QCoreApplication::instance()->postEvent(newDef->getDefaultRolesWidgets().at(role), new QEvent((QEvent::Type)CustomQEvent::EndpointDefaultChange)); @@ -1123,7 +1127,7 @@ void MainWindow::changeFrontDefaults(Roles role, std::wstring endpointId) { if (newDefRoles == Roles::ROLE_ALL) { newDef->setIndex(0); this->ews[0] = newDef; - if (this->ews[1] && newDef->getEndpointHandler()->getId() == endpointId) this->ews[1] = nullptr; + //if (this->ews[1] && newDef->getEndpointHandler()->getId() == endpointId) this->ews[1] = nullptr; QCoreApplication::instance()->postEvent(newDef->getDefaultRolesWidgets().at(Roles::ROLE_ALL), new QEvent((QEvent::Type)CustomQEvent::EndpointDefaultChange)); } else if ((newDefRoles & Roles::ROLE_MULTIMEDIA) || (newDefRoles & Roles::ROLE_CONSOLE)){ diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index c1b6c6e..8b77923 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -200,7 +200,7 @@ private slots: private: //std::vector *ephs; void flushRoleChanges(); - void changeFrontDefaults(Roles role, std::wstring endpointId); + void changeFrontDefaults(Roles role, EndpointWidget* newDef, EndpointWidget* oldDef); std::vector ews; std::vector> roleBucketList; From 13855c2e6fc63d1ea5f928ec35fc8615d1b82c9d Mon Sep 17 00:00:00 2001 From: Hane Date: Wed, 20 Nov 2024 19:21:37 +0100 Subject: [PATCH 54/78] wip: dark/light mode adaptation --- src/back/backlasses.cpp | 38 ++++++++++++++++++++++++++++++++++++++ src/back/backlasses.h | 9 ++++++++- src/back/msinclude.h | 1 + src/cont/contclasses.cpp | 12 ++++++++++++ src/cont/contclasses.h | 3 +++ src/global.h | 5 +++++ src/qt/qtclasses.cpp | 22 ++++++++++++++++++++++ src/qt/qtclasses.h | 11 ++++++----- src/qt/qtcommon.h | 16 ++++++++++++++++ src/qtestmain.cpp | 22 ++++++++++++++-------- 10 files changed, 125 insertions(+), 14 deletions(-) diff --git a/src/back/backlasses.cpp b/src/back/backlasses.cpp index 1654b42..970d79f 100644 --- a/src/back/backlasses.cpp +++ b/src/back/backlasses.cpp @@ -652,6 +652,10 @@ Overseer::Overseer() : epsc(this){ if(FAILED(deviceEnumerator->RegisterEndpointNotificationCallback(((IMMNotificationClient*)&epsc)))) { log_debugcpp("when no enchufas......"); } } +void Overseer::populateSystemValues() { + updateDarkMode(); +} + void Overseer::openControlPanel() { STARTUPINFOEXW startupConfig; PROCESS_INFORMATION processInfo; @@ -678,6 +682,40 @@ void Overseer::openControlPanel() { } } +ProcessedNativeEvent Overseer::processTopLevelWindowMessage(void* msg) { +#ifdef WIN32 + MSG *message = static_cast(msg); + switch(message->message) { + case WM_SETTINGCHANGE: + if(!wcscmp(((wchar_t*)message->lParam), L"ImmersiveColorSet")) + return updateDarkMode(); + break; + default: + return ProcessedNativeEvent::NONE; + break; + } + return ProcessedNativeEvent::NONE; + //if (message->message != WM_SETTINGCHANGE) {return false;} +#endif +} + +ProcessedNativeEvent Overseer::updateDarkMode(){ + // DwmGetColorizationColor( WM_DWMCOLORIZATIONCOLORCHANGED + DWORD value = 0; + DWORD size = sizeof(DWORD); + + LSTATUS result; + + result = RegGetValueW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", L"AppsUseLightTheme", RRF_RT_REG_DWORD, nullptr, &value, &size); + + this->lightMode = (bool)value; + return ProcessedNativeEvent::LIGHT_MODE; +} + +bool Overseer::isLightMode() { + return this->lightMode; +} + NGuid Overseer::getGuid() { return guid; } diff --git a/src/back/backlasses.h b/src/back/backlasses.h index bcc3306..9edc779 100644 --- a/src/back/backlasses.h +++ b/src/back/backlasses.h @@ -109,14 +109,20 @@ class Overseer { public: Overseer(); + NGuid getGuid(); + void populateSystemValues(); void openControlPanel(); + ProcessedNativeEvent processTopLevelWindowMessage(void* msg); + ProcessedNativeEvent updateDarkMode(); + bool isLightMode(); + std::vector getPlaybackEndpoints(); std::vector getCaptureEndpoints(); void updateEndpointInfo(std::wstring endpointId); void reloadEndpoints(Flows flow); Endpoint* addEndpoint(std::wstring endpointId, /* out */ Flows* flow); - NGuid getGuid(); + //void setEndpointStatusCallback(); //void setEndpointStatusCallback(); @@ -131,6 +137,7 @@ class Overseer { void initCOMLibrary(); NGuid guid; + bool lightMode; IMMDeviceEnumerator *deviceEnumerator; EndpointSituationCallback epsc; diff --git a/src/back/msinclude.h b/src/back/msinclude.h index 6f21ae1..f9ab1b4 100644 --- a/src/back/msinclude.h +++ b/src/back/msinclude.h @@ -1,6 +1,7 @@ #pragma once #define _WIN32_WINNT 0x0A00 + #include //done by qt by def #define UNICODE diff --git a/src/cont/contclasses.cpp b/src/cont/contclasses.cpp index fb0e22a..75df484 100644 --- a/src/cont/contclasses.cpp +++ b/src/cont/contclasses.cpp @@ -205,10 +205,22 @@ OverseerHandler::OverseerHandler() { this->os = new Overseer(); } +void OverseerHandler::populateSystemValues() { + this->os->populateSystemValues(); +} + void OverseerHandler::openControlPanel() { this->os->openControlPanel(); } +ProcessedNativeEvent OverseerHandler::processTopLevelWindowMessage(void* msg) { + return this->os->processTopLevelWindowMessage(msg); +} + +bool OverseerHandler::isLightMode() { + return this->os->isLightMode(); +} + std::vector OverseerHandler::getPlaybackEndpoints() { return this->os->getPlaybackEndpoints(); } diff --git a/src/cont/contclasses.h b/src/cont/contclasses.h index e147394..cb3230a 100644 --- a/src/cont/contclasses.h +++ b/src/cont/contclasses.h @@ -97,7 +97,10 @@ class OverseerHandler { public: OverseerHandler(); + void populateSystemValues(); void openControlPanel(); + ProcessedNativeEvent processTopLevelWindowMessage(void* msg); + bool isLightMode(); //void setChangeFrontDefaultsFunction(std::function changeFrontDefaults); //void changeFrontDefaultsCallback(Roles role, std::wstring endpointId); diff --git a/src/global.h b/src/global.h index 4c22755..9cecbfd 100644 --- a/src/global.h +++ b/src/global.h @@ -36,6 +36,11 @@ #define LSTRING_UNNAMED_SESSION L"Unnamed session" //INIT BACK +enum ProcessedNativeEvent { + NONE = 0, + LIGHT_MODE = (1 << 0), +}; + enum AudioChannel { CHANNEL_LEFT = (1 << 0), CHANNEL_RIGHT = (1 << 1), diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 8692897..835daff 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -1,7 +1,29 @@ #include "qtclasses.h" #include "meterslider.h" + #define POLLING_RATE 2 +bool DarkModeEventFilter::nativeEventFilter(const QByteArray &eventType, void *message, qintptr *) { + if (eventType == "windows_generic_MSG") { + ProcessedNativeEvent event = osh->processTopLevelWindowMessage(message); + switch(event) { + case LIGHT_MODE: + StylingHelper::setBackgroundColor(osh->isLightMode()); + return true; + break; + default: + break; + } + /* + * if ([event type] == NSKeyDown) { + * // Handle key event + * qDebug() << QString::fromNSString([event characters]); + * } + */ + } + return false; +} + template CustomWidgetEvent::CustomWidgetEvent(QEvent::Type type, T payload) : QEvent(type){ this->payload = payload; diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index 8b77923..3b60cff 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -22,6 +22,12 @@ enum CustomQEvent { EndpointRoleChange = 1007 }; +class DarkModeEventFilter : public QAbstractNativeEventFilter { + +public: + bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *) override; +}; + template class CustomWidgetEvent : public QEvent { @@ -31,10 +37,6 @@ public: }; //Q_DECLARE_METATYPE(EndpointWidgetEvent) - -//todo: TEST. TEST. -//#include "qtvisuals.h" - class ExtendedCheckBox : public QCheckBox { Q_OBJECT protected: @@ -47,7 +49,6 @@ public: //B(int x) : A(x) { } }; - class SessionWidget : public QWidget { Q_OBJECT diff --git a/src/qt/qtcommon.h b/src/qt/qtcommon.h index b7a7245..afd7929 100644 --- a/src/qt/qtcommon.h +++ b/src/qt/qtcommon.h @@ -42,6 +42,8 @@ #include #include #include +#include +#include //#include //#include /* @@ -62,6 +64,20 @@ enum CustomComplexControl { }; namespace StylingHelper { + + static inline void setBackgroundColor(bool lightMode) { + //QApplication* app = (QApplication*)QApplication::instance(); + QPalette pal = QGuiApplication::palette(); + if(lightMode) { + pal.setColor(QPalette::Window, Qt::white); + pal.setColor(QPalette::WindowText, Qt::black); + } else { + pal.setColor(QPalette::Window, Qt::black); + pal.setColor(QPalette::WindowText, Qt::white); + } + QGuiApplication::setPalette(pal); + } + static inline QLatin1String operator""_L1(const char* ch, uint64_t) { return QLatin1String(ch); } diff --git a/src/qtestmain.cpp b/src/qtestmain.cpp index 490608d..387df74 100644 --- a/src/qtestmain.cpp +++ b/src/qtestmain.cpp @@ -46,14 +46,6 @@ int main (int argc, char* argv[]) { * log_debugcpp(a.toStdString()); * } */ - - QApplication::setStyle(new MixerStyle(QStyleFactory::create("Fusion"))); - //QApplication::setFont(font); - QPalette palette = QGuiApplication::palette(); - //todo: ez full apply os accent colorw - palette.setColor(QPalette::Active, QPalette::Highlight, QColor(255, 192, 203, 200));//QColor(30,30,30,100)); - QGuiApplication::setPalette(palette); - initialize_file_log(); atexit(closeDebugFileLog); @@ -64,7 +56,17 @@ int main (int argc, char* argv[]) { startSingleInstanceServer("Mixer"); else exit(0); + + QApplication::setStyle(new MixerStyle(QStyleFactory::create("Fusion"))); + //QApplication::setFont(font); + //QPalette palette = QGuiApplication::palette(); + //todo: ez full apply os accent colorw + //palette.setColor(QPalette::Active, QPalette::Highlight, QColor(255, 192, 203, 200)); + //QColor(30,30,30,100)); + //QGuiApplication::setPalette(palette); osh = new OverseerHandler(); + osh->populateSystemValues(); + StylingHelper::setBackgroundColor(osh->isLightMode()); //qRegisterMetaType(); //INIT CONT @@ -78,6 +80,10 @@ int main (int argc, char* argv[]) { MainWindow window = MainWindow(); //window.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::QSizePolicy::MinimumExpanding) QApplication::setQuitOnLastWindowClosed(false); + + DarkModeEventFilter* darkMode = new DarkModeEventFilter(); + QAbstractEventDispatcher::instance()->installNativeEventFilter(darkMode); + /* * QFile styleFile(":/assets/style.qss"); * styleFile.open(QFile::ReadOnly); From 60890cecad9ff2172b8be3916a7da92ae6d6c781 Mon Sep 17 00:00:00 2001 From: Hane Date: Tue, 26 Nov 2024 17:11:01 +0100 Subject: [PATCH 55/78] dark/light mode & accent color --- src/back/backlasses.cpp | 21 ++++++++-- src/back/backlasses.h | 4 +- src/cont/contclasses.cpp | 4 ++ src/cont/contclasses.h | 1 + src/global.h | 4 +- src/qt/qtclasses.cpp | 88 ++++++++++++++++++++++++++++++++++++---- src/qt/qtclasses.h | 2 + src/qt/qtcommon.h | 23 +++++++++++ src/qt/qtvisuals.h | 75 +++++++++++++++++++++------------- 9 files changed, 178 insertions(+), 44 deletions(-) diff --git a/src/back/backlasses.cpp b/src/back/backlasses.cpp index 970d79f..e51c011 100644 --- a/src/back/backlasses.cpp +++ b/src/back/backlasses.cpp @@ -653,7 +653,7 @@ Overseer::Overseer() : epsc(this){ } void Overseer::populateSystemValues() { - updateDarkMode(); + updateColors(); } void Overseer::openControlPanel() { @@ -688,7 +688,7 @@ ProcessedNativeEvent Overseer::processTopLevelWindowMessage(void* msg) { switch(message->message) { case WM_SETTINGCHANGE: if(!wcscmp(((wchar_t*)message->lParam), L"ImmersiveColorSet")) - return updateDarkMode(); + return updateColors(); break; default: return ProcessedNativeEvent::NONE; @@ -699,23 +699,36 @@ ProcessedNativeEvent Overseer::processTopLevelWindowMessage(void* msg) { #endif } -ProcessedNativeEvent Overseer::updateDarkMode(){ +ProcessedNativeEvent Overseer::updateColors() { // DwmGetColorizationColor( WM_DWMCOLORIZATIONCOLORCHANGED DWORD value = 0; DWORD size = sizeof(DWORD); LSTATUS result; + //Theme bg color result = RegGetValueW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", L"AppsUseLightTheme", RRF_RT_REG_DWORD, nullptr, &value, &size); this->lightMode = (bool)value; - return ProcessedNativeEvent::LIGHT_MODE; + + //Accent color + result = RegGetValueW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\DWM", L"ColorizationColor", RRF_RT_REG_DWORD, nullptr, &value, &size); + + if (result == ERROR_SUCCESS) { + this->accentColor = value; + } else this->accentColor = 0xffffffff; + + return ProcessedNativeEvent::COLORS; } bool Overseer::isLightMode() { return this->lightMode; } +uint32_t Overseer::getAccentColor() { + return this->accentColor; +} + NGuid Overseer::getGuid() { return guid; } diff --git a/src/back/backlasses.h b/src/back/backlasses.h index 9edc779..f5fba61 100644 --- a/src/back/backlasses.h +++ b/src/back/backlasses.h @@ -113,8 +113,9 @@ class Overseer { void populateSystemValues(); void openControlPanel(); ProcessedNativeEvent processTopLevelWindowMessage(void* msg); - ProcessedNativeEvent updateDarkMode(); + ProcessedNativeEvent updateColors(); bool isLightMode(); + uint32_t getAccentColor(); std::vector getPlaybackEndpoints(); std::vector getCaptureEndpoints(); @@ -138,6 +139,7 @@ class Overseer { NGuid guid; bool lightMode; + uint32_t accentColor; IMMDeviceEnumerator *deviceEnumerator; EndpointSituationCallback epsc; diff --git a/src/cont/contclasses.cpp b/src/cont/contclasses.cpp index 75df484..71f981d 100644 --- a/src/cont/contclasses.cpp +++ b/src/cont/contclasses.cpp @@ -221,6 +221,10 @@ bool OverseerHandler::isLightMode() { return this->os->isLightMode(); } +uint32_t OverseerHandler::getAccentColor() { + return this->os->getAccentColor(); +} + std::vector OverseerHandler::getPlaybackEndpoints() { return this->os->getPlaybackEndpoints(); } diff --git a/src/cont/contclasses.h b/src/cont/contclasses.h index cb3230a..e3ebab6 100644 --- a/src/cont/contclasses.h +++ b/src/cont/contclasses.h @@ -101,6 +101,7 @@ public: void openControlPanel(); ProcessedNativeEvent processTopLevelWindowMessage(void* msg); bool isLightMode(); + uint32_t getAccentColor(); //void setChangeFrontDefaultsFunction(std::function changeFrontDefaults); //void changeFrontDefaultsCallback(Roles role, std::wstring endpointId); diff --git a/src/global.h b/src/global.h index 9cecbfd..4d684a9 100644 --- a/src/global.h +++ b/src/global.h @@ -37,8 +37,8 @@ //INIT BACK enum ProcessedNativeEvent { - NONE = 0, - LIGHT_MODE = (1 << 0), + NONE = 0, + COLORS = (1 << 0), }; enum AudioChannel { diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 835daff..48660f1 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -7,19 +7,27 @@ bool DarkModeEventFilter::nativeEventFilter(const QByteArray &eventType, void *m if (eventType == "windows_generic_MSG") { ProcessedNativeEvent event = osh->processTopLevelWindowMessage(message); switch(event) { - case LIGHT_MODE: + case COLORS: StylingHelper::setBackgroundColor(osh->isLightMode()); + + //Update accent color across entire app + { + uint32_t color = osh->getAccentColor(); + uint8_t r, g, b, a; + if (StylingHelper::argbToDiscreteValues(osh->getAccentColor(), &r, &g, &b, &a)) { + const QWidgetList topLevelWidgets = QApplication::topLevelWidgets(); + for (QWidget *widget : topLevelWidgets) { + if(qobject_cast(widget)) { + ((MainWindow*)widget)->updateColor(r, g, b, a); + } + } + } + } return true; break; default: break; } - /* - * if ([event type] == NSKeyDown) { - * // Handle key event - * qDebug() << QString::fromNSString([event characters]); - * } - */ } return false; } @@ -124,8 +132,8 @@ void MeterSlider::paintEvent(QPaintEvent *event) { sliderComplex2.subControls |= QStyle::SC_SliderTickmarks; QPainter painter(this); - QStyle* stle = QApplication::style(); - stle->drawComplexControl((QStyle::ComplexControl)CC_MeterSlider, &sliderComplex2, &painter, this); + QStyle* style = QApplication::style(); + style->drawComplexControl((QStyle::ComplexControl)CC_MeterSlider, &sliderComplex2, &painter, this); //Q_D(QSlider); @@ -252,6 +260,13 @@ QRect MainWindow::setSizePosition(QScreen* screen, int width, int height) { } } +void MainWindow::updateColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + QColor color(r, g, b, a); + QPalette pal = QPalette(color); + pal.setColor(QPalette::Highlight, color); + scrollArea->verticalScrollBar()->setPalette(pal); +} + void MainWindow::compose() { //todo: invalidate layout when adding sessions with window open //We need dynamically added child widgets to expand so that we know their height @@ -1022,6 +1037,10 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); scrollArea->setContentsMargins(QMargins(0, 0, 0, 0)); + + //ScrollBarFocusFilter focusFilter = new ScrollBarFocusFilter(this); + scrollArea->verticalScrollBar()->installEventFilter(this); + scrollArea->horizontalScrollBar()->installEventFilter(this); //scrollArea->verticalScrollBar()->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); //scrollArea->verticalScrollBar()->setSingleStep(1); @@ -1070,6 +1089,13 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { this->recentlyClosedTimer->start(); } }); + + /* + * Set accent color + */ + uint8_t a, r, g, b; + if (StylingHelper::argbToDiscreteValues(osh->getAccentColor(), &r, &g, &b, &a)) + this->updateColor(r, g, b, a); /* * Set of function callback definitons for EndpointSituationCallback @@ -1090,6 +1116,50 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { } +bool MainWindow::eventFilter(QObject *object, QEvent *event) { + //QScrollBar* widgetCast = qobject_cast(object); + if (object == scrollArea->verticalScrollBar()) { + [[maybe_unused]] QEvent::Type tipo = event->type(); + if (event->type() == QHoverEvent::HoverEnter) { + log_debugcpp("Hover event: "); + log_debugcpp("\tGlobal pos: " + + std::to_string(((QHoverEvent*)event)->globalPosition().x()) + + ", " + + std::to_string(((QHoverEvent*)event)->globalPosition().y())); + log_debugcpp("\tNewWid pos: " + + std::to_string(((QHoverEvent*)event)->position().x()) + + ", " + + std::to_string(((QHoverEvent*)event)->position().y())); + log_debugcpp("\tOldWid pos: " + + std::to_string(((QHoverEvent*)event)->oldPos().x()) + + ", " + + std::to_string(((QHoverEvent*)event)->oldPos().y())); + + } + if (event->type() == QScrollEvent::ScrollFinished) { + QHoverEvent* hoverEvent = new QHoverEvent(QEvent::HoverEnter, + scrollArea->verticalScrollBar()->mapFromGlobal(QCursor::pos()), + QCursor::pos(), + scrollArea->verticalScrollBar()->mapFromGlobal(QCursor::pos())); + log_debugcpp("ScrollFinished event: "); + log_debugcpp("\tGlobal pos: " + + std::to_string(QCursor::pos().x()) + + ", " + + std::to_string(QCursor::pos().y())); + log_debugcpp("\tWidget pos: " + + std::to_string(scrollArea->verticalScrollBar()->mapFromGlobal(QCursor::pos()).x()) + + ", " + + std::to_string(scrollArea->verticalScrollBar()->mapFromGlobal(QCursor::pos()).y())); + QCoreApplication::instance()->postEvent(scrollArea->verticalScrollBar(), hoverEvent); + /* + * QCoreApplication::instance()->postEvent(this, new CustomWidgetEvent((QEvent::Type)CustomQEvent::SessionWidgetObsolete, sessionHandler)); + */ + //scrollArea->setFocus(Qt::MouseFocusReason); + return false; + }} + return false; +} + void MainWindow::flushRoleChanges() { //TODO: bucket list deque std::pair change = roleBucketList.front(); diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index 3b60cff..349b2d3 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -182,6 +182,7 @@ public: MainWindow(QWidget *parent = nullptr); void reloadEndpointWidgets(); void compose(); + void updateColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a); protected: void closeEvent(QCloseEvent *event) override; @@ -200,6 +201,7 @@ private slots: private: //std::vector *ephs; + bool eventFilter(QObject *object, QEvent *event); void flushRoleChanges(); void changeFrontDefaults(Roles role, EndpointWidget* newDef, EndpointWidget* oldDef); diff --git a/src/qt/qtcommon.h b/src/qt/qtcommon.h index afd7929..b059504 100644 --- a/src/qt/qtcommon.h +++ b/src/qt/qtcommon.h @@ -77,6 +77,29 @@ namespace StylingHelper { } QGuiApplication::setPalette(pal); } + + static inline void setAccentColor(bool lightMode) { + //QApplication* app = (QApplication*)QApplication::instance(); + QPalette pal = QGuiApplication::palette(); + if(lightMode) { + pal.setColor(QPalette::Window, Qt::white); + pal.setColor(QPalette::WindowText, Qt::black); + } else { + pal.setColor(QPalette::Window, Qt::black); + pal.setColor(QPalette::WindowText, Qt::white); + } + QGuiApplication::setPalette(pal); + } + + static inline bool argbToDiscreteValues(uint32_t color, uint8_t *r, uint8_t *g, uint8_t *b, uint8_t *a) { + if(!r || !g || !b || !a) return false; + + *a = (color >> 24) & 0xFF; + *r = (color >> 16) & 0xFF; + *g = (color >> 8) & 0xFF; + *b = color & 0xFF; + return true; + } static inline QLatin1String operator""_L1(const char* ch, uint64_t) { return QLatin1String(ch); diff --git a/src/qt/qtvisuals.h b/src/qt/qtvisuals.h index 3095c45..0db58e8 100644 --- a/src/qt/qtvisuals.h +++ b/src/qt/qtvisuals.h @@ -6,6 +6,7 @@ using namespace StylingHelper; class MixerStyle : public QProxyStyle { + public: using QProxyStyle::QProxyStyle; @@ -448,8 +449,8 @@ public: //QColor arrowColor = QColor(188,143,143,100); arrowColor.setAlpha(160); - const QColor bgColor = backgroundColor(option->palette, widget); - const bool isDarkBg = bgColor.red() < 128 && bgColor.green() < 128 && bgColor.blue() < 128; + const QColor appBgColor = QGuiApplication::palette().window().color(); + const bool isDarkBg = appBgColor.red() < 128 && appBgColor.green() < 128 && appBgColor.blue() < 128; if (transient) { if (horizontal) { @@ -482,17 +483,17 @@ public: gradient.setColorAt(0.9, buttonColor.darker(105)); gradient.setColorAt(1, buttonColor.darker(107)); } else { - gradient.setColorAt(0, bgColor.lighter(157)); - gradient.setColorAt(0.1, bgColor.lighter(155)); - gradient.setColorAt(0.9, bgColor.lighter(155)); - gradient.setColorAt(1, bgColor.lighter(157)); + gradient.setColorAt(0, appBgColor.lighter(157)); + gradient.setColorAt(0.1, appBgColor.lighter(155)); + gradient.setColorAt(0.9, appBgColor.lighter(155)); + gradient.setColorAt(1, appBgColor.lighter(157)); } - + painter->save(); //painter->fillRect(rect, gradient); painter->setPen(Qt::NoPen); painter->setPen(alphaOutline); - + QColor subtleEdge = alphaOutline; subtleEdge.setAlpha(40); painter->setPen(subtleEdge); @@ -505,23 +506,27 @@ public: } QRect pixmapRect = scrollBarSlider; - QLinearGradient gradient(pixmapRect.center().x(), pixmapRect.top(), - pixmapRect.center().x(), pixmapRect.bottom()); - if (!horizontal) - gradient = QLinearGradient(pixmapRect.left(), pixmapRect.center().y(), - pixmapRect.right(), pixmapRect.center().y()); + QColor highlightColor = option->palette.highlight().color(); + /* + * QLinearGradient gradient(pixmapRect.center().x(), pixmapRect.top(), + * pixmapRect.center().x(), pixmapRect.bottom()); + * if (!horizontal) + * gradient = QLinearGradient(pixmapRect.left(), pixmapRect.center().y(), + * pixmapRect.right(), pixmapRect.center().y()); + */ - QLinearGradient highlightedGradient = gradient; + //QLinearGradient highlightedGradient = gradient; - QColor midColor2 = mergedColors(gradientStartColor, gradientStopColor, 40); - gradient.setColorAt(0, calculateButtonColor(option->palette).lighter(108)); - gradient.setColorAt(1, calculateButtonColor(option->palette)); - - highlightedGradient.setColorAt(0, gradientStartColor.darker(102)); - highlightedGradient.setColorAt(1, gradientStopColor.lighter(102)); + QColor heldColor = 0xc4e3d7e0;//Qt::gray;//osh->isLightMode() ? Qt::white : Qt::black; + //gradient.setColorAt(0, option->palette.highlight().color()); + //gradient.setColorAt(1, option->palette.highlight().color()); + + //highlightedGradient.setColorAt(0, gradientStartColor.darker(102)); + //highlightedGradient.setColorAt(1, gradientStopColor.lighter(102)); // Paint slider if (scrollBar->subControls & SC_ScrollBarSlider) { + log_debugcpp("Final scrollbar paint if"); if (transient) { QRect rect = scrollBarSlider.adjusted(horizontal ? 1 : 2, horizontal ? 2 : 1, -1, -1); painter->setPen(Qt::NoPen); @@ -535,14 +540,20 @@ public: } else { QRect pixmapRect = scrollBarSlider; painter->setPen(QPen(alphaOutline)); - if (option->state & State_Sunken && scrollBar->activeSubControls & SC_ScrollBarSlider) - painter->setBrush(midColor2); - else if (option->state & State_MouseOver && scrollBar->activeSubControls & SC_ScrollBarSlider) - painter->setBrush(highlightedGradient); - else if (!isDarkBg) - painter->setBrush(gradient); - else - painter->setBrush(midColor2); + if (option->state & State_Sunken + && scrollBar->activeSubControls & SC_ScrollBarSlider) + painter->setBrush(heldColor); + else if (option->state & State_MouseOver + && scrollBar->activeSubControls & SC_ScrollBarSlider) { + painter->setBrush(highlightColor); + } + else //if (!isDarkBg) + + if(!isDarkBg) + painter->setBrush(Qt::black); + else painter->setBrush(Qt::white); + //else + // painter->setBrush(heldColor); painter->drawRoundedRect(pixmapRect.adjusted(horizontal ? -1 : 0, horizontal ? 0 : -1, horizontal ? 0 : -1, horizontal ? -1 : 0), 5, 5); @@ -675,6 +686,13 @@ private: return gradient; } + QColor highlightedOutline(const QPalette &pal) const { + QColor highlightedOutline = highlight(pal);//.darker(25);//QColor(Qt::green); + if (highlightedOutline.value() > 160) + highlightedOutline.setHsl(highlightedOutline.hue(), highlightedOutline.saturation(), 160); + return highlightedOutline; + } + QColor calculateButtonColor(const QPalette &pal) const { QColor buttonColor = pal.button().color(); int val = qGray(buttonColor.rgb()); @@ -835,3 +853,4 @@ private: */ //void MixerStyle:: + From 8e93120555a35458a2289fc5cfb099c62a1bf8da Mon Sep 17 00:00:00 2001 From: Hane Date: Wed, 27 Nov 2024 20:46:02 +0100 Subject: [PATCH 56/78] correct size on all resolutions --- src/qt/qtclasses.cpp | 52 +++++++++++++++----------------------------- src/qt/qtclasses.h | 2 +- 2 files changed, 19 insertions(+), 35 deletions(-) diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 48660f1..78d99f0 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -282,31 +282,24 @@ void MainWindow::compose() { screenRes.getCoords(&srx1, &sry1, &srx2, &sry2); log_debugcpp("(for Percentage) Screen Res: " + std::to_string(srx1) + " " + std::to_string(srx2)+" " + std::to_string(sry1) + " " + std::to_string(sry2)); - dpr = screen->devicePixelRatio(); - log_to_file("dpr: %f \n", dpr); - uint64_t windowWidth = ((uint64_t)std::abs(screenRes.width()) * widthRatio) * dpr; + + uint64_t windowWidth = (((uint64_t)std::abs(screenRes.width())) * widthRatio); uint64_t screenHeight = (uint64_t)std::abs(screenRes.height()); log_debugcpp("Window Width: " + std::to_string(windowWidth)); + log_to_file("Window Width: %d \n", windowWidth); - QFontMetrics fontMetrics = this->fontMetrics(); - int maxCharWidth = fontMetrics.maxWidth(); - int charHeight = fontMetrics.height(); - //QSize QFontMetrics::size(int flags, const QString &text, int tabStops = 0, int *tabArray = nullptr) const this->createLayout(new QGridLayout()); - //scrollArea->verticalScrollBar()->setMinimumWidth(windowWidth * (widthRatio)); - //scrollArea->verticalScrollBar()->setMaximumWidth(windowWidth * (widthRatio)); - /* - * this->setAttribute(Qt::WA_DontShowOnScreen, true); - * this->show(); - * this->widget->layout()->update(); - * this->hide(); - * this->setAttribute(Qt::WA_DontShowOnScreen, false); - */ - + this->scrollArea->setMaximumWidth((int)windowWidth); + this->scrollArea->setMinimumWidth((int)windowWidth); + this->mainMenuBar->setMinimumWidth((int)windowWidth); + this->mainMenuBar->setMaximumWidth((int)windowWidth); for (auto *epw : ews) { if (!epw) continue; - epw->calculateSize(windowWidth, screenHeight); + + epw->calculateSize(windowWidth - scrollArea->verticalScrollBar()->sizeHint().width() + - widgetLayout->contentsMargins().left() + , screenHeight); log_debugcpp("epw loop"); log_debugcpp("epw roles: " + print_as_binary((epw->getEndpointHandler()->getRoles()))); //std::bitset content = @@ -324,7 +317,6 @@ void MainWindow::compose() { ews[i]->layout()->getContentsMargins(&left, &top, &right, &bottom); windowHeight += ews[i]->sizeHint().height(); windowHeight += top * 3; - //windowHeight += bottom; log_debugcpp("windowHeight loop: " + std::to_string(windowHeight)); } windowHeight += mainMenuBar->sizeHint().height(); @@ -522,16 +514,6 @@ ChannelWidget::ChannelWidget(uint32_t channelCount, EndpointHandler* eph, QWidge this->setLayout(widgetLayout); } -/* - * QSize ChannelWidget::minimumSizeHint() const { - * return minimum; - * } - * - * void ChannelWidget::setMinimum(QSize minimum) { - * this->minimum = minimum; - * } - */ - void ChannelWidget::updateChannel(int channel) { this->channelSliders.at(channel)->blockSignals(true); this->channelSliders.at(channel)->setValue((int)((eph->getCallbackInfo()->channelVolumes[channel] + roundingFactor) * 100)); @@ -553,6 +535,7 @@ EndpointWidget::EndpointWidget(EndpointHandler* eph, QWidget *parent, uint64_t i this->eph->setState(EndpointState::ENDPOINT_ACTIVE, idx); this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); widgetLayout = new QGridLayout(this); + //this->setContentsMargins(0, 0, 0, 0); //this->setLayout(widgetLayout); log_debugcpp("epw main layout parent: " + std::to_string((intptr_t)(widgetLayout->parent()))); if (parent == nullptr) { log_debugcpp("ayooooo?"); } @@ -796,7 +779,7 @@ void MainWindow::removeEndpointWidget(CustomWidgetEvent* ev){ } void MainWindow::addEndpointWidget(CustomWidgetEvent* ev){ - EndpointWidget* epw = new EndpointWidget(ev->payload, widget, this->ews.size()); + EndpointWidget* epw = new EndpointWidget(ev->payload, containerWidget, this->ews.size()); //epw->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); //this->widgetLayout->addWidget(epw); ews.push_back(epw); @@ -952,7 +935,7 @@ void MainWindow::createLayout(QGridLayout *newLayout) { log_debugcpp("createLayout"); widgetLayout->removeItem(lastRowSpacer); delete this->widgetLayout; - widget->setLayout(newLayout); + containerWidget->setLayout(newLayout); this->widgetLayout = newLayout; bool areEndpoints = false; @@ -1011,7 +994,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { ewsUpdateTimer = new QTimer(this); recentlyClosedTimer = new QTimer(this); - widget = new QWidget(); + containerWidget = new QWidget(); + //widget->setContentsMargins(0, 0, 0, 0); widgetLayout = new QGridLayout(); trayIcon = new QSystemTrayIcon(); trayIconMenu = new QMenu(); @@ -1032,7 +1016,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { */ scrollArea = new QScrollArea(this); //widget->setAttribute(Qt::WA_TranslucentBackground); - scrollArea->setWidget(widget); + scrollArea->setWidget(containerWidget); scrollArea->setWidgetResizable(true); scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); @@ -1297,7 +1281,7 @@ void MainWindow::reloadEndpointWidgets() { if (osh->getPlaybackEndpointHandlers().at(i)->getState() == EndpointState::ENDPOINT_ACTIVE){ log_debugcpp("EPWidget creation"); //osh->getPlaybackEndpointHandlers().at(i)->getCallbackInfo()->caller = osh->getGuid(); - EndpointWidget *epw = new EndpointWidget(osh->getPlaybackEndpointHandlers().at(i), widget); + EndpointWidget *epw = new EndpointWidget(osh->getPlaybackEndpointHandlers().at(i), containerWidget); log_wdebugcpp(L"epw name: " + epw->getEndpointHandler()->getName()); if ((epw->getEndpointHandler()->getRoles() == Roles::ROLE_ALL) || diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index 349b2d3..36a0add 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -207,7 +207,7 @@ private: std::vector ews; std::vector> roleBucketList; - QWidget *widget; + QWidget *containerWidget; QGridLayout *widgetLayout; QSystemTrayIcon *trayIcon; From 8e07b1efddabeca3157fead56fd0165c33b705b2 Mon Sep 17 00:00:00 2001 From: Hane Date: Tue, 3 Dec 2024 21:13:42 +0100 Subject: [PATCH 57/78] wip: ini parse baseline --- qtest.pro | 8 +- src/back/backsessionclasses.h | 1 - src/back/msinclude.h | 5 +- src/global.h | 11 ++ src/qt/qtclasses.cpp | 53 ++++++--- src/qt/qtclasses.h | 8 +- src/qtestmain.cpp | 14 ++- src/settings.cpp | 208 ++++++++++++++++++++++++++++++++++ src/settings.h | 75 ++++++++++++ 9 files changed, 356 insertions(+), 27 deletions(-) create mode 100644 src/settings.cpp create mode 100644 src/settings.h diff --git a/qtest.pro b/qtest.pro index ba2c13e..be04829 100644 --- a/qtest.pro +++ b/qtest.pro @@ -1,8 +1,8 @@ -QMAKE_CXXFLAGS += --target=x86_64-w64-mingw32 -g -gcodeview -O0 -Werror=return-type +QMAKE_CXXFLAGS += --target=x86_64-w64-mingw32 -g -gcodeview -O0 -Werror=return-type QMAKE_LFLAGS += --target=x86_64-w64-mingw32 -g -Wl,-pdb= -v LIBS += -LC:/capybara/libclang/x86_64-w64-mingw32/lib -lWinmm -lodbc32 -lodbccp32 -luuid -loleaut32 -lole32 -lshell32 -ladvapi32 -lcomdlg32 -lwinspool -lgdi32 -luser32 -lkernel32 -lpropsys -static -stdlib=libc++ -lunwind #"kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" -luuid -loleaut32 -lole32 -lshell32 -ladvapi32 -lcomdlg32 -lwinspool -lgdi32 -luser32 -lkernel32 -DEFINES += DEBUG QT_LOGGING_TO_CONSOLE=1 WIN32_LEAN_AND_MEAN +DEFINES += DEBUG QT_LOGGING_TO_CONSOLE=1 WIN32_LEAN_AND_MEAN _WIN32_WINNT=0x0602 CONFIG += debug QT += widgets network @@ -10,8 +10,8 @@ INCLUDEPATH += "$$PWD\src" "$$PWD\src\qt" "$$PWD\src\back" "$$PWD\src\back\reimp DESTPATH += "$$PWD\src" "$$PWD\src\qt" "$$PWD\src\back" "$$PWD\src\back\reimpl" "$$PWD\src\cont" VPATH += "$$PWD\src" "$$PWD\src\qt" "$$PWD\src\back" "$$PWD\src\back\reimpl" "$$PWD\src\cont" -SOURCES += qtestmain.cpp qtclasses.cpp backlasses.cpp backsessionclasses.cpp contclasses.cpp contsessionclasses.cpp -HEADERS += qtclasses.h backlasses.h backsessionclasses.h contclasses.h contsessionclasses.h global.h debug.h backfuncs.h ipolicyconfig.h msinclude.h meterslider.h qtvisuals.h +SOURCES += qtestmain.cpp qtclasses.cpp backlasses.cpp backsessionclasses.cpp contclasses.cpp contsessionclasses.cpp settings.cpp +HEADERS += qtclasses.h backlasses.h backsessionclasses.h contclasses.h contsessionclasses.h global.h debug.h backfuncs.h ipolicyconfig.h msinclude.h meterslider.h qtvisuals.h settings.h RESOURCES = assets.qrc RC_ICONS += assets/logo.ico diff --git a/src/back/backsessionclasses.h b/src/back/backsessionclasses.h index 3f73f3d..ae87f0a 100644 --- a/src/back/backsessionclasses.h +++ b/src/back/backsessionclasses.h @@ -4,7 +4,6 @@ #include "global.h" #include "contclasses.h" -#include class Endpoint; class SessionStateCallback : public IAudioSessionEvents { diff --git a/src/back/msinclude.h b/src/back/msinclude.h index f9ab1b4..633e079 100644 --- a/src/back/msinclude.h +++ b/src/back/msinclude.h @@ -1,7 +1,5 @@ #pragma once -#define _WIN32_WINNT 0x0A00 - #include //done by qt by def #define UNICODE @@ -9,6 +7,8 @@ #include #include #include +#include +#include #include #include #include @@ -28,6 +28,7 @@ #include #include #include +#include #include "ipolicyconfig.h" #include "audiometerinfo.h" diff --git a/src/global.h b/src/global.h index 4d684a9..10f45ab 100644 --- a/src/global.h +++ b/src/global.h @@ -10,8 +10,10 @@ #include #include #include +#include #include "debug.h" +//#include "settings.h" //TODO: Use tr();? QTranslator #define STRING_MUTE "Mute" @@ -30,12 +32,17 @@ #define STRING_CP "Open Control Panel" #define STRING_ABOUT "About" #define STRING_STARTUP "Run at startup" +#define STRING_CHANNELS "Show endpoint channels" #define STRING_NOENDPOINT "No active endpoints" #define LSTRING_UNNAMED_SESSION L"Unnamed session" + + //INIT BACK + + enum ProcessedNativeEvent { NONE = 0, COLORS = (1 << 0), @@ -92,7 +99,11 @@ struct NGuid { /* }while (i < 8); */ /* } */ }; +namespace ini { + class UserSettings; +} +extern ini::UserSettings *set; class OverseerHandler; extern OverseerHandler *osh; diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 78d99f0..86c9a22 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -296,7 +296,7 @@ void MainWindow::compose() { this->mainMenuBar->setMaximumWidth((int)windowWidth); for (auto *epw : ews) { if (!epw) continue; - + epw->updateChannelsVisibility(); epw->calculateSize(windowWidth - scrollArea->verticalScrollBar()->sizeHint().width() - widgetLayout->contentsMargins().left() , screenHeight); @@ -605,8 +605,12 @@ EndpointWidget::EndpointWidget(EndpointHandler* eph, QWidget *parent, uint64_t i if(epChannelCount > 1) { cw = new ChannelWidget(epChannelCount, eph, nullptr); cw->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - //cw->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); widgetLayout->addWidget(cw, row++, 0, 1, 4 /*colmax*/, Qt::AlignTop); + cw->setVisible(false); + char* const channelSettings = set->getValue("show_channels"); + if(channelSettings && !(strcmp(channelSettings, "true"))){ + cw->setVisible(true); + } } /* @@ -889,6 +893,13 @@ void EndpointWidget::updateMainVolume(int newValue){ * } */ +void EndpointWidget::updateChannelsVisibility() { + char* const channelSettings = set->getValue("show_channels"); + if(channelSettings && !(strcmp(channelSettings, "true"))){ + cw->setVisible(true); + } else cw->setVisible(false); +} + EndpointHandler* EndpointWidget::getEndpointHandler(){ return this->eph; } @@ -915,19 +926,36 @@ std::map EndpointWidget::getDefaultRolesWidgets() { HeaderWidget::HeaderWidget(QWidget *parent) : QWidget(parent) { widgetLayout = new QGridLayout(this); - QString text = "&" STRING_ABOUT; - about = new QPushButton(text, this); + QString text; //= "&" STRING_ABOUT; + //about = new QPushButton(text, this); #ifdef WIN32 text = "&" STRING_CP; openCP = new QPushButton(text, this); connect(openCP, &QPushButton::clicked, [](){ osh->openControlPanel(); }); - text = "&" STRING_STARTUP; - startup = new QPushButton(text, this); + + text = "&" STRING_CHANNELS; + channels = new QCheckBox(text, this); + char* const channelSettings = set->getValue("show_channels"); + if(channelSettings && !(strcmp(channelSettings, "true"))){ + channels->setChecked(true); + } + connect(channels, &QCheckBox::stateChanged, [this, parent](){ + set->setValue("show_channels", channels->isChecked(), sizeof("show_channels")); + if(parent) + QCoreApplication::instance()->postEvent + (parent, new QEvent((QEvent::Type)CustomQEvent::RecomposeMainWindow)); + }); - widgetLayout->addWidget(openCP , 0, 0); - widgetLayout->addWidget(startup, 0, 1); + + text = "&" STRING_STARTUP; + startup = new QCheckBox(text, this); + //connect(openCP, &QPushButton::clicked, [](){ osh->openControlPanel(); }); + + widgetLayout->addWidget(openCP , 0, 0, 2, 2); + widgetLayout->addWidget(channels, 0, 2, 1, 2); + widgetLayout->addWidget(startup , 1, 2, 1, 2); #endif - widgetLayout->addWidget(about , 0, 2); + //widgetLayout->addWidget(about , 0, 2); this->setLayout(widgetLayout); } @@ -1034,19 +1062,12 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { * Menu bar code */ mainMenuBar = new QToolBar(this); - /* - * QPalette pal; - * pal.setColor(QPalette::Window, Qt::transparent); - * mainMenuBar->setPalette(pal); - */ hw = new HeaderWidget(this); mainMenuBar->addWidget(hw); mainMenuBar->setMovable(false); this->addToolBar(Qt::BottomToolBarArea, mainMenuBar); reloadEndpointWidgets(); - //scrollArea->setMinimumWidth(ews.at(0)->minimumWidth()); - log_debugcpp(std::to_string(scrollArea->minimumWidth())); /* * Tray Icon code diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index 36a0add..5531921 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -2,6 +2,7 @@ #include "qtcommon.h" #include "contclasses.h" +#include "settings.h" class MeterSlider; @@ -100,7 +101,7 @@ public: EndpointWidget(EndpointHandler* eph, QWidget *parent = nullptr, uint64_t idx = INT_MAX); //QSize minimumSizeHint() const override; //void setMinimum(uint64_t height, double heightRatio); - + void updateChannelsVisibility(); EndpointHandler* getEndpointHandler(); std::map getDefaultRolesWidgets(); @@ -167,10 +168,11 @@ public: private: QGridLayout *widgetLayout; - QPushButton *about; + //QPushButton *about; #ifdef WIN32 QPushButton *openCP; - QPushButton *startup; + QCheckBox *startup; + QCheckBox *channels; #endif }; diff --git a/src/qtestmain.cpp b/src/qtestmain.cpp index 387df74..e367ae9 100644 --- a/src/qtestmain.cpp +++ b/src/qtestmain.cpp @@ -3,9 +3,11 @@ #include "qtcommon.h" #include "qtclasses.h" #include "qtvisuals.h" +#include "settings.h" //#include "global.h" -OverseerHandler *osh = nullptr; +OverseerHandler *osh = nullptr; +ini::UserSettings *set = nullptr; QApplication* createApplication(int &argc, char *argv[]) { @@ -31,6 +33,14 @@ void closeDebugFileLog() { close_file_log_buffer(); } +char* parseCmdArgs(int argc, char* argv[]) { + if(argc == 1) return nullptr; + char arg[] = "--config-path="; + if(strstr(argv[1], arg)) { + return argv[1] + (sizeof(arg) / sizeof(arg[0])) - 1; + } else return nullptr; +} + /* set_terminate * void closeDebugFileLog2() { * close_file_log_buffer(); @@ -46,6 +56,8 @@ int main (int argc, char* argv[]) { * log_debugcpp(a.toStdString()); * } */ + //std::map* values = new std::map{ {"show_channels", false}, {"test", 7} }; + set = new ini::UserSettings(parseCmdArgs(argc, argv)); initialize_file_log(); atexit(closeDebugFileLog); diff --git a/src/settings.cpp b/src/settings.cpp new file mode 100644 index 0000000..0ced9fd --- /dev/null +++ b/src/settings.cpp @@ -0,0 +1,208 @@ +#include "settings.h" +#include "msinclude.h" + +namespace ini { + + UserSettings::UserSettings(char* path) { + wchar_t* settingsPath = nullptr; + wchar_t settingsFile[] = L"\\settings.ini"; + uint32_t settingsFileLen = (sizeof(settingsFile) / sizeof(wchar_t)) - 1; + wchar_t maxPathBypass[] = L"\\\\?\\"; + log_wdebugcpp(L"Bypass size: " + std::to_wstring((sizeof(maxPathBypass)/sizeof(maxPathBypass[0])))); + + //Executable dir + settingsPath = (wchar_t*)calloc(UNICODE_STRING_MAX_CHARS, sizeof(wchar_t)); + uint32_t exePathLength = GetModuleFileNameW( + NULL, + settingsPath, + UNICODE_STRING_MAX_CHARS + ); + //reverse wcsstr + while(exePathLength >= 0) { + if(settingsPath[exePathLength] == '\\') { + memset(settingsPath + exePathLength, + 0, + (UNICODE_STRING_MAX_CHARS - exePathLength) * sizeof(wchar_t)); + break; + } else exePathLength--; + } + log_wdebugcpp(L"Exe folder: " + std::wstring(settingsPath)); + + HANDLE settingsHandle = nullptr; + if((UNICODE_STRING_MAX_CHARS - exePathLength) > (settingsFileLen + 1)) { + memcpy(settingsPath + exePathLength, settingsFile, sizeof(wchar_t) * settingsFileLen); + settingsHandle = CreateFile2( + settingsPath, + GENERIC_READ | GENERIC_WRITE, + 0, + OPEN_ALWAYS, + NULL); + if(settingsHandle != INVALID_HANDLE_VALUE) { log_debugcpp("Filecreated!"); } + else { CloseHandle(settingsHandle); settingsHandle = nullptr; return; } + } + + //Calculating file size and reading file + if(settingsHandle) { + uint64_t fileSize; + LARGE_INTEGER fileSizeStruct; + if(!GetFileSizeEx(settingsHandle, &fileSizeStruct)) return; + fileSize = fileSizeStruct.QuadPart; + + uint32_t bytesRead = 0; + textContentsSize = fileSize + 1; + textContents = (char*)calloc(textContentsSize, sizeof(char)); + if (ReadFile(settingsHandle, textContents, fileSize, + (LPDWORD)&bytesRead, NULL) != TRUE) { + free(textContents); + return; + } + //textContents.assign(tempTextContents); + //free(tempTextContents); + + //Parsing values + bool isCRLF = false; + char *curLine = textContents; + char *separator = nullptr, *key = nullptr, *value = nullptr; + while(curLine) { + char* nextLine = strchr(curLine, '\n'); + if(nextLine == curLine + 1 || nextLine == curLine + 2) goto nextIteration; + if (nextLine && (isCRLF || *(nextLine - 1) == '\r')) { + isCRLF = true; + *(nextLine - 1) = '\0'; + } else if (nextLine) *nextLine = '\0'; // temporarily terminate the current line + log_debugcpp("curLine: " + std::string(curLine) + " "); + + separator = strchr(curLine, '='); + if(!separator) goto nextIteration; + *separator = '\0'; + key = trimAndAllocate(curLine); + value = trimAndAllocate(separator + 1); + values.try_emplace(key, value); + log_debugcpp("ini Map size: " + std::to_string(values.size())); + *separator = '='; + + nextIteration: + if (nextLine) { // then restore newline-char, just to be tidy + if (isCRLF) + *(nextLine - 1) = '\r'; + else *nextLine = '\n'; + } + curLine = nextLine ? (nextLine + 1) : NULL; + } + /* + * for(const std::pair keyVal : defaultValues) { + * if (textContents.find(keyVal.first) { + * if (keyVal. + * } + * } + */ + } + } + + //todo: buffer overflow. poc + char* const UserSettings::getValue(char* key, uint64_t len) { + if (auto search = values.find(key); search != values.end()) + return (char* const) search->second; + return nullptr; + } + + void UserSettings::setValue(char* key, char* value, uint64_t valueSize, uint64_t keySize) { + char *newValue, *newKey; + if (auto search = values.find(key); search != values.end()) { + if(!(strcmp(value, search->second))) return; + newValue = (char*)calloc(valueSize, sizeof(char)); + if (!(search->second == pos || search->second == neg)) { + free(search->second); + } + search->second = newValue; + return; + } + + newValue = (char*)calloc(valueSize, sizeof(char)); + newKey = (char*)calloc(keySize, sizeof(char)); + values.insert(std::make_pair(newKey, newValue)); + return; + } + + void UserSettings::setValue(char* key, bool value, uint64_t keySize) { + char *newKey; + log_debugcpp("Pos value: " + std::to_string((intptr_t)pos)); + log_debugcpp("Neg value: " + std::to_string((intptr_t)neg)); + if (auto search = values.find(key); search != values.end()) { + log_debugcpp("Previous value: " + std::to_string((intptr_t)values[key])); + if (!(search->second == pos || search->second == neg)) { + free(search->second); + } + if (value) + search->second = pos; + else search->second = neg; + return; + } + + newKey = (char*)calloc(keySize, sizeof(char)); + values.insert(std::make_pair(newKey, value ? pos : neg)); + return; + } + + + + //AppData path + /* + * wchar_t folderPath[] = L"\\mixerq"; + * + * wchar_t* roamingPath = nullptr; + * if(SHGetKnownFolderPath( + * FOLDERID_RoamingAppData, + * 0, + * NULL, + * &roamingPath) + * == S_OK) { + * uint32_t pathLen = 0; + * wchar_t currentChar = roamingPath[pathLen]; + * while(currentChar != '\0') { + * pathLen++; + * currentChar = roamingPath[pathLen]; + * } + * + * uint32_t maxPathBypassLen = (sizeof(maxPathBypass)/ sizeof(wchar_t)) - 1; + * uint32_t folderPathLen = (sizeof(folderPath) / sizeof(wchar_t)) - 1; + * settingsPath = (wchar_t*)calloc(pathLen + + * maxPathBypassLen + + * folderPathLen + + * settingsFileLen, + * sizeof(wchar_t)); + * memcpy(settingsPath, maxPathBypass, sizeof(wchar_t) * maxPathBypassLen); + * memcpy(settingsPath + (maxPathBypassLen), roamingPath, sizeof(wchar_t) * pathLen); + * CoTaskMemFree(roamingPath); + * memcpy(settingsPath + (maxPathBypassLen + pathLen), + * folderPath, sizeof(wchar_t) * folderPathLen); + * log_wdebugcpp(L"Settings folder path: " + std::wstring(settingsPath)); + * + * if(CreateDirectoryW(settingsPath, NULL) || GetLastError() == ERROR_ALREADY_EXISTS) { + * memcpy(settingsPath + (maxPathBypassLen + pathLen + folderPathLen), + * settingsFile, sizeof(wchar_t) * settingsFileLen); + * + * HANDLE settingsHandle = CreateFile2( + * settingsPath, + * GENERIC_READ | GENERIC_WRITE, + * 0, + * OPEN_ALWAYS, + * NULL); + * if(settingsHandle != INVALID_HANDLE_VALUE) log_debugcpp("Filecreated!"); + * + * } + * //End AppData + */ + + + +UserSettings::~UserSettings() { + if(textContents) free(textContents); + for(std::pair entry : values) { + free(entry.first); + free(entry.second); + } +} + + +} diff --git a/src/settings.h b/src/settings.h new file mode 100644 index 0000000..2e88598 --- /dev/null +++ b/src/settings.h @@ -0,0 +1,75 @@ +#pragma once +#include "global.h" + +namespace ini { + + //Trims spaces, LF and CRLF + static inline char* trimAndAllocate(const char* in, uint64_t len = 0) { + if (!in) return nullptr; + + uint64_t startingPos = 0, lastPos = 0; + bool foundStart = false; + for(int i = 0; ;i++) { + char c = in[i]; + if ((len > 0 && startingPos == (len - 1)) || (len > 0 && lastPos == (len - 1))) return nullptr; + if ((c != ' ' || c != '\r' || c != '\n') && !foundStart) { + foundStart = true; + lastPos = startingPos; + } + if (foundStart && (c == ' ' || c == '\r' || c == '\n')) { + break; + } + if(!foundStart) + startingPos++; + else lastPos++; + } + if(!(lastPos - startingPos)) return nullptr; + + char* trimmedString = (char*)calloc(lastPos - startingPos + 1, sizeof(char)); + memcpy(trimmedString, in + startingPos, lastPos - startingPos); + return trimmedString; + } + + struct Djb12Hasher { + size_t operator()(char* str) const { + unsigned long hash = 5381; + int c; + + while (c = *str++) + hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ + + return hash; + } + }; + + struct StrcmpEqual { + bool operator()(char* key1, char* key2) const { + return (!strcmp(key1, key2)); + } + }; + +class UserSettings { + + public: + UserSettings(char* path = nullptr); + ~UserSettings(); + + char* const getValue(char* key, uint64_t len = 0); + void setValue(char* key, char* value, uint64_t valueSize, uint64_t keySize); //'\0' included + void setValue(char* key, bool value, uint64_t keySize); //'\0' included + //void setValue(char* key, uint64_t value, uint64_t valueSize, uint64_t keySize); //'\0' included + + protected: + + private: + //void* returnValue(); + + //std::map values{ {"show_channels", false}, {"test", 7} }; + std::unordered_map values; + char* textContents = nullptr; + uint64_t textContentsSize = 0; + char* pos = "true"; + char* neg = "false"; +}; + +} From 1ae324b68a9f64c82d6408d6c6d2c1d523ff4071 Mon Sep 17 00:00:00 2001 From: Hane Date: Fri, 6 Dec 2024 17:55:46 +0100 Subject: [PATCH 58/78] wip: settings in effect --- src/back/backlasses.cpp | 90 +++++++++++++++ src/back/backlasses.h | 36 ++++++ src/cont/contclasses.cpp | 32 +++++- src/cont/contclasses.h | 9 +- src/global.h | 6 +- src/qtestmain.cpp | 11 +- src/settings.cpp | 242 +++++++++++++++------------------------ src/settings.h | 9 +- 8 files changed, 277 insertions(+), 158 deletions(-) diff --git a/src/back/backlasses.cpp b/src/back/backlasses.cpp index e51c011..adee2d6 100644 --- a/src/back/backlasses.cpp +++ b/src/back/backlasses.cpp @@ -1,6 +1,96 @@ #include "backlasses.h" #include "backfuncs.h" +std::string getPath(SettingsTargetDirectory target, bool create) { + wchar_t* settingsPath = nullptr; + wchar_t settingsFile[] = L"\\settings.ini"; + uint32_t settingsFileLen = (sizeof(settingsFile) / sizeof(wchar_t)) - 1; + wchar_t maxPathBypass[] = L"\\\\?\\"; + uint32_t exePathLength = 0; + wchar_t folderPath[] = L"\\mixerq"; + uint32_t maxPathBypassLen = (sizeof(maxPathBypass)/ sizeof(wchar_t)) - 1; + uint32_t folderPathLen = (sizeof(folderPath) / sizeof(wchar_t)) - 1; + wchar_t* roamingPath = nullptr; + + log_wdebugcpp(L"Bypass size: " + std::to_wstring((sizeof(maxPathBypass)/sizeof(maxPathBypass[0])))); + + switch(target) { + case HOME_DIR: + { + if(SHGetKnownFolderPath( + FOLDERID_RoamingAppData, + 0, + NULL, + &roamingPath) + == S_OK) { + //Retrieve path len + uint32_t pathLen = 0; + wchar_t currentChar = roamingPath[pathLen]; + while(currentChar != '\0') { + pathLen++; + currentChar = roamingPath[pathLen]; + } + + + settingsPath = (wchar_t*)calloc(pathLen + + maxPathBypassLen + + folderPathLen + + settingsFileLen, + sizeof(wchar_t)); + memcpy(settingsPath, maxPathBypass, sizeof(wchar_t) * maxPathBypassLen); + memcpy(settingsPath + (maxPathBypassLen), roamingPath, sizeof(wchar_t) * pathLen); + CoTaskMemFree(roamingPath); + memcpy(settingsPath + (maxPathBypassLen + pathLen), + folderPath, sizeof(wchar_t) * folderPathLen); + log_wdebugcpp(L"Settings folder path: " + std::wstring(settingsPath)); + + if(CreateDirectoryW(settingsPath, NULL) || GetLastError() == ERROR_ALREADY_EXISTS) { + memcpy(settingsPath + (maxPathBypassLen + pathLen + folderPathLen), + settingsFile, sizeof(wchar_t) * settingsFileLen); + std::string utf8path = utf16ToUtf8(settingsPath); + free(settingsPath); + return utf8path; + } + } + } + return nullptr; + break; + case APP_PATH: + { + //Executable dir + settingsPath = (wchar_t*)calloc(UNICODE_STRING_MAX_CHARS, sizeof(wchar_t)); + exePathLength = GetModuleFileNameW( + NULL, + settingsPath, + UNICODE_STRING_MAX_CHARS + ); + //reverse wcsstr + while(exePathLength >= 0) { + if(settingsPath[exePathLength] == '\\') { + memset(settingsPath + exePathLength, + 0, + (UNICODE_STRING_MAX_CHARS - exePathLength) * sizeof(wchar_t)); + break; + } else exePathLength--; + } + log_wdebugcpp(L"Exe folder: " + std::wstring(settingsPath)); + if((UNICODE_STRING_MAX_CHARS - exePathLength) > (settingsFileLen + 1)) { + memcpy(settingsPath + exePathLength, settingsFile, sizeof(wchar_t) * settingsFileLen); + std::string utf8path = utf16ToUtf8(settingsPath); + free(settingsPath); + return utf8path; + } + } + return nullptr; + break; + default: + return nullptr; + break; + } + return nullptr; + +} + EndpointNewSessionCallback::EndpointNewSessionCallback(EndpointHandler* eph){ this->eph = eph; } diff --git a/src/back/backlasses.h b/src/back/backlasses.h index f5fba61..3a94707 100644 --- a/src/back/backlasses.h +++ b/src/back/backlasses.h @@ -7,6 +7,42 @@ class EndpointVolumeCallback; class Session; +std::string getPath(SettingsTargetDirectory target, bool create); + +// Convert a wide UTF16LE string to an UTF8 string +static inline std::string utf16ToUtf8(const wchar_t* wstr) { + if(!wstr || wstr[0] == '\0') return std::string(); + int size_needed = WideCharToMultiByte(CP_UTF8, + 0, + wstr, + -1, + NULL, + 0, + NULL, + NULL); + std::string str(size_needed, 0); + WideCharToMultiByte(CP_UTF8, + 0, + wstr, + -1, + &str[0], + size_needed, + NULL, + NULL); + return str; +} + +// Convert an UTF8 string to a wide UTF16LE String +/* + * std::wstring utf8_decode(const std::string &str) + * { + * if( str.empty() ) return std::wstring(); + * int size_needed = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0); + * std::wstring wstrTo( size_needed, 0 ); + * MultiByteToWideChar (CP_UTF8, 0, &str[0], (int)str.size(), &wstrTo[0], size_needed); + * return wstrTo; + * } + */ class Endpoint { diff --git a/src/cont/contclasses.cpp b/src/cont/contclasses.cpp index 71f981d..c6a2149 100644 --- a/src/cont/contclasses.cpp +++ b/src/cont/contclasses.cpp @@ -1,6 +1,27 @@ #include "backlasses.h" #include "contclasses.h" -//TODO: pragma once + +void setConfigDirToDefaults() { + #define tryFileDir(dir, create) do { \ + OverseerHandler::settingsPath = getPath(dir, create); \ + set = ini::UserSettings::createSettings(OverseerHandler::settingsPath.c_str()); \ + if(set) { \ + return; \ + } else OverseerHandler::settingsPath.clear(); \ + } while(0) + #define tryOpenFileDir(dir) tryFileDir(dir, false) + #define tryCreateFileDir(dir) tryFileDir(dir, true) + + tryOpenFileDir(SettingsTargetDirectory::APP_PATH); + tryOpenFileDir(SettingsTargetDirectory::HOME_DIR); + tryCreateFileDir(SettingsTargetDirectory::HOME_DIR); + tryCreateFileDir(SettingsTargetDirectory::APP_PATH); + + return; + #undef tryOpenFileDir + #undef tryCreateFileDir + #undef tryFileDir +} EndpointHandler::EndpointHandler(uint64_t idx, Flows flow) { //std::vector endpoints = osh->getPlaybackEndpoints().at(idx); @@ -205,6 +226,14 @@ OverseerHandler::OverseerHandler() { this->os = new Overseer(); } +void OverseerHandler::setSettingsPath(std::string path) { + OverseerHandler::settingsPath = path; +} + +std::string OverseerHandler::getSettingsPath(){ + return OverseerHandler::settingsPath; +} + void OverseerHandler::populateSystemValues() { this->os->populateSystemValues(); } @@ -374,3 +403,4 @@ void OverseerHandler::setRemoveEndpointWidgetFunction(std::function ephs){ this->playbackEndpointHandlers = ephs; } + diff --git a/src/cont/contclasses.h b/src/cont/contclasses.h index e3ebab6..0f5cedd 100644 --- a/src/cont/contclasses.h +++ b/src/cont/contclasses.h @@ -1,6 +1,7 @@ #pragma once #include "global.h" +#include "settings.h" #include "contsessionclasses.h" //#define invoke_mem_fn(object,ptrToMember) ((object).*(ptrToMember)) //#define pinvoke_mem_fn(object,ptrToMember) ((object)->*(ptrToMember)) @@ -16,11 +17,13 @@ struct BackEndpointVolumeCallbackInfo { NGuid caller; bool muted; float mainVolume; - size_t channels; + size_t channels; std::vector channelVolumes; bool updateName = false; }; +void setConfigDirToDefaults(); + class EndpointHandler { public: @@ -97,6 +100,9 @@ class OverseerHandler { public: OverseerHandler(); + static void setSettingsPath(std::string path); + static std::string getSettingsPath(); + static inline std::string settingsPath; void populateSystemValues(); void openControlPanel(); ProcessedNativeEvent processTopLevelWindowMessage(void* msg); @@ -143,6 +149,7 @@ private: /* Session's */ std::function changeSessionVolume; + //std::function updateFrontVolumeCallback; //std::function updateFrontMuteCallback; diff --git a/src/global.h b/src/global.h index 10f45ab..7c02902 100644 --- a/src/global.h +++ b/src/global.h @@ -41,7 +41,11 @@ //INIT BACK - +enum SettingsTargetDirectory { + HOME_DIR = 0, + APP_PATH = (1 << 0), + CUSTOM = (1 << 1), +}; enum ProcessedNativeEvent { NONE = 0, diff --git a/src/qtestmain.cpp b/src/qtestmain.cpp index e367ae9..8b34385 100644 --- a/src/qtestmain.cpp +++ b/src/qtestmain.cpp @@ -4,7 +4,6 @@ #include "qtclasses.h" #include "qtvisuals.h" #include "settings.h" -//#include "global.h" OverseerHandler *osh = nullptr; ini::UserSettings *set = nullptr; @@ -56,8 +55,14 @@ int main (int argc, char* argv[]) { * log_debugcpp(a.toStdString()); * } */ - //std::map* values = new std::map{ {"show_channels", false}, {"test", 7} }; - set = new ini::UserSettings(parseCmdArgs(argc, argv)); + char* userSettingsPath = parseCmdArgs(argc, argv); + if (userSettingsPath) + set = ini::UserSettings::createSettings(userSettingsPath, true); + + if (set) + OverseerHandler::settingsPath = std::string(userSettingsPath); + else setConfigDirToDefaults(); + initialize_file_log(); atexit(closeDebugFileLog); diff --git a/src/settings.cpp b/src/settings.cpp index 0ced9fd..01b5114 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -3,100 +3,48 @@ namespace ini { - UserSettings::UserSettings(char* path) { - wchar_t* settingsPath = nullptr; - wchar_t settingsFile[] = L"\\settings.ini"; - uint32_t settingsFileLen = (sizeof(settingsFile) / sizeof(wchar_t)) - 1; - wchar_t maxPathBypass[] = L"\\\\?\\"; - log_wdebugcpp(L"Bypass size: " + std::to_wstring((sizeof(maxPathBypass)/sizeof(maxPathBypass[0])))); + wchar_t* utf8toUtf16(const char* str) { + if(!str || str[0] == '\0') return nullptr; + int sizeNeeded = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0); + wchar_t* utf16 = (wchar_t*)calloc(sizeNeeded, 1); + MultiByteToWideChar(CP_UTF8, 0, str, -1, utf16, sizeNeeded); + return utf16; + } - //Executable dir - settingsPath = (wchar_t*)calloc(UNICODE_STRING_MAX_CHARS, sizeof(wchar_t)); - uint32_t exePathLength = GetModuleFileNameW( - NULL, - settingsPath, - UNICODE_STRING_MAX_CHARS - ); - //reverse wcsstr - while(exePathLength >= 0) { - if(settingsPath[exePathLength] == '\\') { - memset(settingsPath + exePathLength, - 0, - (UNICODE_STRING_MAX_CHARS - exePathLength) * sizeof(wchar_t)); - break; - } else exePathLength--; - } - log_wdebugcpp(L"Exe folder: " + std::wstring(settingsPath)); + UserSettings::UserSettings(char* textContents) { + //Parsing values + bool isCRLF = false; + char *curLine = textContents; + char *separator = nullptr, *key = nullptr, *value = nullptr; + while(curLine) { + char* nextLine = strchr(curLine, '\n'); + if(nextLine == curLine + 1 || nextLine == curLine + 2) + goto nextIteration; + if (nextLine && (isCRLF || *(nextLine - 1) == '\r')) { + isCRLF = true; + *(nextLine - 1) = '\0'; + } else if (nextLine) *nextLine = '\0'; // temporarily terminate the current line + log_debugcpp("curLine: " + std::string(curLine) + " "); - HANDLE settingsHandle = nullptr; - if((UNICODE_STRING_MAX_CHARS - exePathLength) > (settingsFileLen + 1)) { - memcpy(settingsPath + exePathLength, settingsFile, sizeof(wchar_t) * settingsFileLen); - settingsHandle = CreateFile2( - settingsPath, - GENERIC_READ | GENERIC_WRITE, - 0, - OPEN_ALWAYS, - NULL); - if(settingsHandle != INVALID_HANDLE_VALUE) { log_debugcpp("Filecreated!"); } - else { CloseHandle(settingsHandle); settingsHandle = nullptr; return; } - } + separator = strchr(curLine, '='); + if(!separator) + goto nextIteration; + *separator = '\0'; + key = trimAndAllocate(curLine); + value = trimAndAllocate(separator + 1); + values.try_emplace(key, value); + log_debugcpp("ini Map size: " + std::to_string(values.size())); + *separator = '='; - //Calculating file size and reading file - if(settingsHandle) { - uint64_t fileSize; - LARGE_INTEGER fileSizeStruct; - if(!GetFileSizeEx(settingsHandle, &fileSizeStruct)) return; - fileSize = fileSizeStruct.QuadPart; - - uint32_t bytesRead = 0; - textContentsSize = fileSize + 1; - textContents = (char*)calloc(textContentsSize, sizeof(char)); - if (ReadFile(settingsHandle, textContents, fileSize, - (LPDWORD)&bytesRead, NULL) != TRUE) { - free(textContents); - return; + nextIteration: + if (nextLine) { // then restore newline-char, just to be tidy + if (isCRLF) + *(nextLine - 1) = '\r'; + else *nextLine = '\n'; } - //textContents.assign(tempTextContents); - //free(tempTextContents); - - //Parsing values - bool isCRLF = false; - char *curLine = textContents; - char *separator = nullptr, *key = nullptr, *value = nullptr; - while(curLine) { - char* nextLine = strchr(curLine, '\n'); - if(nextLine == curLine + 1 || nextLine == curLine + 2) goto nextIteration; - if (nextLine && (isCRLF || *(nextLine - 1) == '\r')) { - isCRLF = true; - *(nextLine - 1) = '\0'; - } else if (nextLine) *nextLine = '\0'; // temporarily terminate the current line - log_debugcpp("curLine: " + std::string(curLine) + " "); - - separator = strchr(curLine, '='); - if(!separator) goto nextIteration; - *separator = '\0'; - key = trimAndAllocate(curLine); - value = trimAndAllocate(separator + 1); - values.try_emplace(key, value); - log_debugcpp("ini Map size: " + std::to_string(values.size())); - *separator = '='; - - nextIteration: - if (nextLine) { // then restore newline-char, just to be tidy - if (isCRLF) - *(nextLine - 1) = '\r'; - else *nextLine = '\n'; - } - curLine = nextLine ? (nextLine + 1) : NULL; - } - /* - * for(const std::pair keyVal : defaultValues) { - * if (textContents.find(keyVal.first) { - * if (keyVal. - * } - * } - */ + curLine = nextLine ? (nextLine + 1) : NULL; } + free(textContents); } //todo: buffer overflow. poc @@ -139,70 +87,68 @@ namespace ini { return; } - newKey = (char*)calloc(keySize, sizeof(char)); + newKey = (char*)calloc(keySize, sizeof(char)); values.insert(std::make_pair(newKey, value ? pos : neg)); return; } - - - //AppData path - /* - * wchar_t folderPath[] = L"\\mixerq"; - * - * wchar_t* roamingPath = nullptr; - * if(SHGetKnownFolderPath( - * FOLDERID_RoamingAppData, - * 0, - * NULL, - * &roamingPath) - * == S_OK) { - * uint32_t pathLen = 0; - * wchar_t currentChar = roamingPath[pathLen]; - * while(currentChar != '\0') { - * pathLen++; - * currentChar = roamingPath[pathLen]; - * } - * - * uint32_t maxPathBypassLen = (sizeof(maxPathBypass)/ sizeof(wchar_t)) - 1; - * uint32_t folderPathLen = (sizeof(folderPath) / sizeof(wchar_t)) - 1; - * settingsPath = (wchar_t*)calloc(pathLen + - * maxPathBypassLen + - * folderPathLen + - * settingsFileLen, - * sizeof(wchar_t)); - * memcpy(settingsPath, maxPathBypass, sizeof(wchar_t) * maxPathBypassLen); - * memcpy(settingsPath + (maxPathBypassLen), roamingPath, sizeof(wchar_t) * pathLen); - * CoTaskMemFree(roamingPath); - * memcpy(settingsPath + (maxPathBypassLen + pathLen), - * folderPath, sizeof(wchar_t) * folderPathLen); - * log_wdebugcpp(L"Settings folder path: " + std::wstring(settingsPath)); - * - * if(CreateDirectoryW(settingsPath, NULL) || GetLastError() == ERROR_ALREADY_EXISTS) { - * memcpy(settingsPath + (maxPathBypassLen + pathLen + folderPathLen), - * settingsFile, sizeof(wchar_t) * settingsFileLen); - * - * HANDLE settingsHandle = CreateFile2( - * settingsPath, - * GENERIC_READ | GENERIC_WRITE, - * 0, - * OPEN_ALWAYS, - * NULL); - * if(settingsHandle != INVALID_HANDLE_VALUE) log_debugcpp("Filecreated!"); - * - * } - * //End AppData - */ - + UserSettings::~UserSettings() { + //if(textContents) free(textContents); + for(std::pair entry : values) { + free(entry.first); + if (!(entry.second == pos || entry.second == neg)) + free(entry.second); + } + } - -UserSettings::~UserSettings() { - if(textContents) free(textContents); - for(std::pair entry : values) { - free(entry.first); - free(entry.second); + UserSettings* UserSettings::createSettings(const char* path, bool create) { + if(!path) return nullptr; + wchar_t* utf16Path = utf8toUtf16(path); + if(!utf16Path) return nullptr; + + #define releaseBeforeReturn() do { \ + CloseHandle(settingsHandle); \ + settingsHandle = nullptr; \ + free(utf16Path); \ + } while(0) + + char* textContents; + HANDLE settingsHandle = nullptr; + settingsHandle = CreateFile2( + utf16Path, + GENERIC_READ | GENERIC_WRITE, + 0, + (create ? OPEN_ALWAYS : OPEN_EXISTING), + NULL); + if(settingsHandle == INVALID_HANDLE_VALUE) { + releaseBeforeReturn(); + return nullptr; + } + + //Calculating file size and reading file + uint64_t fileSize; + LARGE_INTEGER fileSizeStruct; + if(!GetFileSizeEx(settingsHandle, &fileSizeStruct)) { + releaseBeforeReturn(); + return nullptr; + } + fileSize = fileSizeStruct.QuadPart; + + uint32_t bytesRead = 0; + uint64_t textContentsSize = fileSize + 1; + textContents = (char*)calloc(textContentsSize, sizeof(char)); + if (ReadFile(settingsHandle, textContents, fileSize, + (LPDWORD)&bytesRead, NULL) != TRUE) { + releaseBeforeReturn(); + return nullptr; + } + + releaseBeforeReturn(); + return new UserSettings(textContents); + + //textContents.assign(tempTextContents); + //free(tempTextContents); + #undef releaseBeforeReturn } -} - } diff --git a/src/settings.h b/src/settings.h index 2e88598..e23a0a6 100644 --- a/src/settings.h +++ b/src/settings.h @@ -2,7 +2,7 @@ #include "global.h" namespace ini { - + //Trims spaces, LF and CRLF static inline char* trimAndAllocate(const char* in, uint64_t len = 0) { if (!in) return nullptr; @@ -51,7 +51,7 @@ namespace ini { class UserSettings { public: - UserSettings(char* path = nullptr); + static UserSettings* createSettings(const char* path = nullptr, bool create = false); ~UserSettings(); char* const getValue(char* key, uint64_t len = 0); @@ -63,11 +63,12 @@ class UserSettings { private: //void* returnValue(); + UserSettings(char* text = nullptr); //std::map values{ {"show_channels", false}, {"test", 7} }; std::unordered_map values; - char* textContents = nullptr; - uint64_t textContentsSize = 0; + //char* textContents = nullptr; + //uint64_t textContentsSize = 0; char* pos = "true"; char* neg = "false"; }; From adca5111f6c50022dad6b569696d33fdc6ea7fe2 Mon Sep 17 00:00:00 2001 From: Hane Date: Fri, 6 Dec 2024 19:59:09 +0100 Subject: [PATCH 59/78] finished basic ini skeleton --- src/back/backlasses.cpp | 1 - src/qt/qtclasses.cpp | 2 ++ src/settings.cpp | 80 +++++++++++++++++++++++++++++++++++++++-- src/settings.h | 11 ++---- 4 files changed, 83 insertions(+), 11 deletions(-) diff --git a/src/back/backlasses.cpp b/src/back/backlasses.cpp index adee2d6..f003403 100644 --- a/src/back/backlasses.cpp +++ b/src/back/backlasses.cpp @@ -30,7 +30,6 @@ std::string getPath(SettingsTargetDirectory target, bool create) { pathLen++; currentChar = roamingPath[pathLen]; } - settingsPath = (wchar_t*)calloc(pathLen + maxPathBypassLen + diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 86c9a22..cecaf5a 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -941,6 +941,8 @@ HeaderWidget::HeaderWidget(QWidget *parent) : QWidget(parent) { } connect(channels, &QCheckBox::stateChanged, [this, parent](){ set->setValue("show_channels", channels->isChecked(), sizeof("show_channels")); + if(!OverseerHandler::settingsPath.empty()) + set->save(OverseerHandler::settingsPath.c_str()); if(parent) QCoreApplication::instance()->postEvent (parent, new QEvent((QEvent::Type)CustomQEvent::RecomposeMainWindow)); diff --git a/src/settings.cpp b/src/settings.cpp index 01b5114..3760983 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -3,14 +3,17 @@ namespace ini { - wchar_t* utf8toUtf16(const char* str) { + wchar_t* utf8toUtf16(const char* str, uint64_t* size = nullptr) { if(!str || str[0] == '\0') return nullptr; int sizeNeeded = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0); + if(size) *size = sizeNeeded; wchar_t* utf16 = (wchar_t*)calloc(sizeNeeded, 1); MultiByteToWideChar(CP_UTF8, 0, str, -1, utf16, sizeNeeded); return utf16; } + + UserSettings::UserSettings(char* textContents) { //Parsing values bool isCRLF = false; @@ -47,7 +50,6 @@ namespace ini { free(textContents); } - //todo: buffer overflow. poc char* const UserSettings::getValue(char* key, uint64_t len) { if (auto search = values.find(key); search != values.end()) return (char* const) search->second; @@ -92,6 +94,80 @@ namespace ini { return; } + bool UserSettings::save(const char* path) { + wchar_t maxPathBypass[] = L"\\\\?\\"; + uint32_t maxPathBypassLen = (sizeof(maxPathBypass)/ sizeof(wchar_t)) - 1; + + if(!path) return false; + uint64_t convertedPathSize = 0; + wchar_t* convertedPath = utf8toUtf16(path, &convertedPathSize); + if(!convertedPath) return false; + wchar_t* utf16Path = (wchar_t*)calloc(maxPathBypassLen + convertedPathSize, sizeof(wchar_t)); + memcpy(utf16Path, maxPathBypass, sizeof(wchar_t) * maxPathBypassLen); + memcpy(utf16Path + maxPathBypassLen, convertedPath, sizeof(wchar_t) * convertedPathSize); + free(convertedPath); + + #define releaseBeforeReturn() do { \ + CloseHandle(settingsHandle); \ + settingsHandle = nullptr; \ + free(utf16Path); \ + free(text); \ + } while(0) + + //We initially reserve 1024B for flushing. If storage is exceeded, more same-size chunks are allocated + const uint64_t chunkSize = 1024; + char* text = (char*)calloc(chunkSize, sizeof(char)); + uint64_t mapSize = values.size(); + uint64_t chunks = 1; + uint64_t keySize = 0, valueSize = 0, totalSize = 0, previousStepSize = 0; + //for(std::pair entry : values) { + std::unordered_map::iterator it; + for (it = values.begin(); it != values.end(); it++) { + keySize = strlen(it->first); + valueSize = strlen(it->second); + totalSize += valueSize + keySize + (it == values.begin() ? 1 : 2); //newline and separator + + if(totalSize > (chunkSize * chunks)) { + text = (char*)realloc(text, (++chunks * chunkSize)); + } + + if(it != values.begin()) + memcpy(text + previousStepSize++, "\n", sizeof(char)); + memcpy(text + previousStepSize, it->first, sizeof(char) * keySize); + memcpy(text + previousStepSize + keySize, "=", sizeof(char)); + memcpy(text + previousStepSize + 1 + keySize, it->second, sizeof(char) * valueSize); + + previousStepSize = totalSize; + } + + HANDLE settingsHandle = nullptr; + settingsHandle = CreateFile2( + utf16Path, + GENERIC_READ | GENERIC_WRITE, + 0, + CREATE_ALWAYS, + NULL); + if(settingsHandle == INVALID_HANDLE_VALUE) { + releaseBeforeReturn(); + return false; + } + + DWORD bytesWritten; + BOOL writeSuccess = WriteFile( + settingsHandle, + text, + totalSize, + &bytesWritten, + nullptr + ); + + releaseBeforeReturn(); + if (writeSuccess == TRUE) return true; + else return false; + + return false; + } + UserSettings::~UserSettings() { //if(textContents) free(textContents); for(std::pair entry : values) { diff --git a/src/settings.h b/src/settings.h index e23a0a6..9974507 100644 --- a/src/settings.h +++ b/src/settings.h @@ -57,18 +57,13 @@ class UserSettings { char* const getValue(char* key, uint64_t len = 0); void setValue(char* key, char* value, uint64_t valueSize, uint64_t keySize); //'\0' included void setValue(char* key, bool value, uint64_t keySize); //'\0' included - //void setValue(char* key, uint64_t value, uint64_t valueSize, uint64_t keySize); //'\0' included - + + bool save(const char* path); protected: private: - //void* returnValue(); - UserSettings(char* text = nullptr); - - //std::map values{ {"show_channels", false}, {"test", 7} }; + UserSettings(char* text = nullptr); std::unordered_map values; - //char* textContents = nullptr; - //uint64_t textContentsSize = 0; char* pos = "true"; char* neg = "false"; }; From 9f7e7e30e28b902f94651ec6ba785a075d6e82c9 Mon Sep 17 00:00:00 2001 From: Hane Date: Thu, 12 Dec 2024 20:29:13 +0100 Subject: [PATCH 60/78] ini: fixed insufficient utf8->16 alloc --- src/qt/qtclasses.cpp | 12 ++++++++---- src/settings.cpp | 11 ++++++----- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index cecaf5a..e7d7b35 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -941,11 +941,15 @@ HeaderWidget::HeaderWidget(QWidget *parent) : QWidget(parent) { } connect(channels, &QCheckBox::stateChanged, [this, parent](){ set->setValue("show_channels", channels->isChecked(), sizeof("show_channels")); - if(!OverseerHandler::settingsPath.empty()) + if(!OverseerHandler::settingsPath.empty()){ set->save(OverseerHandler::settingsPath.c_str()); - if(parent) - QCoreApplication::instance()->postEvent - (parent, new QEvent((QEvent::Type)CustomQEvent::RecomposeMainWindow)); + } + if(parent) { + QEvent explosion = QEvent((QEvent::Type)CustomQEvent::RecomposeMainWindow); + QCoreApplication::instance()->sendEvent + (parent, &explosion); + } + }); diff --git a/src/settings.cpp b/src/settings.cpp index 3760983..b45a9f1 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -3,11 +3,11 @@ namespace ini { - wchar_t* utf8toUtf16(const char* str, uint64_t* size = nullptr) { + wchar_t* Utf8toUtf16(const char* str, uint64_t* size = nullptr) { if(!str || str[0] == '\0') return nullptr; int sizeNeeded = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0); if(size) *size = sizeNeeded; - wchar_t* utf16 = (wchar_t*)calloc(sizeNeeded, 1); + wchar_t* utf16 = (wchar_t*)calloc(sizeNeeded, sizeof(wchar_t)); MultiByteToWideChar(CP_UTF8, 0, str, -1, utf16, sizeNeeded); return utf16; } @@ -100,7 +100,7 @@ namespace ini { if(!path) return false; uint64_t convertedPathSize = 0; - wchar_t* convertedPath = utf8toUtf16(path, &convertedPathSize); + wchar_t* convertedPath = Utf8toUtf16(path, &convertedPathSize); if(!convertedPath) return false; wchar_t* utf16Path = (wchar_t*)calloc(maxPathBypassLen + convertedPathSize, sizeof(wchar_t)); memcpy(utf16Path, maxPathBypass, sizeof(wchar_t) * maxPathBypassLen); @@ -164,8 +164,9 @@ namespace ini { releaseBeforeReturn(); if (writeSuccess == TRUE) return true; else return false; - + return false; + #undef releaseBeforeReturn } UserSettings::~UserSettings() { @@ -179,7 +180,7 @@ namespace ini { UserSettings* UserSettings::createSettings(const char* path, bool create) { if(!path) return nullptr; - wchar_t* utf16Path = utf8toUtf16(path); + wchar_t* utf16Path = Utf8toUtf16(path); if(!utf16Path) return nullptr; #define releaseBeforeReturn() do { \ From 2a1b30e1666dfebe6eb06877b654a172d3fe23f1 Mon Sep 17 00:00:00 2001 From: Hane Date: Tue, 17 Dec 2024 00:01:02 +0100 Subject: [PATCH 61/78] fix: sessionmanager life expired under my feet --- src/back/backlasses.cpp | 32 +++++++++++++++++++++++--------- src/back/backlasses.h | 6 +++--- src/cont/contclasses.cpp | 31 ++++++++++++++++++++++--------- src/cont/contclasses.h | 2 ++ src/qt/qtclasses.cpp | 7 +++++++ src/qt/qtclasses.h | 2 +- 6 files changed, 58 insertions(+), 22 deletions(-) diff --git a/src/back/backlasses.cpp b/src/back/backlasses.cpp index f003403..775f929 100644 --- a/src/back/backlasses.cpp +++ b/src/back/backlasses.cpp @@ -351,16 +351,12 @@ Endpoint::Endpoint(IMMDevice* ep, IPolicyConfig7* policyConfig, uint64_t idx){ endpoint->OpenPropertyStore(STGM_READ, &properties); this->updateName(); this->setFlow(); - if (this->flow == Flows::FLOW_PLAYBACK) { - activateEndpointSessions(); - log_debugcpp("plays back"); - } } void Endpoint::updateName() { PROPVARIANT pv; #define store_name(key, propvariant, wstr) do { \ - properties->GetValue(key , &propvariant); \ + properties->GetValue(key, &propvariant); \ if (pv.pwszVal == nullptr) wstr = L"Unnamed Not Present Endpoint"; \ else wstr = std::wstring(pv.pwszVal); \ } while (0) @@ -373,8 +369,16 @@ void Endpoint::updateName() { } void Endpoint::activateEndpointSessions() { - //sessionManager; - if (FAILED(endpoint->Activate(__uuidof(IAudioSessionManager2), CLSCTX_ALL, NULL, (void**) &sessionManager))) { log_wdebugcpp(L"sesionbros..."); return; } + if (this->flow != Flows::FLOW_PLAYBACK) { + log_debugcpp("recording. No seshes for u :("); + return; + } + + if (FAILED(endpoint->Activate(__uuidof(IAudioSessionManager2), + CLSCTX_ALL, NULL, (void**) &sessionManager))) { + log_debugcpp("Couldn't open session manager2, huh"); + return; + } IAudioSessionEnumerator* sessionEnumerator = nullptr; if (FAILED(sessionManager->GetSessionEnumerator(&sessionEnumerator))) { log_wdebugcpp(L"sesEnumeratorBros..."); return; } @@ -382,7 +386,7 @@ void Endpoint::activateEndpointSessions() { endpointSessions.resize(1, nullptr); int sessionCount; sessionEnumerator->GetCount(&sessionCount); - for (int i = 0; i < sessionCount; i++) { + for (int i = 0; i < sessionCount; i++) { IAudioSessionControl* sessionControlTmp; sessionEnumerator->GetSession(i, (IAudioSessionControl**)&sessionControlTmp); /*todo: borrar when donezo @@ -390,7 +394,7 @@ void Endpoint::activateEndpointSessions() { * IAudioMeterInformation* ttmp = (IAudioMeterInformation*)sessionControlTmp; * ttmp->GetPeakValue(&test2); */ - //todo:: asegurar lo del dynamic_cast + IAudioSessionControl2* sessionControl; sessionControlTmp->QueryInterface(__uuidof(IAudioSessionControl2), (void**)&sessionControl); sessionControl->AddRef(); @@ -593,12 +597,22 @@ void Endpoint::unregisterNewSessionNotification(EndpointNewSessionCallback* ensc sessionManager->UnregisterSessionNotification(ensc); } +void Endpoint::deleteSessions() { + for (auto session : endpointSessions) { + delete session; + } + endpointSessions.resize(0); +} + Endpoint::~Endpoint(){ log_wdebugcpp(L"murio endpoint-san uwu"); properties->Release(); endpointVolume->Release(); endpoint->Release(); sessionManager->Release(); + for (auto session : endpointSessions) { + delete session; + } } void Overseer::initCOMLibrary() { diff --git a/src/back/backlasses.h b/src/back/backlasses.h index 3a94707..4e67a09 100644 --- a/src/back/backlasses.h +++ b/src/back/backlasses.h @@ -80,19 +80,19 @@ class Endpoint { void addSession(Session* session); void registerNewSessionNotification(EndpointNewSessionCallback* ensc); void unregisterNewSessionNotification(EndpointNewSessionCallback* ensc); - + void deleteSessions(); + void activateEndpointSessions(); ~Endpoint(); private: void inline activateEndpointVolume(); - void inline activateEndpointSessions(); std::vector endpointSessions; uint32_t channelCount = 0; IMMDevice *endpoint; IAudioClient *audioClient; int64_t defTime, minTime; - IAudioSessionManager2 *sessionManager; + IAudioSessionManager2 *sessionManager = nullptr; Flows flow; IAudioEndpointVolume *endpointVolume = nullptr; IPropertyStore *properties; diff --git a/src/cont/contclasses.cpp b/src/cont/contclasses.cpp index c6a2149..067a32f 100644 --- a/src/cont/contclasses.cpp +++ b/src/cont/contclasses.cpp @@ -30,19 +30,10 @@ EndpointHandler::EndpointHandler(uint64_t idx, Flows flow) { this->ep = (flow == Flows::FLOW_PLAYBACK ? osh->getPlaybackEndpoints().at(idx) : osh->getCaptureEndpoints().at(idx)); epc = new EndpointVolumeCallback(ep); - ensc = new EndpointNewSessionCallback(this); this->callbackInfo.caller = osh->getGuid(); - ep->registerNewSessionNotification(ensc); //epName = ep->getName(); this->setBackEndpointVolumeCallbackInfoContent(this->getState()); osh->pushBackEndpointHandler(this, flow); - - if (this->flow == Flows::FLOW_PLAYBACK) { - for (int i = 0; i < this->getSessionCount(); i++) { - SessionHandler* sessionHandler = new SessionHandler(this, this->getSessions().at(i),i); - sessionHandlers.push_back(sessionHandler); - } - } } void OverseerHandler::pushBackEndpointHandler(EndpointHandler* eph, Flows flow) { @@ -215,6 +206,28 @@ void EndpointHandler::removeSessionFromFront(SessionHandler* sh) { this->removeSessionWidget(sh); } +void EndpointHandler::deleteSessions() { + ep->unregisterNewSessionNotification(ensc); + ensc->Release(); + for (auto sh : sessionHandlers) { + delete sh; + } + sessionHandlers.resize(0); + ep->deleteSessions(); +} + +void EndpointHandler::createSessionHandlers() { + ep->activateEndpointSessions(); + ensc = new EndpointNewSessionCallback(this); + ep->registerNewSessionNotification(ensc); + if (this->flow == Flows::FLOW_PLAYBACK) { + for (int i = 0; i < this->getSessionCount(); i++) { + SessionHandler* sessionHandler = new SessionHandler(this, this->getSessions().at(i),i); + sessionHandlers.push_back(sessionHandler); + } + } +} + EndpointHandler::~EndpointHandler() { ep->removeVolumeCallback(epc); ep->unregisterNewSessionNotification(ensc); diff --git a/src/cont/contclasses.h b/src/cont/contclasses.h index 0f5cedd..2a4a9f0 100644 --- a/src/cont/contclasses.h +++ b/src/cont/contclasses.h @@ -74,6 +74,8 @@ public: void setRemoveSessionWidgetFunction(std::function removeSessionWidget); void sendSessionToFront(SessionHandler* sh); void removeSessionFromFront(SessionHandler* sh); + void deleteSessions(); + void createSessionHandlers(); ~EndpointHandler(); private: diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index e7d7b35..c7b5cc7 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -531,6 +531,7 @@ EndpointWidget::EndpointWidget(EndpointHandler* eph, QWidget *parent, uint64_t i this->idx = idx; this->eph = eph; + eph->createSessionHandlers(); //todo: sussy this->eph->setState(EndpointState::ENDPOINT_ACTIVE, idx); this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); @@ -749,6 +750,10 @@ EndpointWidget::~EndpointWidget() { timer->stop(); delete timer; this->eph->setFrontVisibilityInfo(EndpointState::ENDPOINT_ALL, INT_MAX); + this->eph->deleteSessions(); + for(auto sw : sessionWidgets) { + delete sw; + } } void MainWindow::customEvent(QEvent* ev) { @@ -894,6 +899,8 @@ void EndpointWidget::updateMainVolume(int newValue){ */ void EndpointWidget::updateChannelsVisibility() { + if (!cw) return; + char* const channelSettings = set->getValue("show_channels"); if(channelSettings && !(strcmp(channelSettings, "true"))){ cw->setVisible(true); diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h index 5531921..1ca937d 100644 --- a/src/qt/qtclasses.h +++ b/src/qt/qtclasses.h @@ -149,7 +149,7 @@ private: size_t defaultRolesVectorSize = 4; QTimer* timer = nullptr; uint64_t idx; - ChannelWidget* cw; + ChannelWidget* cw = nullptr; std::vector sessionWidgets; QSize minimum; //std::vector *ephs; From 5e5365274caaef3d6e4fe889bfc83717d2349845 Mon Sep 17 00:00:00 2001 From: Hane Date: Tue, 17 Dec 2024 23:00:44 +0100 Subject: [PATCH 62/78] changed session module name retrieval + name when wnd not shown --- src/back/backsessionclasses.cpp | 47 ++++++++++++--------------------- src/back/msinclude.h | 1 + 2 files changed, 18 insertions(+), 30 deletions(-) diff --git a/src/back/backsessionclasses.cpp b/src/back/backsessionclasses.cpp index 1bfc37f..aafe147 100644 --- a/src/back/backsessionclasses.cpp +++ b/src/back/backsessionclasses.cpp @@ -129,12 +129,15 @@ Session::Session(Endpoint* ep, IAudioSessionControl2* sessionControl, size_t idx if (!wcscmp(sessionDisplayName, L"")) { std::wstring exePath; if (getExePath(pid, &exePath)) { + this->sessionName = exePath; if (fetchName(exePath, pid)) goto nameFound; } if (fetchNameViaWindowName(pid, &this->sessionName)) goto nameFound; - } else + } else { this->sessionName = std::wstring(sessionDisplayName); - + goto nameFound; + } + nameFound: CoTaskMemFree(sessionDisplayName); } @@ -199,37 +202,21 @@ void Session::setMute(NGuid guid, bool muted) { } bool Session::getExePath(DWORD pid, std::wstring *exePath) { - /* - * https://learn.microsoft.com/en-us/windows/win32/api/tlhelp32/nf-tlhelp32-createtoolhelp32snapshot - * https://stackoverflow.com/questions/11843368/how-to-get-process-description - * https://notes.indezine.com/2018/05/microsoft-locale-ids.html#:~:text=Wait%2C%201033%20is%20the%20decimal,ID%20for%20English%20%E2%80%93%20United%20States. - * https://stackoverflow.com/questions/64321036/c-win32-getting-app-name-using-pid-and-executable-path - */ - //std::wstring msixName; - - HANDLE processList = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, pid); - if (processList == INVALID_HANDLE_VALUE) { + HANDLE processHandle; + wchar_t fileName[UNICODE_STRING_MAX_CHARS]; + processHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid); + if (processHandle != NULL) { + if (GetModuleFileNameEx(processHandle, NULL, fileName, UNICODE_STRING_MAX_CHARS) == 0) { + CloseHandle(processHandle); + return false; + } + } else { log_wdebugcpp(L"aye no procname. -> " + std::to_wstring(GetLastError())); return false; } - MODULEENTRY32W me32w; - me32w.dwSize = sizeof(MODULEENTRY32W); - if(Module32FirstW(processList, &me32w)) { - do { - if (me32w.th32ProcessID == pid) { - *exePath = std::wstring(me32w.szExePath); - break; - /* - * However, if the calling process is a 32-bit process, you must call the - * QueryFullProcessImageName function to retrieve the full path of the - * executable file for a 64-bit process. - */ - } - } while(Module32NextW(processList, &me32w)); - } - CloseHandle(processList); + *exePath = std::wstring(fileName); return true; } @@ -362,8 +349,8 @@ bool Session::fetchNameViaWindowName(DWORD pid, std::wstring *sessionName) { auto pParams = (std::pair*)(lParam); DWORD processId; - //&& GetWindow(hwnd, GW_OWNER) == 0 - if (IsWindowVisible(hwnd) && GetWindowThreadProcessId(hwnd, &processId) && processId == pParams->second) { + //IsWindowVisible(hwnd) &&&& GetWindow(hwnd, GW_OWNER) == 0 + if ( GetWindowThreadProcessId(hwnd, &processId) && processId == pParams->second) { int length = GetWindowTextLength(hwnd); if (!length) return TRUE; diff --git a/src/back/msinclude.h b/src/back/msinclude.h index 633e079..52fccbd 100644 --- a/src/back/msinclude.h +++ b/src/back/msinclude.h @@ -17,6 +17,7 @@ #include #include #include +#include //#include #include From 801adbb17ee97055ac09fc5241a82b4c3335c217 Mon Sep 17 00:00:00 2001 From: Hane Date: Wed, 18 Dec 2024 00:44:33 +0100 Subject: [PATCH 63/78] fixed 1px gaps/removed unnecesary com call --- src/back/backlasses.cpp | 16 +++------------- src/qt/qtclasses.cpp | 11 ++++++----- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/src/back/backlasses.cpp b/src/back/backlasses.cpp index 775f929..cf5700b 100644 --- a/src/back/backlasses.cpp +++ b/src/back/backlasses.cpp @@ -128,7 +128,6 @@ HRESULT EndpointNewSessionCallback::QueryInterface(REFIID riid, VOID **ppvInterf HRESULT EndpointNewSessionCallback::OnSessionCreated(IAudioSessionControl *NewSession) { if (eph->getFlow() == Flows::FLOW_CAPTURE) return S_OK; - HRESULT result = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); IAudioSessionControl2* sessionControl; //ISimmpleAudioVolume* sessionVolume; if (FAILED(NewSession->QueryInterface(__uuidof(IAudioSessionControl2), (void**)&sessionControl))) { log_wdebugcpp(L"no nueva sesion......"); }; @@ -139,9 +138,6 @@ HRESULT EndpointNewSessionCallback::OnSessionCreated(IAudioSessionControl *NewSe eph->addSessionSendFront(newSession); } - if (result == S_OK) - CoUninitialize(); - return S_OK; } @@ -296,7 +292,7 @@ HRESULT EndpointSituationCallback::OnDeviceRemoved(LPCWSTR pwstrDeviceId) { HRESULT EndpointSituationCallback::OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState) { std::wstring endpointId = std::wstring(pwstrDeviceId); - switch (dwNewState){ + switch (dwNewState) { case DEVICE_STATE_ACTIVE: osh->reviseEndpointShowing(endpointId, EndpointState::ENDPOINT_ACTIVE); break; @@ -389,12 +385,6 @@ void Endpoint::activateEndpointSessions() { for (int i = 0; i < sessionCount; i++) { IAudioSessionControl* sessionControlTmp; sessionEnumerator->GetSession(i, (IAudioSessionControl**)&sessionControlTmp); - /*todo: borrar when donezo - * float test2; - * IAudioMeterInformation* ttmp = (IAudioMeterInformation*)sessionControlTmp; - * ttmp->GetPeakValue(&test2); - */ - IAudioSessionControl2* sessionControl; sessionControlTmp->QueryInterface(__uuidof(IAudioSessionControl2), (void**)&sessionControl); sessionControl->AddRef(); @@ -566,7 +556,6 @@ void Endpoint::removeRoles(Roles role){ void Endpoint::setFlow() { IMMEndpoint* flowGetter; - //this should be as simple as writing IID_IMMEndpoint, but it just won't find the macro, so I reimpl it. Sad. if(FAILED(this->endpoint->QueryInterface(__uuidof(IMMEndpoint), (void**)&flowGetter))) { log_debugcpp("no flow..."); } EDataFlow MSflow; @@ -790,7 +779,8 @@ ProcessedNativeEvent Overseer::processTopLevelWindowMessage(void* msg) { MSG *message = static_cast(msg); switch(message->message) { case WM_SETTINGCHANGE: - if(!wcscmp(((wchar_t*)message->lParam), L"ImmersiveColorSet")) + //TODO: This looks like a future pain point. Ex handler would come in clutch + if(message->lParam && !wcscmp(((wchar_t*)message->lParam), L"ImmersiveColorSet")) return updateColors(); break; default: diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index c7b5cc7..15f3768 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -225,7 +225,7 @@ QRect MainWindow::setSizePosition(QScreen* screen, int width, int height) { QRect availableRes = screen->availableGeometry(); int arx1, arx2, ary1, ary2; availableRes.getCoords(&arx1, &ary1, &arx2, &ary2); - log_debugcpp("Available res: " + std::to_string(arx1) + " " + std::to_string(arx2)+" " + std::to_string(ary1) + " " + std::to_string(ary2)); + log_debugcpp("Available res: " + std::to_string(arx1) + " " + std::to_string(arx2)+" " + std::to_string(ary1) + " " + std::to_string(ary2);) if(height > (ary2 - ary1)) height = (ary2 - ary1); log_debugcpp("Height res: " + std::to_string(height)); @@ -235,22 +235,22 @@ QRect MainWindow::setSizePosition(QScreen* screen, int width, int height) { int rightDistance = std::abs(srx2 - tix1); int upDistance = std::abs(sry1 - tiy1); int downDistance = std::abs(sry2 - tiy1); - + pos = (upDistance < downDistance) ? SpawnPos::UP : SpawnPos::DOWN; pos = (leftDistance < rightDistance) ? pos | SpawnPos::LEFT : pos | SpawnPos::RIGHT; switch (pos) { case SpawnPos::UP | SpawnPos::RIGHT: this->addToolBar(Qt::BottomToolBarArea, mainMenuBar); - return QRect((arx2 - width), ary1, width, height); + return QRect((arx2 - width + 1), ary1, width, height); break; case SpawnPos::DOWN | SpawnPos::LEFT: this->addToolBar(Qt::TopToolBarArea, mainMenuBar); - return QRect(arx1, (ary2-height), width, height); + return QRect(arx1, (ary2 - height + 1), width, height); break; case SpawnPos::DOWN | SpawnPos::RIGHT: this->addToolBar(Qt::TopToolBarArea, mainMenuBar); - return QRect((arx2 - width), (ary2-height), width, height); + return QRect((arx2 - width + 1), (ary2 - height + 1), width, height); break; default: this->addToolBar(Qt::BottomToolBarArea, mainMenuBar); @@ -414,6 +414,7 @@ SessionWidget::SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent) mainSlider->setValue((int)((sh->getVolumeInfo()->mainVolume + roundingFactor) * 100)); mainSlider->setPeakValue(sh->getPeakVolume()); mainSlider->update(); + //log_wdebugcpp(L"Session: " + sh->getName() + L" peak value: " + std::to_wstring(sh->getPeakVolume())); muteButton->setCheckState((sh->getVolumeInfo()->muted == false ? Qt::Unchecked : Qt::Checked)); muteButton->setText(sh->getVolumeInfo()->muted ? STRING_UNMUTE : STRING_MUTE); //memcpy(osh->callbackInfo[idx]->caller, osh->getGuid(), sizeof(NGuid)); From 94d2ebf1c2d979a08a4a0445b99faa073f6109d3 Mon Sep 17 00:00:00 2001 From: Hane Date: Tue, 24 Dec 2024 00:29:23 +0100 Subject: [PATCH 64/78] wip: working installer. cleanup/bindir/logo missing --- .gitignore | 1 + install/installer.nsi | 210 ++++++++++++++++++++++++++++++++++++++++++ install/version.nsi | 22 +++++ 3 files changed, 233 insertions(+) create mode 100644 install/installer.nsi create mode 100644 install/version.nsi diff --git a/.gitignore b/.gitignore index 9eec94c..a4ce7f0 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ build *.rdbg *.pdb *.ps1 +*.exe Makefile Makefile.Debug Makefile.Release \ No newline at end of file diff --git a/install/installer.nsi b/install/installer.nsi new file mode 100644 index 0000000..b2e29eb --- /dev/null +++ b/install/installer.nsi @@ -0,0 +1,210 @@ +;Auto versioning------------------------------- + +!makensis "version.nsi" +!system "GetVersion.exe" +!include "Version.txt" +; optional cleanup +!delfile "GetVersion.exe" +!delfile "Version.txt" + +;Defines--------------- +;!define MULTIUSER_EXECUTIONLEVEL Highest + + +;Includes-------------------------------- + + !include "MUI2.nsh" + !include "nsDialogs.nsh" + !include "LogicLib.nsh" + ;!include "MultiUser.nsh" + +;-------------------------------- +;General + + ;Name and file + Name "MixerQ" + OutFile "mixerq-installer-${version}.exe" + Unicode True + + ;Default installation folder + ;InstallDir "$LOCALAPPDATA\Modern UI Test" + + ;Get installation folder from registry if available + ;InstallDirRegKey HKCU "Software\Modern UI Test" "" + + Var Is_Admin + Var Install_Type + + ;Request application privileges for UAC. If admin is not available, only user-level install will be available + RequestExecutionLevel highest + + !macro ONINIT un + Function ${un}.onInit + ; The value of SetShellVarContext detetmines whether SHCTX is HKLM or HKCU + ; and whether SMPROGRAMS refers to all users or just the current user + UserInfo::GetAccountType + Pop $0 + ${If} $0 == "Admin" + ; If we're an admin, default to installing to C:\Program Files + ;SetShellVarContext all + ;StrCpy $INSTDIR_BASE "$PROGRAMFILES64" + StrCpy $Is_Admin "true" + StrCpy $Install_Type "machine" + ${Else} + ; If we're just a user, default to installing to ~\AppData\Local + ;SetShellVarContext current + ;StrCpy $INSTDIR_BASE "$LOCALAPPDATA" + StrCpy $Is_Admin "false" + StrCpy $Install_Type "user" + ${EndIf} + + ; ${If} $INSTDIR == "" + ; ; This only happens in the installer, because the uninstaller already knows INSTDIR + ; ReadRegStr $0 SHCTX "Software\${PRODUCT_NAME}" "" + + ; ${If} $0 != "" + ; ; If we're already installed, use the existing directory + ; StrCpy $INSTDIR "$0" + ; ${Else} + ; StrCpy $INSTDIR "$INSTDIR_BASE\${PRODUCT_NAME}" + ; ${Endif} + ; ${Endif} + FunctionEnd + !macroend + +!insertmacro ONINIT "" +!insertmacro ONINIT "un" + +;-------------------------------- +;Interface Settings + + !define MUI_ABORTWARNING + +;-------------------------------- +;Pages + + !insertmacro MUI_PAGE_WELCOME + !insertmacro MUI_PAGE_LICENSE "..\LICENSE.txt" + ;!insertmacro MULTIUSER_PAGE_INSTALLMODE + Page Custom InstallTargetPage + ;!insertmacro MUI_PAGE_COMPONENTS + !insertmacro MUI_PAGE_DIRECTORY + !insertmacro MUI_PAGE_INSTFILES + !insertmacro MUI_PAGE_FINISH + + !insertmacro MUI_UNPAGE_WELCOME + !insertmacro MUI_UNPAGE_CONFIRM + !insertmacro MUI_UNPAGE_INSTFILES + !insertmacro MUI_UNPAGE_FINISH + +;-------------------------------- +;Languages + + !insertmacro MUI_LANGUAGE "English" + +;NSDialog InstallTarget Page definition--------------------------------- + +Function InstallTargetPage + !insertmacro MUI_HEADER_TEXT "Configure Install" "Customize install settings" + + nsDialogs::Create 1018 + Pop $0 + + ${NSD_CreateLabel} 0 0 100% 10% "Select for whom will $(^Name) be installed: " + Pop $3 + + ${NSD_CreateFirstRadioButton} 0 12% 40% 6% "All users" + Pop $1 + ${If} $Is_Admin == "false" + EnableWindow $1 0 + StrCpy $INSTDIR "$LOCALAPPDATA\$(^Name)" + ${Else} + SendMessage $1 ${BM_CLICK} "" "" ;Set default + StrCpy $INSTDIR "$PROGRAMFILES64\$(^Name)" + ${EndIf} + ${NSD_OnClick} $1 All_Users_Click + + ${NSD_CreateAdditionalRadioButton} 0 24% 40% 6% "Current user" + Pop $2 + ${IfThen} $Is_Admin == "false" ${|} SendMessage $2 ${BM_CLICK} "" "" ${|} + ${NSD_OnClick} $2 Current_User_Click + + nsDialogs::Show +FunctionEnd + +Function All_Users_Click + Pop $0 + StrCpy $INSTDIR "$PROGRAMFILES64\$(^Name)" + StrCpy $Install_Type "machine" + ;${NSD_SetText} $0 "machine" +FunctionEnd + +Function Current_User_Click + Pop $0 + StrCpy $INSTDIR "$LOCALAPPDATA\$(^Name)" + StrCpy $Install_Type "user" + ;${NSD_SetText} $0 "user" +FunctionEnd + +;Default section---------------------- +Section + SetRegView 64 + SetOutPath $INSTDIR + + ;File "..\build\debug\qtest.exe" + File "..\LICENSE.txt" + + ;Store installation folder + + ${If} $Install_Type == "user" + WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" "DisplayName" "$(^Name)" + WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" "UninstallString" '"$INSTDIR\Uninstall.exe"' + WriteRegDWORD HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" "NoModify" 1 + WriteRegDWORD HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" "NoRepair" 1 + WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Run" "$(^Name)" "$INSTDIR\$(^Name)" + ${Else} + WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" "DisplayName" "$(^Name)" + WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" "UninstallString" '"$INSTDIR\Uninstall.exe"' + WriteRegDWORD HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" "NoModify" 1 + WriteRegDWORD HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" "NoRepair" 1 + WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Run" "$(^Name)" "$INSTDIR\$(^Name)" + ${EndIf} + + ;Create uninstaller + WriteUninstaller "$INSTDIR\Uninstall.exe" + +SectionEnd + + +;-------------------------------- +;Descriptions + + ; ;Language strings + ; LangString DESC_SecDummy ${LANG_ENGLISH} "A test section." + + ; ;Assign language strings to sections + ; !insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN + ; !insertmacro MUI_DESCRIPTION_TEXT ${SecDummy} $(DESC_SecDummy) + ; !insertmacro MUI_FUNCTION_DESCRIPTION_END + +;-------------------------------- +;Uninstaller Section + +Section "Uninstall" + SetRegView 64 + ;ADD YOUR OWN FILES HERE... + Delete "$INSTDIR\qtest.exe" + Delete "$INSTDIR\LICENSE.txt" + Delete "$INSTDIR\Uninstall.exe" +;!define PRODUCT_UNINST_ROOT_KEY "HKLM" + RMDir "$INSTDIR" + + ${If} $Install_Type == "user" + DeleteRegValue HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Run" "$(^Name)" + DeleteRegKey HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" + ${Else} + DeleteRegValue HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Run" "$(^Name)" + DeleteRegKey HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" + ${EndIf} + +SectionEnd diff --git a/install/version.nsi b/install/version.nsi new file mode 100644 index 0000000..6f5ac83 --- /dev/null +++ b/install/version.nsi @@ -0,0 +1,22 @@ +!define File "..\build\debug\qtest.exe" + +OutFile "GetVersion.exe" +SilentInstall silent +RequestExecutionLevel user ; don't write $EXEDIR\Version.txt with admin permissions and prevent invoking UAC + +Section + + ## Get file version + GetDllVersion "${File}" $R0 $R1 + IntOp $R2 $R0 / 0x00010000 + IntOp $R3 $R0 & 0x0000FFFF + IntOp $R4 $R1 / 0x00010000 + IntOp $R5 $R1 & 0x0000FFFF + StrCpy $R1 "$R2.$R3.$R4.$R5" + + ## Write it to a !define for use in main script + FileOpen $R0 "$EXEDIR\Version.txt" w + FileWrite $R0 '!define version "$R1"' + FileClose $R0 + +SectionEnd \ No newline at end of file From 047808c89fcdb35eecb431e57c1beabd52de6c42 Mon Sep 17 00:00:00 2001 From: Hane Date: Tue, 7 Jan 2025 00:00:50 +0100 Subject: [PATCH 65/78] ref Env namespace for win32/reg stuff / startup toggle Bigger than usual commit, but all of this had to be done simultaneously --- src/back/backlasses.cpp | 214 ++++++++++++++++++++++++++++++--------- src/back/backlasses.h | 34 +++++-- src/cont/contclasses.cpp | 24 +++-- src/cont/contclasses.h | 3 + src/debug.h | 5 +- src/global.h | 4 +- src/qt/qtclasses.cpp | 11 +- src/qtestmain.cpp | 99 ++++++++++++------ 8 files changed, 292 insertions(+), 102 deletions(-) diff --git a/src/back/backlasses.cpp b/src/back/backlasses.cpp index cf5700b..e0238c7 100644 --- a/src/back/backlasses.cpp +++ b/src/back/backlasses.cpp @@ -1,7 +1,19 @@ #include "backlasses.h" #include "backfuncs.h" -std::string getPath(SettingsTargetDirectory target, bool create) { +using namespace Environment; + +wchar_t* getExeAbsPath(uint32_t *exeAbsPathLength) { + wchar_t *exeAbsPath = (wchar_t*)calloc(UNICODE_STRING_MAX_CHARS, sizeof(wchar_t)); + *exeAbsPathLength = GetModuleFileNameW( + NULL, + exeAbsPath, + UNICODE_STRING_MAX_CHARS + ); + return exeAbsPath; +} + +std::string getSettingsPath(SettingsTargetDirectory target, bool create) { wchar_t* settingsPath = nullptr; wchar_t settingsFile[] = L"\\settings.ini"; uint32_t settingsFileLen = (sizeof(settingsFile) / sizeof(wchar_t)) - 1; @@ -57,12 +69,8 @@ std::string getPath(SettingsTargetDirectory target, bool create) { case APP_PATH: { //Executable dir - settingsPath = (wchar_t*)calloc(UNICODE_STRING_MAX_CHARS, sizeof(wchar_t)); - exePathLength = GetModuleFileNameW( - NULL, - settingsPath, - UNICODE_STRING_MAX_CHARS - ); + settingsPath = getExeAbsPath(&exePathLength); + //reverse wcsstr while(exePathLength >= 0) { if(settingsPath[exePathLength] == '\\') { @@ -730,8 +738,23 @@ Endpoint* Overseer::addEndpoint(std::wstring endpointId, /* out */Flows* flow = } Overseer::Overseer() : epsc(this){ - //Initializing COM library log_debugcpp("Initializing Overseer"); + + //Storing exe path for later use (mainly "Run on startup") + log_debugcpp("-Caching exe path"); + uint32_t cPathLen = 0; + wchar_t* cPath = getExeAbsPath(&cPathLen); + exeAbsPath = cPath; + + //Detecting install scope + wchar_t *machine = wcsstr(cPath, L"Program Files"); + if (!machine) + Environment::scope = HKEY_CURRENT_USER; + else Environment::scope = HKEY_LOCAL_MACHINE; + free(cPath); + + //Initializing COM library + log_debugcpp("-Initializing COM"); initCOMLibrary(); //Obtaining playback endpoint collection on this point in time @@ -744,11 +767,44 @@ Overseer::Overseer() : epsc(this){ if(FAILED(deviceEnumerator->RegisterEndpointNotificationCallback(((IMMNotificationClient*)&epsc)))) { log_debugcpp("when no enchufas......"); } } -void Overseer::populateSystemValues() { - updateColors(); +NGuid Overseer::getGuid() { + return guid; } -void Overseer::openControlPanel() { +std::vector Overseer::getPlaybackEndpoints() { + return playbackDevices; +} + +std::vector Overseer::getCaptureEndpoints() { + return captureDevices; +} + +void Overseer::updateEndpointInfo(std::wstring endpointId) { + log_wdebugcpp(L"new name Endpoint id: " + endpointId); + //todo: reintroduce capture devices + for(auto ep : playbackDevices) { + if (ep->getId() == endpointId) { + ep->updateName(); + osh->updateFrontEndpointName(ep); + break; + } + } +} + +Overseer::~Overseer(){ + log_debugcpp("cum"); + deviceEnumerator->Release(); + for(unsigned long long i = 0; i < playbackDevices.size(); i++){ + delete(playbackDevices.at(i)); + } +} + +void Environment::populateSystemValues() { + updateColors(); + Environment::startup = checkStartup(scope); +} + +void Environment::openControlPanel() { STARTUPINFOEXW startupConfig; PROCESS_INFORMATION processInfo; SecureZeroMemory(&startupConfig, sizeof(STARTUPINFOEXW)); @@ -774,7 +830,7 @@ void Overseer::openControlPanel() { } } -ProcessedNativeEvent Overseer::processTopLevelWindowMessage(void* msg) { +ProcessedNativeEvent Environment::processTopLevelWindowMessage(void* msg) { #ifdef WIN32 MSG *message = static_cast(msg); switch(message->message) { @@ -792,7 +848,7 @@ ProcessedNativeEvent Overseer::processTopLevelWindowMessage(void* msg) { #endif } -ProcessedNativeEvent Overseer::updateColors() { +ProcessedNativeEvent Environment::updateColors() { // DwmGetColorizationColor( WM_DWMCOLORIZATIONCOLORCHANGED DWORD value = 0; DWORD size = sizeof(DWORD); @@ -801,57 +857,119 @@ ProcessedNativeEvent Overseer::updateColors() { //Theme bg color result = RegGetValueW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", L"AppsUseLightTheme", RRF_RT_REG_DWORD, nullptr, &value, &size); - - this->lightMode = (bool)value; + lightMode = (bool)value; //Accent color result = RegGetValueW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\DWM", L"ColorizationColor", RRF_RT_REG_DWORD, nullptr, &value, &size); - if (result == ERROR_SUCCESS) { - this->accentColor = value; - } else this->accentColor = 0xffffffff; + accentColor = value; + } else accentColor = 0xffffffff; return ProcessedNativeEvent::COLORS; } -bool Overseer::isLightMode() { - return this->lightMode; +bool Environment::checkStartup(HKEY rootKeyFlags) { + //LSTATUS result; + DWORD typeReturned; + + //Checking if app entry exists + if(RegGetValueW(rootKeyFlags, L"Software\\Microsoft\\Windows\\CurrentVersion\\Run", LAPP_NAME, RRF_RT_REG_SZ, &typeReturned, nullptr, nullptr) != ERROR_SUCCESS && typeReturned != REG_SZ) + return false; + else return true; } -uint32_t Overseer::getAccentColor() { - return this->accentColor; -} +void Environment::updateStartupConfig(bool onStartup) { + DWORD typeReturned; + wchar_t regSubKey[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\Run\\"; -NGuid Overseer::getGuid() { - return guid; -} - -std::vector Overseer::getPlaybackEndpoints() { - return playbackDevices; -} + if(!onStartup) { + HKEY runKey; + if (RegOpenKeyExW( + scope, + regSubKey, + 0, + KEY_SET_VALUE, + &runKey + ) + == ERROR_SUCCESS) { + RegDeleteValueW(runKey, LAPP_NAME); + RegCloseKey(runKey); + } + } else { + LSTATUS result; + uint32_t cPathLen = 0; + wchar_t* cPath = getExeAbsPath(&cPathLen); + wchar_t* regPath = (wchar_t*)calloc(UNICODE_STRING_MAX_CHARS + 2, sizeof(wchar_t)); + //char* v = 0xFF'00'00'00'00'00'00'21; + regPath[0] = L'"'; + memcpy(regPath + 1, cPath, sizeof(wchar_t) * cPathLen); + memcpy(regPath + cPathLen + 1, L"\"", sizeof(wchar_t) * 2); -std::vector Overseer::getCaptureEndpoints() { - return captureDevices; -} - -void Overseer::updateEndpointInfo(std::wstring endpointId) { - log_wdebugcpp(L"new name Endpoint id: " + endpointId); - //todo: reintroduce capture devices - for(auto ep : playbackDevices) { - if (ep->getId() == endpointId) { - ep->updateName(); - osh->updateFrontEndpointName(ep); - break; - } + result = RegSetKeyValueW( + scope, + regSubKey, + LAPP_NAME, + REG_SZ, + (void*)regPath, + (cPathLen + 3) * sizeof(wchar_t)); + /* + * if (result != ERROR_SUCCESS) { + * wchar_t* error; + * FormatMessageW( + * FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + * nullptr, + * (DWORD)result, + * LANG_USER_DEFAULT, + * error, + * 1, + * nullptr); + * LocalFree(error); + * } + */ + free(cPath); } + return; } -Overseer::~Overseer(){ - log_debugcpp("cum"); - deviceEnumerator->Release(); - for(unsigned long long i = 0; i < playbackDevices.size(); i++){ - delete(playbackDevices.at(i)); +void Environment::setStartupConfig(bool onStartup) { + uint32_t cPathLen = 0; + wchar_t* cPath = getExeAbsPath(&cPathLen); + wchar_t startupParam[] = L"--change-startup"; + uint32_t startupParamLen = (sizeof(startupParam) / sizeof(wchar_t)) - 1; + wchar_t* completeParam = (wchar_t*)calloc(startupParamLen + 3, sizeof(wchar_t)); + memcpy(completeParam, startupParam, sizeof(wchar_t) * startupParamLen); + if (onStartup) + memcpy(completeParam + startupParamLen, L" 1", sizeof(wchar_t) * 3); + else + memcpy(completeParam + startupParamLen, L" 0", sizeof(wchar_t) * 3); + + if(scope == HKEY_LOCAL_MACHINE) { + ShellExecuteW( + NULL, + L"runas", + cPath, + completeParam, + NULL, // default dir + SW_SHOWNORMAL + ); + } else { + Environment::updateStartupConfig(onStartup); } + free(cPath); + free(completeParam); + return; +} + +bool Environment::isLightMode() { + return lightMode; +} + +bool Environment::isToRunAtStartup() { + return startup; +} + +uint32_t Environment::getAccentColor() { + return accentColor; } //int Overseer::getCaptureEndpoints(std::vector *captureEndpoints); diff --git a/src/back/backlasses.h b/src/back/backlasses.h index 4e67a09..7c7c412 100644 --- a/src/back/backlasses.h +++ b/src/back/backlasses.h @@ -4,10 +4,13 @@ #include "backsessionclasses.h" #include "global.h" #include "contclasses.h" +//#include "environment.h" class EndpointVolumeCallback; class Session; -std::string getPath(SettingsTargetDirectory target, bool create); + +wchar_t* getExeAbsPath(uint32_t *exeAbsPathLength); +std::string getSettingsPath(SettingsTargetDirectory target, bool create); // Convert a wide UTF16LE string to an UTF8 string static inline std::string utf16ToUtf8(const wchar_t* wstr) { @@ -146,12 +149,6 @@ class Overseer { public: Overseer(); NGuid getGuid(); - void populateSystemValues(); - void openControlPanel(); - ProcessedNativeEvent processTopLevelWindowMessage(void* msg); - ProcessedNativeEvent updateColors(); - bool isLightMode(); - uint32_t getAccentColor(); std::vector getPlaybackEndpoints(); std::vector getCaptureEndpoints(); @@ -171,11 +168,11 @@ class Overseer { ~Overseer(); private: + std::wstring exeAbsPath; + void initCOMLibrary(); NGuid guid; - bool lightMode; - uint32_t accentColor; IMMDeviceEnumerator *deviceEnumerator; EndpointSituationCallback epsc; @@ -202,3 +199,22 @@ class EndpointNewSessionCallback : public IAudioSessionNotification { ULONG ref = 1; EndpointHandler *eph; }; + +namespace Environment { + void populateSystemValues(); + void openControlPanel(); + ProcessedNativeEvent processTopLevelWindowMessage(void* msg); + ProcessedNativeEvent updateColors(); + bool checkStartup(HKEY rootKeyFlags); + void updateStartupConfig(bool onStartup); + void setStartupConfig(bool onStartup); + bool isLightMode(); + bool isToRunAtStartup(); + uint32_t getAccentColor(); + + static std::wstring exeAbsPath; + static bool lightMode; + static bool startup = false; + static HKEY scope; + static uint32_t accentColor; +}; diff --git a/src/cont/contclasses.cpp b/src/cont/contclasses.cpp index 067a32f..368f466 100644 --- a/src/cont/contclasses.cpp +++ b/src/cont/contclasses.cpp @@ -3,7 +3,7 @@ void setConfigDirToDefaults() { #define tryFileDir(dir, create) do { \ - OverseerHandler::settingsPath = getPath(dir, create); \ + OverseerHandler::settingsPath = getSettingsPath(dir, create); \ set = ini::UserSettings::createSettings(OverseerHandler::settingsPath.c_str()); \ if(set) { \ return; \ @@ -247,24 +247,36 @@ std::string OverseerHandler::getSettingsPath(){ return OverseerHandler::settingsPath; } +void OverseerHandler::updateStartupConfig(bool onStartup) { + Environment::updateStartupConfig(onStartup); +} + +void OverseerHandler::setStartupConfig(bool onStartup) { + Environment::setStartupConfig(onStartup); +} + void OverseerHandler::populateSystemValues() { - this->os->populateSystemValues(); + Environment::populateSystemValues(); } void OverseerHandler::openControlPanel() { - this->os->openControlPanel(); + Environment::openControlPanel(); } ProcessedNativeEvent OverseerHandler::processTopLevelWindowMessage(void* msg) { - return this->os->processTopLevelWindowMessage(msg); + return Environment::processTopLevelWindowMessage(msg); } bool OverseerHandler::isLightMode() { - return this->os->isLightMode(); + return Environment::isLightMode(); +} + +bool OverseerHandler::isToRunAtStartup() { + return Environment::isToRunAtStartup(); } uint32_t OverseerHandler::getAccentColor() { - return this->os->getAccentColor(); + return Environment::getAccentColor(); } std::vector OverseerHandler::getPlaybackEndpoints() { diff --git a/src/cont/contclasses.h b/src/cont/contclasses.h index 2a4a9f0..ac4ecdc 100644 --- a/src/cont/contclasses.h +++ b/src/cont/contclasses.h @@ -105,10 +105,13 @@ public: static void setSettingsPath(std::string path); static std::string getSettingsPath(); static inline std::string settingsPath; + void updateStartupConfig(bool onStartup); + void setStartupConfig(bool onStartup); void populateSystemValues(); void openControlPanel(); ProcessedNativeEvent processTopLevelWindowMessage(void* msg); bool isLightMode(); + bool isToRunAtStartup(); uint32_t getAccentColor(); //void setChangeFrontDefaultsFunction(std::function changeFrontDefaults); diff --git a/src/debug.h b/src/debug.h index 176825a..d4b9195 100644 --- a/src/debug.h +++ b/src/debug.h @@ -2,6 +2,8 @@ #if defined (QT_DEBUG) || defined (DEBUG) || defined (_DEBUG) +#define PIPE_NAME "Mixerq-dev" + #ifdef INIT_FILELOG std::wstring_convert, wchar_t> converter; FILE* fileLog; @@ -72,7 +74,8 @@ std::bitset varToBitset(T info) { #define print_as_binary(info) #define log_to_file(fmt, cnt...) #define initialize_file_log() false -#define close_file_log_buffer() +#define close_file_log_buffer() +#define PIPE_NAME "Mixerq" #endif /* Here as a quick reference, in case smthn similar is needed again */ diff --git a/src/global.h b/src/global.h index 7c02902..ba5678b 100644 --- a/src/global.h +++ b/src/global.h @@ -16,6 +16,9 @@ //#include "settings.h" //TODO: Use tr();? QTranslator +#define APP_NAME "MixerQ" +#define LAPP_NAME L"MixerQ" + #define STRING_MUTE "Mute" #define STRING_UNMUTE "Unmute" #define STRING_QUIT "Quit" @@ -38,7 +41,6 @@ #define LSTRING_UNNAMED_SESSION L"Unnamed session" - //INIT BACK enum SettingsTargetDirectory { diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 15f3768..fa91703 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -37,7 +37,6 @@ CustomWidgetEvent::CustomWidgetEvent(QEvent::Type type, T payload) : QEvent(t this->payload = payload; } - /* * MeterSlider::MeterSlider(Qt::Orientation orientation, QWidget* parent) { * //style = new MixerStyle(); @@ -225,7 +224,7 @@ QRect MainWindow::setSizePosition(QScreen* screen, int width, int height) { QRect availableRes = screen->availableGeometry(); int arx1, arx2, ary1, ary2; availableRes.getCoords(&arx1, &ary1, &arx2, &ary2); - log_debugcpp("Available res: " + std::to_string(arx1) + " " + std::to_string(arx2)+" " + std::to_string(ary1) + " " + std::to_string(ary2);) + log_debugcpp("Available res: " + std::to_string(arx1) + " " + std::to_string(arx2)+" " + std::to_string(ary1) + " " + std::to_string(ary2)); if(height > (ary2 - ary1)) height = (ary2 - ary1); log_debugcpp("Height res: " + std::to_string(height)); @@ -959,11 +958,15 @@ HeaderWidget::HeaderWidget(QWidget *parent) : QWidget(parent) { } }); - text = "&" STRING_STARTUP; startup = new QCheckBox(text, this); - //connect(openCP, &QPushButton::clicked, [](){ osh->openControlPanel(); }); + if(osh->isToRunAtStartup()) { + startup->setChecked(true); + } + connect(startup, &QCheckBox::stateChanged, [this](){ + osh->setStartupConfig(startup->isChecked()); + }); widgetLayout->addWidget(openCP , 0, 0, 2, 2); widgetLayout->addWidget(channels, 0, 2, 1, 2); diff --git a/src/qtestmain.cpp b/src/qtestmain.cpp index 8b34385..b036c72 100644 --- a/src/qtestmain.cpp +++ b/src/qtestmain.cpp @@ -7,13 +7,16 @@ OverseerHandler *osh = nullptr; ini::UserSettings *set = nullptr; - -QApplication* createApplication(int &argc, char *argv[]) -{ + +bool startupRun = false; +bool onStartup = false; +char* userSettingsPath = nullptr; + +QApplication* createApplication(int &argc, char *argv[]) { return new QApplication(argc, argv); } -bool isSingleInstanceRunning(QString appName) { +bool isInstanceRunning(QString appName) { QLocalSocket socket; socket.connectToServer(appName); bool isOpen = socket.isOpen(); @@ -21,7 +24,7 @@ bool isSingleInstanceRunning(QString appName) { return isOpen; } -QLocalServer* startSingleInstanceServer(QString appName) { +QLocalServer* startInstanceServer(QString appName) { QLocalServer* server = new QLocalServer; server->setSocketOptions(QLocalServer::WorldAccessOption); server->listen(appName); @@ -32,12 +35,34 @@ void closeDebugFileLog() { close_file_log_buffer(); } -char* parseCmdArgs(int argc, char* argv[]) { - if(argc == 1) return nullptr; - char arg[] = "--config-path="; - if(strstr(argv[1], arg)) { - return argv[1] + (sizeof(arg) / sizeof(arg[0])) - 1; - } else return nullptr; +void parseCmdArgs(int argc, char* argv[]) { + if(argc == 1) return; + //char* configPath = nullptr; + char* arg[] = { (char*)"--config-path=", (char*)"--change-startup" }; + + for (int i = 1; i < argc; i++) { + if(strstr(argv[i], arg[0])) { + userSettingsPath = argv[i] + strlen(arg[0]); + } + + if(strstr(argv[i], arg[1])) { + if (++i >= argc) return; + switch (argv[i][0]) { + case '0': + startupRun = true; + onStartup = false; + break; + case '1': + startupRun = true; + onStartup = true; + break; + default: + break; + } + return; + } + } + return; } /* set_terminate @@ -48,41 +73,49 @@ char* parseCmdArgs(int argc, char* argv[]) { */ int main (int argc, char* argv[]) { - /* - * QStringList styles = QStyleFactory::keys(); - * for(QString a : styles) { - * log_debugcpp(a.toStdString()); - * } + * Debug: logging report file */ - char* userSettingsPath = parseCmdArgs(argc, argv); - if (userSettingsPath) - set = ini::UserSettings::createSettings(userSettingsPath, true); - - if (set) - OverseerHandler::settingsPath = std::string(userSettingsPath); - else setConfigDirToDefaults(); - initialize_file_log(); atexit(closeDebugFileLog); - //Check if running - //https://stackoverflow.com/questions/48060989/qt-show-application-if-currently-running - //std::set_terminate(closeDebugFileLog2); - if (!isSingleInstanceRunning("Mixer")) - startSingleInstanceServer("Mixer"); - else exit(0); - - QApplication::setStyle(new MixerStyle(QStyleFactory::create("Fusion"))); //QApplication::setFont(font); //QPalette palette = QGuiApplication::palette(); - //todo: ez full apply os accent colorw + //palette.setColor(QPalette::Active, QPalette::Highlight, QColor(255, 192, 203, 200)); //QColor(30,30,30,100)); //QGuiApplication::setPalette(palette); osh = new OverseerHandler(); osh->populateSystemValues(); + + /* + * Arg parsing: (admin?) startup change run + */ + parseCmdArgs(argc, argv); + if (startupRun) { + if (!isInstanceRunning(PIPE_NAME)) exit(-1); + //if (startupChangeInfo->permissionChangeScope == NONE) exit(-1); + osh->updateStartupConfig(onStartup); + exit(0); + } + + //Check if running + //https://stackoverflow.com/questions/48060989/qt-show-application-if-currently-running + if (!isInstanceRunning(PIPE_NAME)) + startInstanceServer(PIPE_NAME); + else exit(0); + + /* + * Config file init + */ + if (userSettingsPath) + set = ini::UserSettings::createSettings(userSettingsPath, true); + + if (set) + OverseerHandler::settingsPath = std::string(userSettingsPath); + else setConfigDirToDefaults(); + StylingHelper::setBackgroundColor(osh->isLightMode()); //qRegisterMetaType(); From c87c8d099061d1d9353692220245e9b6cbff7573 Mon Sep 17 00:00:00 2001 From: Hane Date: Tue, 7 Jan 2025 18:53:17 +0100 Subject: [PATCH 66/78] set: fix wrong save path / fixed null deref / more env refactor --- src/back/backlasses.cpp | 190 +++++++++++++++++++-------------------- src/back/backlasses.h | 5 +- src/cont/contclasses.cpp | 4 +- src/qt/qtclasses.cpp | 9 +- src/settings.cpp | 28 +++--- 5 files changed, 117 insertions(+), 119 deletions(-) diff --git a/src/back/backlasses.cpp b/src/back/backlasses.cpp index e0238c7..a0f0e06 100644 --- a/src/back/backlasses.cpp +++ b/src/back/backlasses.cpp @@ -3,101 +3,6 @@ using namespace Environment; -wchar_t* getExeAbsPath(uint32_t *exeAbsPathLength) { - wchar_t *exeAbsPath = (wchar_t*)calloc(UNICODE_STRING_MAX_CHARS, sizeof(wchar_t)); - *exeAbsPathLength = GetModuleFileNameW( - NULL, - exeAbsPath, - UNICODE_STRING_MAX_CHARS - ); - return exeAbsPath; -} - -std::string getSettingsPath(SettingsTargetDirectory target, bool create) { - wchar_t* settingsPath = nullptr; - wchar_t settingsFile[] = L"\\settings.ini"; - uint32_t settingsFileLen = (sizeof(settingsFile) / sizeof(wchar_t)) - 1; - wchar_t maxPathBypass[] = L"\\\\?\\"; - uint32_t exePathLength = 0; - wchar_t folderPath[] = L"\\mixerq"; - uint32_t maxPathBypassLen = (sizeof(maxPathBypass)/ sizeof(wchar_t)) - 1; - uint32_t folderPathLen = (sizeof(folderPath) / sizeof(wchar_t)) - 1; - wchar_t* roamingPath = nullptr; - - log_wdebugcpp(L"Bypass size: " + std::to_wstring((sizeof(maxPathBypass)/sizeof(maxPathBypass[0])))); - - switch(target) { - case HOME_DIR: - { - if(SHGetKnownFolderPath( - FOLDERID_RoamingAppData, - 0, - NULL, - &roamingPath) - == S_OK) { - //Retrieve path len - uint32_t pathLen = 0; - wchar_t currentChar = roamingPath[pathLen]; - while(currentChar != '\0') { - pathLen++; - currentChar = roamingPath[pathLen]; - } - - settingsPath = (wchar_t*)calloc(pathLen + - maxPathBypassLen + - folderPathLen + - settingsFileLen, - sizeof(wchar_t)); - memcpy(settingsPath, maxPathBypass, sizeof(wchar_t) * maxPathBypassLen); - memcpy(settingsPath + (maxPathBypassLen), roamingPath, sizeof(wchar_t) * pathLen); - CoTaskMemFree(roamingPath); - memcpy(settingsPath + (maxPathBypassLen + pathLen), - folderPath, sizeof(wchar_t) * folderPathLen); - log_wdebugcpp(L"Settings folder path: " + std::wstring(settingsPath)); - - if(CreateDirectoryW(settingsPath, NULL) || GetLastError() == ERROR_ALREADY_EXISTS) { - memcpy(settingsPath + (maxPathBypassLen + pathLen + folderPathLen), - settingsFile, sizeof(wchar_t) * settingsFileLen); - std::string utf8path = utf16ToUtf8(settingsPath); - free(settingsPath); - return utf8path; - } - } - } - return nullptr; - break; - case APP_PATH: - { - //Executable dir - settingsPath = getExeAbsPath(&exePathLength); - - //reverse wcsstr - while(exePathLength >= 0) { - if(settingsPath[exePathLength] == '\\') { - memset(settingsPath + exePathLength, - 0, - (UNICODE_STRING_MAX_CHARS - exePathLength) * sizeof(wchar_t)); - break; - } else exePathLength--; - } - log_wdebugcpp(L"Exe folder: " + std::wstring(settingsPath)); - if((UNICODE_STRING_MAX_CHARS - exePathLength) > (settingsFileLen + 1)) { - memcpy(settingsPath + exePathLength, settingsFile, sizeof(wchar_t) * settingsFileLen); - std::string utf8path = utf16ToUtf8(settingsPath); - free(settingsPath); - return utf8path; - } - } - return nullptr; - break; - default: - return nullptr; - break; - } - return nullptr; - -} - EndpointNewSessionCallback::EndpointNewSessionCallback(EndpointHandler* eph){ this->eph = eph; } @@ -799,6 +704,100 @@ Overseer::~Overseer(){ } } +wchar_t* Environment::getExeAbsPath(uint32_t *exeAbsPathLength) { + wchar_t *exeAbsPath = (wchar_t*)calloc(UNICODE_STRING_MAX_CHARS, sizeof(wchar_t)); + *exeAbsPathLength = GetModuleFileNameW( + NULL, + exeAbsPath, + UNICODE_STRING_MAX_CHARS + ); + return exeAbsPath; +} + +std::string Environment::createSettingsPath(SettingsTargetDirectory target, bool create) { + wchar_t* settingsPath = nullptr; + wchar_t settingsFile[] = L"\\settings.ini"; + uint32_t settingsFileLen = (sizeof(settingsFile) / sizeof(wchar_t)) - 1; + wchar_t maxPathBypass[] = L"\\\\?\\"; + uint32_t exePathLength = 0; + wchar_t folderPath[] = L"\\" LAPP_NAME; + uint32_t maxPathBypassLen = (sizeof(maxPathBypass)/ sizeof(wchar_t)) - 1; + uint32_t folderPathLen = (sizeof(folderPath) / sizeof(wchar_t)) - 1; + wchar_t* roamingPath = nullptr; + + log_wdebugcpp(L"Bypass size: " + std::to_wstring((sizeof(maxPathBypass)/sizeof(maxPathBypass[0])))); + + switch(target) { + case HOME_DIR: + { + if(SHGetKnownFolderPath( + FOLDERID_RoamingAppData, + 0, + NULL, + &roamingPath) + == S_OK) { + //Retrieve path len + uint32_t pathLen = 0; + wchar_t currentChar = roamingPath[pathLen]; + while(currentChar != '\0') { + pathLen++; + currentChar = roamingPath[pathLen]; + } + + settingsPath = (wchar_t*)calloc(pathLen + + maxPathBypassLen + + folderPathLen + + settingsFileLen, + sizeof(wchar_t)); + memcpy(settingsPath, maxPathBypass, sizeof(wchar_t) * maxPathBypassLen); + memcpy(settingsPath + (maxPathBypassLen), roamingPath, sizeof(wchar_t) * pathLen); + CoTaskMemFree(roamingPath); + memcpy(settingsPath + (maxPathBypassLen + pathLen), + folderPath, sizeof(wchar_t) * folderPathLen); + log_wdebugcpp(L"Settings folder path: " + std::wstring(settingsPath)); + + if(CreateDirectoryW(settingsPath, NULL) || GetLastError() == ERROR_ALREADY_EXISTS) { + memcpy(settingsPath + (maxPathBypassLen + pathLen + folderPathLen), + settingsFile, sizeof(wchar_t) * settingsFileLen); + std::string utf8path = utf16ToUtf8(settingsPath); + free(settingsPath); + return utf8path; + } + } + } + return nullptr; + break; + case APP_PATH: + { + //Executable dir + settingsPath = getExeAbsPath(&exePathLength); + + //reverse wcsstr + while(exePathLength >= 0) { + if(settingsPath[exePathLength] == '\\') { + memset(settingsPath + exePathLength, + 0, + (UNICODE_STRING_MAX_CHARS - exePathLength) * sizeof(wchar_t)); + break; + } else exePathLength--; + } + log_wdebugcpp(L"Exe folder: " + std::wstring(settingsPath)); + if((UNICODE_STRING_MAX_CHARS - exePathLength) > (settingsFileLen + 1)) { + memcpy(settingsPath + exePathLength, settingsFile, sizeof(wchar_t) * settingsFileLen); + std::string utf8path = utf16ToUtf8(settingsPath); + free(settingsPath); + return utf8path; + } + } + return nullptr; + break; + default: + return nullptr; + break; + } + return nullptr; +} + void Environment::populateSystemValues() { updateColors(); Environment::startup = checkStartup(scope); @@ -932,6 +931,7 @@ void Environment::updateStartupConfig(bool onStartup) { } void Environment::setStartupConfig(bool onStartup) { + //TODO: Use the cache!!!! lol uint32_t cPathLen = 0; wchar_t* cPath = getExeAbsPath(&cPathLen); wchar_t startupParam[] = L"--change-startup"; diff --git a/src/back/backlasses.h b/src/back/backlasses.h index 7c7c412..071e0e1 100644 --- a/src/back/backlasses.h +++ b/src/back/backlasses.h @@ -9,9 +9,6 @@ class EndpointVolumeCallback; class Session; -wchar_t* getExeAbsPath(uint32_t *exeAbsPathLength); -std::string getSettingsPath(SettingsTargetDirectory target, bool create); - // Convert a wide UTF16LE string to an UTF8 string static inline std::string utf16ToUtf8(const wchar_t* wstr) { if(!wstr || wstr[0] == '\0') return std::string(); @@ -201,6 +198,8 @@ class EndpointNewSessionCallback : public IAudioSessionNotification { }; namespace Environment { + wchar_t* getExeAbsPath(uint32_t *exeAbsPathLength); + std::string createSettingsPath(SettingsTargetDirectory target, bool create); void populateSystemValues(); void openControlPanel(); ProcessedNativeEvent processTopLevelWindowMessage(void* msg); diff --git a/src/cont/contclasses.cpp b/src/cont/contclasses.cpp index 368f466..e9561f0 100644 --- a/src/cont/contclasses.cpp +++ b/src/cont/contclasses.cpp @@ -3,8 +3,8 @@ void setConfigDirToDefaults() { #define tryFileDir(dir, create) do { \ - OverseerHandler::settingsPath = getSettingsPath(dir, create); \ - set = ini::UserSettings::createSettings(OverseerHandler::settingsPath.c_str()); \ + OverseerHandler::settingsPath = Environment::createSettingsPath(dir, create); \ + set = ini::UserSettings::createSettings(OverseerHandler::settingsPath.c_str(), create); \ if(set) { \ return; \ } else OverseerHandler::settingsPath.clear(); \ diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index fa91703..376cdcd 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -947,9 +947,12 @@ HeaderWidget::HeaderWidget(QWidget *parent) : QWidget(parent) { channels->setChecked(true); } connect(channels, &QCheckBox::stateChanged, [this, parent](){ - set->setValue("show_channels", channels->isChecked(), sizeof("show_channels")); - if(!OverseerHandler::settingsPath.empty()){ - set->save(OverseerHandler::settingsPath.c_str()); + //TODO: Find a better way to auto no-op when there's no settings file + if(set) { + set->setValue("show_channels", channels->isChecked(), sizeof("show_channels")); + if(!OverseerHandler::settingsPath.empty()){ + set->save(OverseerHandler::settingsPath.c_str()); + } } if(parent) { QEvent explosion = QEvent((QEvent::Type)CustomQEvent::RecomposeMainWindow); diff --git a/src/settings.cpp b/src/settings.cpp index b45a9f1..6845254 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -27,7 +27,7 @@ namespace ini { isCRLF = true; *(nextLine - 1) = '\0'; } else if (nextLine) *nextLine = '\0'; // temporarily terminate the current line - log_debugcpp("curLine: " + std::string(curLine) + " "); + log_debugcpp("[SET] curLine: " + std::string(curLine) + " "); separator = strchr(curLine, '='); if(!separator) @@ -36,7 +36,7 @@ namespace ini { key = trimAndAllocate(curLine); value = trimAndAllocate(separator + 1); values.try_emplace(key, value); - log_debugcpp("ini Map size: " + std::to_string(values.size())); + log_debugcpp("[SET] ini Map size: " + std::to_string(values.size())); *separator = '='; nextIteration: @@ -76,10 +76,10 @@ namespace ini { void UserSettings::setValue(char* key, bool value, uint64_t keySize) { char *newKey; - log_debugcpp("Pos value: " + std::to_string((intptr_t)pos)); - log_debugcpp("Neg value: " + std::to_string((intptr_t)neg)); + log_debugcpp("[SET] Pos value: " + std::to_string((intptr_t)pos)); + log_debugcpp("[SET] Neg value: " + std::to_string((intptr_t)neg)); if (auto search = values.find(key); search != values.end()) { - log_debugcpp("Previous value: " + std::to_string((intptr_t)values[key])); + log_debugcpp("[SET] Previous value: " + std::to_string((intptr_t)values[key])); if (!(search->second == pos || search->second == neg)) { free(search->second); } @@ -90,22 +90,16 @@ namespace ini { } newKey = (char*)calloc(keySize, sizeof(char)); + memcpy(newKey, key, keySize * sizeof(char)); values.insert(std::make_pair(newKey, value ? pos : neg)); return; } bool UserSettings::save(const char* path) { - wchar_t maxPathBypass[] = L"\\\\?\\"; - uint32_t maxPathBypassLen = (sizeof(maxPathBypass)/ sizeof(wchar_t)) - 1; - if(!path) return false; uint64_t convertedPathSize = 0; - wchar_t* convertedPath = Utf8toUtf16(path, &convertedPathSize); - if(!convertedPath) return false; - wchar_t* utf16Path = (wchar_t*)calloc(maxPathBypassLen + convertedPathSize, sizeof(wchar_t)); - memcpy(utf16Path, maxPathBypass, sizeof(wchar_t) * maxPathBypassLen); - memcpy(utf16Path + maxPathBypassLen, convertedPath, sizeof(wchar_t) * convertedPathSize); - free(convertedPath); + wchar_t* utf16Path = Utf8toUtf16(path, &convertedPathSize); + if(!utf16Path) return false; #define releaseBeforeReturn() do { \ CloseHandle(settingsHandle); \ @@ -147,7 +141,8 @@ namespace ini { 0, CREATE_ALWAYS, NULL); - if(settingsHandle == INVALID_HANDLE_VALUE) { + if(settingsHandle == INVALID_HANDLE_VALUE) { + log_debugcpp("[SET] Can't save to file: " + std::to_string(GetLastError())); releaseBeforeReturn(); return false; } @@ -197,7 +192,8 @@ namespace ini { 0, (create ? OPEN_ALWAYS : OPEN_EXISTING), NULL); - if(settingsHandle == INVALID_HANDLE_VALUE) { + if(settingsHandle == INVALID_HANDLE_VALUE) { + log_debugcpp("[SET] Can't create settings file: " + std::to_string(GetLastError())); releaseBeforeReturn(); return nullptr; } From cdde1ce9b8b8e9ec1a344bac84b373dac4a4caff Mon Sep 17 00:00:00 2001 From: Hane Date: Sun, 12 Jan 2025 23:16:36 +0100 Subject: [PATCH 67/78] installer done, recipe cleanup --- assets/installer.ico | Bin 0 -> 196630 bytes assets/installer.xcf | Bin 0 -> 103610 bytes assets/uninstaller.ico | Bin 0 -> 196273 bytes bueno.bat | 5 +- install/installer.nsi | 233 +++++++++++++++++++++++++---------------- install/version.nsi | 6 +- qtest.pro | 30 ++++-- 7 files changed, 172 insertions(+), 102 deletions(-) create mode 100644 assets/installer.ico create mode 100644 assets/installer.xcf create mode 100644 assets/uninstaller.ico diff --git a/assets/installer.ico b/assets/installer.ico new file mode 100644 index 0000000000000000000000000000000000000000..b4e6f82b645cfabb8830e8421e97a78897465330 GIT binary patch literal 196630 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