summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/Makefile.am14
-rw-r--r--src/cardwidget.cc94
-rw-r--r--src/cardwidget.h67
-rw-r--r--src/channelwidget.cc132
-rw-r--r--src/channelwidget.h57
-rw-r--r--src/mainwindow.cc834
-rw-r--r--src/mainwindow.h99
-rw-r--r--src/minimalstreamwidget.cc122
-rw-r--r--src/minimalstreamwidget.h56
-rw-r--r--src/pavucontrol.cc1835
-rw-r--r--src/pavucontrol.glade107
-rw-r--r--src/pavucontrol.h67
-rw-r--r--src/rolewidget.cc72
-rw-r--r--src/rolewidget.h40
-rw-r--r--src/sinkinputwidget.cc132
-rw-r--r--src/sinkinputwidget.h70
-rw-r--r--src/sinkwidget.cc85
-rw-r--r--src/sinkwidget.h46
-rw-r--r--src/sourceoutputwidget.cc106
-rw-r--r--src/sourceoutputwidget.h68
-rw-r--r--src/sourcewidget.cc85
-rw-r--r--src/sourcewidget.h46
-rw-r--r--src/streamwidget.cc115
-rw-r--r--src/streamwidget.h56
24 files changed, 2482 insertions, 1923 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index a990de3..629219c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -24,7 +24,19 @@ glade_DATA=pavucontrol.glade
desktop_in_files=pavucontrol.desktop.in
desktop_DATA=$(desktop_in_files:.desktop.in=.desktop)
-pavucontrol_SOURCES=pavucontrol.cc i18n.h
+pavucontrol_SOURCES= \
+ minimalstreamwidget.h minimalstreamwidget.cc \
+ channelwidget.h channelwidget.cc \
+ streamwidget.h streamwidget.cc \
+ cardwidget.h cardwidget.cc \
+ sinkwidget.h sinkwidget.cc \
+ sourcewidget.h sourcewidget.cc \
+ sinkinputwidget.h sinkinputwidget.cc \
+ sourceoutputwidget.h sourceoutputwidget.cc \
+ rolewidget.h rolewidget.cc \
+ mainwindow.h mainwindow.cc \
+ pavucontrol.h pavucontrol.cc \
+ i18n.h
pavucontrol_LDADD=$(AM_LDADD) $(GUILIBS_LIBS) $(PULSE_LIBS)
pavucontrol_CXXFLAGS=$(AM_CXXFLAGS) $(GUILIBS_CFLAGS) $(PULSE_CFLAGS) -DLOCALEDIR=\"$(localedir)\"
diff --git a/src/cardwidget.cc b/src/cardwidget.cc
new file mode 100644
index 0000000..ea4b208
--- /dev/null
+++ b/src/cardwidget.cc
@@ -0,0 +1,94 @@
+/***
+ 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 "cardwidget.h"
+
+#include "i18n.h"
+
+/*** CardWidget ***/
+CardWidget::CardWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
+ Gtk::VBox(cobject) {
+
+ x->get_widget("nameLabel", nameLabel);
+ x->get_widget("profileList", profileList);
+ x->get_widget("iconImage", iconImage);
+
+ treeModel = Gtk::ListStore::create(profileModel);
+ profileList->set_model(treeModel);
+ profileList->pack_start(profileModel.desc);
+
+ profileList->signal_changed().connect( sigc::mem_fun(*this, &CardWidget::onProfileChange));
+}
+
+CardWidget* CardWidget::create() {
+ CardWidget* w;
+ Glib::RefPtr<Gnome::Glade::Xml> x = Gnome::Glade::Xml::create(GLADE_FILE, "cardWidget");
+ x->get_widget_derived("cardWidget", w);
+ return w;
+}
+
+
+void CardWidget::prepareMenu() {
+ int idx = 0;
+ int active_idx = -1;
+
+ treeModel->clear();
+ /* Fill the ComboBox's Tree Model */
+ for (std::map<Glib::ustring, Glib::ustring>::iterator i = profiles.begin(); i != profiles.end(); ++i) {
+ Gtk::TreeModel::Row row = *(treeModel->append());
+ row[profileModel.name] = i->first;
+ row[profileModel.desc] = i->second;
+ if (i->first == activeProfile)
+ active_idx = idx;
+ idx++;
+ }
+
+ if (active_idx >= 0)
+ profileList->set_active(active_idx);
+}
+
+void CardWidget::onProfileChange() {
+ Gtk::TreeModel::iterator iter;
+
+ if (updating)
+ return;
+
+ iter = profileList->get_active();
+ if (iter)
+ {
+ Gtk::TreeModel::Row row = *iter;
+ if (row)
+ {
+ pa_operation* o;
+ Glib::ustring profile = row[profileModel.name];
+
+ if (!(o = pa_context_set_card_profile_by_index(get_context(), index, profile.c_str(), NULL, NULL))) {
+ show_error(_("pa_context_set_card_profile_by_index() failed"));
+ return;
+ }
+
+ pa_operation_unref(o);
+ }
+ }
+}
diff --git a/src/cardwidget.h b/src/cardwidget.h
new file mode 100644
index 0000000..d420c83
--- /dev/null
+++ b/src/cardwidget.h
@@ -0,0 +1,67 @@
+/***
+ 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/>.
+***/
+
+#ifndef cardwidget_h
+#define cardwidget_h
+
+#include "pavucontrol.h"
+
+class CardWidget : public Gtk::VBox {
+public:
+ CardWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
+ static CardWidget* create();
+
+ Gtk::Label *nameLabel;
+ Gtk::ToggleButton *streamToggleButton;
+ Gtk::Menu menu;
+ Gtk::Image *iconImage;
+ Glib::ustring name;
+ uint32_t index;
+ bool updating;
+
+ std::map<Glib::ustring,Glib::ustring> profiles;
+ Glib::ustring activeProfile;
+ bool hasSinks;
+ bool hasSources;
+
+ void prepareMenu();
+
+protected:
+ virtual void onProfileChange();
+
+ /* Tree model columns */
+ class ModelColumns : public Gtk::TreeModel::ColumnRecord
+ {
+ public:
+
+ ModelColumns()
+ { add(name); add(desc); }
+
+ Gtk::TreeModelColumn<Glib::ustring> name;
+ Gtk::TreeModelColumn<Glib::ustring> desc;
+ };
+
+ ModelColumns profileModel;
+
+ Gtk::ComboBox *profileList;
+ Glib::RefPtr<Gtk::ListStore> treeModel;
+};
+
+#endif
diff --git a/src/channelwidget.cc b/src/channelwidget.cc
new file mode 100644
index 0000000..eac41ad
--- /dev/null
+++ b/src/channelwidget.cc
@@ -0,0 +1,132 @@
+/***
+ 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 "channelwidget.h"
+#include "streamwidget.h"
+
+#include "i18n.h"
+
+static bool show_decibel = true;
+
+/*** ChannelWidget ***/
+
+ChannelWidget::ChannelWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
+ Gtk::EventBox(cobject),
+ volumeScaleEnabled(true) {
+
+ x->get_widget("channelLabel", channelLabel);
+ x->get_widget("volumeLabel", volumeLabel);
+ x->get_widget("volumeScale", volumeScale);
+
+ volumeScale->set_value(100.0);
+ volumeScale->set_increments(100.0/PA_VOLUME_NORM, 100.0/PA_VOLUME_NORM);
+
+ volumeScale->signal_value_changed().connect(sigc::mem_fun(*this, &ChannelWidget::onVolumeScaleValueChanged));
+}
+
+ChannelWidget* ChannelWidget::create() {
+ ChannelWidget* w;
+ Glib::RefPtr<Gnome::Glade::Xml> x = Gnome::Glade::Xml::create(GLADE_FILE, "channelWidget");
+ x->get_widget_derived("channelWidget", w);
+ return w;
+}
+
+void ChannelWidget::setVolume(pa_volume_t volume) {
+ double v;
+ char txt[64];
+
+ v = ((gdouble) volume * 100) / PA_VOLUME_NORM;
+
+ if (can_decibel && show_decibel) {
+ double dB = pa_sw_volume_to_dB(volume);
+
+ if (dB > PA_DECIBEL_MININFTY) {
+ snprintf(txt, sizeof(txt), "%0.2f dB", dB);
+ volumeLabel->set_tooltip_text(txt);
+ } else
+ volumeLabel->set_tooltip_markup("-&#8734;dB");
+ volumeLabel->set_has_tooltip(TRUE);
+ } else
+ volumeLabel->set_has_tooltip(FALSE);
+
+ snprintf(txt, sizeof(txt), "%0.0f%%", v);
+ volumeLabel->set_text(txt);
+
+ volumeScaleEnabled = false;
+ volumeScale->set_value(v > 100 ? 100 : v);
+ volumeScaleEnabled = true;
+}
+
+void ChannelWidget::onVolumeScaleValueChanged() {
+
+ if (!volumeScaleEnabled)
+ return;
+
+ if (streamWidget->updating)
+ return;
+
+ pa_volume_t volume = (pa_volume_t) ((volumeScale->get_value() * PA_VOLUME_NORM) / 100);
+ streamWidget->updateChannelVolume(channel, volume);
+
+ if (beepDevice != "") {
+ ca_context_change_device(ca_gtk_context_get(), beepDevice.c_str());
+
+ ca_context_cancel(ca_gtk_context_get(), 2);
+
+ ca_gtk_play_for_widget(GTK_WIDGET(volumeScale->gobj()),
+ 2,
+ CA_PROP_EVENT_DESCRIPTION, _("Volume Control Feedback Sound"),
+ CA_PROP_EVENT_ID, "audio-volume-change",
+ CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
+ CA_PROP_CANBERRA_VOLUME, "0",
+ CA_PROP_CANBERRA_ENABLE, "1",
+ NULL);
+
+ ca_context_change_device(ca_gtk_context_get(), NULL);
+ }
+}
+
+void ChannelWidget::set_sensitive(bool enabled) {
+ Gtk::EventBox::set_sensitive(enabled);
+
+ channelLabel->set_sensitive(enabled);
+ volumeLabel->set_sensitive(enabled);
+ volumeScale->set_sensitive(enabled);
+}
+
+void ChannelWidget::setBaseVolume(pa_volume_t v) {
+
+ gtk_scale_add_mark(GTK_SCALE(volumeScale->gobj()), 0.0, (GtkPositionType) GTK_POS_BOTTOM, _("<small>Silence</small>"));
+ gtk_scale_add_mark(GTK_SCALE(volumeScale->gobj()), 100.0, (GtkPositionType) GTK_POS_BOTTOM, _("<small>Max</small>"));
+
+ if (v > PA_VOLUME_MUTED && v < PA_VOLUME_NORM) {
+ double p = ((double) v * 100) / PA_VOLUME_NORM;
+ gtk_scale_add_mark(GTK_SCALE(volumeScale->gobj()), p, (GtkPositionType) GTK_POS_BOTTOM, _("<small><i>Base</i></small>"));
+ }
+
+}
+
+void ChannelWidget::setSteps(unsigned n) {
+ volumeScale->set_increments(100.0/(n-1), 100.0/(n-1));
+}
diff --git a/src/channelwidget.h b/src/channelwidget.h
new file mode 100644
index 0000000..1de22d0
--- /dev/null
+++ b/src/channelwidget.h
@@ -0,0 +1,57 @@
+/***
+ 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/>.
+***/
+
+#ifndef channelwidget_h
+#define channelwidget_h
+
+#include "pavucontrol.h"
+
+#include <canberra-gtk.h>
+
+class StreamWidget;
+
+class ChannelWidget : public Gtk::EventBox {
+public:
+ ChannelWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
+ static ChannelWidget* create();
+
+ void setVolume(pa_volume_t volume);
+
+ Gtk::Label *channelLabel;
+ Gtk::Label *volumeLabel;
+ Gtk::HScale *volumeScale;
+
+ int channel;
+ StreamWidget *streamWidget;
+
+ void onVolumeScaleValueChanged();
+
+ bool can_decibel;
+ bool volumeScaleEnabled;
+
+ Glib::ustring beepDevice;
+
+ virtual void set_sensitive(bool enabled);
+ virtual void setBaseVolume(pa_volume_t);
+ virtual void setSteps(unsigned n);
+};
+
+
+#endif
diff --git a/src/mainwindow.cc b/src/mainwindow.cc
new file mode 100644
index 0000000..0277bb0
--- /dev/null
+++ b/src/mainwindow.cc
@@ -0,0 +1,834 @@
+/***
+ 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"
+
+/*** MainWindow ***/
+
+MainWindow::MainWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
+ Gtk::Window(cobject),
+ showSinkInputType(SINK_INPUT_CLIENT),
+ showSinkType(SINK_ALL),
+ showSourceOutputType(SOURCE_OUTPUT_CLIENT),
+ showSourceType(SOURCE_NO_MONITOR),
+ eventRoleWidget(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("sinkInputTypeComboBox", sinkInputTypeComboBox);
+ x->get_widget("sourceOutputTypeComboBox", sourceOutputTypeComboBox);
+ x->get_widget("sinkTypeComboBox", sinkTypeComboBox);
+ x->get_widget("sourceTypeComboBox", sourceTypeComboBox);
+ x->get_widget("notebook", notebook);
+
+ 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));
+}
+
+MainWindow* MainWindow::create() {
+ MainWindow* w;
+ Glib::RefPtr<Gnome::Glade::Xml> x = Gnome::Glade::Xml::create(GLADE_FILE, "mainWindow");
+ x->get_widget_derived("mainWindow", w);
+ return w;
+}
+
+void MainWindow::on_realize() {
+ Gtk::Window::on_realize();
+
+ get_window()->set_cursor(Gdk::Cursor(Gdk::WATCH));
+}
+
+MainWindow::~MainWindow() {
+ 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);
+
+ if (pixbuf)
+ i->set(pixbuf);
+ else
+ i->set(name);
+}
+
+void MainWindow::updateCard(const pa_card_info &info) {
+ CardWidget *w;
+ bool is_new = false;
+ const char *description, *icon;
+
+ if (cardWidgets.count(info.index))
+ w = cardWidgets[info.index];
+ else {
+ cardWidgets[info.index] = w = CardWidget::create();
+ cardsVBox->pack_start(*w, false, false, 0);
+ 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;
+ w->nameLabel->set_markup(w->name.c_str());
+
+ 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;
+ w->profiles.clear();
+ 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);
+ w->profiles.insert(std::pair<Glib::ustring,Glib::ustring>(info.profiles[i].name, info.profiles[i].description));
+ }
+ w->activeProfile = info.active_profile->name;
+
+ w->updating = false;
+
+ w->prepareMenu();
+
+ if (is_new)
+ updateDeviceVisibility();
+}
+
+void MainWindow::updateSink(const pa_sink_info &info) {
+ SinkWidget *w;
+ bool is_new = false;
+ const char *icon;
+
+ if (sinkWidgets.count(info.index))
+ w = sinkWidgets[info.index];
+ else {
+ sinkWidgets[info.index] = w = SinkWidget::create();
+ w->beepDevice = info.name;
+ w->setChannelMap(info.channel_map, !!(info.flags & PA_SINK_DECIBEL_VOLUME));
+ sinksVBox->pack_start(*w, false, false, 0);
+ w->index = info.index;
+ w->monitor_index = info.monitor_source;
+ is_new = true;
+
+ w->setBaseVolume(info.base_volume);
+ w->setSteps(info.n_volume_steps);
+ }
+
+ 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));
+ 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->defaultMenuItem.set_active(w->name == defaultSinkName);
+
+ w->updating = false;
+
+ if (is_new)
+ updateDeviceVisibility();
+}
+
+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;
+ }
+
+ 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);
+}
+
+void MainWindow::createMonitorStreamForSource(uint32_t source_idx) {
+ pa_stream *s;
+ char t[16];
+ pa_buffer_attr attr;
+ pa_sample_spec ss;
+ return;
+
+ 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"));
+ return;
+ }
+
+ pa_stream_set_read_callback(s, read_callback, this);
+ pa_stream_set_suspended_callback(s, suspended_callback, this);
+
+ if (pa_stream_connect_record(s, t, &attr, (pa_stream_flags_t) (PA_STREAM_DONT_MOVE|PA_STREAM_PEAK_DETECT|PA_STREAM_ADJUST_LATENCY)) < 0) {
+ show_error(_("Failed to connect monitoring stream"));
+ pa_stream_unref(s);
+ return;
+ }
+}
+
+void MainWindow::createMonitorStreamForSinkInput(uint32_t sink_input_idx, uint32_t sink_idx) {
+ pa_stream *s;
+ char t[16];
+ pa_buffer_attr attr;
+ pa_sample_spec ss;
+ uint32_t monitor_source_idx;
+ return;
+
+ ss.channels = 1;
+ ss.format = PA_SAMPLE_FLOAT32;
+ ss.rate = 25;
+
+ if (!sinkWidgets.count(sink_idx))
+ return;
+
+ monitor_source_idx = sinkWidgets[sink_idx]->monitor_index;
+
+ memset(&attr, 0, sizeof(attr));
+ attr.fragsize = sizeof(float);
+ attr.maxlength = (uint32_t) -1;
+
+ snprintf(t, sizeof(t), "%u", monitor_source_idx);
+
+ if (!(s = pa_stream_new(get_context(), _("Peak detect"), &ss, NULL))) {
+ show_error(_("Failed to create monitoring stream"));
+ return;
+ }
+
+ pa_stream_set_monitor_stream(s, sink_input_idx);
+ pa_stream_set_read_callback(s, read_callback, this);
+ pa_stream_set_suspended_callback(s, suspended_callback, this);
+
+ if (pa_stream_connect_record(s, t, &attr, (pa_stream_flags_t) (PA_STREAM_DONT_MOVE|PA_STREAM_PEAK_DETECT|PA_STREAM_ADJUST_LATENCY)) < 0) {
+ show_error(_("Failed to connect monitoring stream"));
+ pa_stream_unref(s);
+ return;
+ }
+}
+
+void MainWindow::updateSource(const pa_source_info &info) {
+ SourceWidget *w;
+ bool is_new = false;
+ const char *icon;
+
+ if (sourceWidgets.count(info.index))
+ w = sourceWidgets[info.index];
+ else {
+ sourceWidgets[info.index] = w = SourceWidget::create();
+ w->setChannelMap(info.channel_map, !!(info.flags & PA_SOURCE_DECIBEL_VOLUME));
+ sourcesVBox->pack_start(*w, false, false, 0);
+ w->index = info.index;
+ is_new = true;
+
+ w->setBaseVolume(info.base_volume);
+ w->setSteps(info.n_volume_steps);
+
+ if (pa_context_get_server_protocol_version(get_context()) >= 13)
+ createMonitorStreamForSource(info.index);
+ }
+
+ 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));
+ 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->defaultMenuItem.set_active(w->name == defaultSourceName);
+
+ w->updating = false;
+
+ if (is_new)
+ updateDeviceVisibility();
+}
+
+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:
+
+ icon->set_from_icon_name(t, Gtk::ICON_SIZE_SMALL_TOOLBAR);
+}
+
+void MainWindow::updateSinkInput(const pa_sink_input_info &info) {
+ SinkInputWidget *w;
+ bool is_new = false;
+
+ if (sinkInputWidgets.count(info.index))
+ w = sinkInputWidgets[info.index];
+ else {
+ sinkInputWidgets[info.index] = w = SinkInputWidget::create();
+ w->setChannelMap(info.channel_map, true);
+ streamsVBox->pack_start(*w, false, false, 0);
+ w->index = info.index;
+ w->clientIndex = info.client;
+ w->mainWindow = this;
+ is_new = true;
+
+ if (pa_context_get_server_protocol_version(get_context()) >= 13)
+ createMonitorStreamForSinkInput(info.index, info.sink);
+ }
+
+ w->updating = true;
+
+ w->type = info.client != PA_INVALID_INDEX ? SINK_INPUT_CLIENT : SINK_INPUT_VIRTUAL;
+
+ w->sinkIndex = 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);
+ }
+
+ 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)
+ return;
+
+ if (sourceOutputWidgets.count(info.index))
+ w = sourceOutputWidgets[info.index];
+ else {
+ sourceOutputWidgets[info.index] = w = SourceOutputWidget::create();
+ recsVBox->pack_start(*w, false, false, 0);
+ w->index = info.index;
+ w->clientIndex = info.client;
+ w->mainWindow = this;
+ }
+
+ w->updating = true;
+
+ w->type = info.client != PA_INVALID_INDEX ? SOURCE_OUTPUT_CLIENT : SOURCE_OUTPUT_VIRTUAL;
+
+ w->sourceIndex = 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);
+ }
+
+ setIconFromProplist(w->iconImage, info.proplist, "audio-input-microphone");
+
+ 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->defaultMenuItem.set_active(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->defaultMenuItem.set_active(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->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();
+}
+
+void MainWindow::updateVolumeMeter(uint32_t source_index, uint32_t sink_input_idx, double v) {
+
+ if (sink_input_idx != PA_INVALID_INDEX) {
+ SinkInputWidget *w;
+
+ if (sinkInputWidgets.count(sink_input_idx)) {
+ w = sinkInputWidgets[sink_input_idx];
+ w->updatePeak(v);
+ }
+
+ } else {
+
+ for (std::map<uint32_t, SinkWidget*>::iterator i = sinkWidgets.begin(); i != sinkWidgets.end(); ++i) {
+ SinkWidget* w = i->second;
+
+ if (w->monitor_index == source_index)
+ w->updatePeak(v);
+ }
+
+ for (std::map<uint32_t, SourceWidget*>::iterator i = sourceWidgets.begin(); i != sourceWidgets.end(); ++i) {
+ SourceWidget* w = i->second;
+
+ if (w->index == source_index)
+ w->updatePeak(v);
+ }
+
+ for (std::map<uint32_t, SourceOutputWidget*>::iterator i = sourceOutputWidgets.begin(); i != sourceOutputWidgets.end(); ++i) {
+ SourceOutputWidget* w = i->second;
+
+ if (w->sourceIndex == source_index)
+ w->updatePeak(v);
+ }
+ }
+}
+
+static guint idle_source = 0;
+
+gboolean idle_cb(gpointer data) {
+ ((MainWindow*) data)->reallyUpdateDeviceVisibility();
+ idle_source = 0;
+ return FALSE;
+}
+
+void MainWindow::updateDeviceVisibility() {
+
+ if (idle_source)
+ return;
+
+ idle_source = g_idle_add(idle_cb, this);
+}
+
+void MainWindow::reallyUpdateDeviceVisibility() {
+ bool is_empty = true;
+
+ for (std::map<uint32_t, SinkInputWidget*>::iterator i = sinkInputWidgets.begin(); i != sinkInputWidgets.end(); ++i) {
+ SinkInputWidget* w = i->second;
+
+ if (showSinkInputType == SINK_INPUT_ALL || w->type == showSinkInputType) {
+ w->show();
+ is_empty = false;
+ } else
+ w->hide();
+ }
+
+ if (eventRoleWidget)
+ is_empty = false;
+
+ if (is_empty)
+ noStreamsLabel->show();
+ else
+ noStreamsLabel->hide();
+
+ is_empty = true;
+
+ for (std::map<uint32_t, SourceOutputWidget*>::iterator i = sourceOutputWidgets.begin(); i != sourceOutputWidgets.end(); ++i) {
+ SourceOutputWidget* w = i->second;
+
+ if (showSourceOutputType == SOURCE_OUTPUT_ALL || w->type == showSourceOutputType) {
+ w->show();
+ is_empty = false;
+ } else
+ w->hide();
+ }
+
+ if (is_empty)
+ noRecsLabel->show();
+ else
+ noRecsLabel->hide();
+
+ is_empty = true;
+
+ for (std::map<uint32_t, SinkWidget*>::iterator i = sinkWidgets.begin(); i != sinkWidgets.end(); ++i) {
+ SinkWidget* w = i->second;
+
+ if (showSinkType == SINK_ALL || w->type == showSinkType) {
+ w->show();
+ is_empty = false;
+ } else
+ w->hide();
+ }
+
+ if (is_empty)
+ noSinksLabel->show();
+ else
+ noSinksLabel->hide();
+
+ is_empty = true;
+
+ for (std::map<uint32_t, CardWidget*>::iterator i = cardWidgets.begin(); i != cardWidgets.end(); ++i) {
+ CardWidget* w = i->second;
+
+ w->show();
+ is_empty = false;
+ }
+
+ if (is_empty)
+ noCardsLabel->show();
+ else
+ noCardsLabel->hide();
+
+ is_empty = true;
+
+ for (std::map<uint32_t, SourceWidget*>::iterator i = sourceWidgets.begin(); i != sourceWidgets.end(); ++i) {
+ SourceWidget* w = i->second;
+
+ if (showSourceType == SOURCE_ALL ||
+ w->type == showSourceType ||
+ (showSourceType == SOURCE_NO_MONITOR && w->type != SOURCE_MONITOR)) {
+ w->show();
+ is_empty = false;
+ } else
+ w->hide();
+ }
+
+ if (is_empty)
+ noSourcesLabel->show();
+ else
+ noSourcesLabel->hide();
+
+ /* Hmm, if I don't call hide()/show() here some widgets will never
+ * get their proper space allocated */
+ sinksVBox->hide();
+ sinksVBox->show();
+ sourcesVBox->hide();
+ sourcesVBox->show();
+ streamsVBox->hide();
+ streamsVBox->show();
+ recsVBox->hide();
+ recsVBox->show();
+ cardsVBox->hide();
+ cardsVBox->show();
+}
+
+void MainWindow::removeCard(uint32_t index) {
+ if (!cardWidgets.count(index))
+ return;
+
+ delete cardWidgets[index];
+ cardWidgets.erase(index);
+ updateDeviceVisibility();
+}
+
+void MainWindow::removeSink(uint32_t index) {
+ if (!sinkWidgets.count(index))
+ return;
+
+ delete sinkWidgets[index];
+ sinkWidgets.erase(index);
+ updateDeviceVisibility();
+}
+
+void MainWindow::removeSource(uint32_t index) {
+ if (!sourceWidgets.count(index))
+ return;
+
+ delete sourceWidgets[index];
+ sourceWidgets.erase(index);
+ updateDeviceVisibility();
+}
+
+void MainWindow::removeSinkInput(uint32_t index) {
+ if (!sinkInputWidgets.count(index))
+ return;
+
+ delete sinkInputWidgets[index];
+ sinkInputWidgets.erase(index);
+ updateDeviceVisibility();
+}
+
+void MainWindow::removeSourceOutput(uint32_t index) {
+ if (!sourceOutputWidgets.count(index))
+ return;
+
+ delete sourceOutputWidgets[index];
+ sourceOutputWidgets.erase(index);
+ updateDeviceVisibility();
+}
+
+void MainWindow::removeClient(uint32_t index) {
+ g_free(clientNames[index]);
+ clientNames.erase(index);
+}
+
+void MainWindow::onSinkTypeComboBoxChanged() {
+ showSinkType = (SinkType) sinkTypeComboBox->get_active_row_number();
+
+ if (showSinkType == (SinkType) -1)
+ sinkTypeComboBox->set_active((int) SINK_ALL);
+
+ updateDeviceVisibility();
+}
+
+void MainWindow::onSourceTypeComboBoxChanged() {
+ showSourceType = (SourceType) sourceTypeComboBox->get_active_row_number();
+
+ if (showSourceType == (SourceType) -1)
+ sourceTypeComboBox->set_active((int) SOURCE_NO_MONITOR);
+
+ updateDeviceVisibility();
+}
+
+void MainWindow::onSinkInputTypeComboBoxChanged() {
+ showSinkInputType = (SinkInputType) sinkInputTypeComboBox->get_active_row_number();
+
+ if (showSinkInputType == (SinkInputType) -1)
+ sinkInputTypeComboBox->set_active((int) SINK_INPUT_CLIENT);
+
+ updateDeviceVisibility();
+}
+
+void MainWindow::onSourceOutputTypeComboBoxChanged() {
+ showSourceOutputType = (SourceOutputType) sourceOutputTypeComboBox->get_active_row_number();
+
+ if (showSourceOutputType == (SourceOutputType) -1)
+ sourceOutputTypeComboBox->set_active((int) SOURCE_OUTPUT_CLIENT);
+
+ updateDeviceVisibility();
+}
diff --git a/src/mainwindow.h b/src/mainwindow.h
new file mode 100644
index 0000000..a9f0f64
--- /dev/null
+++ b/src/mainwindow.h
@@ -0,0 +1,99 @@
+/***
+ 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/>.
+***/
+
+#ifndef mainwindow_h
+#define mainwindow_h
+
+#include "pavucontrol.h"
+#include <pulse/ext-stream-restore.h>
+
+
+class CardWidget;
+class SinkWidget;
+class SourceWidget;
+class SinkInputWidget;
+class SourceOutputWidget;
+class RoleWidget;
+
+class MainWindow : public Gtk::Window {
+public:
+ MainWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
+ static MainWindow* create();
+ virtual ~MainWindow();
+
+ void updateCard(const pa_card_info &info);
+ void updateSink(const pa_sink_info &info);
+ void updateSource(const pa_source_info &info);
+ void updateSinkInput(const pa_sink_input_info &info);
+ void updateSourceOutput(const pa_source_output_info &info);
+ void updateClient(const pa_client_info &info);
+ void updateServer(const pa_server_info &info);
+ void updateVolumeMeter(uint32_t source_index, uint32_t sink_input_index, double v);
+ void updateRole(const pa_ext_stream_restore_info &info);
+
+ void removeCard(uint32_t index);
+ void removeSink(uint32_t index);
+ void removeSource(uint32_t index);
+ void removeSinkInput(uint32_t index);
+ void removeSourceOutput(uint32_t index);
+ void removeClient(uint32_t index);
+
+ Gtk::Notebook *notebook;
+ Gtk::VBox *streamsVBox, *recsVBox, *sinksVBox, *sourcesVBox, *cardsVBox;
+ Gtk::Label *noStreamsLabel, *noRecsLabel, *noSinksLabel, *noSourcesLabel, *noCardsLabel;
+ Gtk::ComboBox *sinkInputTypeComboBox, *sourceOutputTypeComboBox, *sinkTypeComboBox, *sourceTypeComboBox;
+
+ std::map<uint32_t, CardWidget*> cardWidgets;
+ std::map<uint32_t, SinkWidget*> sinkWidgets;
+ std::map<uint32_t, SourceWidget*> sourceWidgets;
+ std::map<uint32_t, SinkInputWidget*> sinkInputWidgets;
+ std::map<uint32_t, SourceOutputWidget*> sourceOutputWidgets;
+ std::map<uint32_t, char*> clientNames;
+
+ SinkInputType showSinkInputType;
+ SinkType showSinkType;
+ SourceOutputType showSourceOutputType;
+ SourceType showSourceType;
+
+ virtual void onSinkInputTypeComboBoxChanged();
+ virtual void onSourceOutputTypeComboBoxChanged();
+ virtual void onSinkTypeComboBoxChanged();
+ virtual void onSourceTypeComboBoxChanged();
+
+ void updateDeviceVisibility();
+ void reallyUpdateDeviceVisibility();
+ void createMonitorStreamForSource(uint32_t source_idx);
+ void createMonitorStreamForSinkInput(uint32_t sink_input_idx, uint32_t sink_idx);
+
+ void setIconFromProplist(Gtk::Image *icon, pa_proplist *l, const char *name);
+
+ RoleWidget *eventRoleWidget;
+
+ bool createEventRoleWidget();
+ void deleteEventRoleWidget();
+
+ Glib::ustring defaultSinkName, defaultSourceName;
+
+protected:
+ virtual void on_realize();
+};
+
+
+#endif
diff --git a/src/minimalstreamwidget.cc b/src/minimalstreamwidget.cc
new file mode 100644
index 0000000..1044d7a
--- /dev/null
+++ b/src/minimalstreamwidget.cc
@@ -0,0 +1,122 @@
+/***
+ 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 "minimalstreamwidget.h"
+
+/*** MinimalStreamWidget ***/
+MinimalStreamWidget::MinimalStreamWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
+ Gtk::VBox(cobject),
+ peakProgressBar(),
+ lastPeak(0),
+ updating(false),
+ volumeMeterEnabled(false) {
+
+ x->get_widget("channelsVBox", channelsVBox);
+ x->get_widget("nameLabel", nameLabel);
+ x->get_widget("boldNameLabel", boldNameLabel);
+ x->get_widget("streamToggle", streamToggleButton);
+ x->get_widget("iconImage", iconImage);
+
+ peakProgressBar.set_size_request(-1, 10);
+ channelsVBox->pack_end(peakProgressBar, false, false);
+
+ streamToggleButton->set_active(false);
+ streamToggleButton->signal_clicked().connect(sigc::mem_fun(*this, &MinimalStreamWidget::onStreamToggleButton));
+ menu.signal_deactivate().connect(sigc::mem_fun(*this, &MinimalStreamWidget::onMenuDeactivated));
+
+ peakProgressBar.hide();
+}
+
+void MinimalStreamWidget::prepareMenu(void) {
+}
+
+void MinimalStreamWidget::onMenuDeactivated(void) {
+ streamToggleButton->set_active(false);
+}
+
+void MinimalStreamWidget::popupMenuPosition(int& x, int& y, bool& push_in G_GNUC_UNUSED) {
+ Gtk::Requisition r;
+
+ streamToggleButton->get_window()->get_origin(x, y);
+ r = menu.size_request();
+
+ /* Align the right side of the menu with the right side of the togglebutton */
+ x += streamToggleButton->get_allocation().get_x();
+ x += streamToggleButton->get_allocation().get_width();
+ x -= r.width;
+
+ /* Align the top of the menu with the buttom of the togglebutton */
+ y += streamToggleButton->get_allocation().get_y();
+ y += streamToggleButton->get_allocation().get_height();
+}
+
+void MinimalStreamWidget::onStreamToggleButton(void) {
+ if (streamToggleButton->get_active()) {
+ prepareMenu();
+ menu.popup(sigc::mem_fun(*this, &MinimalStreamWidget::popupMenuPosition), 0, gtk_get_current_event_time());
+ }
+}
+
+bool MinimalStreamWidget::on_button_press_event (GdkEventButton* event) {
+ if (Gtk::VBox::on_button_press_event(event))
+ return TRUE;
+
+ if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
+ prepareMenu();
+ menu.popup(0, event->time);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+#define DECAY_STEP .04
+
+void MinimalStreamWidget::updatePeak(double v) {
+
+ if (lastPeak >= DECAY_STEP)
+ if (v < lastPeak - DECAY_STEP)
+ v = lastPeak - DECAY_STEP;
+
+ lastPeak = v;
+
+ if (v >= 0) {
+ peakProgressBar.set_sensitive(TRUE);
+ peakProgressBar.set_fraction(v);
+ } else {
+ peakProgressBar.set_sensitive(FALSE);
+ peakProgressBar.set_fraction(0);
+ }
+
+ enableVolumeMeter();
+}
+
+void MinimalStreamWidget::enableVolumeMeter() {
+ if (volumeMeterEnabled)
+ return;
+
+ volumeMeterEnabled = true;
+ peakProgressBar.show();
+}
+
diff --git a/src/minimalstreamwidget.h b/src/minimalstreamwidget.h
new file mode 100644
index 0000000..af5c9b5
--- /dev/null
+++ b/src/minimalstreamwidget.h
@@ -0,0 +1,56 @@
+/***
+ 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/>.
+***/
+
+#ifndef minimalstreamwidget_h
+#define minimalstreamwidget_h
+
+#include "pavucontrol.h"
+
+class MinimalStreamWidget : public Gtk::VBox {
+public:
+ MinimalStreamWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
+
+ Gtk::VBox *channelsVBox;
+ Gtk::Label *nameLabel, *boldNameLabel;
+ Gtk::ToggleButton *streamToggleButton;
+ Gtk::Menu menu;
+ Gtk::Image *iconImage;
+ Gtk::ProgressBar peakProgressBar;
+ double lastPeak;
+
+ bool updating;
+
+ void onStreamToggleButton();
+ void onMenuDeactivated();
+ void popupMenuPosition(int& x, int& y, bool& push_in);
+
+ virtual void prepareMenu(void);
+
+ bool volumeMeterEnabled;
+ void enableVolumeMeter();
+ void updatePeak(double v);
+
+ Glib::ustring beepDevice;
+
+protected:
+ virtual bool on_button_press_event(GdkEventButton* event);
+};
+
+#endif
diff --git a/src/pavucontrol.cc b/src/pavucontrol.cc
index 7fef582..423c44b 100644
--- a/src/pavucontrol.cc
+++ b/src/pavucontrol.cc
@@ -22,366 +22,25 @@
#include <config.h>
#endif
-#include <signal.h>
-#include <string.h>
+#include "pavucontrol.h"
-#include <gtkmm.h>
-#include <libglademm.h>
-#include <libintl.h>
-
-#include <canberra-gtk.h>
-
-#include <pulse/pulseaudio.h>
#include <pulse/glib-mainloop.h>
#include <pulse/ext-stream-restore.h>
#include "i18n.h"
-
-#ifndef GLADE_FILE
-#define GLADE_FILE "pavucontrol.glade"
-#endif
+#include "minimalstreamwidget.h"
+#include "channelwidget.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"
static pa_context *context = NULL;
static int n_outstanding = 0;
-static bool show_decibel = true;
-
-enum SinkInputType {
- SINK_INPUT_ALL,
- SINK_INPUT_CLIENT,
- SINK_INPUT_VIRTUAL
-};
-
-enum SinkType {
- SINK_ALL,
- SINK_HARDWARE,
- SINK_VIRTUAL,
-};
-
-enum SourceOutputType {
- SOURCE_OUTPUT_ALL,
- SOURCE_OUTPUT_CLIENT,
- SOURCE_OUTPUT_VIRTUAL
-};
-
-enum SourceType{
- SOURCE_ALL,
- SOURCE_NO_MONITOR,
- SOURCE_HARDWARE,
- SOURCE_VIRTUAL,
- SOURCE_MONITOR,
-};
-
-class StreamWidget;
-class MainWindow;
-
-class ChannelWidget : public Gtk::EventBox {
-public:
- ChannelWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
- static ChannelWidget* create();
-
- void setVolume(pa_volume_t volume);
-
- Gtk::Label *channelLabel;
- Gtk::Label *volumeLabel;
- Gtk::HScale *volumeScale;
-
- int channel;
- StreamWidget *streamWidget;
-
- void onVolumeScaleValueChanged();
-
- bool can_decibel;
- bool volumeScaleEnabled;
-
- Glib::ustring beepDevice;
-
- virtual void set_sensitive(bool enabled);
-};
-
-class MinimalStreamWidget : public Gtk::VBox {
-public:
- MinimalStreamWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
-
- Gtk::VBox *channelsVBox;
- Gtk::Label *nameLabel, *boldNameLabel;
- Gtk::ToggleButton *streamToggleButton;
- Gtk::Menu menu;
- Gtk::Image *iconImage;
- Gtk::ProgressBar peakProgressBar;
- double lastPeak;
-
- bool updating;
-
- void onStreamToggleButton();
- void onMenuDeactivated();
- void popupMenuPosition(int& x, int& y, bool& push_in);
-
- virtual void prepareMenu(void);
-
- bool volumeMeterEnabled;
- void enableVolumeMeter();
- void updatePeak(double v);
-
- Glib::ustring beepDevice;
-
-protected:
- virtual bool on_button_press_event(GdkEventButton* event);
-};
-
-class StreamWidget : public MinimalStreamWidget {
-public:
- StreamWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
-
- void setChannelMap(const pa_channel_map &m, bool can_decibel);
- void setVolume(const pa_cvolume &volume, bool force);
- virtual void updateChannelVolume(int channel, pa_volume_t v);
-
- Gtk::ToggleButton *lockToggleButton, *muteToggleButton;
-
- pa_channel_map channelMap;
- pa_cvolume volume;
-
- ChannelWidget *channelWidgets[PA_CHANNELS_MAX];
-
- virtual void onMuteToggleButton();
-
- sigc::connection timeoutConnection;
-
- bool timeoutEvent();
-
- virtual void executeVolumeUpdate();
-};
-
-class CardWidget : public Gtk::VBox {
-public:
- CardWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
- static CardWidget* create();
-
- Gtk::Label *nameLabel;
- Gtk::ToggleButton *streamToggleButton;
- Gtk::Menu menu;
- Gtk::Image *iconImage;
- Glib::ustring name;
- uint32_t index;
- bool updating;
-
- std::map<Glib::ustring,Glib::ustring> profiles;
- Glib::ustring activeProfile;
- bool hasSinks;
- bool hasSources;
-
- void prepareMenu();
-
-protected:
- virtual void onProfileChange();
-
- //Tree model columns:
- class ModelColumns : public Gtk::TreeModel::ColumnRecord
- {
- public:
-
- ModelColumns()
- { add(name); add(desc); }
-
- Gtk::TreeModelColumn<Glib::ustring> name;
- Gtk::TreeModelColumn<Glib::ustring> desc;
- };
-
- ModelColumns profileModel;
-
- //Child widgets:
- Gtk::ComboBox *profileList;
- Glib::RefPtr<Gtk::ListStore> treeModel;
-};
-
-class SinkWidget : public StreamWidget {
-public:
- SinkWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
- static SinkWidget* create();
-
- SinkType type;
- Glib::ustring description;
- Glib::ustring name;
- uint32_t index, monitor_index, card_index;
- bool can_decibel;
-
- Gtk::CheckMenuItem defaultMenuItem;
-
- virtual void onMuteToggleButton();
- virtual void executeVolumeUpdate();
- virtual void onDefaultToggle();
-};
-
-class SourceWidget : public StreamWidget {
-public:
- SourceWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
- static SourceWidget* create();
-
- SourceType type;
- Glib::ustring name;
- Glib::ustring description;
- uint32_t index, card_index;
- bool can_decibel;
-
- Gtk::CheckMenuItem defaultMenuItem;
-
- virtual void onMuteToggleButton();
- virtual void executeVolumeUpdate();
- virtual void onDefaultToggle();
-};
-
-class SinkInputWidget : public StreamWidget {
-public:
- SinkInputWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
- static SinkInputWidget* create();
- virtual ~SinkInputWidget();
-
- SinkInputType type;
-
- uint32_t index, clientIndex, sinkIndex;
- virtual void executeVolumeUpdate();
- virtual void onMuteToggleButton();
- virtual void onKill();
- virtual void prepareMenu();
-
- MainWindow *mainWindow;
- Gtk::Menu submenu;
- Gtk::MenuItem titleMenuItem, killMenuItem;
-
- struct SinkMenuItem {
- SinkMenuItem(SinkInputWidget *w, const char *label, uint32_t i, bool active) :
- widget(w),
- menuItem(label),
- index(i) {
- menuItem.set_active(active);
- menuItem.set_draw_as_radio(true);
- menuItem.signal_toggled().connect(sigc::mem_fun(*this, &SinkMenuItem::onToggle));
- }
-
- SinkInputWidget *widget;
- Gtk::CheckMenuItem menuItem;
- uint32_t index;
- void onToggle();
- };
-
- std::map<uint32_t, SinkMenuItem*> sinkMenuItems;
-
- void clearMenu();
- void buildMenu();
-};
-
-class SourceOutputWidget : public MinimalStreamWidget {
-public:
- SourceOutputWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
- static SourceOutputWidget* create();
- virtual ~SourceOutputWidget();
-
- SourceOutputType type;
-
- uint32_t index, clientIndex, sourceIndex;
- virtual void onKill();
-
- MainWindow *mainWindow;
- Gtk::Menu submenu;
- Gtk::MenuItem titleMenuItem, killMenuItem;
-
- struct SourceMenuItem {
- SourceMenuItem(SourceOutputWidget *w, const char *label, uint32_t i, bool active) :
- widget(w),
- menuItem(label),
- index(i) {
- menuItem.set_active(active);
- menuItem.set_draw_as_radio(true);
- menuItem.signal_toggled().connect(sigc::mem_fun(*this, &SourceMenuItem::onToggle));
- }
-
- SourceOutputWidget *widget;
- Gtk::CheckMenuItem menuItem;
- uint32_t index;
- void onToggle();
- };
-
- std::map<uint32_t, SourceMenuItem*> sourceMenuItems;
-
- void clearMenu();
- void buildMenu();
- virtual void prepareMenu();
-};
-
-class RoleWidget : public StreamWidget {
-public:
- RoleWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
- static RoleWidget* create();
-
- Glib::ustring role;
- Glib::ustring device;
-
- virtual void onMuteToggleButton();
- virtual void executeVolumeUpdate();
-};
-
-class MainWindow : public Gtk::Window {
-public:
- MainWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
- static MainWindow* create();
- virtual ~MainWindow();
-
- void updateCard(const pa_card_info &info);
- void updateSink(const pa_sink_info &info);
- void updateSource(const pa_source_info &info);
- void updateSinkInput(const pa_sink_input_info &info);
- void updateSourceOutput(const pa_source_output_info &info);
- void updateClient(const pa_client_info &info);
- void updateServer(const pa_server_info &info);
- void updateVolumeMeter(uint32_t source_index, uint32_t sink_input_index, double v);
- void updateRole(const pa_ext_stream_restore_info &info);
-
- void removeCard(uint32_t index);
- void removeSink(uint32_t index);
- void removeSource(uint32_t index);
- void removeSinkInput(uint32_t index);
- void removeSourceOutput(uint32_t index);
- void removeClient(uint32_t index);
-
- Gtk::Notebook *notebook;
- Gtk::VBox *streamsVBox, *recsVBox, *sinksVBox, *sourcesVBox, *cardsVBox;
- Gtk::Label *noStreamsLabel, *noRecsLabel, *noSinksLabel, *noSourcesLabel, *noCardsLabel;
- Gtk::ComboBox *sinkInputTypeComboBox, *sourceOutputTypeComboBox, *sinkTypeComboBox, *sourceTypeComboBox;
-
- std::map<uint32_t, CardWidget*> cardWidgets;
- std::map<uint32_t, SinkWidget*> sinkWidgets;
- std::map<uint32_t, SourceWidget*> sourceWidgets;
- std::map<uint32_t, SinkInputWidget*> sinkInputWidgets;
- std::map<uint32_t, SourceOutputWidget*> sourceOutputWidgets;
- std::map<uint32_t, char*> clientNames;
-
- SinkInputType showSinkInputType;
- SinkType showSinkType;
- SourceOutputType showSourceOutputType;
- SourceType showSourceType;
-
- virtual void onSinkInputTypeComboBoxChanged();
- virtual void onSourceOutputTypeComboBoxChanged();
- virtual void onSinkTypeComboBoxChanged();
- virtual void onSourceTypeComboBoxChanged();
-
- void updateDeviceVisibility();
- void reallyUpdateDeviceVisibility();
- void createMonitorStreamForSource(uint32_t source_idx);
- void createMonitorStreamForSinkInput(uint32_t sink_input_idx, uint32_t sink_idx);
-
- void setIconFromProplist(Gtk::Image *icon, pa_proplist *l, const char *name);
-
- RoleWidget *eventRoleWidget;
-
- bool createEventRoleWidget();
- void deleteEventRoleWidget();
-
- Glib::ustring defaultSinkName, defaultSourceName;
-
-protected:
- virtual void on_realize();
-};
void show_error(const char *txt) {
char buf[256];
@@ -394,1466 +53,6 @@ void show_error(const char *txt) {
Gtk::Main::quit();
}
-/*** ChannelWidget ***/
-
-ChannelWidget::ChannelWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
- Gtk::EventBox(cobject),
- volumeScaleEnabled(true) {
-
- x->get_widget("channelLabel", channelLabel);
- x->get_widget("volumeLabel", volumeLabel);
- x->get_widget("volumeScale", volumeScale);
-
- volumeScale->set_value(100);
-
- volumeScale->signal_value_changed().connect(sigc::mem_fun(*this, &ChannelWidget::onVolumeScaleValueChanged));
-}
-
-ChannelWidget* ChannelWidget::create() {
- ChannelWidget* w;
- Glib::RefPtr<Gnome::Glade::Xml> x = Gnome::Glade::Xml::create(GLADE_FILE, "channelWidget");
- x->get_widget_derived("channelWidget", w);
- return w;
-}
-
-void ChannelWidget::setVolume(pa_volume_t volume) {
- double v;
- char txt[64];
-
- v = ((gdouble) volume * 100) / PA_VOLUME_NORM;
-
- if (can_decibel && show_decibel) {
- double dB = pa_sw_volume_to_dB(volume);
-
- if (dB > PA_DECIBEL_MININFTY) {
- snprintf(txt, sizeof(txt), "%0.2f dB", dB);
- volumeLabel->set_tooltip_text(txt);
- } else
- volumeLabel->set_tooltip_markup("-&#8734;dB");
- volumeLabel->set_has_tooltip(TRUE);
- } else
- volumeLabel->set_has_tooltip(FALSE);
-
- snprintf(txt, sizeof(txt), "%0.0f%%", v);
- volumeLabel->set_text(txt);
-
- volumeScaleEnabled = false;
- volumeScale->set_value(v > 100 ? 100 : v);
- volumeScaleEnabled = true;
-}
-
-void ChannelWidget::onVolumeScaleValueChanged() {
-
- if (!volumeScaleEnabled)
- return;
-
- if (streamWidget->updating)
- return;
-
- pa_volume_t volume = (pa_volume_t) ((volumeScale->get_value() * PA_VOLUME_NORM) / 100);
- streamWidget->updateChannelVolume(channel, volume);
-
- if (beepDevice != "") {
- ca_context_change_device(ca_gtk_context_get(), beepDevice.c_str());
-
- ca_context_cancel(ca_gtk_context_get(), 2);
-
- ca_gtk_play_for_widget(GTK_WIDGET(volumeScale->gobj()),
- 2,
- CA_PROP_EVENT_DESCRIPTION, _("Volume Control Feedback Sound"),
- CA_PROP_EVENT_ID, "audio-volume-change",
- CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
- CA_PROP_CANBERRA_VOLUME, "0",
- CA_PROP_CANBERRA_ENABLE, "1",
- NULL);
-
- ca_context_change_device(ca_gtk_context_get(), NULL);
- }
-}
-
-void ChannelWidget::set_sensitive(bool enabled) {
- Gtk::EventBox::set_sensitive(enabled);
-
- channelLabel->set_sensitive(enabled);
- volumeLabel->set_sensitive(enabled);
- volumeScale->set_sensitive(enabled);
-}
-
-
-
-/*** CardWidget ***/
-CardWidget::CardWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
- Gtk::VBox(cobject) {
-
- x->get_widget("nameLabel", nameLabel);
- x->get_widget("profileList", profileList);
- x->get_widget("iconImage", iconImage);
-
- treeModel = Gtk::ListStore::create(profileModel);
- profileList->set_model(treeModel);
- profileList->pack_start(profileModel.desc);
-
- profileList->signal_changed().connect( sigc::mem_fun(*this, &CardWidget::onProfileChange));
-}
-
-CardWidget* CardWidget::create() {
- CardWidget* w;
- Glib::RefPtr<Gnome::Glade::Xml> x = Gnome::Glade::Xml::create(GLADE_FILE, "cardWidget");
- x->get_widget_derived("cardWidget", w);
- return w;
-}
-
-
-void CardWidget::prepareMenu() {
- int idx = 0;
- int active_idx = -1;
-
- treeModel->clear();
- //Fill the ComboBox's Tree Model:
- for (std::map<Glib::ustring, Glib::ustring>::iterator i = profiles.begin(); i != profiles.end(); ++i) {
- Gtk::TreeModel::Row row = *(treeModel->append());
- row[profileModel.name] = i->first;
- row[profileModel.desc] = i->second;
- if (i->first == activeProfile)
- active_idx = idx;
- idx++;
- }
-
- if (active_idx >= 0)
- profileList->set_active(active_idx);
-}
-
-void CardWidget::onProfileChange() {
- Gtk::TreeModel::iterator iter;
-
- if (updating)
- return;
-
- iter = profileList->get_active();
- if (iter)
- {
- Gtk::TreeModel::Row row = *iter;
- if (row)
- {
- pa_operation* o;
- Glib::ustring profile = row[profileModel.name];
-
- if (!(o = pa_context_set_card_profile_by_index(context, index, profile.c_str(), NULL, NULL))) {
- show_error(_("pa_context_set_card_profile_by_index() failed"));
- return;
- }
-
- pa_operation_unref(o);
- }
- }
-}
-
-
-/*** MinimalStreamWidget ***/
-MinimalStreamWidget::MinimalStreamWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
- Gtk::VBox(cobject),
- peakProgressBar(),
- lastPeak(0),
- updating(false),
- volumeMeterEnabled(false) {
-
- x->get_widget("channelsVBox", channelsVBox);
- x->get_widget("nameLabel", nameLabel);
- x->get_widget("boldNameLabel", boldNameLabel);
- x->get_widget("streamToggle", streamToggleButton);
- x->get_widget("iconImage", iconImage);
-
- peakProgressBar.set_size_request(-1, 10);
- channelsVBox->pack_end(peakProgressBar, false, false);
-
- streamToggleButton->set_active(false);
- streamToggleButton->signal_clicked().connect(sigc::mem_fun(*this, &MinimalStreamWidget::onStreamToggleButton));
- menu.signal_deactivate().connect(sigc::mem_fun(*this, &MinimalStreamWidget::onMenuDeactivated));
-
- peakProgressBar.hide();
-}
-
-void MinimalStreamWidget::prepareMenu(void) {
-}
-
-void MinimalStreamWidget::onMenuDeactivated(void) {
- streamToggleButton->set_active(false);
-}
-
-void MinimalStreamWidget::popupMenuPosition(int& x, int& y, bool& push_in G_GNUC_UNUSED) {
- Gtk::Requisition r;
-
- streamToggleButton->get_window()->get_origin(x, y);
- r = menu.size_request();
-
- /* Align the right side of the menu with the right side of the togglebutton */
- x += streamToggleButton->get_allocation().get_x();
- x += streamToggleButton->get_allocation().get_width();
- x -= r.width;
-
- /* Align the top of the menu with the buttom of the togglebutton */
- y += streamToggleButton->get_allocation().get_y();
- y += streamToggleButton->get_allocation().get_height();
-}
-
-void MinimalStreamWidget::onStreamToggleButton(void) {
- if (streamToggleButton->get_active()) {
- prepareMenu();
- menu.popup(sigc::mem_fun(*this, &MinimalStreamWidget::popupMenuPosition), 0, gtk_get_current_event_time());
- }
-}
-
-bool MinimalStreamWidget::on_button_press_event (GdkEventButton* event) {
- if (Gtk::VBox::on_button_press_event(event))
- return TRUE;
-
- if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
- prepareMenu();
- menu.popup(0, event->time);
- return TRUE;
- }
-
- return FALSE;
-}
-
-#define DECAY_STEP .04
-
-void MinimalStreamWidget::updatePeak(double v) {
-
- if (lastPeak >= DECAY_STEP)
- if (v < lastPeak - DECAY_STEP)
- v = lastPeak - DECAY_STEP;
-
- lastPeak = v;
-
- if (v >= 0) {
- peakProgressBar.set_sensitive(TRUE);
- peakProgressBar.set_fraction(v);
- } else {
- peakProgressBar.set_sensitive(FALSE);
- peakProgressBar.set_fraction(0);
- }
-
- enableVolumeMeter();
-}
-
-void MinimalStreamWidget::enableVolumeMeter() {
- if (volumeMeterEnabled)
- return;
-
- volumeMeterEnabled = true;
- peakProgressBar.show();
-}
-
-/*** StreamWidget ***/
-
-StreamWidget::StreamWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
- MinimalStreamWidget(cobject, x) {
-
- x->get_widget("lockToggleButton", lockToggleButton);
- x->get_widget("muteToggleButton", muteToggleButton);
-
- muteToggleButton->signal_clicked().connect(sigc::mem_fun(*this, &StreamWidget::onMuteToggleButton));
-
- for (unsigned i = 0; i < PA_CHANNELS_MAX; i++)
- channelWidgets[i] = NULL;
-}
-
-void StreamWidget::setChannelMap(const pa_channel_map &m, bool can_decibel) {
- channelMap = m;
-
- for (int i = 0; i < m.channels; i++) {
- ChannelWidget *cw = channelWidgets[i] = ChannelWidget::create();
- cw->beepDevice = beepDevice;
- cw->channel = i;
- cw->can_decibel = can_decibel;
- cw->streamWidget = this;
- char text[64];
- snprintf(text, sizeof(text), "<b>%s</b>", pa_channel_position_to_pretty_string(m.map[i]));
- cw->channelLabel->set_markup(text);
- channelsVBox->pack_start(*cw, false, false, 0);
- }
-
- lockToggleButton->set_sensitive(m.channels > 1);
-}
-
-void StreamWidget::setVolume(const pa_cvolume &v, bool force = false) {
- g_assert(v.channels == channelMap.channels);
-
- volume = v;
-
- if (timeoutConnection.empty() || force) { /* do not update the volume when a volume change is still in flux */
- for (int i = 0; i < volume.channels; i++)
- channelWidgets[i]->setVolume(volume.values[i]);
- }
-}
-
-void StreamWidget::updateChannelVolume(int channel, pa_volume_t v) {
- pa_cvolume n;
- g_assert(channel < volume.channels);
-
- n = volume;
- if (lockToggleButton->get_active()) {
- for (int i = 0; i < n.channels; i++)
- n.values[i] = v;
- } else
- n.values[channel] = v;
-
- setVolume(n, true);
-
- if (timeoutConnection.empty())
- timeoutConnection = Glib::signal_timeout().connect(sigc::mem_fun(*this, &StreamWidget::timeoutEvent), 100);
-}
-
-void StreamWidget::onMuteToggleButton() {
-
- lockToggleButton->set_sensitive(!muteToggleButton->get_active());
-
- for (int i = 0; i < channelMap.channels; i++)
- channelWidgets[i]->set_sensitive(!muteToggleButton->get_active());
-}
-
-bool StreamWidget::timeoutEvent() {
- executeVolumeUpdate();
- return false;
-}
-
-void StreamWidget::executeVolumeUpdate() {
-}
-
-SinkWidget::SinkWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
- StreamWidget(cobject, x),
- defaultMenuItem("_Default", true){
-
- add_events(Gdk::BUTTON_PRESS_MASK);
-
- defaultMenuItem.set_active(false);
- defaultMenuItem.signal_toggled().connect(sigc::mem_fun(*this, &SinkWidget::onDefaultToggle));
- menu.append(defaultMenuItem);
- menu.show_all();
-}
-
-SinkWidget* SinkWidget::create() {
- SinkWidget* w;
- Glib::RefPtr<Gnome::Glade::Xml> x = Gnome::Glade::Xml::create(GLADE_FILE, "streamWidget");
- x->get_widget_derived("streamWidget", w);
- return w;
-}
-
-void SinkWidget::executeVolumeUpdate() {
- pa_operation* o;
-
- if (!(o = pa_context_set_sink_volume_by_index(context, index, &volume, NULL, NULL))) {
- show_error(_("pa_context_set_sink_volume_by_index() failed"));
- return;
- }
-
- pa_operation_unref(o);
-}
-
-void SinkWidget::onMuteToggleButton() {
- StreamWidget::onMuteToggleButton();
-
- if (updating)
- return;
-
- pa_operation* o;
- if (!(o = pa_context_set_sink_mute_by_index(context, index, muteToggleButton->get_active(), NULL, NULL))) {
- show_error(_("pa_context_set_sink_mute_by_index() failed"));
- return;
- }
-
- pa_operation_unref(o);
-}
-
-void SinkWidget::onDefaultToggle() {
- pa_operation* o;
-
- if (updating)
- return;
-
- if (!(o = pa_context_set_default_sink(context, name.c_str(), NULL, NULL))) {
- show_error(_("pa_context_set_default_sink() failed"));
- return;
- }
- pa_operation_unref(o);
-}
-
-SourceWidget::SourceWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
- StreamWidget(cobject, x),
- defaultMenuItem(_("_Default"), true){
-
- add_events(Gdk::BUTTON_PRESS_MASK);
-
- defaultMenuItem.set_active(false);
- defaultMenuItem.signal_toggled().connect(sigc::mem_fun(*this, &SourceWidget::onDefaultToggle));
- menu.append(defaultMenuItem);
- menu.show_all();
-}
-
-SourceWidget* SourceWidget::create() {
- SourceWidget* w;
- Glib::RefPtr<Gnome::Glade::Xml> x = Gnome::Glade::Xml::create(GLADE_FILE, "streamWidget");
- x->get_widget_derived("streamWidget", w);
- return w;
-}
-
-void SourceWidget::executeVolumeUpdate() {
- pa_operation* o;
-
- if (!(o = pa_context_set_source_volume_by_index(context, index, &volume, NULL, NULL))) {
- show_error(_("pa_context_set_source_volume_by_index() failed"));
- return;
- }
-
- pa_operation_unref(o);
-}
-
-void SourceWidget::onMuteToggleButton() {
- StreamWidget::onMuteToggleButton();
-
- if (updating)
- return;
-
- pa_operation* o;
- if (!(o = pa_context_set_source_mute_by_index(context, index, muteToggleButton->get_active(), NULL, NULL))) {
- show_error(_("pa_context_set_source_mute_by_index() failed"));
- return;
- }
-
- pa_operation_unref(o);
-}
-
-void SourceWidget::onDefaultToggle() {
- pa_operation* o;
-
- if (updating)
- return;
-
- if (!(o = pa_context_set_default_source(context, name.c_str(), NULL, NULL))) {
- show_error(_("pa_context_set_default_source() failed"));
- return;
- }
- pa_operation_unref(o);
-}
-
-SinkInputWidget::SinkInputWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
- StreamWidget(cobject, x),
- mainWindow(NULL),
- titleMenuItem(_("_Move Stream..."), true),
- killMenuItem(_("_Terminate Stream"), true) {
-
- add_events(Gdk::BUTTON_PRESS_MASK);
-
- menu.append(titleMenuItem);
- titleMenuItem.set_submenu(submenu);
-
- menu.append(killMenuItem);
- killMenuItem.signal_activate().connect(sigc::mem_fun(*this, &SinkInputWidget::onKill));
-}
-
-SinkInputWidget::~SinkInputWidget() {
- clearMenu();
-}
-
-SinkInputWidget* SinkInputWidget::create() {
- SinkInputWidget* w;
- Glib::RefPtr<Gnome::Glade::Xml> x = Gnome::Glade::Xml::create(GLADE_FILE, "streamWidget");
- x->get_widget_derived("streamWidget", w);
- return w;
-}
-
-void SinkInputWidget::executeVolumeUpdate() {
- pa_operation* o;
-
- if (!(o = pa_context_set_sink_input_volume(context, index, &volume, NULL, NULL))) {
- show_error(_("pa_context_set_sink_input_volume() failed"));
- return;
- }
-
- pa_operation_unref(o);
-}
-
-void SinkInputWidget::onMuteToggleButton() {
- StreamWidget::onMuteToggleButton();
-
- if (updating)
- return;
-
- pa_operation* o;
- if (!(o = pa_context_set_sink_input_mute(context, index, muteToggleButton->get_active(), NULL, NULL))) {
- show_error(_("pa_context_set_sink_input_mute() failed"));
- return;
- }
-
- pa_operation_unref(o);
-}
-
-void SinkInputWidget::prepareMenu() {
- clearMenu();
- buildMenu();
-}
-
-void SinkInputWidget::clearMenu() {
-
- while (!sinkMenuItems.empty()) {
- std::map<uint32_t, SinkMenuItem*>::iterator i = sinkMenuItems.begin();
- delete i->second;
- sinkMenuItems.erase(i);
- }
-}
-
-void SinkInputWidget::buildMenu() {
- for (std::map<uint32_t, SinkWidget*>::iterator i = mainWindow->sinkWidgets.begin(); i != mainWindow->sinkWidgets.end(); ++i) {
- SinkMenuItem *m;
- sinkMenuItems[i->second->index] = m = new SinkMenuItem(this, i->second->description.c_str(), i->second->index, i->second->index == sinkIndex);
- submenu.append(m->menuItem);
- }
-
- menu.show_all();
-}
-
-void SinkInputWidget::onKill() {
- pa_operation* o;
- if (!(o = pa_context_kill_sink_input(context, index, NULL, NULL))) {
- show_error(_("pa_context_kill_sink_input() failed"));
- return;
- }
-
- pa_operation_unref(o);
-}
-
-void SinkInputWidget::SinkMenuItem::onToggle() {
-
- if (widget->updating)
- return;
-
- if (!menuItem.get_active())
- return;
-
- pa_operation* o;
- if (!(o = pa_context_move_sink_input_by_index(context, widget->index, index, NULL, NULL))) {
- show_error(_("pa_context_move_sink_input_by_index() failed"));
- return;
- }
-
- pa_operation_unref(o);
-}
-
-SourceOutputWidget::SourceOutputWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
- MinimalStreamWidget(cobject, x),
- mainWindow(NULL),
- titleMenuItem(_("_Move Stream..."), true),
- killMenuItem(_("_Terminate Stream"), true) {
-
- add_events(Gdk::BUTTON_PRESS_MASK);
-
- menu.append(titleMenuItem);
- titleMenuItem.set_submenu(submenu);
-
- menu.append(killMenuItem);
- killMenuItem.signal_activate().connect(sigc::mem_fun(*this, &SourceOutputWidget::onKill));
-}
-
-SourceOutputWidget::~SourceOutputWidget() {
- clearMenu();
-}
-
-SourceOutputWidget* SourceOutputWidget::create() {
- SourceOutputWidget* w;
- Glib::RefPtr<Gnome::Glade::Xml> x = Gnome::Glade::Xml::create(GLADE_FILE, "streamWidget");
- x->get_widget_derived("streamWidget", w);
- return w;
-}
-
-void SourceOutputWidget::onKill() {
- pa_operation* o;
- if (!(o = pa_context_kill_source_output(context, index, NULL, NULL))) {
- show_error(_("pa_context_kill_source_output() failed"));
- return;
- }
-
- pa_operation_unref(o);
-}
-
-void SourceOutputWidget::clearMenu() {
-
- while (!sourceMenuItems.empty()) {
- std::map<uint32_t, SourceMenuItem*>::iterator i = sourceMenuItems.begin();
- delete i->second;
- sourceMenuItems.erase(i);
- }
-}
-
-void SourceOutputWidget::buildMenu() {
- for (std::map<uint32_t, SourceWidget*>::iterator i = mainWindow->sourceWidgets.begin(); i != mainWindow->sourceWidgets.end(); ++i) {
- SourceMenuItem *m;
- sourceMenuItems[i->second->index] = m = new SourceMenuItem(this, i->second->description.c_str(), i->second->index, i->second->index == sourceIndex);
- submenu.append(m->menuItem);
- }
-
- menu.show_all();
-}
-
-void SourceOutputWidget::prepareMenu(void) {
- clearMenu();
- buildMenu();
-}
-
-void SourceOutputWidget::SourceMenuItem::onToggle() {
-
- if (widget->updating)
- return;
-
- if (!menuItem.get_active())
- return;
-
- pa_operation* o;
- if (!(o = pa_context_move_source_output_by_index(context, widget->index, index, NULL, NULL))) {
- show_error(_("pa_context_move_source_output_by_index() failed"));
- return;
- }
-
- pa_operation_unref(o);
-}
-
-RoleWidget::RoleWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
- StreamWidget(cobject, x) {
-
- lockToggleButton->hide();
- streamToggleButton->hide();
-}
-
-RoleWidget* RoleWidget::create() {
- RoleWidget* w;
- Glib::RefPtr<Gnome::Glade::Xml> x = Gnome::Glade::Xml::create(GLADE_FILE, "streamWidget");
- x->get_widget_derived("streamWidget", w);
- return w;
-}
-
-void RoleWidget::onMuteToggleButton() {
- StreamWidget::onMuteToggleButton();
-
- executeVolumeUpdate();
-}
-
-void RoleWidget::executeVolumeUpdate() {
- pa_ext_stream_restore_info info;
-
- if (updating)
- return;
-
- info.name = role.c_str();
- info.channel_map.channels = 1;
- info.channel_map.map[0] = PA_CHANNEL_POSITION_MONO;
- info.volume = volume;
- info.device = device == "" ? NULL : device.c_str();
- info.mute = muteToggleButton->get_active();
-
- pa_operation* o;
- if (!(o = pa_ext_stream_restore_write(context, PA_UPDATE_REPLACE, &info, 1, TRUE, NULL, NULL))) {
- show_error(_("pa_ext_stream_restore_write() failed"));
- return;
- }
-
- pa_operation_unref(o);
-}
-
-/*** MainWindow ***/
-
-MainWindow::MainWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
- Gtk::Window(cobject),
- showSinkInputType(SINK_INPUT_CLIENT),
- showSinkType(SINK_ALL),
- showSourceOutputType(SOURCE_OUTPUT_CLIENT),
- showSourceType(SOURCE_NO_MONITOR),
- eventRoleWidget(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("sinkInputTypeComboBox", sinkInputTypeComboBox);
- x->get_widget("sourceOutputTypeComboBox", sourceOutputTypeComboBox);
- x->get_widget("sinkTypeComboBox", sinkTypeComboBox);
- x->get_widget("sourceTypeComboBox", sourceTypeComboBox);
- x->get_widget("notebook", notebook);
-
- 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));
-}
-
-MainWindow* MainWindow::create() {
- MainWindow* w;
- Glib::RefPtr<Gnome::Glade::Xml> x = Gnome::Glade::Xml::create(GLADE_FILE, "mainWindow");
- x->get_widget_derived("mainWindow", w);
- return w;
-}
-
-void MainWindow::on_realize() {
- Gtk::Window::on_realize();
-
- get_window()->set_cursor(Gdk::Cursor(Gdk::WATCH));
-}
-
-MainWindow::~MainWindow() {
- 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);
-
- if (pixbuf)
- i->set(pixbuf);
- else
- i->set(name);
-}
-
-void MainWindow::updateCard(const pa_card_info &info) {
- CardWidget *w;
- bool is_new = false;
- const char *description, *icon;
-
- if (cardWidgets.count(info.index))
- w = cardWidgets[info.index];
- else {
- cardWidgets[info.index] = w = CardWidget::create();
- cardsVBox->pack_start(*w, false, false, 0);
- 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;
- w->nameLabel->set_markup(w->name.c_str());
-
- 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;
- w->profiles.clear();
- 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);
- w->profiles.insert(std::pair<Glib::ustring,Glib::ustring>(info.profiles[i].name, info.profiles[i].description));
- }
- w->activeProfile = info.active_profile->name;
- //w->defaultMenuItem.set_active(w->name == defaultSinkName);
-
- w->updating = false;
-
- w->prepareMenu();
-
- if (is_new)
- updateDeviceVisibility();
-}
-
-void MainWindow::updateSink(const pa_sink_info &info) {
- SinkWidget *w;
- bool is_new = false;
- const char *icon;
-
- if (sinkWidgets.count(info.index))
- w = sinkWidgets[info.index];
- else {
- sinkWidgets[info.index] = w = SinkWidget::create();
- w->beepDevice = info.name;
- w->setChannelMap(info.channel_map, !!(info.flags & PA_SINK_DECIBEL_VOLUME));
- sinksVBox->pack_start(*w, false, false, 0);
- w->index = info.index;
- w->monitor_index = info.monitor_source;
- is_new = true;
- }
-
- 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));
- 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->defaultMenuItem.set_active(w->name == defaultSinkName);
-
- w->updating = false;
-
- if (is_new)
- updateDeviceVisibility();
-}
-
-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;
- }
-
- 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);
-}
-
-void MainWindow::createMonitorStreamForSource(uint32_t source_idx) {
- pa_stream *s;
- char t[16];
- pa_buffer_attr attr;
- pa_sample_spec ss;
- return;
-
- 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(context, _("Peak detect"), &ss, NULL))) {
- show_error(_("Failed to create monitoring stream"));
- return;
- }
-
- pa_stream_set_read_callback(s, read_callback, this);
- pa_stream_set_suspended_callback(s, suspended_callback, this);
-
- if (pa_stream_connect_record(s, t, &attr, (pa_stream_flags_t) (PA_STREAM_DONT_MOVE|PA_STREAM_PEAK_DETECT|PA_STREAM_ADJUST_LATENCY)) < 0) {
- show_error(_("Failed to connect monitoring stream"));
- pa_stream_unref(s);
- return;
- }
-}
-
-void MainWindow::createMonitorStreamForSinkInput(uint32_t sink_input_idx, uint32_t sink_idx) {
- pa_stream *s;
- char t[16];
- pa_buffer_attr attr;
- pa_sample_spec ss;
- uint32_t monitor_source_idx;
- return;
-
- ss.channels = 1;
- ss.format = PA_SAMPLE_FLOAT32;
- ss.rate = 25;
-
- if (!sinkWidgets.count(sink_idx))
- return;
-
- monitor_source_idx = sinkWidgets[sink_idx]->monitor_index;
-
- memset(&attr, 0, sizeof(attr));
- attr.fragsize = sizeof(float);
- attr.maxlength = (uint32_t) -1;
-
- snprintf(t, sizeof(t), "%u", monitor_source_idx);
-
- if (!(s = pa_stream_new(context, _("Peak detect"), &ss, NULL))) {
- show_error(_("Failed to create monitoring stream"));
- return;
- }
-
- pa_stream_set_monitor_stream(s, sink_input_idx);
- pa_stream_set_read_callback(s, read_callback, this);
- pa_stream_set_suspended_callback(s, suspended_callback, this);
-
- if (pa_stream_connect_record(s, t, &attr, (pa_stream_flags_t) (PA_STREAM_DONT_MOVE|PA_STREAM_PEAK_DETECT|PA_STREAM_ADJUST_LATENCY)) < 0) {
- show_error(_("Failed to connect monitoring stream"));
- pa_stream_unref(s);
- return;
- }
-}
-
-void MainWindow::updateSource(const pa_source_info &info) {
- SourceWidget *w;
- bool is_new = false;
- const char *icon;
-
- if (sourceWidgets.count(info.index))
- w = sourceWidgets[info.index];
- else {
- sourceWidgets[info.index] = w = SourceWidget::create();
- w->setChannelMap(info.channel_map, !!(info.flags & PA_SOURCE_DECIBEL_VOLUME));
- sourcesVBox->pack_start(*w, false, false, 0);
- w->index = info.index;
- is_new = true;
-
- if (pa_context_get_server_protocol_version(context) >= 13)
- createMonitorStreamForSource(info.index);
- }
-
- 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));
- 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->defaultMenuItem.set_active(w->name == defaultSourceName);
-
- w->updating = false;
-
- if (is_new)
- updateDeviceVisibility();
-}
-
-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:
-
- icon->set_from_icon_name(t, Gtk::ICON_SIZE_SMALL_TOOLBAR);
-}
-
-void MainWindow::updateSinkInput(const pa_sink_input_info &info) {
- SinkInputWidget *w;
- bool is_new = false;
-
- if (sinkInputWidgets.count(info.index))
- w = sinkInputWidgets[info.index];
- else {
- sinkInputWidgets[info.index] = w = SinkInputWidget::create();
- w->setChannelMap(info.channel_map, true);
- streamsVBox->pack_start(*w, false, false, 0);
- w->index = info.index;
- w->clientIndex = info.client;
- w->mainWindow = this;
- is_new = true;
-
- if (pa_context_get_server_protocol_version(context) >= 13)
- createMonitorStreamForSinkInput(info.index, info.sink);
- }
-
- w->updating = true;
-
- w->type = info.client != PA_INVALID_INDEX ? SINK_INPUT_CLIENT : SINK_INPUT_VIRTUAL;
-
- w->sinkIndex = 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);
- }
-
- 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)
- return;
-
- if (sourceOutputWidgets.count(info.index))
- w = sourceOutputWidgets[info.index];
- else {
- sourceOutputWidgets[info.index] = w = SourceOutputWidget::create();
- recsVBox->pack_start(*w, false, false, 0);
- w->index = info.index;
- w->clientIndex = info.client;
- w->mainWindow = this;
- }
-
- w->updating = true;
-
- w->type = info.client != PA_INVALID_INDEX ? SOURCE_OUTPUT_CLIENT : SOURCE_OUTPUT_VIRTUAL;
-
- w->sourceIndex = 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);
- }
-
- setIconFromProplist(w->iconImage, info.proplist, "audio-input-microphone");
-
- 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->defaultMenuItem.set_active(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->defaultMenuItem.set_active(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->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();
-}
-
-void MainWindow::updateVolumeMeter(uint32_t source_index, uint32_t sink_input_idx, double v) {
-
- if (sink_input_idx != PA_INVALID_INDEX) {
- SinkInputWidget *w;
-
- if (sinkInputWidgets.count(sink_input_idx)) {
- w = sinkInputWidgets[sink_input_idx];
- w->updatePeak(v);
- }
-
- } else {
-
- for (std::map<uint32_t, SinkWidget*>::iterator i = sinkWidgets.begin(); i != sinkWidgets.end(); ++i) {
- SinkWidget* w = i->second;
-
- if (w->monitor_index == source_index)
- w->updatePeak(v);
- }
-
- for (std::map<uint32_t, SourceWidget*>::iterator i = sourceWidgets.begin(); i != sourceWidgets.end(); ++i) {
- SourceWidget* w = i->second;
-
- if (w->index == source_index)
- w->updatePeak(v);
- }
-
- for (std::map<uint32_t, SourceOutputWidget*>::iterator i = sourceOutputWidgets.begin(); i != sourceOutputWidgets.end(); ++i) {
- SourceOutputWidget* w = i->second;
-
- if (w->sourceIndex == source_index)
- w->updatePeak(v);
- }
- }
-}
-
-static guint idle_source = 0;
-
-gboolean idle_cb(gpointer data) {
- ((MainWindow*) data)->reallyUpdateDeviceVisibility();
- idle_source = 0;
- return FALSE;
-}
-
-void MainWindow::updateDeviceVisibility() {
-
- if (idle_source)
- return;
-
- idle_source = g_idle_add(idle_cb, this);
-}
-
-void MainWindow::reallyUpdateDeviceVisibility() {
- bool is_empty = true;
-
- for (std::map<uint32_t, SinkInputWidget*>::iterator i = sinkInputWidgets.begin(); i != sinkInputWidgets.end(); ++i) {
- SinkInputWidget* w = i->second;
-
- if (showSinkInputType == SINK_INPUT_ALL || w->type == showSinkInputType) {
- w->show();
- is_empty = false;
- } else
- w->hide();
- }
-
- if (eventRoleWidget)
- is_empty = false;
-
- if (is_empty)
- noStreamsLabel->show();
- else
- noStreamsLabel->hide();
-
- is_empty = true;
-
- for (std::map<uint32_t, SourceOutputWidget*>::iterator i = sourceOutputWidgets.begin(); i != sourceOutputWidgets.end(); ++i) {
- SourceOutputWidget* w = i->second;
-
- if (showSourceOutputType == SOURCE_OUTPUT_ALL || w->type == showSourceOutputType) {
- w->show();
- is_empty = false;
- } else
- w->hide();
- }
-
- if (is_empty)
- noRecsLabel->show();
- else
- noRecsLabel->hide();
-
- is_empty = true;
-
- for (std::map<uint32_t, SinkWidget*>::iterator i = sinkWidgets.begin(); i != sinkWidgets.end(); ++i) {
- SinkWidget* w = i->second;
-
- if (showSinkType == SINK_ALL || w->type == showSinkType) {
- w->show();
- is_empty = false;
- } else
- w->hide();
- }
-
- if (is_empty)
- noSinksLabel->show();
- else
- noSinksLabel->hide();
-
- is_empty = true;
-
- for (std::map<uint32_t, CardWidget*>::iterator i = cardWidgets.begin(); i != cardWidgets.end(); ++i) {
- CardWidget* w = i->second;
-
- w->show();
- is_empty = false;
- }
-
- if (is_empty)
- noCardsLabel->show();
- else
- noCardsLabel->hide();
-
- is_empty = true;
-
- for (std::map<uint32_t, SourceWidget*>::iterator i = sourceWidgets.begin(); i != sourceWidgets.end(); ++i) {
- SourceWidget* w = i->second;
-
- if (showSourceType == SOURCE_ALL ||
- w->type == showSourceType ||
- (showSourceType == SOURCE_NO_MONITOR && w->type != SOURCE_MONITOR)) {
- w->show();
- is_empty = false;
- } else
- w->hide();
- }
-
- if (is_empty)
- noSourcesLabel->show();
- else
- noSourcesLabel->hide();
-
- /* Hmm, if I don't call hide()/show() here some widgets will never
- * get their proper space allocated */
- sinksVBox->hide();
- sinksVBox->show();
- sourcesVBox->hide();
- sourcesVBox->show();
- streamsVBox->hide();
- streamsVBox->show();
- recsVBox->hide();
- recsVBox->show();
- cardsVBox->hide();
- cardsVBox->show();
-}
-
-void MainWindow::removeCard(uint32_t index) {
- if (!cardWidgets.count(index))
- return;
-
- delete cardWidgets[index];
- cardWidgets.erase(index);
- updateDeviceVisibility();
-}
-
-void MainWindow::removeSink(uint32_t index) {
- if (!sinkWidgets.count(index))
- return;
-
- delete sinkWidgets[index];
- sinkWidgets.erase(index);
- updateDeviceVisibility();
-}
-
-void MainWindow::removeSource(uint32_t index) {
- if (!sourceWidgets.count(index))
- return;
-
- delete sourceWidgets[index];
- sourceWidgets.erase(index);
- updateDeviceVisibility();
-}
-
-void MainWindow::removeSinkInput(uint32_t index) {
- if (!sinkInputWidgets.count(index))
- return;
-
- delete sinkInputWidgets[index];
- sinkInputWidgets.erase(index);
- updateDeviceVisibility();
-}
-
-void MainWindow::removeSourceOutput(uint32_t index) {
- if (!sourceOutputWidgets.count(index))
- return;
-
- delete sourceOutputWidgets[index];
- sourceOutputWidgets.erase(index);
- updateDeviceVisibility();
-}
-
-void MainWindow::removeClient(uint32_t index) {
- g_free(clientNames[index]);
- clientNames.erase(index);
-}
-
-void MainWindow::onSinkTypeComboBoxChanged() {
- showSinkType = (SinkType) sinkTypeComboBox->get_active_row_number();
-
- if (showSinkType == (SinkType) -1)
- sinkTypeComboBox->set_active((int) SINK_ALL);
-
- updateDeviceVisibility();
-}
-
-void MainWindow::onSourceTypeComboBoxChanged() {
- showSourceType = (SourceType) sourceTypeComboBox->get_active_row_number();
-
- if (showSourceType == (SourceType) -1)
- sourceTypeComboBox->set_active((int) SOURCE_NO_MONITOR);
-
- updateDeviceVisibility();
-}
-
-void MainWindow::onSinkInputTypeComboBoxChanged() {
- showSinkInputType = (SinkInputType) sinkInputTypeComboBox->get_active_row_number();
-
- if (showSinkInputType == (SinkInputType) -1)
- sinkInputTypeComboBox->set_active((int) SINK_INPUT_CLIENT);
-
- updateDeviceVisibility();
-}
-
-void MainWindow::onSourceOutputTypeComboBoxChanged() {
- showSourceOutputType = (SourceOutputType) sourceOutputTypeComboBox->get_active_row_number();
-
- if (showSourceOutputType == (SourceOutputType) -1)
- sourceOutputTypeComboBox->set_active((int) SOURCE_OUTPUT_CLIENT);
-
- updateDeviceVisibility();
-}
-
static void dec_outstanding(MainWindow *w) {
if (n_outstanding <= 0)
return;
@@ -2012,7 +211,8 @@ void ext_stream_restore_read_cb(
MainWindow *w = static_cast<MainWindow*>(userdata);
if (eol < 0) {
- g_debug(_("Failed to initialized stream_restore extension: %s"), pa_strerror(pa_context_errno(context)));
+ dec_outstanding(w);
+ g_debug(_("Failed to initialize stream_restore extension: %s"), pa_strerror(pa_context_errno(context)));
w->deleteEventRoleWidget();
return;
}
@@ -2164,7 +364,7 @@ void context_state_callback(pa_context *c, void *userdata) {
}
pa_operation_unref(o);
- // Keep track of the outstanding callbacks for UI tweaks
+ /* Keep track of the outstanding callbacks for UI tweaks */
n_outstanding = 0;
if (!(o = pa_context_get_server_info(c, server_info_cb, w))) {
@@ -2227,7 +427,8 @@ void context_state_callback(pa_context *c, void *userdata) {
pa_operation_unref(o);
} else
- g_debug(_("Failed to initialized stream_restore extension: %s"), pa_strerror(pa_context_errno(context)));
+ g_debug(_("Failed to initialize stream_restore extension: %s"), pa_strerror(pa_context_errno(context)));
+
break;
}
@@ -2243,6 +444,10 @@ void context_state_callback(pa_context *c, void *userdata) {
}
}
+pa_context* get_context(void) {
+ return context;
+}
+
int main(int argc, char *argv[]) {
/* Initialize the i18n stuff */
diff --git a/src/pavucontrol.glade b/src/pavucontrol.glade
index 5e09f8a..ff4f00e 100644
--- a/src/pavucontrol.glade
+++ b/src/pavucontrol.glade
@@ -703,113 +703,6 @@ Monitors</property>
</widget>
</child>
</widget>
- <widget class="GtkWindow" id="minimalStreamWindow">
- <property name="visible">True</property>
- <property name="title" translatable="yes">window1</property>
- <child>
- <widget class="GtkEventBox" id="minimalStreamWidget">
- <property name="visible">True</property>
- <child>
- <widget class="GtkVBox" id="streamWidget7">
- <property name="visible">True</property>
- <child>
- <widget class="GtkVBox" id="vbox7">
- <property name="visible">True</property>
- <property name="border_width">12</property>
- <property name="spacing">6</property>
- <child>
- <widget class="GtkHBox" id="hbox9">
- <property name="visible">True</property>
- <property name="spacing">6</property>
- <child>
- <widget class="GtkImage" id="iconImage">
- <property name="visible">True</property>
- <property name="xalign">0</property>
- <property name="stock">gtk-missing-image</property>
- <property name="icon-size">4</property>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <widget class="GtkHBox" id="hbox11">
- <property name="visible">True</property>
- <child>
- <widget class="GtkLabel" id="boldNameLabel">
- <property name="visible">True</property>
- <property name="use_markup">True</property>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <widget class="GtkLabel" id="nameLabel">
- <property name="visible">True</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">Stream Title</property>
- <property name="use_markup">True</property>
- <property name="ellipsize">middle</property>
- </widget>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- </widget>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <widget class="GtkToggleButton" id="streamToggle">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="relief">none</property>
- <child>
- <widget class="GtkArrow" id="arrow1">
- <property name="visible">True</property>
- <property name="arrow_type">down</property>
- </widget>
- </child>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="position">2</property>
- </packing>
- </child>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <widget class="GtkHSeparator" id="hseparator5">
- <property name="visible">True</property>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- </widget>
- </child>
- </widget>
- </child>
- </widget>
<widget class="GtkWindow" id="cardWindow">
<property name="visible">True</property>
<property name="title" translatable="yes">window1</property>
diff --git a/src/pavucontrol.h b/src/pavucontrol.h
new file mode 100644
index 0000000..0e0f6bd
--- /dev/null
+++ b/src/pavucontrol.h
@@ -0,0 +1,67 @@
+/***
+ 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/>.
+***/
+
+#ifndef pavucontrol_h
+#define pavucontrol_h
+
+#include <signal.h>
+#include <string.h>
+
+#include <libintl.h>
+
+#include <gtkmm.h>
+#include <libglademm.h>
+
+#include <pulse/pulseaudio.h>
+
+#ifndef GLADE_FILE
+#define GLADE_FILE "pavucontrol.glade"
+#endif
+
+enum SinkInputType {
+ SINK_INPUT_ALL,
+ SINK_INPUT_CLIENT,
+ SINK_INPUT_VIRTUAL
+};
+
+enum SinkType {
+ SINK_ALL,
+ SINK_HARDWARE,
+ SINK_VIRTUAL,
+};
+
+enum SourceOutputType {
+ SOURCE_OUTPUT_ALL,
+ SOURCE_OUTPUT_CLIENT,
+ SOURCE_OUTPUT_VIRTUAL
+};
+
+enum SourceType {
+ SOURCE_ALL,
+ SOURCE_NO_MONITOR,
+ SOURCE_HARDWARE,
+ SOURCE_VIRTUAL,
+ SOURCE_MONITOR,
+};
+
+pa_context* get_context(void);
+void show_error(const char *txt);
+
+#endif
diff --git a/src/rolewidget.cc b/src/rolewidget.cc
new file mode 100644
index 0000000..e86ba3d
--- /dev/null
+++ b/src/rolewidget.cc
@@ -0,0 +1,72 @@
+/***
+ 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 "rolewidget.h"
+
+#include <pulse/ext-stream-restore.h>
+
+#include "i18n.h"
+
+RoleWidget::RoleWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
+ StreamWidget(cobject, x) {
+
+ lockToggleButton->hide();
+ streamToggleButton->hide();
+}
+
+RoleWidget* RoleWidget::create() {
+ RoleWidget* w;
+ Glib::RefPtr<Gnome::Glade::Xml> x = Gnome::Glade::Xml::create(GLADE_FILE, "streamWidget");
+ x->get_widget_derived("streamWidget", w);
+ return w;
+}
+
+void RoleWidget::onMuteToggleButton() {
+ StreamWidget::onMuteToggleButton();
+
+ executeVolumeUpdate();
+}
+
+void RoleWidget::executeVolumeUpdate() {
+ pa_ext_stream_restore_info info;
+
+ if (updating)
+ return;
+
+ info.name = role.c_str();
+ info.channel_map.channels = 1;
+ info.channel_map.map[0] = PA_CHANNEL_POSITION_MONO;
+ info.volume = volume;
+ info.device = device == "" ? NULL : device.c_str();
+ info.mute = muteToggleButton->get_active();
+
+ pa_operation* o;
+ if (!(o = pa_ext_stream_restore_write(get_context(), PA_UPDATE_REPLACE, &info, 1, TRUE, NULL, NULL))) {
+ show_error(_("pa_ext_stream_restore_write() failed"));
+ return;
+ }
+
+ pa_operation_unref(o);
+}
+
diff --git a/src/rolewidget.h b/src/rolewidget.h
new file mode 100644
index 0000000..bbd39d6
--- /dev/null
+++ b/src/rolewidget.h
@@ -0,0 +1,40 @@
+/***
+ 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/>.
+***/
+
+#ifndef rolewidget_h
+#define rolewidget_h
+
+#include "pavucontrol.h"
+
+#include "streamwidget.h"
+
+class RoleWidget : public StreamWidget {
+public:
+ RoleWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
+ static RoleWidget* create();
+
+ Glib::ustring role;
+ Glib::ustring device;
+
+ virtual void onMuteToggleButton();
+ virtual void executeVolumeUpdate();
+};
+
+#endif
diff --git a/src/sinkinputwidget.cc b/src/sinkinputwidget.cc
new file mode 100644
index 0000000..e5443f1
--- /dev/null
+++ b/src/sinkinputwidget.cc
@@ -0,0 +1,132 @@
+/***
+ 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 "sinkinputwidget.h"
+#include "mainwindow.h"
+#include "sinkwidget.h"
+
+#include "i18n.h"
+
+SinkInputWidget::SinkInputWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
+ StreamWidget(cobject, x),
+ mainWindow(NULL),
+ titleMenuItem(_("_Move Stream..."), true),
+ killMenuItem(_("_Terminate Stream"), true) {
+
+ add_events(Gdk::BUTTON_PRESS_MASK);
+
+ menu.append(titleMenuItem);
+ titleMenuItem.set_submenu(submenu);
+
+ menu.append(killMenuItem);
+ killMenuItem.signal_activate().connect(sigc::mem_fun(*this, &SinkInputWidget::onKill));
+}
+
+SinkInputWidget::~SinkInputWidget() {
+ clearMenu();
+}
+
+SinkInputWidget* SinkInputWidget::create() {
+ SinkInputWidget* w;
+ Glib::RefPtr<Gnome::Glade::Xml> x = Gnome::Glade::Xml::create(GLADE_FILE, "streamWidget");
+ x->get_widget_derived("streamWidget", w);
+ return w;
+}
+
+void SinkInputWidget::executeVolumeUpdate() {
+ pa_operation* o;
+
+ if (!(o = pa_context_set_sink_input_volume(get_context(), index, &volume, NULL, NULL))) {
+ show_error(_("pa_context_set_sink_input_volume() failed"));
+ return;
+ }
+
+ pa_operation_unref(o);
+}
+
+void SinkInputWidget::onMuteToggleButton() {
+ StreamWidget::onMuteToggleButton();
+
+ if (updating)
+ return;
+
+ pa_operation* o;
+ if (!(o = pa_context_set_sink_input_mute(get_context(), index, muteToggleButton->get_active(), NULL, NULL))) {
+ show_error(_("pa_context_set_sink_input_mute() failed"));
+ return;
+ }
+
+ pa_operation_unref(o);
+}
+
+void SinkInputWidget::prepareMenu() {
+ clearMenu();
+ buildMenu();
+}
+
+void SinkInputWidget::clearMenu() {
+
+ while (!sinkMenuItems.empty()) {
+ std::map<uint32_t, SinkMenuItem*>::iterator i = sinkMenuItems.begin();
+ delete i->second;
+ sinkMenuItems.erase(i);
+ }
+}
+
+void SinkInputWidget::buildMenu() {
+ for (std::map<uint32_t, SinkWidget*>::iterator i = mainWindow->sinkWidgets.begin(); i != mainWindow->sinkWidgets.end(); ++i) {
+ SinkMenuItem *m;
+ sinkMenuItems[i->second->index] = m = new SinkMenuItem(this, i->second->description.c_str(), i->second->index, i->second->index == sinkIndex);
+ submenu.append(m->menuItem);
+ }
+
+ menu.show_all();
+}
+
+void SinkInputWidget::onKill() {
+ pa_operation* o;
+ if (!(o = pa_context_kill_sink_input(get_context(), index, NULL, NULL))) {
+ show_error(_("pa_context_kill_sink_input() failed"));
+ return;
+ }
+
+ pa_operation_unref(o);
+}
+
+void SinkInputWidget::SinkMenuItem::onToggle() {
+
+ if (widget->updating)
+ return;
+
+ if (!menuItem.get_active())
+ return;
+
+ pa_operation* o;
+ if (!(o = pa_context_move_sink_input_by_index(get_context(), widget->index, index, NULL, NULL))) {
+ show_error(_("pa_context_move_sink_input_by_index() failed"));
+ return;
+ }
+
+ pa_operation_unref(o);
+}
diff --git a/src/sinkinputwidget.h b/src/sinkinputwidget.h
new file mode 100644
index 0000000..6db504b
--- /dev/null
+++ b/src/sinkinputwidget.h
@@ -0,0 +1,70 @@
+/***
+ 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/>.
+***/
+
+#ifndef sinkinputwidget_h
+#define sinkinputwidget_h
+
+#include "pavucontrol.h"
+
+#include "streamwidget.h"
+
+class MainWindow;
+
+class SinkInputWidget : public StreamWidget {
+public:
+ SinkInputWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
+ static SinkInputWidget* create();
+ virtual ~SinkInputWidget();
+
+ SinkInputType type;
+
+ uint32_t index, clientIndex, sinkIndex;
+ virtual void executeVolumeUpdate();
+ virtual void onMuteToggleButton();
+ virtual void onKill();
+ virtual void prepareMenu();
+
+ MainWindow *mainWindow;
+ Gtk::Menu submenu;
+ Gtk::MenuItem titleMenuItem, killMenuItem;
+
+ struct SinkMenuItem {
+ SinkMenuItem(SinkInputWidget *w, const char *label, uint32_t i, bool active) :
+ widget(w),
+ menuItem(label),
+ index(i) {
+ menuItem.set_active(active);
+ menuItem.set_draw_as_radio(true);
+ menuItem.signal_toggled().connect(sigc::mem_fun(*this, &SinkMenuItem::onToggle));
+ }
+
+ SinkInputWidget *widget;
+ Gtk::CheckMenuItem menuItem;
+ uint32_t index;
+ void onToggle();
+ };
+
+ std::map<uint32_t, SinkMenuItem*> sinkMenuItems;
+
+ void clearMenu();
+ void buildMenu();
+};
+
+#endif
diff --git a/src/sinkwidget.cc b/src/sinkwidget.cc
new file mode 100644
index 0000000..8d21bb0
--- /dev/null
+++ b/src/sinkwidget.cc
@@ -0,0 +1,85 @@
+/***
+ 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 "sinkwidget.h"
+
+#include "i18n.h"
+
+SinkWidget::SinkWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
+ StreamWidget(cobject, x),
+ defaultMenuItem("_Default", true){
+
+ add_events(Gdk::BUTTON_PRESS_MASK);
+
+ defaultMenuItem.set_active(false);
+ defaultMenuItem.signal_toggled().connect(sigc::mem_fun(*this, &SinkWidget::onDefaultToggle));
+ menu.append(defaultMenuItem);
+ menu.show_all();
+}
+
+SinkWidget* SinkWidget::create() {
+ SinkWidget* w;
+ Glib::RefPtr<Gnome::Glade::Xml> x = Gnome::Glade::Xml::create(GLADE_FILE, "streamWidget");
+ x->get_widget_derived("streamWidget", w);
+ return w;
+}
+
+void SinkWidget::executeVolumeUpdate() {
+ pa_operation* o;
+
+ if (!(o = pa_context_set_sink_volume_by_index(get_context(), index, &volume, NULL, NULL))) {
+ show_error(_("pa_context_set_sink_volume_by_index() failed"));
+ return;
+ }
+
+ pa_operation_unref(o);
+}
+
+void SinkWidget::onMuteToggleButton() {
+ StreamWidget::onMuteToggleButton();
+
+ if (updating)
+ return;
+
+ pa_operation* o;
+ if (!(o = pa_context_set_sink_mute_by_index(get_context(), index, muteToggleButton->get_active(), NULL, NULL))) {
+ show_error(_("pa_context_set_sink_mute_by_index() failed"));
+ return;
+ }
+
+ pa_operation_unref(o);
+}
+
+void SinkWidget::onDefaultToggle() {
+ pa_operation* o;
+
+ if (updating)
+ return;
+
+ if (!(o = pa_context_set_default_sink(get_context(), name.c_str(), NULL, NULL))) {
+ show_error(_("pa_context_set_default_sink() failed"));
+ return;
+ }
+ pa_operation_unref(o);
+}
diff --git a/src/sinkwidget.h b/src/sinkwidget.h
new file mode 100644
index 0000000..d5ce315
--- /dev/null
+++ b/src/sinkwidget.h
@@ -0,0 +1,46 @@
+/***
+ 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/>.
+***/
+
+#ifndef sinkwidget_h
+#define sinkwidget_h
+
+#include "pavucontrol.h"
+
+#include "streamwidget.h"
+
+class SinkWidget : public StreamWidget {
+public:
+ SinkWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
+ static SinkWidget* create();
+
+ SinkType type;
+ Glib::ustring description;
+ Glib::ustring name;
+ uint32_t index, monitor_index, card_index;
+ bool can_decibel;
+
+ Gtk::CheckMenuItem defaultMenuItem;
+
+ virtual void onMuteToggleButton();
+ virtual void executeVolumeUpdate();
+ virtual void onDefaultToggle();
+};
+
+#endif
diff --git a/src/sourceoutputwidget.cc b/src/sourceoutputwidget.cc
new file mode 100644
index 0000000..eafe620
--- /dev/null
+++ b/src/sourceoutputwidget.cc
@@ -0,0 +1,106 @@
+/***
+ 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 "sourceoutputwidget.h"
+#include "mainwindow.h"
+#include "sourcewidget.h"
+
+#include "i18n.h"
+
+SourceOutputWidget::SourceOutputWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
+ MinimalStreamWidget(cobject, x),
+ mainWindow(NULL),
+ titleMenuItem(_("_Move Stream..."), true),
+ killMenuItem(_("_Terminate Stream"), true) {
+
+ add_events(Gdk::BUTTON_PRESS_MASK);
+
+ menu.append(titleMenuItem);
+ titleMenuItem.set_submenu(submenu);
+
+ menu.append(killMenuItem);
+ killMenuItem.signal_activate().connect(sigc::mem_fun(*this, &SourceOutputWidget::onKill));
+}
+
+SourceOutputWidget::~SourceOutputWidget() {
+ clearMenu();
+}
+
+SourceOutputWidget* SourceOutputWidget::create() {
+ SourceOutputWidget* w;
+ Glib::RefPtr<Gnome::Glade::Xml> x = Gnome::Glade::Xml::create(GLADE_FILE, "streamWidget");
+ x->get_widget_derived("streamWidget", w);
+ return w;
+}
+
+void SourceOutputWidget::onKill() {
+ pa_operation* o;
+ if (!(o = pa_context_kill_source_output(get_context(), index, NULL, NULL))) {
+ show_error(_("pa_context_kill_source_output() failed"));
+ return;
+ }
+
+ pa_operation_unref(o);
+}
+
+void SourceOutputWidget::clearMenu() {
+
+ while (!sourceMenuItems.empty()) {
+ std::map<uint32_t, SourceMenuItem*>::iterator i = sourceMenuItems.begin();
+ delete i->second;
+ sourceMenuItems.erase(i);
+ }
+}
+
+void SourceOutputWidget::buildMenu() {
+ for (std::map<uint32_t, SourceWidget*>::iterator i = mainWindow->sourceWidgets.begin(); i != mainWindow->sourceWidgets.end(); ++i) {
+ SourceMenuItem *m;
+ sourceMenuItems[i->second->index] = m = new SourceMenuItem(this, i->second->description.c_str(), i->second->index, i->second->index == sourceIndex);
+ submenu.append(m->menuItem);
+ }
+
+ menu.show_all();
+}
+
+void SourceOutputWidget::prepareMenu(void) {
+ clearMenu();
+ buildMenu();
+}
+
+void SourceOutputWidget::SourceMenuItem::onToggle() {
+
+ if (widget->updating)
+ return;
+
+ if (!menuItem.get_active())
+ return;
+
+ pa_operation* o;
+ if (!(o = pa_context_move_source_output_by_index(get_context(), widget->index, index, NULL, NULL))) {
+ show_error(_("pa_context_move_source_output_by_index() failed"));
+ return;
+ }
+
+ pa_operation_unref(o);
+}
diff --git a/src/sourceoutputwidget.h b/src/sourceoutputwidget.h
new file mode 100644
index 0000000..cdaf28a
--- /dev/null
+++ b/src/sourceoutputwidget.h
@@ -0,0 +1,68 @@
+/***
+ 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/>.
+***/
+
+#ifndef sourceoutputwidget_h
+#define sourceoutputwidget_h
+
+#include "pavucontrol.h"
+
+#include "minimalstreamwidget.h"
+
+class MainWindow;
+
+class SourceOutputWidget : public MinimalStreamWidget {
+public:
+ SourceOutputWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
+ static SourceOutputWidget* create();
+ virtual ~SourceOutputWidget();
+
+ SourceOutputType type;
+
+ uint32_t index, clientIndex, sourceIndex;
+ virtual void onKill();
+
+ MainWindow *mainWindow;
+ Gtk::Menu submenu;
+ Gtk::MenuItem titleMenuItem, killMenuItem;
+
+ struct SourceMenuItem {
+ SourceMenuItem(SourceOutputWidget *w, const char *label, uint32_t i, bool active) :
+ widget(w),
+ menuItem(label),
+ index(i) {
+ menuItem.set_active(active);
+ menuItem.set_draw_as_radio(true);
+ menuItem.signal_toggled().connect(sigc::mem_fun(*this, &SourceMenuItem::onToggle));
+ }
+
+ SourceOutputWidget *widget;
+ Gtk::CheckMenuItem menuItem;
+ uint32_t index;
+ void onToggle();
+ };
+
+ std::map<uint32_t, SourceMenuItem*> sourceMenuItems;
+
+ void clearMenu();
+ void buildMenu();
+ virtual void prepareMenu();
+};
+
+#endif
diff --git a/src/sourcewidget.cc b/src/sourcewidget.cc
new file mode 100644
index 0000000..dc13f7f
--- /dev/null
+++ b/src/sourcewidget.cc
@@ -0,0 +1,85 @@
+/***
+ 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 "sourcewidget.h"
+
+#include "i18n.h"
+
+SourceWidget::SourceWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
+ StreamWidget(cobject, x),
+ defaultMenuItem(_("_Default"), true){
+
+ add_events(Gdk::BUTTON_PRESS_MASK);
+
+ defaultMenuItem.set_active(false);
+ defaultMenuItem.signal_toggled().connect(sigc::mem_fun(*this, &SourceWidget::onDefaultToggle));
+ menu.append(defaultMenuItem);
+ menu.show_all();
+}
+
+SourceWidget* SourceWidget::create() {
+ SourceWidget* w;
+ Glib::RefPtr<Gnome::Glade::Xml> x = Gnome::Glade::Xml::create(GLADE_FILE, "streamWidget");
+ x->get_widget_derived("streamWidget", w);
+ return w;
+}
+
+void SourceWidget::executeVolumeUpdate() {
+ pa_operation* o;
+
+ if (!(o = pa_context_set_source_volume_by_index(get_context(), index, &volume, NULL, NULL))) {
+ show_error(_("pa_context_set_source_volume_by_index() failed"));
+ return;
+ }
+
+ pa_operation_unref(o);
+}
+
+void SourceWidget::onMuteToggleButton() {
+ StreamWidget::onMuteToggleButton();
+
+ if (updating)
+ return;
+
+ pa_operation* o;
+ if (!(o = pa_context_set_source_mute_by_index(get_context(), index, muteToggleButton->get_active(), NULL, NULL))) {
+ show_error(_("pa_context_set_source_mute_by_index() failed"));
+ return;
+ }
+
+ pa_operation_unref(o);
+}
+
+void SourceWidget::onDefaultToggle() {
+ pa_operation* o;
+
+ if (updating)
+ return;
+
+ if (!(o = pa_context_set_default_source(get_context(), name.c_str(), NULL, NULL))) {
+ show_error(_("pa_context_set_default_source() failed"));
+ return;
+ }
+ pa_operation_unref(o);
+}
diff --git a/src/sourcewidget.h b/src/sourcewidget.h
new file mode 100644
index 0000000..2fd137e
--- /dev/null
+++ b/src/sourcewidget.h
@@ -0,0 +1,46 @@
+/***
+ 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/>.
+***/
+
+#ifndef sourcewidget_h
+#define sourcewidget_h
+
+#include "pavucontrol.h"
+
+#include "streamwidget.h"
+
+class SourceWidget : public StreamWidget {
+public:
+ SourceWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
+ static SourceWidget* create();
+
+ SourceType type;
+ Glib::ustring name;
+ Glib::ustring description;
+ uint32_t index, card_index;
+ bool can_decibel;
+
+ Gtk::CheckMenuItem defaultMenuItem;
+
+ virtual void onMuteToggleButton();
+ virtual void executeVolumeUpdate();
+ virtual void onDefaultToggle();
+};
+
+#endif
diff --git a/src/streamwidget.cc b/src/streamwidget.cc
new file mode 100644
index 0000000..fae605f
--- /dev/null
+++ b/src/streamwidget.cc
@@ -0,0 +1,115 @@
+/***
+ 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 "streamwidget.h"
+#include "channelwidget.h"
+
+/*** StreamWidget ***/
+
+StreamWidget::StreamWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
+ MinimalStreamWidget(cobject, x) {
+
+ x->get_widget("lockToggleButton", lockToggleButton);
+ x->get_widget("muteToggleButton", muteToggleButton);
+
+ muteToggleButton->signal_clicked().connect(sigc::mem_fun(*this, &StreamWidget::onMuteToggleButton));
+
+ for (unsigned i = 0; i < PA_CHANNELS_MAX; i++)
+ channelWidgets[i] = NULL;
+}
+
+void StreamWidget::setChannelMap(const pa_channel_map &m, bool can_decibel) {
+ channelMap = m;
+
+ for (int i = 0; i < m.channels; i++) {
+ ChannelWidget *cw = channelWidgets[i] = ChannelWidget::create();
+ cw->beepDevice = beepDevice;
+ cw->channel = i;
+ cw->can_decibel = can_decibel;
+ cw->streamWidget = this;
+ char text[64];
+ snprintf(text, sizeof(text), "<b>%s</b>", pa_channel_position_to_pretty_string(m.map[i]));
+ cw->channelLabel->set_markup(text);
+ channelsVBox->pack_start(*cw, false, false, 0);
+ }
+
+ lockToggleButton->set_sensitive(m.channels > 1);
+}
+
+void StreamWidget::setVolume(const pa_cvolume &v, bool force) {
+ g_assert(v.channels == channelMap.channels);
+
+ volume = v;
+
+ if (timeoutConnection.empty() || force) { /* do not update the volume when a volume change is still in flux */
+ for (int i = 0; i < volume.channels; i++)
+ channelWidgets[i]->setVolume(volume.values[i]);
+ }
+}
+
+void StreamWidget::updateChannelVolume(int channel, pa_volume_t v) {
+ pa_cvolume n;
+ g_assert(channel < volume.channels);
+
+ n = volume;
+ if (lockToggleButton->get_active()) {
+ for (int i = 0; i < n.channels; i++)
+ n.values[i] = v;
+ } else
+ n.values[channel] = v;
+
+ setVolume(n, true);
+
+ if (timeoutConnection.empty())
+ timeoutConnection = Glib::signal_timeout().connect(sigc::mem_fun(*this, &StreamWidget::timeoutEvent), 100);
+}
+
+void StreamWidget::onMuteToggleButton() {
+
+ lockToggleButton->set_sensitive(!muteToggleButton->get_active());
+
+ for (int i = 0; i < channelMap.channels; i++)
+ channelWidgets[i]->set_sensitive(!muteToggleButton->get_active());
+}
+
+bool StreamWidget::timeoutEvent() {
+ executeVolumeUpdate();
+ return false;
+}
+
+void StreamWidget::executeVolumeUpdate() {
+}
+
+
+void StreamWidget::setBaseVolume(pa_volume_t v) {
+
+ if (channelMap.channels > 0)
+ channelWidgets[channelMap.channels-1]->setBaseVolume(v);
+}
+
+void StreamWidget::setSteps(unsigned n) {
+
+ for (int i = 0; i < channelMap.channels; i++)
+ channelWidgets[channelMap.channels-1]->setSteps(n);
+}
diff --git a/src/streamwidget.h b/src/streamwidget.h
new file mode 100644
index 0000000..af5f0b9
--- /dev/null
+++ b/src/streamwidget.h
@@ -0,0 +1,56 @@
+/***
+ 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/>.
+***/
+
+#ifndef streamwidget_h
+#define streamwidget_h
+
+#include "pavucontrol.h"
+
+#include "minimalstreamwidget.h"
+
+class ChannelWidget;
+
+class StreamWidget : public MinimalStreamWidget {
+public:
+ StreamWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
+
+ void setChannelMap(const pa_channel_map &m, bool can_decibel);
+ void setVolume(const pa_cvolume &volume, bool force = false);
+ virtual void updateChannelVolume(int channel, pa_volume_t v);
+
+ Gtk::ToggleButton *lockToggleButton, *muteToggleButton;
+
+ pa_channel_map channelMap;
+ pa_cvolume volume;
+
+ ChannelWidget *channelWidgets[PA_CHANNELS_MAX];
+
+ virtual void onMuteToggleButton();
+
+ sigc::connection timeoutConnection;
+
+ bool timeoutEvent();
+
+ virtual void executeVolumeUpdate();
+ virtual void setBaseVolume(pa_volume_t v);
+ virtual void setSteps(unsigned n);
+};
+
+#endif