Newer
Older
/***
This file is part of pavucontrol.
Copyright 2006-2008 Lennart Poettering
Copyright 2009 Colin Guthrie
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 <http://www.gnu.org/licenses/>.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "mainwindow.h"
#include "cardwidget.h"
#include "sinkwidget.h"
#include "sourcewidget.h"
#include "sinkinputwidget.h"
#include "sourceoutputwidget.h"
#include "rolewidget.h"
#include "i18n.h"
/* Used for profile sorting */
struct profile_prio_compare {
bool operator() (const pa_card_profile_info2& lhs, const pa_card_profile_info2& rhs) const {
if (lhs.priority == rhs.priority)
return strcmp(lhs.name, rhs.name) > 0;
return lhs.priority > rhs.priority;
}
bool operator() (const pa_sink_port_info& lhs, const pa_sink_port_info& rhs) const {
if (lhs.priority == rhs.priority)
return strcmp(lhs.name, rhs.name) > 0;
return lhs.priority > rhs.priority;
}
bool operator() (const pa_source_port_info& lhs, const pa_source_port_info& rhs) const {
if (lhs.priority == rhs.priority)
return strcmp(lhs.name, rhs.name) > 0;
return lhs.priority > rhs.priority;
}
};
MainWindow::MainWindow(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& x) :
Gtk::Window(cobject),
showSinkInputType(SINK_INPUT_CLIENT),
showSinkType(SINK_ALL),
showSourceOutputType(SOURCE_OUTPUT_CLIENT),
showSourceType(SOURCE_NO_MONITOR),
canRenameDevices(false),
m_connected(false),
m_config_filename(NULL) {
x->get_widget("cardsVBox", cardsVBox);
x->get_widget("streamsVBox", streamsVBox);
x->get_widget("recsVBox", recsVBox);
x->get_widget("sinksVBox", sinksVBox);
x->get_widget("sourcesVBox", sourcesVBox);
x->get_widget("noCardsLabel", noCardsLabel);
x->get_widget("noStreamsLabel", noStreamsLabel);
x->get_widget("noRecsLabel", noRecsLabel);
x->get_widget("noSinksLabel", noSinksLabel);
x->get_widget("noSourcesLabel", noSourcesLabel);
x->get_widget("connectingLabel", connectingLabel);
x->get_widget("sinkInputTypeComboBox", sinkInputTypeComboBox);
x->get_widget("sourceOutputTypeComboBox", sourceOutputTypeComboBox);
x->get_widget("sinkTypeComboBox", sinkTypeComboBox);
x->get_widget("sourceTypeComboBox", sourceTypeComboBox);
x->get_widget("notebook", notebook);
x->get_widget("showVolumeMetersCheckButton", showVolumeMetersCheckButton);
cardsVBox->set_reallocate_redraws(true);
sourcesVBox->set_reallocate_redraws(true);
streamsVBox->set_reallocate_redraws(true);
recsVBox->set_reallocate_redraws(true);
sinksVBox->set_reallocate_redraws(true);
sinkInputTypeComboBox->set_active((int) showSinkInputType);
sourceOutputTypeComboBox->set_active((int) showSourceOutputType);
sinkTypeComboBox->set_active((int) showSinkType);
sourceTypeComboBox->set_active((int) showSourceType);
sinkInputTypeComboBox->signal_changed().connect(sigc::mem_fun(*this, &MainWindow::onSinkInputTypeComboBoxChanged));
sourceOutputTypeComboBox->signal_changed().connect(sigc::mem_fun(*this, &MainWindow::onSourceOutputTypeComboBoxChanged));
sinkTypeComboBox->signal_changed().connect(sigc::mem_fun(*this, &MainWindow::onSinkTypeComboBoxChanged));
sourceTypeComboBox->signal_changed().connect(sigc::mem_fun(*this, &MainWindow::onSourceTypeComboBoxChanged));
showVolumeMetersCheckButton->signal_toggled().connect(sigc::mem_fun(*this, &MainWindow::onShowVolumeMetersCheckButtonToggled));
GKeyFile* config = g_key_file_new();
g_assert(config);
GKeyFileFlags flags = (GKeyFileFlags)( G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS);
GError *err = NULL;
m_config_filename = g_strconcat(g_get_user_config_dir(), "/pavucontrol.ini", NULL);
/* Load the GKeyFile from keyfile.conf or return. */
if (g_key_file_load_from_file (config, m_config_filename, flags, &err)) {
int width = g_key_file_get_integer(config, "window", "width", NULL);
int height = g_key_file_get_integer(config, "window", "height", NULL);
/* When upgrading from a previous version, set showVolumeMeters to TRUE
* (default from glade file), so users don't complain about missing
* volume meters. */
if (g_key_file_has_key(config, "window", "showVolumeMeters", NULL)) {
showVolumeMetersCheckButton->set_active(g_key_file_get_boolean(config, "window", "showVolumeMeters", NULL));
}
int default_width, default_height;
get_default_size(default_width, default_height);
if (width >= default_width && height >= default_height)
resize(width, height);
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
int sinkInputTypeSelection = g_key_file_get_integer(config, "window", "sinkInputType", &err);
if (err == NULL)
sinkInputTypeComboBox->set_active(sinkInputTypeSelection);
else {
g_error_free(err);
err = NULL;
}
int sourceOutputTypeSelection = g_key_file_get_integer(config, "window", "sourceOutputType", &err);
if (err == NULL)
sourceOutputTypeComboBox->set_active(sourceOutputTypeSelection);
else {
g_error_free(err);
err = NULL;
}
int sinkTypeSelection = g_key_file_get_integer(config, "window", "sinkType", &err);
if (err == NULL)
sinkTypeComboBox->set_active(sinkTypeSelection);
else {
g_error_free(err);
err = NULL;
}
int sourceTypeSelection = g_key_file_get_integer(config, "window", "sourceType", &err);
if (err == NULL)
sourceTypeComboBox->set_active(sourceTypeSelection);
else {
g_error_free(err);
err = NULL;
}
} else {
g_debug(_("Error reading config file %s: %s"), m_config_filename, err->message);
g_error_free(err);
}
g_key_file_free(config);
/* Hide first and show when we're connected */
notebook->hide();
connectingLabel->show();
MainWindow* MainWindow::create(bool maximize) {
Glib::RefPtr<Gtk::Builder> x = Gtk::Builder::create();
x->add_from_file(GLADE_FILE, "liststore1");
x->add_from_file(GLADE_FILE, "liststore2");
x->add_from_file(GLADE_FILE, "liststore3");
x->add_from_file(GLADE_FILE, "liststore4");
x->add_from_file(GLADE_FILE, "mainWindow");
x->get_widget_derived("mainWindow", w);
w->get_style_context()->add_class("pavucontrol-window");
if (w && maximize)
w->maximize();
return w;
}
void MainWindow::on_realize() {
Gtk::Window::on_realize();
get_window()->set_cursor(Gdk::Cursor::create(Gdk::WATCH));
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
bool MainWindow::on_key_press_event(GdkEventKey* event) {
if (event->state & GDK_CONTROL_MASK) {
switch (event->keyval) {
case GDK_KEY_KP_1:
case GDK_KEY_KP_2:
case GDK_KEY_KP_3:
case GDK_KEY_KP_4:
case GDK_KEY_KP_5:
notebook->set_current_page(event->keyval - GDK_KEY_KP_1);
return true;
case GDK_KEY_1:
case GDK_KEY_2:
case GDK_KEY_3:
case GDK_KEY_4:
case GDK_KEY_5:
notebook->set_current_page(event->keyval - GDK_KEY_1);
return true;
case GDK_KEY_W:
case GDK_KEY_Q:
case GDK_KEY_w:
case GDK_KEY_q:
Gtk::Main::quit();
return true;
}
}
return Gtk::Window::on_key_press_event(event);
}
GKeyFile* config = g_key_file_new();
g_assert(config);
int width, height;
get_size(width, height);
g_key_file_set_integer(config, "window", "width", width);
g_key_file_set_integer(config, "window", "height", height);
g_key_file_set_integer(config, "window", "sinkInputType", sinkInputTypeComboBox->get_active_row_number());
g_key_file_set_integer(config, "window", "sourceOutputType", sourceOutputTypeComboBox->get_active_row_number());
g_key_file_set_integer(config, "window", "sinkType", sinkTypeComboBox->get_active_row_number());
g_key_file_set_integer(config, "window", "sourceType", sourceTypeComboBox->get_active_row_number());
g_key_file_set_integer(config, "window", "showVolumeMeters", showVolumeMetersCheckButton->get_active());
gsize filelen;
GError *err = NULL;
gchar *filedata = g_key_file_to_data(config, &filelen, &err);
if (err) {
show_error(_("Error saving preferences"));
g_error_free(err);
goto finish;
}
g_file_set_contents(m_config_filename, filedata, filelen, &err);
g_free(filedata);
if (err) {
gchar* msg = g_strconcat(_("Error writing config file %s"), m_config_filename, NULL);
show_error(msg);
g_free(msg);
g_error_free(err);
goto finish;
}
finish:
g_key_file_free(config);
g_free(m_config_filename);
while (!clientNames.empty()) {
std::map<uint32_t, char*>::iterator i = clientNames.begin();
g_free(i->second);
clientNames.erase(i);
}
}
static void set_icon_name_fallback(Gtk::Image *i, const char *name, Gtk::IconSize size) {
Glib::RefPtr<Gtk::IconTheme> theme;
Glib::RefPtr<Gdk::Pixbuf> pixbuf;
gint width = 24, height = 24;
Gtk::IconSize::lookup(size, width, height);
theme = Gtk::IconTheme::get_default();
pixbuf = theme->load_icon(name, width, Gtk::ICON_LOOKUP_GENERIC_FALLBACK | Gtk::ICON_LOOKUP_FORCE_SIZE);
} catch (Glib::Error &e) {
/* Ignore errors. */
}
if (!pixbuf) {
try {
pixbuf = Gdk::Pixbuf::create_from_file(name);
} catch (Glib::FileError &e) {
/* Ignore errors. */
} catch (Gdk::PixbufError &e) {
/* Ignore errors. */
}
}
if (pixbuf) {
pixbuf = pixbuf->scale_simple(width, height, Gdk::INTERP_BILINEAR);
i->set(pixbuf);
static void updatePorts(DeviceWidget *w, std::map<Glib::ustring, PortInfo> &ports) {
std::map<Glib::ustring, PortInfo>::iterator it;
PortInfo p;
for (uint32_t i = 0; i < w->ports.size(); i++) {
Glib::ustring desc;
it = ports.find(w->ports[i].first);
if (it == ports.end())
continue;
desc = p.description;
if (p.available == PA_PORT_AVAILABLE_YES)
desc += _(" (plugged in)");
else if (p.available == PA_PORT_AVAILABLE_NO) {
if (p.name == "analog-output-speaker" ||
p.name == "analog-input-microphone-internal")
desc += _(" (unavailable)");
else
desc += _(" (unplugged)");
}
w->ports[i].second = desc;
}
it = ports.find(w->activePort);
if (it != ports.end()) {
w->setLatencyOffset(p.latency_offset);
void MainWindow::selectBestTab() {
if (sinkInputWidgets.size() > 0)
notebook->set_current_page(0);
else if (sourceOutputWidgets.size() > 0)
notebook->set_current_page(1);
else if (sourceWidgets.size() > 0 && sinkWidgets.size() == 0)
notebook->set_current_page(3);
else
notebook->set_current_page(2);
}
void MainWindow::selectTab(int tab_number) {
if (tab_number > 0 && tab_number <= notebook->get_n_pages())
notebook->set_current_page(tab_number - 1);
}
void MainWindow::updateCard(const pa_card_info &info) {
CardWidget *w;
bool is_new = false;
const char *description, *icon;
std::set<pa_card_profile_info2,profile_prio_compare> profile_priorities;
if (cardWidgets.count(info.index))
w = cardWidgets[info.index];
else {
cardWidgets[info.index] = w = CardWidget::create();
cardsVBox->pack_start(*w, false, false, 0);
w->unreference();
w->index = info.index;
is_new = true;
}
w->updating = true;
description = pa_proplist_gets(info.proplist, PA_PROP_DEVICE_DESCRIPTION);
w->name = description ? description : info.name;
gchar *txt;
w->nameLabel->set_markup(txt = g_markup_printf_escaped("%s", w->name.c_str()));
g_free(txt);
icon = pa_proplist_gets(info.proplist, PA_PROP_DEVICE_ICON_NAME);
set_icon_name_fallback(w->iconImage, icon ? icon : "audio-card", Gtk::ICON_SIZE_SMALL_TOOLBAR);
w->hasSinks = w->hasSources = false;
for (uint32_t i=0; i<info.n_profiles; ++i) {
w->hasSinks = w->hasSinks || (info.profiles[i].n_sinks > 0);
w->hasSources = w->hasSources || (info.profiles[i].n_sources > 0);
profile_priorities.insert(*info.profiles2[i]);
Lennart Poettering
committed
w->ports.clear();
for (uint32_t i = 0; i < info.n_ports; ++i) {
PortInfo p;
p.name = info.ports[i]->name;
p.description = info.ports[i]->description;
p.priority = info.ports[i]->priority;
p.available = info.ports[i]->available;
p.direction = info.ports[i]->direction;
p.latency_offset = info.ports[i]->latency_offset;
for (uint32_t j = 0; j < info.ports[i]->n_profiles; j++)
p.profiles.push_back(info.ports[i]->profiles[j]->name);
w->profiles.clear();
for (std::set<pa_card_profile_info2>::iterator profileIt = profile_priorities.begin(); profileIt != profile_priorities.end(); ++profileIt) {
bool hasNo = false, hasOther = false;
std::map<Glib::ustring, PortInfo>::iterator portIt;
Glib::ustring desc = profileIt->description;
for (portIt = w->ports.begin(); portIt != w->ports.end(); portIt++) {
PortInfo port = portIt->second;
if (std::find(port.profiles.begin(), port.profiles.end(), profileIt->name) == port.profiles.end())
continue;
if (port.available == PA_PORT_AVAILABLE_NO)
hasNo = true;
else {
hasOther = true;
break;
}
}
if (hasNo && !hasOther)
desc += _(" (unplugged)");
if (!profileIt->available)
desc += _(" (unavailable)");
w->profiles.push_back(std::pair<Glib::ustring,Glib::ustring>(profileIt->name, desc));
}
w->activeProfile = info.active_profile ? info.active_profile->name : "";
/* Because the port info for sinks and sources is discontinued we need
* to update the port info for them here. */
if (w->hasSinks) {
std::map<uint32_t, SinkWidget*>::iterator it;
for (it = sinkWidgets.begin() ; it != sinkWidgets.end(); it++) {
SinkWidget *sw = it->second;
if (sw->card_index == w->index) {
sw->updating = true;
updatePorts(sw, w->ports);
sw->updating = false;
}
}
}
if (w->hasSources) {
std::map<uint32_t, SourceWidget*>::iterator it;
for (it = sourceWidgets.begin() ; it != sourceWidgets.end(); it++) {
SourceWidget *sw = it->second;
if (sw->card_index == w->index) {
sw->updating = true;
updatePorts(sw, w->ports);
sw->updating = false;
}
}
}
w->prepareMenu();
if (is_new)
updateDeviceVisibility();
w->updating = false;
Colin Guthrie
committed
bool MainWindow::updateSink(const pa_sink_info &info) {
SinkWidget *w;
bool is_new = false;
std::map<uint32_t, CardWidget*>::iterator cw;
std::set<pa_sink_port_info,sink_port_prio_compare> port_priorities;
if (sinkWidgets.count(info.index))
w = sinkWidgets[info.index];
else {
sinkWidgets[info.index] = w = SinkWidget::create(this);
w->setChannelMap(info.channel_map, !!(info.flags & PA_SINK_DECIBEL_VOLUME));
sinksVBox->pack_start(*w, false, false, 0);
w->unreference();
w->index = info.index;
w->monitor_index = info.monitor_source;
is_new = true;
w->setVolumeMeterVisible(showVolumeMetersCheckButton->get_active());
}
w->updating = true;
w->card_index = info.card;
w->name = info.name;
w->description = info.description;
w->type = info.flags & PA_SINK_HARDWARE ? SINK_HARDWARE : SINK_VIRTUAL;
w->boldNameLabel->set_text("");
gchar *txt;
w->nameLabel->set_markup(txt = g_markup_printf_escaped("%s", info.description));
w->nameLabel->set_tooltip_text(info.description);
g_free(txt);
icon = pa_proplist_gets(info.proplist, PA_PROP_DEVICE_ICON_NAME);
set_icon_name_fallback(w->iconImage, icon ? icon : "audio-card", Gtk::ICON_SIZE_SMALL_TOOLBAR);
w->setVolume(info.volume);
w->muteToggleButton->set_active(info.mute);
w->setDefault(w->name == defaultSinkName);
port_priorities.clear();
for (uint32_t i=0; i<info.n_ports; ++i) {
port_priorities.insert(*info.ports[i]);
}
w->ports.clear();
for (std::set<pa_sink_port_info>::iterator i = port_priorities.begin(); i != port_priorities.end(); ++i)
w->ports.push_back(std::pair<Glib::ustring,Glib::ustring>(i->name, i->description));
w->activePort = info.active_port ? info.active_port->name : "";
cw = cardWidgets.find(info.card);
if (cw != cardWidgets.end())
updatePorts(w, cw->second->ports);
#ifdef PA_SINK_SET_FORMATS
w->setDigital(info.flags & PA_SINK_SET_FORMATS);
Colin Guthrie
committed
if (is_new)
updateDeviceVisibility();
Colin Guthrie
committed
w->updating = false;
Colin Guthrie
committed
return is_new;
}
static void suspended_callback(pa_stream *s, void *userdata) {
MainWindow *w = static_cast<MainWindow*>(userdata);
if (pa_stream_is_suspended(s))
w->updateVolumeMeter(pa_stream_get_device_index(s), PA_INVALID_INDEX, -1);
}
static void read_callback(pa_stream *s, size_t length, void *userdata) {
MainWindow *w = static_cast<MainWindow*>(userdata);
const void *data;
double v;
if (pa_stream_peek(s, &data, &length) < 0) {
show_error(_("Failed to read data from stream"));
return;
}
if (!data) {
/* NULL data means either a hole or empty buffer.
* Only drop the stream when there is a hole (length > 0) */
if (length)
pa_stream_drop(s);
return;
}
assert(length > 0);
assert(length % sizeof(float) == 0);
v = ((const float*) data)[length / sizeof(float) -1];
pa_stream_drop(s);
if (v < 0)
v = 0;
if (v > 1)
v = 1;
w->updateVolumeMeter(pa_stream_get_device_index(s), pa_stream_get_monitor_stream(s), v);
}
pa_stream* MainWindow::createMonitorStreamForSource(uint32_t source_idx, uint32_t stream_idx = -1, bool suspend = false) {
pa_stream *s;
char t[16];
pa_buffer_attr attr;
pa_sample_spec ss;
ss.channels = 1;
ss.format = PA_SAMPLE_FLOAT32;
ss.rate = 25;
memset(&attr, 0, sizeof(attr));
attr.fragsize = sizeof(float);
attr.maxlength = (uint32_t) -1;
snprintf(t, sizeof(t), "%u", source_idx);
if (!(s = pa_stream_new(get_context(), _("Peak detect"), &ss, NULL))) {
show_error(_("Failed to create monitoring stream"));
if (stream_idx != (uint32_t) -1)
pa_stream_set_monitor_stream(s, stream_idx);
pa_stream_set_read_callback(s, read_callback, this);
pa_stream_set_suspended_callback(s, suspended_callback, this);
flags = (pa_stream_flags_t) (PA_STREAM_DONT_MOVE | PA_STREAM_PEAK_DETECT | PA_STREAM_ADJUST_LATENCY |
(suspend ? PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND : PA_STREAM_NOFLAGS) |
(!showVolumeMetersCheckButton->get_active() ? PA_STREAM_START_CORKED : PA_STREAM_NOFLAGS));
if (pa_stream_connect_record(s, t, &attr, flags) < 0) {
show_error(_("Failed to connect monitoring stream"));
pa_stream_unref(s);
void MainWindow::createMonitorStreamForSinkInput(SinkInputWidget* w, uint32_t sink_idx) {
if (!sinkWidgets.count(sink_idx))
return;
if (w->peak) {
pa_stream_disconnect(w->peak);
w->peak = NULL;
}
w->peak = createMonitorStreamForSource(sinkWidgets[sink_idx]->monitor_index, w->index);
}
void MainWindow::updateSource(const pa_source_info &info) {
SourceWidget *w;
bool is_new = false;
const char *icon;
std::map<uint32_t, CardWidget*>::iterator cw;
std::set<pa_source_port_info,source_port_prio_compare> port_priorities;
if (sourceWidgets.count(info.index))
w = sourceWidgets[info.index];
else {
sourceWidgets[info.index] = w = SourceWidget::create(this);
w->setChannelMap(info.channel_map, !!(info.flags & PA_SOURCE_DECIBEL_VOLUME));
sourcesVBox->pack_start(*w, false, false, 0);
w->unreference();
w->index = info.index;
is_new = true;
w->setVolumeMeterVisible(showVolumeMetersCheckButton->get_active());
if (pa_context_get_server_protocol_version(get_context()) >= 13)
w->peak = createMonitorStreamForSource(info.index, -1, !!(info.flags & PA_SOURCE_NETWORK));
}
w->updating = true;
w->card_index = info.card;
w->name = info.name;
w->description = info.description;
w->type = info.monitor_of_sink != PA_INVALID_INDEX ? SOURCE_MONITOR : (info.flags & PA_SOURCE_HARDWARE ? SOURCE_HARDWARE : SOURCE_VIRTUAL);
w->boldNameLabel->set_text("");
gchar *txt;
w->nameLabel->set_markup(txt = g_markup_printf_escaped("%s", info.description));
w->nameLabel->set_tooltip_text(info.description);
g_free(txt);
icon = pa_proplist_gets(info.proplist, PA_PROP_DEVICE_ICON_NAME);
set_icon_name_fallback(w->iconImage, icon ? icon : "audio-input-microphone", Gtk::ICON_SIZE_SMALL_TOOLBAR);
w->setVolume(info.volume);
w->muteToggleButton->set_active(info.mute);
w->setDefault(w->name == defaultSourceName);
port_priorities.clear();
for (uint32_t i=0; i<info.n_ports; ++i) {
port_priorities.insert(*info.ports[i]);
}
w->ports.clear();
for (std::set<pa_source_port_info>::iterator i = port_priorities.begin(); i != port_priorities.end(); ++i)
w->ports.push_back(std::pair<Glib::ustring,Glib::ustring>(i->name, i->description));
w->activePort = info.active_port ? info.active_port->name : "";
cw = cardWidgets.find(info.card);
if (cw != cardWidgets.end())
updatePorts(w, cw->second->ports);
if (is_new)
updateDeviceVisibility();
w->updating = false;
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
}
void MainWindow::setIconFromProplist(Gtk::Image *icon, pa_proplist *l, const char *def) {
const char *t;
if ((t = pa_proplist_gets(l, PA_PROP_MEDIA_ICON_NAME)))
goto finish;
if ((t = pa_proplist_gets(l, PA_PROP_WINDOW_ICON_NAME)))
goto finish;
if ((t = pa_proplist_gets(l, PA_PROP_APPLICATION_ICON_NAME)))
goto finish;
if ((t = pa_proplist_gets(l, PA_PROP_MEDIA_ROLE))) {
if (strcmp(t, "video") == 0 ||
strcmp(t, "phone") == 0)
goto finish;
if (strcmp(t, "music") == 0) {
t = "audio";
goto finish;
}
if (strcmp(t, "game") == 0) {
t = "applications-games";
goto finish;
}
if (strcmp(t, "event") == 0) {
t = "dialog-information";
goto finish;
}
}
t = def;
finish:
set_icon_name_fallback(icon, t, Gtk::ICON_SIZE_SMALL_TOOLBAR);
}
void MainWindow::updateSinkInput(const pa_sink_input_info &info) {
Colin Guthrie
committed
const char *t;
SinkInputWidget *w;
bool is_new = false;
Colin Guthrie
committed
if ((t = pa_proplist_gets(info.proplist, "module-stream-restore.id"))) {
if (strcmp(t, "sink-input-by-media-role:event") == 0) {
g_debug(_("Ignoring sink-input due to it being designated as an event and thus handled by the Event widget"));
return;
}
}
if (sinkInputWidgets.count(info.index)) {
if (pa_context_get_server_protocol_version(get_context()) >= 13)
if (w->sinkIndex() != info.sink)
createMonitorStreamForSinkInput(w, info.sink);
} else {
Colin Guthrie
committed
sinkInputWidgets[info.index] = w = SinkInputWidget::create(this);
w->setChannelMap(info.channel_map, true);
streamsVBox->pack_start(*w, false, false, 0);
w->unreference();
w->index = info.index;
w->clientIndex = info.client;
is_new = true;
w->setVolumeMeterVisible(showVolumeMetersCheckButton->get_active());
if (pa_context_get_server_protocol_version(get_context()) >= 13)
createMonitorStreamForSinkInput(w, info.sink);
}
w->updating = true;
w->type = info.client != PA_INVALID_INDEX ? SINK_INPUT_CLIENT : SINK_INPUT_VIRTUAL;
Colin Guthrie
committed
w->setSinkIndex(info.sink);
char *txt;
if (clientNames.count(info.client)) {
w->boldNameLabel->set_markup(txt = g_markup_printf_escaped("<b>%s</b>", clientNames[info.client]));
g_free(txt);
w->nameLabel->set_markup(txt = g_markup_printf_escaped(": %s", info.name));
g_free(txt);
} else {
w->boldNameLabel->set_text("");
w->nameLabel->set_label(info.name);
}
w->nameLabel->set_tooltip_text(info.name);
setIconFromProplist(w->iconImage, info.proplist, "audio-card");
w->setVolume(info.volume);
w->muteToggleButton->set_active(info.mute);
w->updating = false;
if (is_new)
updateDeviceVisibility();
}
void MainWindow::updateSourceOutput(const pa_source_output_info &info) {
SourceOutputWidget *w;
const char *app;
bool is_new = false;
if ((app = pa_proplist_gets(info.proplist, PA_PROP_APPLICATION_ID)))
if (strcmp(app, "org.PulseAudio.pavucontrol") == 0
|| strcmp(app, "org.gnome.VolumeControl") == 0
|| strcmp(app, "org.kde.kmixd") == 0)
return;
if (sourceOutputWidgets.count(info.index))
w = sourceOutputWidgets[info.index];
else {
Colin Guthrie
committed
sourceOutputWidgets[info.index] = w = SourceOutputWidget::create(this);
Colin Guthrie
committed
#if HAVE_SOURCE_OUTPUT_VOLUMES
w->setChannelMap(info.channel_map, true);
Colin Guthrie
committed
#endif
recsVBox->pack_start(*w, false, false, 0);
w->unreference();
w->index = info.index;
w->clientIndex = info.client;
Colin Guthrie
committed
is_new = true;
w->setVolumeMeterVisible(showVolumeMetersCheckButton->get_active());
}
w->updating = true;
w->type = info.client != PA_INVALID_INDEX ? SOURCE_OUTPUT_CLIENT : SOURCE_OUTPUT_VIRTUAL;
Colin Guthrie
committed
w->setSourceIndex(info.source);
char *txt;
if (clientNames.count(info.client)) {
w->boldNameLabel->set_markup(txt = g_markup_printf_escaped("<b>%s</b>", clientNames[info.client]));
g_free(txt);
w->nameLabel->set_markup(txt = g_markup_printf_escaped(": %s", info.name));
g_free(txt);
} else {
w->boldNameLabel->set_text("");
w->nameLabel->set_label(info.name);
}
w->nameLabel->set_tooltip_text(info.name);
setIconFromProplist(w->iconImage, info.proplist, "audio-input-microphone");
#if HAVE_SOURCE_OUTPUT_VOLUMES
w->setVolume(info.volume);
w->muteToggleButton->set_active(info.mute);
#endif
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
w->updating = false;
if (is_new)
updateDeviceVisibility();
}
void MainWindow::updateClient(const pa_client_info &info) {
g_free(clientNames[info.index]);
clientNames[info.index] = g_strdup(info.name);
for (std::map<uint32_t, SinkInputWidget*>::iterator i = sinkInputWidgets.begin(); i != sinkInputWidgets.end(); ++i) {
SinkInputWidget *w = i->second;
if (!w)
continue;
if (w->clientIndex == info.index) {
gchar *txt;
w->boldNameLabel->set_markup(txt = g_markup_printf_escaped("<b>%s</b>", info.name));
g_free(txt);
}
}
}
void MainWindow::updateServer(const pa_server_info &info) {
defaultSourceName = info.default_source_name ? info.default_source_name : "";
defaultSinkName = info.default_sink_name ? info.default_sink_name : "";
for (std::map<uint32_t, SinkWidget*>::iterator i = sinkWidgets.begin(); i != sinkWidgets.end(); ++i) {
SinkWidget *w = i->second;
if (!w)
continue;
w->updating = true;
w->setDefault(w->name == defaultSinkName);
w->updating = false;
}
for (std::map<uint32_t, SourceWidget*>::iterator i = sourceWidgets.begin(); i != sourceWidgets.end(); ++i) {
SourceWidget *w = i->second;
if (!w)
continue;
w->updating = true;
w->setDefault(w->name == defaultSourceName);
w->updating = false;
}
}
bool MainWindow::createEventRoleWidget() {
if (eventRoleWidget)
return FALSE;
pa_channel_map cm = {
1, { PA_CHANNEL_POSITION_MONO }
};
eventRoleWidget = RoleWidget::create();
streamsVBox->pack_start(*eventRoleWidget, false, false, 0);
eventRoleWidget->unreference();
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
eventRoleWidget->role = "sink-input-by-media-role:event";
eventRoleWidget->setChannelMap(cm, true);
eventRoleWidget->boldNameLabel->set_text("");
eventRoleWidget->nameLabel->set_label(_("System Sounds"));
eventRoleWidget->iconImage->set_from_icon_name("multimedia-volume-control", Gtk::ICON_SIZE_SMALL_TOOLBAR);
eventRoleWidget->device = "";
eventRoleWidget->updating = true;
pa_cvolume volume;
volume.channels = 1;
volume.values[0] = PA_VOLUME_NORM;
eventRoleWidget->setVolume(volume);
eventRoleWidget->muteToggleButton->set_active(false);
eventRoleWidget->updating = false;
return TRUE;
}
void MainWindow::deleteEventRoleWidget() {
if (eventRoleWidget)
delete eventRoleWidget;
eventRoleWidget = NULL;
}
void MainWindow::updateRole(const pa_ext_stream_restore_info &info) {
pa_cvolume volume;
bool is_new = false;
if (strcmp(info.name, "sink-input-by-media-role:event") != 0)
return;
is_new = createEventRoleWidget();
eventRoleWidget->updating = true;
eventRoleWidget->device = info.device ? info.device : "";
volume.channels = 1;
volume.values[0] = pa_cvolume_max(&info.volume);
eventRoleWidget->setVolume(volume);
eventRoleWidget->muteToggleButton->set_active(info.mute);
eventRoleWidget->updating = false;
if (is_new)
updateDeviceVisibility();
}
Colin Guthrie
committed
#if HAVE_EXT_DEVICE_RESTORE_API
void MainWindow::updateDeviceInfo(const pa_ext_device_restore_info &info) {
if (sinkWidgets.count(info.index)) {
SinkWidget *w;
pa_format_info *format;
w = sinkWidgets[info.index];
w->updating = true;
/* Unselect everything */
for (int j = 1; j < PAVU_NUM_ENCODINGS; ++j)