From 4174e5bf93a7a9bdadd2675fc94d145debe9fd6f Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Thu, 1 Sep 2022 16:01:29 +0200 Subject: TabView -> two colums [Output, Input] --- src/CMakeLists.txt | 3 + src/icons/checkmark.svg | 20 +++---- src/main.cpp | 25 +++++++- src/mainwindow.cpp | 12 ++-- src/mainwindow.ui | 86 ++++++++++++++++++++++----- src/setdefault.cpp | 151 ++++++++++++++++++++++++++++++++++++++++++++++++ src/setdefault.h | 8 +++ src/slxoutput.cpp | 4 +- 8 files changed, 276 insertions(+), 33 deletions(-) create mode 100644 src/setdefault.cpp create mode 100644 src/setdefault.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 709a77e..78b49b4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -12,12 +12,14 @@ find_package(Qt5Core ${QT_MINIMUM_VERSION} REQUIRED) set(pavucontrol-qt_HDRS main.h mainwindow.h + setdefault.h slxoutput.h ) set(pavucontrol-qt_SRCS main.cpp mainwindow.cpp + setdefault.cpp slxoutput.cpp ) @@ -31,6 +33,7 @@ qt5_add_resources(pavucontrol-qt_RCS resources.qrc) add_executable(pavucontrol-qt ${pavucontrol-qt_SRCS} + ${pavucontrol-qt_HDRS} ${pavucontrol-qt_RCS} ${pavucontrol-qt_UI_HEADERS} ) diff --git a/src/icons/checkmark.svg b/src/icons/checkmark.svg index 214db50..fad3bec 100644 --- a/src/icons/checkmark.svg +++ b/src/icons/checkmark.svg @@ -1,21 +1,19 @@ - - - + id="surface1" + transform="matrix(0.83191502,0,0,0.83175649,-1.3106403,-1.587701)" + style="fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:0.999583;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"> + d="M 28.28125,6.28125 11,23.5625 3.71875,16.28125 l -1.4375,1.4375 8,8 0.71875,0.6875 0.71875,-0.6875 18,-18 z" + id="path2" + style="fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:0.999583;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> diff --git a/src/main.cpp b/src/main.cpp index cc149a4..b1736cc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,7 @@ #include "main.h" #include "mainwindow.h" #include "slxoutput.h" +#include "setdefault.h" #include #include @@ -19,6 +20,7 @@ #include #include #include +#include // libkf5pulseaudioqt-dev // Public @@ -76,6 +78,7 @@ static void addDevicePortToWindow(PulseAudioQt::Card *card, PulseAudioQt::Device volume = 65535; device->setVolume(volume); } + device->setMuted(false); } //knownCardPortCombos.insert(card->name() + ":" + port->name()); _mainWindow->getDevice(card, device, port, isOutput)->updateDeviceAndPort(defaultSinkAndPort, device->isMuted(), @@ -430,10 +433,26 @@ void enableSource(const QString &source, const QString &port) int main(int argc, char **argv) { QApplication a(argc, argv); - printf("Muh\n"); auto *i = PulseAudioQt::Context::instance(); - printf("Pa is %p = %d\n", i, (int)i->isValid()); - printf("Cards: %d\n", (int)i->cards().size()); + + a.setOrganizationName(QStringLiteral("slxmix")); + a.setApplicationVersion(QLatin1String("1.0.0.0.0")); + QCommandLineParser parser; + parser.setApplicationDescription(QObject::tr("SLXmix Volume Control")); + parser.addHelpOption(); + QCommandLineOption selectOption(QStringList() << QStringLiteral("output") << QStringLiteral("o"), + QObject::tr("Select a specific output configuration and quit. Will select the Profile/Sink/Port combo that best matches the list of given keywords, e.g. 'HDMI 1 5.1'"), + QStringLiteral("keywords")); + parser.addOption(selectOption); + parser.process(a); + // Select default output and exit + if (parser.isSet(selectOption)) { + QString what = parser.value(selectOption); + QTimer::singleShot(100, [what]() { + setDefaultOutput(what); + }); + return a.exec(); + } // There's no signal, or no way to check if we're connected to PA, so // just wait a bit and hope for the best. diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 0fc6341..83b999e 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -10,8 +10,12 @@ MainWindow::MainWindow() : QDialog() { setupUi(this); setWindowFlags(Qt::WindowStaysOnTopHint | windowFlags()); - outputTab->setLayout(new QVBoxLayout(outputTab)); - inputTab->setLayout(new QVBoxLayout(inputTab)); + // Make heading 20% larger than system default + QFont f = inputLabel->font(); + f.setPointSize(f.pointSize() * 6 / 5); + inputLabel->setFont(f); + outputLabel->setFont(f); + cardLabel->setFont(f); } MainWindow::~MainWindow() @@ -86,9 +90,9 @@ void MainWindow::insertItemWidget(SlxOutput* w, bool isDevice) if (w->type() == SlxOutput::CardProfileItem) { container = cards; } else if (w->type() == SlxOutput::SinkPortItem) { - container = qobject_cast(outputTab->layout()); + container = outputListLayout; } else { - container = qobject_cast(inputTab->layout()); + container = inputListLayout; } for (idx = 0; idx < container->count(); ++idx) { auto *l = container->itemAt(idx); diff --git a/src/mainwindow.ui b/src/mainwindow.ui index af88c18..03e1aae 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -15,20 +15,80 @@ - - - 0 + + + + + + + + 75 + true + + + + Output + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + 75 + true + + + + Input + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Card/Profile - - - Output - - - - - Input - - diff --git a/src/setdefault.cpp b/src/setdefault.cpp new file mode 100644 index 0000000..21b368e --- /dev/null +++ b/src/setdefault.cpp @@ -0,0 +1,151 @@ +#include "setdefault.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static const QRegularExpression _splitter("[() \t-]"); + +static int countMatches(const QStringList &find, const QString &haystack); + +static void setDefaultSink(const QString &bestCardId, const QStringList &words); + +void setDefaultOutput(const QString &name) +{ + auto *ctx = PulseAudioQt::Context::instance(); + const QStringList words = name.toLower().split(_splitter, QString::SkipEmptyParts); + int bestNum = -1000; + int bestProfileIdx = -1; + PulseAudioQt::Card *bestCard = nullptr; + for (auto *card : ctx->cards()) { + int i = -1; + for (auto *profile : card->profiles()) { + ++i; + if (profile->sinks() == 0) + continue; + int matches = countMatches(words, profile->description()); + // Prefer Duplex + if (profile->description().contains(QLatin1String("Duplex"))) { + matches += 2; + } + // Penaltize unavailable profiles + if (profile->availability() != PulseAudioQt::Profile::Available) { + matches -= 11; + } + qDebug() << matches << "Profile" << profile->description(); + if (matches > bestNum) { + bestNum = matches; + bestCard = card; + bestProfileIdx = i; + } + } + } + if (bestCard == nullptr) { + qDebug() << "No match found"; + qApp->quit(); + return; + } + QString bestCardId = bestCard->name(); + if (bestCard->activeProfileIndex() == bestProfileIdx) { + setDefaultSink(bestCardId, words); + return; + } + QObject::connect(bestCard, &PulseAudioQt::Card::activeProfileIndexChanged, [bestCardId, words]() { + QTimer::singleShot(10, [bestCardId, words]() { + setDefaultSink(bestCardId, words); + }); + }); + qDebug() << "Switching card's profile..."; + bestCard->setActiveProfileIndex(bestProfileIdx); + // Wait for switch - add safety timeout + QTimer::singleShot(1000, []() { + qDebug() << "WARNING - timeout reached, exiting..."; + qApp->exit(1); + }); +} + +static void setDefaultSink(const QString &bestCardId, const QStringList &words) +{ + auto *ctx = PulseAudioQt::Context::instance(); + int idx = -1; + int bestCardIdx = -1; + for (auto *card : ctx->cards()) { + idx++; + if (card->name() == bestCardId) { + bestCardIdx = idx; + break; + } + } + int bestNum = -1000; + int bestPortIdx = -1; + PulseAudioQt::Sink *bestSink = nullptr; + for (auto *sink : ctx->sinks()) { + if (bestCardIdx != -1 && sink->cardIndex() != bestCardIdx) + continue; // Not sink of card + int sinkMatchVal = countMatches(words, sink->description()); + int pi = -1; + for (auto *port : sink->ports()) { + pi++; + int portMatchVal = sinkMatchVal + countMatches(words, port->description()); + qDebug() << portMatchVal << "SinkPort" << sink->description() << port->description(); + if (portMatchVal > bestNum) { + bestNum = portMatchVal; + bestSink = sink; + bestPortIdx = pi; + } + + } + } + if (bestSink == nullptr) { + qDebug() << "No match found"; + qApp->exit(1); + } + if (bestPortIdx != -1 && bestSink->activePortIndex() != bestPortIdx) { + qDebug() << "Selecting port"; + bestSink->setActivePortIndex(bestPortIdx); + } + QTimer::singleShot(10, [bestSink]() { + qDebug() << "Unmuting sink and selecting as default:" << bestSink->description(); + bestSink->setDefault(true); + bestSink->setMuted(false); + bestSink->setVolume(65535); + }); + QTimer::singleShot(20, []() { + qDebug() << "Done"; + qApp->quit(); + }); +} + +static int countMatches(const QStringList &find, const QString &haystack) +{ + int ret = 0; + QStringList hayList = haystack.toLower().split(_splitter, QString::SkipEmptyParts); + qDebug() << "Comparing" << hayList; + // Look for all the words we want to find + for (const QString &word : find) { + if (hayList.contains(word)) { + ret += 10; + } + } + if (ret == 0) { + // If not a single word matched, bail out + return -1000; + } + // Penaltize additonal words in haystack that we don't want to find + for (const QString &word : hayList) { + if (!find.contains(word)) { + ret -= 1; + } + } + return ret; +} diff --git a/src/setdefault.h b/src/setdefault.h new file mode 100644 index 0000000..123ad20 --- /dev/null +++ b/src/setdefault.h @@ -0,0 +1,8 @@ +#ifndef _SET_DEFAULT_H_ +#define _SET_DEFAULT_H_ + +#include + +void setDefaultOutput(const QString &name); + +#endif diff --git a/src/slxoutput.cpp b/src/slxoutput.cpp index b1da26f..637ffc3 100644 --- a/src/slxoutput.cpp +++ b/src/slxoutput.cpp @@ -15,9 +15,9 @@ SlxOutput::SlxOutput(ItemType type, const QString &id, QWidget(nullptr) { setupUi(this); - // Make heading 25% larger than system default + // Make heading 20% larger than system default QFont f = headingLabel->font(); - f.setPointSize(f.pointSize() * 5 / 4); + f.setPointSize(f.pointSize() * 6 / 5); f.setBold(_type == SinkPortItem || _type == SourcePortItem); headingLabel->setFont(f); // -- cgit v1.2.3-55-g7522