summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Rettberg2022-08-25 11:35:19 +0200
committerSimon Rettberg2022-08-25 11:35:19 +0200
commit70f80cdee60dfebaa93d16ec0fbbaa2728ea8a23 (patch)
treed6c216fc8af1ade8ad2869cf732269ce9b0b8aa6
parentReplace everything with new "slxmix" (work in progress) (diff)
downloadpavucontrol-slx-70f80cdee60dfebaa93d16ec0fbbaa2728ea8a23.tar.gz
pavucontrol-slx-70f80cdee60dfebaa93d16ec0fbbaa2728ea8a23.tar.xz
pavucontrol-slx-70f80cdee60dfebaa93d16ec0fbbaa2728ea8a23.zip
Add comments
-rw-r--r--src/main.cpp89
-rw-r--r--src/mainwindow.cpp7
-rw-r--r--src/slxoutput.cpp3
3 files changed, 81 insertions, 18 deletions
diff --git a/src/main.cpp b/src/main.cpp
index a06f6f5..bf22453 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -31,47 +31,64 @@ bool g_IgnoreGui;
/** If not empty, on profile change, if profile belings to this card, make its sink default */
static QString _pendingCardDefault;
+/** While running, we want to set each port's volume to 100% the first time we see it. Unfortunately
+ * a port only startes to exist once its according profile is selected, so there is no way to have
+ * a simple "set everything to 100%" method run on startup, at least not without switching through
+ * all profiles on all cards, which sounds like it could be annoying and buggy. So do it this way
+ * for now.
+ */
static QSet<QString> _donePorts;
+/** Whe something changes, (re)start a short timeout first before actually updating the GUI, to
+ * avoid multiple updates in rapid succession, for example when changing the active profile.
+ */
static QTimer _updateDelay;
static MainWindow *_mainWindow;
+/**
+ * Queue a GUI update. By default, delay it by 50ms. If the timer is already active but hasn't
+ * expired yet, it will be restarted with the given timeout.
+ */
static void queueGuiUpdate(int ms = 50)
{
_updateDelay.start(ms);
}
+/**
+ * The actual GUI update logic. Iterate over all sinks and their ports, and add them to the GUI.
+ * Then iterate over all non-selected profiles, and add them to the GUI too, so we have a simple, flat
+ * list of "outputs" to select, which should be a bit more easy to grasp for a non-technical person
+ * trying to make the computer go beep. (Hopefully)
+ */
static void updateActiveOutput()
{
- g_IgnoreGui = true;
- _mainWindow->mark();
+ g_IgnoreGui = true; // Block GUI updates
+ _mainWindow->mark(); // Mark all items in the lists as unused
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());
+ qint64 volume = sink->volume();
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();
+ (int)volume, (int)sink->isMuted());
QString id = cardName + sink->name() + port->name();
- if (active) {
- if (!_donePorts.contains(id)) {
- _donePorts.insert(id);
- if (volume < 60000) {
- volume = 65535;
- sink->setVolume(volume);
- }
+ // Only once, update port volume to be 100% if it's low
+ if (active && !_donePorts.contains(id)) {
+ _donePorts.insert(id);
+ if (volume < 60000) {
+ volume = 65535;
+ sink->setVolume(volume);
}
}
QString title = sink->description() + ", " + port->description();
@@ -79,13 +96,16 @@ static void updateActiveOutput()
active, sink->isMuted(), sink->isVolumeWritable() ? sink->volume() : -1, QString(), sink->name(), port->name());
}
}
+ // Now find all the profiles from all cards that are not active and add an entry to the list for each one
for (auto *card : cards) {
QString cardDescription = card->description();
int profIndex = -1;
for (auto *profile : card->profiles()) {
profIndex++;
+ // Ignore the active profile, and profiles that are unavailable
if (profIndex == card->activeProfileIndex() || profile->availability() == PulseAudioQt::Profile::Unavailable)
continue;
+ // No point in selecting something that doesn't have any way to output sound
if (profile->sinks() == 0)
continue;
printf("[ ] Output: '%s', sinks: %d\n",
@@ -97,25 +117,34 @@ static void updateActiveOutput()
}
}
printf("`--------------------------------------------ยด\n");
- _mainWindow->sweep();
- g_IgnoreGui = false;
+ _mainWindow->sweep(); // Removes all items from the list that are still marked unused, i.e. updateOutput() was not called on those
+ g_IgnoreGui = false; // Allow GUI updates again
}
+/**
+ * Called after the user set a GUI entry as default that represents a profile, after the active profile
+ * or the list of available ports changed. Only after this happened can we actually set as default the sink/port
+ * that was created from selecting that profile as the default sink.
+ */
static void checkShouldSetDefault()
{
- if (_pendingCardDefault.isEmpty())
+ if (_pendingCardDefault.isEmpty()) // No switch actually pending
return;
+ // Otherwise, it's the ID of the card that the profile belongs to that the user wants to set as default
printf("Pending card default to %s\n", _pendingCardDefault.toLocal8Bit().constData());
auto *i = PulseAudioQt::Context::instance();
int cardIdx = -1;
+ // Find the current index of that card
for (auto *card : i->cards()) {
cardIdx++;
if (card->name() == _pendingCardDefault && !card->ports().isEmpty())
break;
}
+ // Then iterate over all sinks until we find one that belongs to the card
for (auto *sink : i->sinks()) {
if (sink->cardIndex() == cardIdx) {
printf("MATCH SET!\n");
+ // Set as default, unmute, and clear the pending switch
sink->setDefault(true);
sink->setMuted(false);
_pendingCardDefault.clear();
@@ -123,6 +152,9 @@ static void checkShouldSetDefault()
}
}
+/**
+ * Called when PA tells us about a new card; start listening to signals we're interesed in.
+ */
static void newCardAppeared(PulseAudioQt::Card *card)
{
//if (_doneCards.contains(card->name()))
@@ -148,6 +180,9 @@ static void newCardAppeared(PulseAudioQt::Card *card)
*/
}
+/**
+ * Called when PA tells us about a new sink; start listening to signals we're interested in.
+ */
static void newSinkAppeared(PulseAudioQt::Sink *sink)
{
for (auto *port : sink->ports()) {
@@ -170,6 +205,9 @@ static void newSinkAppeared(PulseAudioQt::Sink *sink)
queueGuiUpdate();
}
+/**
+ * Mute/unmute sink by its string identifier.
+ */
void setMuted(const QString &sink, bool muted)
{
auto *i = PulseAudioQt::Context::instance();
@@ -182,6 +220,9 @@ void setMuted(const QString &sink, bool muted)
queueGuiUpdate();
}
+/**
+ * Set a sink's volume, by its string identifier.
+ */
void setSinkVolume(const QString &sink, int volume)
{
auto *i = PulseAudioQt::Context::instance();
@@ -194,6 +235,12 @@ void setSinkVolume(const QString &sink, int volume)
}
}
+/**
+ * Set the given card as default, preferably enabling the given profile
+ * on it. If this card doesn't have a profile with the given ID, try
+ * to use a profile that contains the string "Duplex" in its name,
+ * otherwise, just use the first profile available.
+ */
void enableCard(const QString &card, const QString &profile)
{
auto *i = PulseAudioQt::Context::instance();
@@ -242,12 +289,20 @@ void enableCard(const QString &card, const QString &profile)
}
} else {
matchingCard->setActiveProfileIndex(profileIdx);
+ // Remember the ID of the card we switched to; we need this as only after PA is done
+ // switching to the desired profile will we see any sink belonging to it, so we can only
+ // set the according sink as fallback after that happens. See checkShouldSetDefault().
_pendingCardDefault = matchingCard->name();
}
}
queueGuiUpdate();
}
+/**
+ * Set given sink as default sink, unmute it, and switch to its
+ * port as passed to this function. If the sink doesn't have a
+ * port with this ID, nothing happens.
+ */
void enableSink(const QString &sink, const QString &port)
{
auto *i = PulseAudioQt::Context::instance();
@@ -277,6 +332,8 @@ int main(int argc, char **argv)
printf("Pa is %p = %d\n", i, (int)i->isValid());
printf("Cards: %d\n", (int)i->cards().size());
+ // 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.
QTimer::singleShot(100, [=]() {
for (auto *card : i->cards()) {
newCardAppeared(card);
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index 4860f2b..9ae66d3 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -12,7 +12,6 @@ MainWindow::~MainWindow()
}
-// Need to pass newSink as new widget's sink isn't set here yet, so always empty
SlxOutput* MainWindow::getOutput(const QString &id, bool isSink, const QString &newTitle)
{
SlxOutput* w = _widgets.value(id, nullptr);
@@ -52,6 +51,9 @@ SlxOutput* MainWindow::getOutput(const QString &id, bool isSink, const QString &
return w;
}
+/**
+ * Mark all entries as unused.
+ */
void MainWindow::mark()
{
for (auto *w : _widgets) {
@@ -59,6 +61,9 @@ void MainWindow::mark()
}
}
+/**
+ * Remove all entries marked as unused.
+ */
void MainWindow::sweep()
{
for (QMutableMapIterator<QString, SlxOutput*> it(_widgets); it.hasNext(); ) {
diff --git a/src/slxoutput.cpp b/src/slxoutput.cpp
index d46b0e9..0ac38b1 100644
--- a/src/slxoutput.cpp
+++ b/src/slxoutput.cpp
@@ -50,6 +50,7 @@ void SlxOutput::volumeSliderChanged(int value)
{
if (g_IgnoreGui)
return;
+ // Update slider every 100ms
if (_volumeTimer.isActive())
return;
_volumeTimer.start();
@@ -78,5 +79,5 @@ void SlxOutput::updateOutput(const QString &name, bool isDefault, bool isMuted,
volumeSlider->setEnabled(isSink());
}
muteToggleButton->setEnabled(isSink());
- unused = false;
+ unused = false; // Entry was updated, so it's being used
}