#include "main.h" #include "mainwindow.h" #include "slxoutput.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // libkf5pulseaudioqt-dev // Public /** Ignore any signals from GUI elements when this is true. Set when we're updating the GUI */ bool g_IgnoreGui; // Private /** If not empty, on profile change, if profile belings to this card, make its sink default */ static QString _pendingCardDefault; static QSet _donePorts; static QTimer _updateDelay; static MainWindow *_mainWindow; static void queueGuiUpdate(int ms = 50) { _updateDelay.start(ms); } static void updateActiveOutput() { g_IgnoreGui = true; _mainWindow->mark(); auto *i = PulseAudioQt::Context::instance(); //QSet usedCards; auto cards = i->cards(); printf(".--------------------------------------------.\n"); for (auto *sink : i->sinks()) { QString cardName = sink->cardIndex() < cards.size() ? cards.at(sink->cardIndex())->name() : QString(); int portIndex = -1; //usedCards.insert(sink->cardIndex()); for (auto *port : sink->ports()) { portIndex++; //if (port->availability() == PulseAudioQt::Port::Unavailable) // continue; bool active = (sink->isDefault() && portIndex == sink->activePortIndex()); printf("[%c] Output: '%s %s', volume: %d, mute: %d\n", active ? 'x' : ' ', sink->description().toLocal8Bit().constData(), port->description().toLocal8Bit().constData(), (int)sink->volume(), (int)sink->isMuted()); qint64 volume = sink->volume(); QString id = cardName + sink->name() + port->name(); if (active) { if (!_donePorts.contains(id)) { _donePorts.insert(id); if (volume < 60000) { volume = 65535; sink->setVolume(volume); } } } QString title = sink->description() + ", " + port->description(); _mainWindow->getOutput(id, true, title)->updateOutput(title, active, sink->isMuted(), sink->isVolumeWritable() ? sink->volume() : -1, QString(), sink->name(), port->name()); } } for (auto *card : cards) { QString cardDescription = card->description(); int profIndex = -1; for (auto *profile : card->profiles()) { profIndex++; if (profIndex == card->activeProfileIndex() || profile->availability() == PulseAudioQt::Profile::Unavailable) continue; if (profile->sinks() == 0) continue; printf("[ ] Output: '%s', sinks: %d\n", profile->description().toLocal8Bit().constData(), profile->sinks()); QString id = card->name() + profile->name(); QString title = cardDescription + ", " + profile->description(); _mainWindow->getOutput(id, false, title)->updateOutput(title, false, false, -1, card->name(), QString(), profile->name()); } } printf("`--------------------------------------------ยด\n"); _mainWindow->sweep(); g_IgnoreGui = false; } static void checkShouldSetDefault() { if (_pendingCardDefault.isEmpty()) return; printf("Pending card default to %s\n", _pendingCardDefault.toLocal8Bit().constData()); auto *i = PulseAudioQt::Context::instance(); int cardIdx = -1; for (auto *card : i->cards()) { cardIdx++; if (card->name() == _pendingCardDefault && !card->ports().isEmpty()) break; } for (auto *sink : i->sinks()) { if (sink->cardIndex() == cardIdx) { printf("MATCH SET!\n"); sink->setDefault(true); sink->setMuted(false); _pendingCardDefault.clear(); } } } static void newCardAppeared(PulseAudioQt::Card *card) { //if (_doneCards.contains(card->name())) // return; auto *i = PulseAudioQt::Context::instance(); QCoreApplication::connect(card, &PulseAudioQt::Card::profilesChanged, [=]() { printf("Card %p profiles changed\n", card); }); QCoreApplication::connect(card, &PulseAudioQt::Card::activeProfileIndexChanged, [=]() { printf("Card %p active profile index changed\n", card); checkShouldSetDefault(); queueGuiUpdate(); }); QCoreApplication::connect(card, &PulseAudioQt::Card::portsChanged, [=]() { printf("Card %p ports changed\n", card); checkShouldSetDefault(); queueGuiUpdate(); }); /* QCoreApplication::connect(card, &PulseAudioQt::Card::sinksChanged, [=]() { printf("Card %p sinks changed\n", card); }); */ } static void newSinkAppeared(PulseAudioQt::Sink *sink) { for (auto *port : sink->ports()) { printf(" Port: %s\n", port->name().toLocal8Bit().constData()); } QCoreApplication::connect(sink, &PulseAudioQt::Sink::activePortIndexChanged, [=]() { printf("Sink %p changed active port\n", sink); queueGuiUpdate(); }); QCoreApplication::connect(sink, &PulseAudioQt::Sink::defaultChanged, [=]() { printf("Sink %p changed default\n", sink); queueGuiUpdate(); }); QCoreApplication::connect(sink, &PulseAudioQt::Sink::volumeChanged, [=]() { queueGuiUpdate(); }); QCoreApplication::connect(sink, &PulseAudioQt::Sink::isVolumeWritableChanged, [=]() { queueGuiUpdate(); }); queueGuiUpdate(); } void setMuted(const QString &sink, bool muted) { auto *i = PulseAudioQt::Context::instance(); for (auto *sp : i->sinks()) { if (sp->name() == sink) { sp->setMuted(muted); } } queueGuiUpdate(); } void setSinkVolume(const QString &sink, int volume) { auto *i = PulseAudioQt::Context::instance(); for (auto *sp : i->sinks()) { if (sp->name() == sink) { sp->setVolume(volume); queueGuiUpdate(250); } } } void enableCard(const QString &card, const QString &profile) { auto *i = PulseAudioQt::Context::instance(); PulseAudioQt::Card *matchingCard = nullptr; int profileIdx = -1; int cardIdx = -1; _pendingCardDefault.clear(); for (auto *cp : i->cards()) { cardIdx++; if (cp->name() != card) continue; int i = -1; int exactProfileIdx = -1; for (auto *pp : cp->profiles()) { i++; if (pp->availability() == PulseAudioQt::Profile::Unavailable) continue; if (pp->description().contains(QLatin1String("Duplex"))) { // Prefer Duplex mode for analog outputs, it's usually listed after output only profileIdx = i; } if (profileIdx == -1) { // Otherwise default to first one in list profileIdx = i; } if (pp->name() == profile) { exactProfileIdx = i; } } if (exactProfileIdx != -1) { profileIdx = exactProfileIdx; } if (profileIdx != -1) { matchingCard = cp; break; } } if (matchingCard != nullptr && profileIdx < matchingCard->profiles().size()) { if (matchingCard->activeProfileIndex() == profileIdx) { for (auto *sink : i->sinks()) { if (sink->cardIndex() == cardIdx) { sink->setMuted(false); sink->setDefault(true); } } } else { matchingCard->setActiveProfileIndex(profileIdx); _pendingCardDefault = matchingCard->name(); } } queueGuiUpdate(); } void enableSink(const QString &sink, const QString &port) { auto *i = PulseAudioQt::Context::instance(); _pendingCardDefault.clear(); for (auto *sp : i->sinks()) { if (sp->name() != sink) continue; int i = -1; for (auto *pp : sp->ports()) { i++; if (pp->name() == port) { sp->setDefault(true); sp->setMuted(false); sp->setActivePortIndex(i); } } } queueGuiUpdate(); } 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()); QTimer::singleShot(100, [=]() { for (auto *card : i->cards()) { newCardAppeared(card); printf("Card: %s (index: %d)\n", card->name().toLocal8Bit().constData(), (int)card->index()); for (auto *profile : card->profiles()) { printf(" Profile: %s\n", profile->name().toLocal8Bit().constData()); } } for (auto *sink : i->sinks()) { printf("Sink: %s (for card index %d)\n", sink->name().toLocal8Bit().constData(), sink->cardIndex()); newSinkAppeared(sink); } QCoreApplication::connect(i, &PulseAudioQt::Context::cardAdded, &newCardAppeared); QCoreApplication::connect(i, &PulseAudioQt::Context::sinkAdded, &newSinkAppeared); printf("Initial output\n"); queueGuiUpdate(); }); QCoreApplication::connect(&_updateDelay, &QTimer::timeout, &updateActiveOutput); _updateDelay.setInterval(50); _updateDelay.setSingleShot(true); _mainWindow = new MainWindow; _mainWindow->show(); return a.exec(); }