/***
This file is part of pavucontrol.
Copyright 2006-2008 Lennart Poettering
Copyright 2008 Sjoerd Simons <sjoerd@luon.net>
pavucontrol is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
pavucontrol is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with pavucontrol. If not, see <https://www.gnu.org/licenses/>.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#define PACKAGE_VERSION "0.1"
#include <pulse/pulseaudio.h>
#include <pulse/glib-mainloop.h>
#include <pulse/ext-stream-restore.h>
#include <pulse/ext-device-manager.h>
// #include <canberra-gtk.h>
#include "helper.h"
#include "pavucontrol.h"
#include "minimalstreamwidget.h"
#include "channel.h"
#include "streamwidget.h"
#include "cardwidget.h"
#include "sinkwidget.h"
#include "sourcewidget.h"
#include "sinkinputwidget.h"
#include "sourceoutputwidget.h"
#include "rolewidget.h"
#include "mainwindow.h"
#include <QMessageBox>
#include <QApplication>
#include <QLocale>
#include <QLibraryInfo>
#include <QTranslator>
#include <QCommandLineParser>
#include <QCommandLineOption>
#include <QString>
#include <QRegularExpression>
#include <unistd.h>
static pa_context* context = nullptr;
static pa_mainloop_api* api = nullptr;
static int n_outstanding = 0;
static int default_tab = 0;
static bool retry = false;
static int reconnect_timeout = 1;
static QRegularExpression select_output;
void show_error(const char *txt) {
char buf[256];
snprintf(buf, sizeof(buf), "%s: %s", txt, pa_strerror(pa_context_errno(context)));
QMessageBox::critical(nullptr, QObject::tr("Error"), QString::fromUtf8(buf));
qApp->quit();
}
static void dec_outstanding(MainWindow *w) {
if (n_outstanding <= 0)
return;
if (--n_outstanding <= 0) {
// w->get_window()->set_cursor();
w->setConnectionState(true);
}
}
static void card_cb(pa_context *, const pa_card_info *i, int eol, void *userdata) {
MainWindow *w = static_cast<MainWindow*>(userdata);
if (eol < 0) {
if (pa_context_errno(context) == PA_ERR_NOENTITY)
return;
show_error(QObject::tr("Card callback failure").toUtf8().constData());
return;
}
if (eol > 0) {
dec_outstanding(w);
return;
}
w->updateCard(*i);
}
static void card_cb_setdef(pa_context * ctx, const pa_card_info *i, int eol, void *) {
if (eol < 0) {
if (pa_context_errno(context) == PA_ERR_NOENTITY)
return;
show_error(QObject::tr("Card callback failure").toUtf8().constData());
return;
}
if (eol > 0) {
return;
}
// TODO Check stuff
std::set<pa_card_profile_info2 *, profile_prio_compare> profile_priorities;
std::map<QByteArray, PortInfo> ports;
QMap<QString, ProfileGroup> profiles;
profile_priorities.clear();
for (pa_card_profile_info2 ** p_profile = i->profiles2; p_profile && *p_profile != nullptr; ++p_profile) {
profile_priorities.insert(*p_profile);
}
populatePorts(*i, ports);
groupProfiles(profile_priorities, ports, profiles);
ProfileGroup *best = nullptr;
for (auto &p : profiles) {
if (p.getProfileName().contains(select_output)) {
// Maybe we should track per-profile availability too and scan the list...
if (best == nullptr || p.available) {
best = &p;
if (p.available)
break;
}
}
}
if (best != nullptr) {
// Can we do this inside the callback?
pa_operation* o;
const auto *entry = best->entries.first().id.constData();
printf("Selecting profile %s\n", entry);
if (!(o = pa_context_set_card_profile_by_index(ctx, i->index, entry, nullptr, nullptr))) {
printf("pa_context_set_card_profile_by_index() failed\n");
return;
}
pa_operation_unref(o);
}
}
static void context_state_callback_setdef(pa_context *c, void *) {
if (pa_context_get_state(c) == PA_CONTEXT_READY) {
pa_operation *o;
if (!(o = pa_context_get_card_info_list(c, card_cb_setdef, nullptr))) {
show_error(QObject::tr("pa_context_get_card_info_list() failed").toUtf8().constData());
return;
}
pa_operation_unref(o);
}
}
#if HAVE_EXT_DEVICE_RESTORE_API
static void ext_device_restore_subscribe_cb(pa_context *c, pa_device_type_t type, uint32_t idx, void *userdata);
#endif
void sink_cb(pa_context *c, const pa_sink_info *i, int eol, void *userdata) {
MainWindow *w = static_cast<MainWindow*>(userdata);
if (eol < 0) {
if (pa_context_errno(context) == PA_ERR_NOENTITY)
return;
show_error(QObject::tr("Sink callback failure").toUtf8().constData());
return;
}
if (eol > 0) {
dec_outstanding(w);
return;
}
#if HAVE_EXT_DEVICE_RESTORE_API
if (w->updateSink(*i))
ext_device_restore_subscribe_cb(c, PA_DEVICE_TYPE_SINK, i->index, w);
#else
w->updateSink(*i);
#endif
}
void source_cb(pa_context *, const pa_source_info *i, int eol, void *userdata) {
MainWindow *w = static_cast<MainWindow*>(userdata);
if (eol < 0) {
if (pa_context_errno(context) == PA_ERR_NOENTITY)
return;
show_error(QObject::tr("Source callback failure").toUtf8().constData());
return;
}
if (eol > 0) {
dec_outstanding(w);
return;
}
w->updateSource(*i);
}
void sink_input_cb(pa_context *, const pa_sink_input_info *i, int eol, void *userdata) {
MainWindow *w = static_cast<MainWindow*>(userdata);
if (eol < 0) {
if (pa_context_errno(context) == PA_ERR_NOENTITY)
return;
show_error(QObject::tr("Sink input callback failure").toUtf8().constData());
return;
}
if (eol > 0) {
dec_outstanding(w);
return;
}
w->updateSinkInput(*i);
}
void source_output_cb(pa_context *, const pa_source_output_info *i, int eol, void *userdata) {
MainWindow *w = static_cast<MainWindow*>(userdata);
if (eol < 0) {
if (pa_context_errno(context) == PA_ERR_NOENTITY)
return;
show_error(QObject::tr("Source output callback failure").toUtf8().constData());
return;
}
if (eol > 0) {
if (n_outstanding > 0) {
/* At this point all notebook pages have been populated, so
* let's open one that isn't empty */
if (default_tab != -1) {
if (default_tab < 1 || default_tab > w->notebook->count()) {
if (!w->sinkInputWidgets.empty())
w->notebook->setCurrentIndex(0);
else if (!w->sourceOutputWidgets.empty())
w->notebook->setCurrentIndex(1);
else if (!w->sourceWidgets.empty() && w->sinkWidgets.empty())
w->notebook->setCurrentIndex(3);
else
w->notebook->setCurrentIndex(2);
} else {
w->notebook->setCurrentIndex(default_tab - 1);
}
default_tab = -1;
}
}
dec_outstanding(w);
return;
}
w->updateSourceOutput(*i);
}
void client_cb(pa_context *, const pa_client_info *i, int eol, void *userdata) {
MainWindow *w = static_cast<MainWindow*>(userdata);
if (eol < 0) {
if (pa_context_errno(context) == PA_ERR_NOENTITY)
return;
show_error(QObject::tr("Client callback failure").toUtf8().constData());
return;
}
if (eol > 0) {
dec_outstanding(w);
return;
}
w->updateClient(*i);
}
void server_info_cb(pa_context *, const pa_server_info *i, void *userdata) {
MainWindow *w = static_cast<MainWindow*>(userdata);
if (!i) {
show_error(QObject::tr("Server info callback failure").toUtf8().constData());
return;
}
w->updateServer(*i);
dec_outstanding(w);
}
void ext_stream_restore_read_cb(
pa_context *,
const pa_ext_stream_restore_info *i,
int eol,
void *userdata) {
MainWindow *w = static_cast<MainWindow*>(userdata);
if (eol < 0) {
dec_outstanding(w);
g_debug(QObject::tr("Failed to initialize stream_restore extension: %s").toUtf8().constData(), pa_strerror(pa_context_errno(context)));
w->deleteEventRoleWidget();
return;
}
if (eol > 0) {
dec_outstanding(w);
return;
}
w->updateRole(*i);
}
static void ext_stream_restore_subscribe_cb(pa_context *c, void *userdata) {
MainWindow *w = static_cast<MainWindow*>(userdata);
pa_operation *o;
if (!(o = pa_ext_stream_restore_read(c, ext_stream_restore_read_cb, w))) {
show_error(QObject::tr("pa_ext_stream_restore_read() failed").toUtf8().constData());
return;
}
pa_operation_unref(o);
}
#if HAVE_EXT_DEVICE_RESTORE_API
void ext_device_restore_read_cb(
pa_context *,
const pa_ext_device_restore_info *i,
int eol,
void *userdata) {
MainWindow *w = static_cast<MainWindow*>(userdata);
if (eol < 0) {
dec_outstanding(w);
g_debug(QObject::tr("Failed to initialize device restore extension: %s").toUtf8().constData(), pa_strerror(pa_context_errno(context)));
return;
}
if (eol > 0) {
dec_outstanding(w);
return;
}
/* Do something with a widget when this part is written */
w->updateDeviceInfo(*i);
}
static void ext_device_restore_subscribe_cb(pa_context *c, pa_device_type_t type, uint32_t idx, void *userdata) {
MainWindow *w = static_cast<MainWindow*>(userdata);
pa_operation *o;
if (type != PA_DEVICE_TYPE_SINK)
return;
if (!(o = pa_ext_device_restore_read_formats(c, type, idx, ext_device_restore_read_cb, w))) {
show_error(QObject::tr("pa_ext_device_restore_read_sink_formats() failed").toUtf8().constData());
return;
}
pa_operation_unref(o);
}
#endif
void ext_device_manager_read_cb(
pa_context *,
const pa_ext_device_manager_info *,
int eol,
void *userdata) {
MainWindow *w = static_cast<MainWindow*>(userdata);
if (eol < 0) {
dec_outstanding(w);
g_debug(QObject::tr("Failed to initialize device manager extension: %s").toUtf8().constData(), pa_strerror(pa_context_errno(context)));
return;
}
w->canRenameDevices = true;
if (eol > 0) {
dec_outstanding(w);
return;
}
/* Do something with a widget when this part is written */
}
static void ext_device_manager_subscribe_cb(pa_context *c, void *userdata) {
MainWindow *w = static_cast<MainWindow*>(userdata);
pa_operation *o;
if (!(o = pa_ext_device_manager_read(c, ext_device_manager_read_cb, w))) {
show_error(QObject::tr("pa_ext_device_manager_read() failed").toUtf8().constData());
return;
}
pa_operation_unref(o);
}
void subscribe_cb(pa_context *c, pa_subscription_event_type_t t, uint32_t index, void *userdata) {
MainWindow *w = static_cast<MainWindow*>(userdata);
switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
case PA_SUBSCRIPTION_EVENT_SINK:
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
w->removeSink(index);
else {
pa_operation *o;
if (!(o = pa_context_get_sink_info_by_index(c, index, sink_cb, w))) {
show_error(QObject::tr("pa_context_get_sink_info_by_index() failed").toUtf8().constData());
return;
}
pa_operation_unref(o);
}
break;
case PA_SUBSCRIPTION_EVENT_SOURCE:
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
w->removeSource(index);
else {
pa_operation *o;
if (!(o = pa_context_get_source_info_by_index(c, index, source_cb, w))) {
show_error(QObject::tr("pa_context_get_source_info_by_index() failed").toUtf8().constData());
return;
}
pa_operation_unref(o);
}
break;
case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
w->removeSinkInput(index);
else {
pa_operation *o;
if (!(o = pa_context_get_sink_input_info(c, index, sink_input_cb, w))) {
show_error(QObject::tr("pa_context_get_sink_input_info() failed").toUtf8().constData());
return;
}
pa_operation_unref(o);
}
break;
case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT:
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
w->removeSourceOutput(index);
else {
pa_operation *o;
if (!(o = pa_context_get_source_output_info(c, index, source_output_cb, w))) {
show_error(QObject::tr("pa_context_get_sink_input_info() failed").toUtf8().constData());
return;
}
pa_operation_unref(o);
}
break;
case PA_SUBSCRIPTION_EVENT_CLIENT:
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
w->removeClient(index);
else {
pa_operation *o;
if (!(o = pa_context_get_client_info(c, index, client_cb, w))) {
show_error(QObject::tr("pa_context_get_client_info() failed").toUtf8().constData());
return;
}
pa_operation_unref(o);
}
break;
case PA_SUBSCRIPTION_EVENT_SERVER: {
pa_operation *o;
if (!(o = pa_context_get_server_info(c, server_info_cb, w))) {
show_error(QObject::tr("pa_context_get_server_info() failed").toUtf8().constData());
return;
}
pa_operation_unref(o);
}
break;
case PA_SUBSCRIPTION_EVENT_CARD:
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
w->removeCard(index);
else {
pa_operation *o;
if (!(o = pa_context_get_card_info_by_index(c, index, card_cb, w))) {
show_error(QObject::tr("pa_context_get_card_info_by_index() failed").toUtf8().constData());
return;
}
pa_operation_unref(o);
}
break;
}
}
/* Forward Declaration */
gboolean connect_to_pulse(gpointer userdata);
void context_state_callback(pa_context *c, void *userdata) {
MainWindow *w = static_cast<MainWindow*>(userdata);
g_assert(c);
switch (pa_context_get_state(c)) {
case PA_CONTEXT_UNCONNECTED:
case PA_CONTEXT_CONNECTING:
case PA_CONTEXT_AUTHORIZING:
case PA_CONTEXT_SETTING_NAME:
break;
case PA_CONTEXT_READY: {
pa_operation *o;
reconnect_timeout = 1;
/* Create event widget immediately so it's first in the list */
w->createEventRoleWidget();
pa_context_set_subscribe_callback(c, subscribe_cb, w);
if (!(o = pa_context_subscribe(c, (pa_subscription_mask_t)
(PA_SUBSCRIPTION_MASK_SINK|
PA_SUBSCRIPTION_MASK_SOURCE|
PA_SUBSCRIPTION_MASK_SINK_INPUT|
PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT|
PA_SUBSCRIPTION_MASK_CLIENT|
PA_SUBSCRIPTION_MASK_SERVER|
PA_SUBSCRIPTION_MASK_CARD), nullptr, nullptr))) {
show_error(QObject::tr("pa_context_subscribe() failed").toUtf8().constData());
return;
}
pa_operation_unref(o);
/* Keep track of the outstanding callbacks for UI tweaks */
n_outstanding = 0;
if (!(o = pa_context_get_server_info(c, server_info_cb, w))) {
show_error(QObject::tr("pa_context_get_server_info() failed").toUtf8().constData());
return;
}
pa_operation_unref(o);
n_outstanding++;
if (!(o = pa_context_get_client_info_list(c, client_cb, w))) {
show_error(QObject::tr("pa_context_client_info_list() failed").toUtf8().constData());
return;
}
pa_operation_unref(o);
n_outstanding++;
if (!(o = pa_context_get_card_info_list(c, card_cb, w))) {
show_error(QObject::tr("pa_context_get_card_info_list() failed").toUtf8().constData());
return;
}
pa_operation_unref(o);
n_outstanding++;
if (!(o = pa_context_get_sink_info_list(c, sink_cb, w))) {
show_error(QObject::tr("pa_context_get_sink_info_list() failed").toUtf8().constData());
return;
}
pa_operation_unref(o);
n_outstanding++;
if (!(o = pa_context_get_source_info_list(c, source_cb, w))) {
show_error(QObject::tr("pa_context_get_source_info_list() failed").toUtf8().constData());
return;
}
pa_operation_unref(o);
n_outstanding++;
if (!(o = pa_context_get_sink_input_info_list(c, sink_input_cb, w))) {
show_error(QObject::tr("pa_context_get_sink_input_info_list() failed").toUtf8().constData());
return;
}
pa_operation_unref(o);
n_outstanding++;
if (!(o = pa_context_get_source_output_info_list(c, source_output_cb, w))) {
show_error(QObject::tr("pa_context_get_source_output_info_list() failed").toUtf8().constData());
return;
}
pa_operation_unref(o);
n_outstanding++;
/* These calls are not always supported */
if ((o = pa_ext_stream_restore_read(c, ext_stream_restore_read_cb, w))) {
pa_operation_unref(o);
n_outstanding++;
pa_ext_stream_restore_set_subscribe_cb(c, ext_stream_restore_subscribe_cb, w);
if ((o = pa_ext_stream_restore_subscribe(c, 1, nullptr, nullptr)))
pa_operation_unref(o);
} else
g_debug(QObject::tr("Failed to initialize stream_restore extension: %s").toUtf8().constData(), pa_strerror(pa_context_errno(context)));
#if HAVE_EXT_DEVICE_RESTORE_API
/* TODO Change this to just the test function */
if ((o = pa_ext_device_restore_read_formats_all(c, ext_device_restore_read_cb, w))) {
pa_operation_unref(o);
n_outstanding++;
pa_ext_device_restore_set_subscribe_cb(c, ext_device_restore_subscribe_cb, w);
if ((o = pa_ext_device_restore_subscribe(c, 1, nullptr, nullptr)))
pa_operation_unref(o);
} else
g_debug(QObject::tr("Failed to initialize device restore extension: %s").toUtf8().constData(), pa_strerror(pa_context_errno(context)));
#endif
if ((o = pa_ext_device_manager_read(c, ext_device_manager_read_cb, w))) {
pa_operation_unref(o);
n_outstanding++;
pa_ext_device_manager_set_subscribe_cb(c, ext_device_manager_subscribe_cb, w);
if ((o = pa_ext_device_manager_subscribe(c, 1, nullptr, nullptr)))
pa_operation_unref(o);
} else
g_debug(QObject::tr("Failed to initialize device manager extension: %s").toUtf8().constData(), pa_strerror(pa_context_errno(context)));
break;
}
case PA_CONTEXT_FAILED:
w->setConnectionState(false);
w->removeAllWidgets();
w->updateDeviceVisibility();
pa_context_unref(context);
context = nullptr;
if (reconnect_timeout > 0) {
g_debug("%s", QObject::tr("Connection failed, attempting reconnect").toUtf8().constData());
g_timeout_add_seconds(reconnect_timeout, connect_to_pulse, w);
}
return;
case PA_CONTEXT_TERMINATED:
default:
qApp->quit();
return;
}
}
pa_context* get_context(void) {
return context;
}
void update_default_sinks(MainWindow *w) {
pa_operation *o;
if (!(o = pa_context_get_server_info(context, server_info_cb, w))) {
show_error(QObject::tr("pa_context_get_server_info() failed").toUtf8().constData());
return;
}
pa_operation_unref(o);
n_outstanding++;
}
void connectToPulse(void) {
pa_proplist *proplist = pa_proplist_new();
pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, QObject::tr("PulseAudio Volume Control").toUtf8().constData());
pa_proplist_sets(proplist, PA_PROP_APPLICATION_ID, "org.PulseAudio.pavucontrol");
pa_proplist_sets(proplist, PA_PROP_APPLICATION_ICON_NAME, "audio-card");
pa_proplist_sets(proplist, PA_PROP_APPLICATION_VERSION, PACKAGE_VERSION);
context = pa_context_new_with_proplist(api, nullptr, proplist);
g_assert(context);
pa_proplist_free(proplist);
}
gboolean connect_to_pulse(gpointer userdata) {
MainWindow *w = static_cast<MainWindow*>(userdata);
if (context)
return false;
connectToPulse();
pa_context_set_state_callback(context, context_state_callback, w);
w->setConnectingMessage();
if (pa_context_connect(context, nullptr, PA_CONTEXT_NOFAIL, nullptr) < 0) {
if (pa_context_errno(context) == PA_ERR_INVALID) {
w->setConnectingMessage(QObject::tr("Connection to PulseAudio failed. Automatic retry in 5s.<br><br>"
"In this case this is likely because PULSE_SERVER in the Environment/X11 Root Window Properties"
"or default-server in client.conf is misconfigured.<br>"
"This situation can also arrise when PulseAudio crashed and left stale details in the X11 Root Window.<br>"
"If this is the case, then PulseAudio should autospawn again, or if this is not configured you should"
"run start-pulseaudio-x11 manually.").toUtf8().constData());
reconnect_timeout = 5;
}
else {
if(!retry) {
reconnect_timeout = -1;
qApp->quit();
} else {
g_debug("%s", QObject::tr("Connection failed, attempting reconnect").toUtf8().constData());
reconnect_timeout = 5;
g_timeout_add_seconds(reconnect_timeout, connect_to_pulse, userdata);
}
}
}
return false;
}
int main(int argc, char *argv[]) {
int exit_code = 0, have_regex = 0;
signal(SIGPIPE, SIG_IGN);
QApplication app(argc, argv);
app.setOrganizationName(QStringLiteral("pavucontrol-qt"));
app.setAttribute(Qt::AA_UseHighDpiPixmaps, true);
QString locale = QLocale::system().name();
QTranslator qtTranslator;
if(qtTranslator.load(QStringLiteral("qt_") + locale, QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
qApp->installTranslator(&qtTranslator);
QTranslator appTranslator;
if(appTranslator.load(QStringLiteral("pavucontrol-qt_") + locale, QStringLiteral(PAVUCONTROL_QT_DATA_DIR) + QStringLiteral("/translations")))
qApp->installTranslator(&appTranslator);
QCommandLineParser parser;
parser.setApplicationDescription(QObject::tr("PulseAudio Volume Control"));
parser.addHelpOption();
const QString VERINFO = QStringLiteral(PAVUCONTROLQT_VERSION
"\nQt " QT_VERSION_STR);
app.setApplicationVersion(VERINFO);
parser.addVersionOption();
QCommandLineOption tabOption(QStringList() << QStringLiteral("tab") << QStringLiteral("t"), QObject::tr("Select a specific tab on load."), QStringLiteral("tab"));
parser.addOption(tabOption);
QCommandLineOption retryOption(QStringList() << QStringLiteral("retry") << QStringLiteral("r"), QObject::tr("Retry forever if pa quits (every 5 seconds)."));
parser.addOption(retryOption);
QCommandLineOption maximizeOption(QStringList() << QStringLiteral("maximize") << QStringLiteral("m"), QObject::tr("Maximize the window."));
parser.addOption(maximizeOption);
QCommandLineOption selectOption(QStringList() << QStringLiteral("output") << QStringLiteral("o"), QObject::tr("Select a specific output configuration and quit."), QStringLiteral("regex"));
parser.addOption(selectOption);
parser.process(app);
default_tab = parser.value(tabOption).toInt();
retry = parser.isSet(retryOption);
if (parser.isSet(selectOption)) {
select_output = QRegularExpression(parser.value(selectOption));
if (!select_output.isValid()) {
select_output = QRegularExpression(QRegularExpression::escape(parser.value(selectOption)));
}
have_regex = select_output.isValid();
}
if (have_regex) {
pa_mainloop *m = pa_mainloop_new();
g_assert(m);
api = pa_mainloop_get_api(m);
g_assert(api);
connectToPulse();
pa_context_set_state_callback(context, context_state_callback_setdef, nullptr);
if (pa_context_connect(context, nullptr, PA_CONTEXT_NOFAIL, nullptr) >= 0) {
if (pa_context_errno(context) == PA_ERR_INVALID) {
printf("pa_context_errno = PA_ERR_INVALID\n");
exit_code = 1;
} else {
int evs;
int tries = 10;
exit_code = 100;
// WTF
while ((evs = pa_mainloop_iterate(m, 0, &exit_code)) >= 0) {
if (evs == 0) {
if (--tries == 0)
break;
usleep(100000);
}
}
printf("Exit: %d\n", exit_code);
}
}
if (context)
pa_context_unref(context);
pa_mainloop_free(m);
} else {
MainWindow* mainWindow = new MainWindow();
if(parser.isSet(maximizeOption))
mainWindow->showMaximized();
pa_glib_mainloop *m = pa_glib_mainloop_new(g_main_context_default());
g_assert(m);
api = pa_glib_mainloop_get_api(m);
g_assert(api);
connect_to_pulse(mainWindow);
if (reconnect_timeout >= 0) {
mainWindow->show();
app.exec();
}
if (reconnect_timeout < 0)
show_error(QObject::tr("Fatal Error: Unable to connect to PulseAudio").toUtf8().constData());
delete mainWindow;
if (context)
pa_context_unref(context);
pa_glib_mainloop_free(m);
}
return exit_code;
}