From 296f986a9e77b2983b6dde63cbab04d4351d1303 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 21 Apr 2006 14:51:33 +0000 Subject: initial commit git-svn-id: file:///home/lennart/svn/public/pavucontrol/trunk@3 c17c95f2-f111-0410-90bf-f30a9569010c --- Makefile.am | 41 +++ bootstrap.sh | 46 +++ configure.ac | 83 ++++++ src/Makefile.am | 28 ++ src/pavucontrol.cc | 687 +++++++++++++++++++++++++++++++++++++++++++++ src/pavucontrol.glade | 743 +++++++++++++++++++++++++++++++++++++++++++++++++ src/pavucontrol.gladep | 8 + 7 files changed, 1636 insertions(+) create mode 100644 Makefile.am create mode 100755 bootstrap.sh create mode 100644 configure.ac create mode 100644 src/Makefile.am create mode 100644 src/pavucontrol.cc create mode 100644 src/pavucontrol.glade create mode 100644 src/pavucontrol.gladep diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..5bbd86e --- /dev/null +++ b/Makefile.am @@ -0,0 +1,41 @@ +# $Id$ +# +# This file is part of pavucontrol. +# +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +# USA. + +EXTRA_DIST = bootstrap.sh# README LICENSE +SUBDIRS=src # doc + +#MAINTAINERCLEANFILES=README +#noinst_DATA = README + +#README: +# rm -f README +# $(MAKE) -C doc README +# cd $(srcdir) && ln -s doc/README README + +homepage: all dist + test -d $$HOME/homepage/private + mkdir -p $$HOME/homepage/private/projects/pavucontrol + cp pavucontrol-@PACKAGE_VERSION@.tar.gz $$HOME/homepage/private/projects/pavucontrol + cp doc/README.html doc/screenshot.png doc/style.css $$HOME/homepage/private/projects/pavucontrol + cp $$HOME/homepage/private/projects/pavucontrol/README.html $$HOME/homepage/private/projects/pavucontrol/index.html + +distcleancheck: + @: + +.PHONY: homepage distcleancheck diff --git a/bootstrap.sh b/bootstrap.sh new file mode 100755 index 0000000..2f2a10a --- /dev/null +++ b/bootstrap.sh @@ -0,0 +1,46 @@ +#!/bin/bash +# $Id$ + +# This file is part of pavucontrol. +# +# 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, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +run_versioned() { + local P + type -p "$1-$2" &> /dev/null && P="$1-$2" || local P="$1" + + shift 2 + "$P" "$@" +} + +if [ "x$1" = "xam" ] ; then + set -ex + run_versioned automake 1.7 -a -c --foreign + ./config.status +else + set -ex + + rm -rf autom4te.cache + rm -f config.cache + + run_versioned aclocal 1.7 + autoheader + run_versioned automake 1.7 -a -c --foreign + autoconf -Wall + + CFLAGS="-g -O0" ./configure --sysconfdir=/etc "$@" + + make clean +fi diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..4f6c9a9 --- /dev/null +++ b/configure.ac @@ -0,0 +1,83 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +# $Id$ + +# This file is part of pavucontrol. +# +# 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, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +AC_PREREQ(2.57) +AC_INIT([pavucontrol],[0.1],[mzcnzna (at) 0pointer (dot) de]) +AC_CONFIG_SRCDIR([src/pavucontrol.cc]) +AC_CONFIG_HEADERS([config.h]) +AM_INIT_AUTOMAKE([foreign -Wall]) + +AC_SUBST(PACKAGE_URL, [http://0pointer.de/lennart/projects/pavucontrol/]) + +if type -p stow > /dev/null && test -d /usr/local/stow ; then + AC_MSG_NOTICE([*** Found /usr/local/stow: default install prefix set to /usr/local/stow/${PACKAGE_NAME}-${PACKAGE_VERSION} ***]) + ac_default_prefix="/usr/local/stow/${PACKAGE_NAME}-${PACKAGE_VERSION}" +fi + +# Checks for programs. +AC_PROG_CXX +AC_PROG_CC +AC_PROG_LN_S +AC_TYPE_SIGNAL +AC_HEADER_STDC + +PKG_CHECK_MODULES(GUILIBS, [ gtkmm-2.4 libglademm-2.4 ]) +AC_SUBST(GUILIBS_CFLAGS) +AC_SUBST(GUILIBS_LIBS) + +if test -d ../polypaudio ; then + POLYP_CFLAGS='-I$(top_srcdir)/../polypaudio/src' + POLYP_LIBS='-L$(top_srcdir)/../polypaudio/src/.libs -lpolyp-0.8 -lpolyp-mainloop-glib-0.8' + echo "*** Found polypaudio in ../polypaudio, using that version ***" +else + PKG_CHECK_MODULES(POLYP, [ polyplib >= 0.8 polyplib-glib-mainloop >= 0.8 ]) +fi + +AC_SUBST(POLYP_LIBS) +AC_SUBST(POLYP_CFLAGS) + +# If using GCC specifiy some additional parameters +if test "x$GCC" = "xyes" ; then + CFLAGS="$CFLAGS -pipe -Wall -W -Wno-unused-parameter" + CXXFLAGS="$CXXFLAGS -pipe -Wall -W" +fi + +# LYNX documentation generation +AC_ARG_ENABLE(lynx, + AS_HELP_STRING(--disable-lynx,Turn off lynx usage for documentation generation), +[case "${enableval}" in + yes) lynx=yes ;; + no) lynx=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for --disable-lynx) ;; +esac],[lynx=yes]) + +if test x$lynx = xyes ; then + AC_CHECK_PROG(have_lynx, lynx, yes, no) + + if test x$have_lynx = xno ; then + AC_MSG_ERROR([*** Sorry, you have to install lynx or use --disable-lynx ***]) + fi +fi + +AM_CONDITIONAL([USE_LYNX], [test "x$lynx" = xyes]) + +AC_CONFIG_FILES([Makefile src/Makefile])dnl doc/Makefile doc/README.html]) +AC_OUTPUT diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..11ffac0 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,28 @@ +# $Id$ +# +# This file is part of pavucontrol. +# +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +# USA. + +bin_PROGRAMS=pavucontrol + +dist_pkgdata_DATA=pavucontrol.glade + +pavucontrol_SOURCES=pavucontrol.cc + +pavucontrol_LDADD=$(AM_LDADD) $(GUILIBS_LIBS) $(POLYP_LIBS) +pavucontrol_CXXFLAGS=$(AM_CXXFLAGS) $(GUILIBS_CFLAGS) $(POLYP_CFLAGS) +pavucontrol_CXXFLAGS+=-DGLADE_FILE=\"$(pkgdatadir)/pavucontrol.glade\" diff --git a/src/pavucontrol.cc b/src/pavucontrol.cc new file mode 100644 index 0000000..e1cc740 --- /dev/null +++ b/src/pavucontrol.cc @@ -0,0 +1,687 @@ +/* $Id$ */ + +/*** + This file is part of pavucontrol. + + pavucontrol is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 Lesser General Public License + along with pavucontrol; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include + +#include +#include + +#undef GLADE_FILE +#define GLADE_FILE "pavucontrol.glade" + +pa_context *context = NULL; + +class StreamWidget; + +class ChannelWidget : public Gtk::VBox { +public: + ChannelWidget(BaseObjectType* cobject, const Glib::RefPtr& 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 volumeScaleEnabled; + + virtual void set_sensitive(bool enabled); +}; + +class StreamWidget : public Gtk::VBox { +public: + StreamWidget(BaseObjectType* cobject, const Glib::RefPtr& x); + + void setChannelMap(const pa_channel_map &m); + void setVolume(const pa_cvolume &volume); + virtual void updateChannelVolume(int channel, pa_volume_t v); + + Gtk::Label *nameLabel; + Gtk::VBox *channelsVBox; + Gtk::ToggleButton *lockToggleButton, *muteToggleButton; + + pa_channel_map channelMap; + pa_cvolume volume; + + ChannelWidget *channelWidgets[PA_CHANNELS_MAX]; + + virtual void onMuteToggleButton(); +}; + +class SinkWidget : public StreamWidget { +public: + SinkWidget(BaseObjectType* cobject, const Glib::RefPtr& x); + static SinkWidget* create(); + + virtual void updateChannelVolume(int channel, pa_volume_t v); + virtual void onMuteToggleButton(); + uint32_t index; +}; + +class SourceWidget : public StreamWidget { +public: + SourceWidget(BaseObjectType* cobject, const Glib::RefPtr& x); + static SourceWidget* create(); + + virtual void updateChannelVolume(int channel, pa_volume_t v); + virtual void onMuteToggleButton(); + uint32_t index; +}; + +class SinkInputWidget : public StreamWidget { +public: + SinkInputWidget(BaseObjectType* cobject, const Glib::RefPtr& x); + static SinkInputWidget* create(); + + virtual void updateChannelVolume(int channel, pa_volume_t v); + uint32_t index; +}; + +class MainWindow : public Gtk::Window { +public: + MainWindow(BaseObjectType* cobject, const Glib::RefPtr& x); + static MainWindow* create(); + + void updateSink(const pa_sink_info &info); + void updateSource(const pa_source_info &info); + void updateSinkInput(const pa_sink_input_info &info); + + void removeSink(uint32_t index); + void removeSource(uint32_t index); + void removeSinkInput(uint32_t index); + + Gtk::VBox *streamsVBox, *sinksVBox, *sourcesVBox, *monitorsVBox; + Gtk::EventBox *titleEventBox; + Gtk::Label *noStreamsLabel, *noSinksLabel, *noSourcesLabel, *noMonitorsLabel; + + std::map sinkWidgets; + std::map sourceWidgets; + std::map monitorWidgets; + std::map streamWidgets; + + void updateLabels(); +}; + +/*** ChannelWidget ***/ + +ChannelWidget::ChannelWidget(BaseObjectType* cobject, const Glib::RefPtr& x) : + Gtk::VBox(cobject), + volumeScaleEnabled(true) { + + x->get_widget("channelLabel", channelLabel); + x->get_widget("volumeLabel", volumeLabel); + x->get_widget("volumeScale", volumeScale); + + volumeScale->signal_value_changed().connect(sigc::mem_fun(*this, &ChannelWidget::onVolumeScaleValueChanged)); +} + +ChannelWidget* ChannelWidget::create() { + ChannelWidget* w; + Glib::RefPtr 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 = ((gdouble) volume * 100) / PA_VOLUME_NORM; + char txt[64]; + + snprintf(txt, sizeof(txt), "%0.0f%%", v); + volumeLabel->set_text(txt); + + if (v > 100) + v = 1000; + + volumeScaleEnabled = false; + volumeScale->set_value(v); + volumeScaleEnabled = true; +} + +void ChannelWidget::onVolumeScaleValueChanged() { + + if (!volumeScaleEnabled) + return; + + pa_volume_t volume = (pa_volume_t) ((volumeScale->get_value() * PA_VOLUME_NORM) / 100); + streamWidget->updateChannelVolume(channel, volume); +} + +void ChannelWidget::set_sensitive(bool enabled) { + Gtk::VBox::set_sensitive(enabled); + + channelLabel->set_sensitive(enabled); + volumeLabel->set_sensitive(enabled); + volumeScale->set_sensitive(enabled); +} + +/*** StreamWidget ***/ + +StreamWidget::StreamWidget(BaseObjectType* cobject, const Glib::RefPtr& x) : + Gtk::VBox(cobject) { + + x->get_widget("channelsVBox", channelsVBox); + x->get_widget("nameLabel", nameLabel); + x->get_widget("lockToggleButton", lockToggleButton); + x->get_widget("muteToggleButton", muteToggleButton); + + muteToggleButton->signal_clicked().connect(sigc::mem_fun(*this, &StreamWidget::onMuteToggleButton)); + + for (int i = 0; i < PA_CHANNELS_MAX; i++) + channelWidgets[i] = NULL; +} + +void StreamWidget::setChannelMap(const pa_channel_map &m) { + channelMap = m; + + for (int i = 0; i < m.channels; i++) { + ChannelWidget *cw = channelWidgets[i] = ChannelWidget::create(); + cw->channel = i; + cw->streamWidget = this; + char text[64]; + snprintf(text, sizeof(text), "%s", pa_channel_position_to_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) { + g_assert(v.channels == channelMap.channels); + + volume = v; + + for (int i = 0; i < volume.channels; i++) + channelWidgets[i]->setVolume(volume.values[i]); +} + +void StreamWidget::updateChannelVolume(int channel, pa_volume_t v) { + g_assert(channel < volume.channels); + + if (lockToggleButton->get_active()) { + for (int i = 0; i < volume.channels; i++) + volume.values[i] = v; + } else + volume.values[channel] = v; +} + +void StreamWidget::onMuteToggleButton() { + lockToggleButton->set_sensitive(!muteToggleButton->get_active()); + + for (int i = 0; i < channelMap.channels; i++) + channelWidgets[i]->set_sensitive(!muteToggleButton->get_active()); +} + +SinkWidget::SinkWidget(BaseObjectType* cobject, const Glib::RefPtr& x) : + StreamWidget(cobject, x) { +} + +SinkWidget* SinkWidget::create() { + SinkWidget* w; + Glib::RefPtr x = Gnome::Glade::Xml::create(GLADE_FILE, "streamWidget"); + x->get_widget_derived("streamWidget", w); + return w; +} + +void SinkWidget::updateChannelVolume(int channel, pa_volume_t v) { + StreamWidget::updateChannelVolume(channel, v); + + pa_operation* o; + if (!(o = pa_context_set_sink_volume_by_index(context, index, &volume, NULL, NULL))) { + g_message("pa_context_set_sink_volume_by_index() failed: %s", pa_strerror(pa_context_errno(context))); + goto fail; + } + + pa_operation_unref(o); + return; + +fail: + Gtk::Main::quit(); +} + +void SinkWidget::onMuteToggleButton() { + StreamWidget::onMuteToggleButton(); + + pa_operation* o; + if (!(o = pa_context_set_sink_mute_by_index(context, index, muteToggleButton->get_active(), NULL, NULL))) { + g_message("pa_context_set_sink_mute_by_index() failed: %s", pa_strerror(pa_context_errno(context))); + goto fail; + } + + pa_operation_unref(o); + return; + +fail: + Gtk::Main::quit(); + +} + +SourceWidget::SourceWidget(BaseObjectType* cobject, const Glib::RefPtr& x) : + StreamWidget(cobject, x) { +} + +SourceWidget* SourceWidget::create() { + SourceWidget* w; + Glib::RefPtr x = Gnome::Glade::Xml::create(GLADE_FILE, "streamWidget"); + x->get_widget_derived("streamWidget", w); + return w; +} + +void SourceWidget::updateChannelVolume(int channel, pa_volume_t v) { + StreamWidget::updateChannelVolume(channel, v); + + pa_operation* o; + if (!(o = pa_context_set_source_volume_by_index(context, index, &volume, NULL, NULL))) { + g_message("pa_context_set_source_volume_by_index() failed: %s", pa_strerror(pa_context_errno(context))); + goto fail; + } + + pa_operation_unref(o); + return; + +fail: + Gtk::Main::quit(); +} + +void SourceWidget::onMuteToggleButton() { + StreamWidget::onMuteToggleButton(); + + pa_operation* o; + if (!(o = pa_context_set_source_mute_by_index(context, index, muteToggleButton->get_active(), NULL, NULL))) { + g_message("pa_context_set_source_mute_by_index() failed: %s", pa_strerror(pa_context_errno(context))); + goto fail; + } + + pa_operation_unref(o); + return; + +fail: + Gtk::Main::quit(); +} + +SinkInputWidget::SinkInputWidget(BaseObjectType* cobject, const Glib::RefPtr& x) : + StreamWidget(cobject, x) { +} + +SinkInputWidget* SinkInputWidget::create() { + SinkInputWidget* w; + Glib::RefPtr x = Gnome::Glade::Xml::create(GLADE_FILE, "streamWidget"); + x->get_widget_derived("streamWidget", w); + return w; +} + +void SinkInputWidget::updateChannelVolume(int channel, pa_volume_t v) { + StreamWidget::updateChannelVolume(channel, v); + + pa_operation* o; + if (!(o = pa_context_set_sink_input_volume(context, index, &volume, NULL, NULL))) { + g_message("pa_context_set_sink_input_volume() failed: %s", pa_strerror(pa_context_errno(context))); + goto fail; + } + + pa_operation_unref(o); + return; + +fail: + Gtk::Main::quit(); +} + +/*** MainWindow ***/ + +MainWindow::MainWindow(BaseObjectType* cobject, const Glib::RefPtr& x) : + Gtk::Window(cobject) { + + x->get_widget("streamsVBox", streamsVBox); + x->get_widget("sinksVBox", sinksVBox); + x->get_widget("sourcesVBox", sourcesVBox); + x->get_widget("monitorsVBox", monitorsVBox); + x->get_widget("titleEventBox", titleEventBox); + x->get_widget("noStreamsLabel", noStreamsLabel); + x->get_widget("noSinksLabel", noSinksLabel); + x->get_widget("noSourcesLabel", noSourcesLabel); + x->get_widget("noMonitorsLabel", noMonitorsLabel); + + sourcesVBox->set_reallocate_redraws(true); + monitorsVBox->set_reallocate_redraws(true); + + Gdk::Color c("white"); + titleEventBox->modify_bg(Gtk::STATE_NORMAL, c); +} + +MainWindow* MainWindow::create() { + MainWindow* w; + Glib::RefPtr x = Gnome::Glade::Xml::create(GLADE_FILE, "mainWindow"); + x->get_widget_derived("mainWindow", w); + return w; +} + +void MainWindow::updateSink(const pa_sink_info &info) { + SinkWidget *w; + + if (sinkWidgets.count(info.index)) + w = sinkWidgets[info.index]; + else { + sinkWidgets[info.index] = w = SinkWidget::create(); + w->setChannelMap(info.channel_map); + sinksVBox->pack_start(*w, false, false, 0); + w->index = info.index; + } + + char txt[256]; + snprintf(txt, sizeof(txt), "%s - %s", info.name, info.description); + w->nameLabel->set_markup(txt); + w->setVolume(info.volume); + w->muteToggleButton->set_active(info.mute); + w->onMuteToggleButton(); + + updateLabels(); + w->check_resize(); +} + +void MainWindow::updateSource(const pa_source_info &info) { + SourceWidget *w; + + std::map &l = info.monitor_of_sink != PA_INVALID_INDEX ? monitorWidgets : sourceWidgets; + Gtk::VBox *vbox = info.monitor_of_sink != PA_INVALID_INDEX ? monitorsVBox : sourcesVBox; + + if (l.count(info.index)) + w = l[info.index]; + else { + l[info.index] = w = SourceWidget::create(); + w->setChannelMap(info.channel_map); + vbox->pack_start(*w, false, false, 0); + w->index = info.index; + } + + char txt[256]; + snprintf(txt, sizeof(txt), "%s - %s", info.name, info.description); + w->nameLabel->set_markup(txt); + w->setVolume(info.volume); + w->muteToggleButton->set_active(info.mute); + w->onMuteToggleButton(); + + updateLabels(); + w->check_resize(); +} + +void MainWindow::updateSinkInput(const pa_sink_input_info &info) { + SinkInputWidget *w; + + if (streamWidgets.count(info.index)) + w = streamWidgets[info.index]; + else { + streamWidgets[info.index] = w = SinkInputWidget::create(); + w->setChannelMap(info.channel_map); + streamsVBox->pack_start(*w, false, false, 0); + w->muteToggleButton->hide(); + w->index = info.index; + } + + char txt[256]; + snprintf(txt, sizeof(txt), "%s", info.name); + w->nameLabel->set_markup(txt); + w->setVolume(info.volume); + + updateLabels(); + w->check_resize(); +} + +void MainWindow::updateLabels() { + if (streamWidgets.empty()) + noStreamsLabel->show(); + else + noStreamsLabel->hide(); + + if (sinkWidgets.empty()) + noSinksLabel->show(); + else + noSinksLabel->hide(); + + if (sourceWidgets.empty()) + noSourcesLabel->show(); + else + noSourcesLabel->hide(); + + if (monitorWidgets.empty()) + noMonitorsLabel->show(); + else + noMonitorsLabel->hide(); +} + +void MainWindow::removeSink(uint32_t index) { + StreamWidget *w; + + if (!(w = sinkWidgets[index])) + return; + + sinkWidgets.erase(index); + delete w; + updateLabels(); +} + +void MainWindow::removeSource(uint32_t index) { + StreamWidget *w; + + if (sourceWidgets.count(index)) { + w = sourceWidgets[index]; + sourceWidgets.erase(index); + } else if (monitorWidgets.count(index)) { + w = monitorWidgets[index]; + monitorWidgets.erase(index); + } else + return; + + delete w; + updateLabels(); +} + +void MainWindow::removeSinkInput(uint32_t index) { + StreamWidget *w; + + if (!(w = streamWidgets[index])) + return; + + streamWidgets.erase(index); + delete w; + updateLabels(); +} + +void sink_cb(pa_context *c, const pa_sink_info *i, int eol, void *userdata) { + MainWindow *w = static_cast(userdata); + + if (eol) + return; + + if (!i) { + g_message("sink callback: %s", pa_strerror(pa_context_errno(c))); + Gtk::Main::quit(); + return; + } + + w->updateSink(*i); +} + +void source_cb(pa_context *c, const pa_source_info *i, int eol, void *userdata) { + MainWindow *w = static_cast(userdata); + + if (eol) + return; + + if (!i) { + g_message("source callback: %s", pa_strerror(pa_context_errno(c))); + Gtk::Main::quit(); + return; + } + + w->updateSource(*i); +} + +void sink_input_cb(pa_context *c, const pa_sink_input_info *i, int eol, void *userdata) { + MainWindow *w = static_cast(userdata); + + if (eol) + return; + + if (!i) { + g_message("sink input callback: %s", pa_strerror(pa_context_errno(c))); + Gtk::Main::quit(); + return; + } + + w->updateSinkInput(*i); +} + +void subscribe_cb(pa_context *c, pa_subscription_event_type_t t, uint32_t index, void *userdata) { + MainWindow *w = static_cast(userdata); + + switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) { + case PA_SUBSCRIPTION_EVENT_SINK: + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) + w->removeSink(index); + else { + pa_operation *o; + if (!(o = pa_context_get_sink_info_by_index(c, index, sink_cb, w))) { + g_message("pa_context_get_sink_info_by_index() failed: %s", pa_strerror(pa_context_errno(c))); + goto fail; + } + pa_operation_unref(o); + } + break; + case PA_SUBSCRIPTION_EVENT_SOURCE: + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) + w->removeSource(index); + else { + pa_operation *o; + if (!(o = pa_context_get_source_info_by_index(c, index, source_cb, w))) { + g_message("pa_context_get_source_info_by_index() failed: %s", pa_strerror(pa_context_errno(c))); + goto fail; + } + pa_operation_unref(o); + } + break; + case PA_SUBSCRIPTION_EVENT_SINK_INPUT: + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) + w->removeSinkInput(index); + else { + pa_operation *o; + if (!(o = pa_context_get_sink_input_info(c, index, sink_input_cb, w))) { + g_message("pa_context_get_sink_input_info() failed: %s", pa_strerror(pa_context_errno(c))); + goto fail; + } + pa_operation_unref(o); + } + break; + } + + return; + +fail: + Gtk::Main::quit(); +} + +void context_state_callback(pa_context *c, void *userdata) { + MainWindow *w = static_cast(userdata); + + g_assert(c); + + switch (pa_context_get_state(c)) { + case PA_CONTEXT_UNCONNECTED: + case PA_CONTEXT_CONNECTING: + case PA_CONTEXT_AUTHORIZING: + case PA_CONTEXT_SETTING_NAME: + break; + + case PA_CONTEXT_READY: { + pa_operation *o; + + pa_context_set_subscribe_callback(c, subscribe_cb, w); + + if (!(o = pa_context_subscribe(c, (pa_subscription_mask_t) (PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE|PA_SUBSCRIPTION_MASK_SINK_INPUT), NULL, NULL))) { + g_message("pa_context_subscribe() failed: %s", pa_strerror(pa_context_errno(c))); + goto fail; + } + pa_operation_unref(o); + + if (!(o = pa_context_get_sink_info_list(c, sink_cb, w))) { + g_message("pa_context_get_sink_info_list() failed: %s", pa_strerror(pa_context_errno(c))); + goto fail; + } + pa_operation_unref(o); + + if (!(o = pa_context_get_source_info_list(c, source_cb, w))) { + g_message("pa_context_get_source_info_list() failed: %s", pa_strerror(pa_context_errno(c))); + goto fail; + } + pa_operation_unref(o); + + if (!(o = pa_context_get_sink_input_info_list(c, sink_input_cb, w))) { + g_message("pa_context_get_sink_input_info_list() failed: %s", pa_strerror(pa_context_errno(c))); + goto fail; + } + pa_operation_unref(o); + + break; + } + + case PA_CONTEXT_FAILED: + g_message("Connection failed: %s", pa_strerror(pa_context_errno(c))); + goto fail; + + case PA_CONTEXT_TERMINATED: + default: + goto fail; + + } + + return; + +fail: + Gtk::Main::quit(); +} + +int main(int argc, char *argv[]) { + Gtk::Main kit(argc, argv); + + Gtk::Window* mainWindow = MainWindow::create(); + + pa_glib_mainloop *m = pa_glib_mainloop_new(g_main_context_default()); + g_assert(m); + pa_mainloop_api *api = pa_glib_mainloop_get_api(m); + g_assert(api); + context = pa_context_new(api, "Polypaudio Volume Control"); + g_assert(context); + + pa_context_set_state_callback(context, context_state_callback, mainWindow); + pa_context_connect(context, NULL, (pa_context_flags_t) 0, NULL); + + Gtk::Main::run(*mainWindow); + delete mainWindow; + + pa_context_unref(context); + pa_glib_mainloop_free(m); +} diff --git a/src/pavucontrol.glade b/src/pavucontrol.glade new file mode 100644 index 0000000..6283f06 --- /dev/null +++ b/src/pavucontrol.glade @@ -0,0 +1,743 @@ + + + + + + + Polypaudio Volume Control + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + 500 + 500 + True + False + True + False + False + GDK_WINDOW_TYPE_HINT_NORMAL + GDK_GRAVITY_NORTH_WEST + True + False + + + + True + False + 0 + + + + True + True + False + + + + 12 + True + False + 6 + + + + True + <span size="18000" color="black"><b>Polypaudio Volume Control</b></span> + False + True + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + Use this application to view and modify the volumes of audio streams and devices + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + + 0 + False + False + + + + + + True + + + 0 + False + False + + + + + + 12 + True + False + 12 + + + + True + True + True + False + GTK_POS_RIGHT + False + False + + + + 12 + True + True + GTK_POLICY_NEVER + GTK_POLICY_AUTOMATIC + GTK_SHADOW_NONE + GTK_CORNER_TOP_LEFT + + + + True + GTK_SHADOW_IN + + + + True + False + 0 + + + + True + False + <i>No Sinks Available</i> + False + True + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 16 + 16 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + + + + False + True + + + + + + True + _Sinks + True + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 270 + + + tab + + + + + + 12 + True + True + GTK_POLICY_NEVER + GTK_POLICY_AUTOMATIC + GTK_SHADOW_NONE + GTK_CORNER_TOP_LEFT + + + + True + GTK_SHADOW_IN + + + + True + False + 0 + + + + True + False + <i>No Streams Available</i> + False + True + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 16 + 16 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + + + + False + True + + + + + + True + _Streams + True + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 270 + + + tab + + + + + + 12 + True + True + GTK_POLICY_NEVER + GTK_POLICY_AUTOMATIC + GTK_SHADOW_NONE + GTK_CORNER_TOP_LEFT + + + + True + GTK_SHADOW_IN + + + + True + False + 0 + + + + True + False + 0 + + + + True + False + <i>No Sources Available</i> + False + True + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 16 + 16 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + 0 + False + False + + + + + + True + True + False + 0 + + + + True + False + 0 + + + + True + False + <i>No Monitor Sources Available</i> + False + True + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 16 + 16 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + + + True + Monitor Sources + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + + + 0 + True + True + + + + + + + + + False + True + + + + + + True + _Sources + True + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 270 + + + tab + + + + + 0 + True + True + + + + + 0 + True + True + + + + + + + + True + window1 + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + True + False + True + False + False + GDK_WINDOW_TYPE_HINT_NORMAL + GDK_GRAVITY_NORTH_WEST + True + False + + + + True + False + 0 + + + + 12 + True + False + 6 + + + + True + <b>MPlayer</b> - A good movie + False + True + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_END + -1 + False + 0 + + + 0 + False + False + + + + + + True + False + 6 + + + + True + False + 6 + + + + True + Lock Channels Together + True + GTK_RELIEF_NONE + True + True + False + + + + True + gtk-dialog-authentication + 4 + 0.5 + 0.5 + 0 + 0 + + + + + 0 + False + False + + + + + + True + Mute + True + GTK_RELIEF_NONE + True + False + False + + + + True + gtk-dialog-error + 4 + 0.5 + 0.5 + 0 + 0 + + + + + 0 + False + False + + + + + 0 + False + False + + + + + + True + False + 6 + + + + + + + + + + + 0 + True + True + + + + + 0 + False + False + + + + + 0 + False + False + + + + + + True + + + 0 + False + False + + + + + + + + True + window2 + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + True + False + True + False + False + GDK_WINDOW_TYPE_HINT_NORMAL + GDK_GRAVITY_NORTH_WEST + True + False + + + + True + False + 6 + + + + True + <b>left-front</b> + False + True + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + 10 + False + 0 + + + 0 + False + False + + + + + + True + True + False + GTK_POS_TOP + 0 + GTK_UPDATE_DELAYED + False + 44.2408370972 0 100 5 0 0 + + + 0 + True + True + + + + + + True + 50% + False + False + GTK_JUSTIFY_LEFT + False + False + 1 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + 5 + False + 0 + + + 0 + False + False + + + + + + + diff --git a/src/pavucontrol.gladep b/src/pavucontrol.gladep new file mode 100644 index 0000000..183077b --- /dev/null +++ b/src/pavucontrol.gladep @@ -0,0 +1,8 @@ + + + + + + + FALSE + -- cgit