#include "main.h"
#include "mainwindow.h"
#include "slxoutput.h"
#include <cstdio>
#include <cstring>
#include <cstddef>
#include <QVector>
#include <QGuiApplication>
#include <QTimer>
#include <PulseAudioQt/Context>
#include <PulseAudioQt/Server>
#include <PulseAudioQt/Profile>
#include <PulseAudioQt/PulseObject>
#include <PulseAudioQt/SinkInput>
#include <PulseAudioQt/Sink>
#include <PulseAudioQt/Card>
#include <PulseAudioQt/Port>
#include <QList>
#include <QSet>
#include <QTimer>
// 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<QString> _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<int> 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();
}