2006-04-21 14:51:33 +00:00
|
|
|
/* $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.
|
|
|
|
***/
|
|
|
|
|
2006-06-21 14:10:22 +00:00
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include <config.h>
|
|
|
|
#endif
|
|
|
|
|
2006-04-22 18:21:12 +00:00
|
|
|
#include <signal.h>
|
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
#include <gtkmm.h>
|
|
|
|
#include <libglademm.h>
|
|
|
|
|
2006-06-20 23:48:27 +00:00
|
|
|
#include <pulse/pulseaudio.h>
|
|
|
|
#include <pulse/glib-mainloop.h>
|
2006-04-21 14:51:33 +00:00
|
|
|
|
2006-04-21 20:38:39 +00:00
|
|
|
#ifndef GLADE_FILE
|
2006-04-21 14:51:33 +00:00
|
|
|
#define GLADE_FILE "pavucontrol.glade"
|
2006-04-21 20:38:39 +00:00
|
|
|
#endif
|
2006-04-21 14:51:33 +00:00
|
|
|
|
2006-08-07 13:29:46 +00:00
|
|
|
static pa_context *context = NULL;
|
2006-08-07 13:50:02 +00:00
|
|
|
static int n_outstanding = 0;
|
2006-08-07 13:29:46 +00:00
|
|
|
|
|
|
|
enum SinkType {
|
|
|
|
SINK_ALL,
|
|
|
|
SINK_HARDWARE,
|
|
|
|
SINK_VIRTUAL,
|
|
|
|
};
|
|
|
|
|
|
|
|
enum SourceType{
|
|
|
|
SOURCE_ALL,
|
|
|
|
SOURCE_HARDWARE,
|
|
|
|
SOURCE_VIRTUAL,
|
|
|
|
SOURCE_MONITOR,
|
|
|
|
};
|
2006-04-21 14:51:33 +00:00
|
|
|
|
|
|
|
class StreamWidget;
|
2006-08-07 15:26:52 +00:00
|
|
|
class MainWindow;
|
2006-04-21 14:51:33 +00:00
|
|
|
|
2006-08-07 15:26:52 +00:00
|
|
|
class ChannelWidget : public Gtk::EventBox {
|
2006-04-21 14:51:33 +00:00
|
|
|
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 volumeScaleEnabled;
|
|
|
|
|
|
|
|
virtual void set_sensitive(bool enabled);
|
|
|
|
};
|
|
|
|
|
|
|
|
class StreamWidget : public Gtk::VBox {
|
|
|
|
public:
|
|
|
|
StreamWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
|
|
|
|
|
|
|
|
void setChannelMap(const pa_channel_map &m);
|
2006-05-20 17:28:50 +00:00
|
|
|
void setVolume(const pa_cvolume &volume, bool force);
|
2006-04-21 14:51:33 +00:00
|
|
|
virtual void updateChannelVolume(int channel, pa_volume_t v);
|
|
|
|
|
2006-05-20 17:28:50 +00:00
|
|
|
Gtk::Label *nameLabel, *boldNameLabel;
|
2006-04-21 14:51:33 +00:00
|
|
|
Gtk::VBox *channelsVBox;
|
|
|
|
Gtk::ToggleButton *lockToggleButton, *muteToggleButton;
|
|
|
|
|
|
|
|
pa_channel_map channelMap;
|
|
|
|
pa_cvolume volume;
|
2006-08-07 13:29:46 +00:00
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
ChannelWidget *channelWidgets[PA_CHANNELS_MAX];
|
|
|
|
|
|
|
|
virtual void onMuteToggleButton();
|
2006-05-16 21:13:41 +00:00
|
|
|
|
|
|
|
sigc::connection timeoutConnection;
|
|
|
|
|
|
|
|
bool timeoutEvent();
|
|
|
|
|
|
|
|
virtual void executeVolumeUpdate();
|
2006-04-21 14:51:33 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
class SinkWidget : public StreamWidget {
|
|
|
|
public:
|
|
|
|
SinkWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
|
|
|
|
static SinkWidget* create();
|
|
|
|
|
2006-08-07 13:29:46 +00:00
|
|
|
SinkType type;
|
2006-08-07 15:26:52 +00:00
|
|
|
Glib::ustring name;
|
2006-08-07 13:29:46 +00:00
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
virtual void onMuteToggleButton();
|
|
|
|
uint32_t index;
|
2006-05-16 21:13:41 +00:00
|
|
|
virtual void executeVolumeUpdate();
|
2006-04-21 14:51:33 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
class SourceWidget : public StreamWidget {
|
|
|
|
public:
|
|
|
|
SourceWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
|
|
|
|
static SourceWidget* create();
|
|
|
|
|
2006-08-07 13:29:46 +00:00
|
|
|
SourceType type;
|
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
virtual void onMuteToggleButton();
|
|
|
|
uint32_t index;
|
2006-05-16 21:13:41 +00:00
|
|
|
virtual void executeVolumeUpdate();
|
2006-04-21 14:51:33 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
class SinkInputWidget : public StreamWidget {
|
|
|
|
public:
|
|
|
|
SinkInputWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
|
|
|
|
static SinkInputWidget* create();
|
2006-08-07 15:26:52 +00:00
|
|
|
virtual ~SinkInputWidget();
|
2006-04-21 14:51:33 +00:00
|
|
|
|
2006-08-07 15:26:52 +00:00
|
|
|
uint32_t index, clientIndex, sinkIndex;
|
2006-05-16 21:13:41 +00:00
|
|
|
virtual void executeVolumeUpdate();
|
2006-08-07 15:26:52 +00:00
|
|
|
|
|
|
|
MainWindow *mainWindow;
|
|
|
|
Gtk::Menu menu, submenu;
|
|
|
|
Gtk::MenuItem titleMenuItem;
|
|
|
|
|
|
|
|
struct SinkMenuItem {
|
|
|
|
SinkMenuItem(SinkInputWidget *w, const char *label, uint32_t i, bool active) :
|
|
|
|
widget(w),
|
|
|
|
menuItem(label),
|
|
|
|
index(i) {
|
|
|
|
menuItem.set_active(active);
|
|
|
|
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();
|
|
|
|
|
|
|
|
protected:
|
|
|
|
virtual bool on_button_press_event(GdkEventButton* event);
|
2006-04-21 14:51:33 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
class MainWindow : public Gtk::Window {
|
|
|
|
public:
|
|
|
|
MainWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
|
|
|
|
static MainWindow* create();
|
2006-05-20 17:28:50 +00:00
|
|
|
virtual ~MainWindow();
|
2006-04-21 14:51:33 +00:00
|
|
|
|
|
|
|
void updateSink(const pa_sink_info &info);
|
|
|
|
void updateSource(const pa_source_info &info);
|
|
|
|
void updateSinkInput(const pa_sink_input_info &info);
|
2006-05-20 17:28:50 +00:00
|
|
|
void updateClient(const pa_client_info &info);
|
2006-04-21 14:51:33 +00:00
|
|
|
|
|
|
|
void removeSink(uint32_t index);
|
|
|
|
void removeSource(uint32_t index);
|
|
|
|
void removeSinkInput(uint32_t index);
|
2006-05-20 17:28:50 +00:00
|
|
|
void removeClient(uint32_t index);
|
2006-04-21 14:51:33 +00:00
|
|
|
|
2006-08-07 13:29:46 +00:00
|
|
|
Gtk::VBox *streamsVBox, *sinksVBox, *sourcesVBox;
|
2006-04-21 14:51:33 +00:00
|
|
|
Gtk::EventBox *titleEventBox;
|
2006-08-07 13:29:46 +00:00
|
|
|
Gtk::Label *noStreamsLabel, *noSinksLabel, *noSourcesLabel;
|
|
|
|
Gtk::ComboBox *sinkTypeComboBox, *sourceTypeComboBox;
|
2006-04-21 14:51:33 +00:00
|
|
|
|
2006-08-07 15:26:52 +00:00
|
|
|
std::map<uint32_t, SinkWidget*> sinkWidgets;
|
|
|
|
std::map<uint32_t, SourceWidget*> sourceWidgets;
|
|
|
|
std::map<uint32_t, SinkInputWidget*> streamWidgets;
|
|
|
|
std::map<uint32_t, char*> clientNames;
|
2006-04-21 14:51:33 +00:00
|
|
|
|
2006-08-07 13:29:46 +00:00
|
|
|
SinkType showSinkType;
|
|
|
|
SourceType showSourceType;
|
|
|
|
|
|
|
|
virtual void onSinkTypeComboBoxChanged();
|
|
|
|
virtual void onSourceTypeComboBoxChanged();
|
|
|
|
|
|
|
|
void updateDeviceVisibility();
|
2006-08-07 15:26:52 +00:00
|
|
|
|
2006-08-07 13:50:02 +00:00
|
|
|
protected:
|
|
|
|
virtual void on_realize();
|
2006-04-21 14:51:33 +00:00
|
|
|
};
|
|
|
|
|
2006-04-21 19:24:32 +00:00
|
|
|
void show_error(const char *txt) {
|
|
|
|
char buf[256];
|
|
|
|
|
|
|
|
snprintf(buf, sizeof(buf), "%s: %s", txt, pa_strerror(pa_context_errno(context)));
|
|
|
|
|
|
|
|
Gtk::MessageDialog dialog(buf, false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
|
|
|
|
dialog.run();
|
|
|
|
|
|
|
|
Gtk::Main::quit();
|
|
|
|
}
|
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
/*** ChannelWidget ***/
|
|
|
|
|
|
|
|
ChannelWidget::ChannelWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
|
2006-08-07 15:26:52 +00:00
|
|
|
Gtk::EventBox(cobject),
|
2006-04-21 14:51:33 +00:00
|
|
|
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<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 = ((gdouble) volume * 100) / PA_VOLUME_NORM;
|
|
|
|
char txt[64];
|
|
|
|
|
|
|
|
snprintf(txt, sizeof(txt), "%0.0f%%", v);
|
|
|
|
volumeLabel->set_text(txt);
|
|
|
|
|
|
|
|
if (v > 100)
|
2006-05-16 21:13:41 +00:00
|
|
|
v = 100;
|
2006-04-21 14:51:33 +00:00
|
|
|
|
|
|
|
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) {
|
2006-08-07 15:26:52 +00:00
|
|
|
Gtk::EventBox::set_sensitive(enabled);
|
2006-04-21 14:51:33 +00:00
|
|
|
|
|
|
|
channelLabel->set_sensitive(enabled);
|
|
|
|
volumeLabel->set_sensitive(enabled);
|
|
|
|
volumeScale->set_sensitive(enabled);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*** StreamWidget ***/
|
|
|
|
|
|
|
|
StreamWidget::StreamWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
|
|
|
|
Gtk::VBox(cobject) {
|
|
|
|
|
|
|
|
x->get_widget("channelsVBox", channelsVBox);
|
|
|
|
x->get_widget("nameLabel", nameLabel);
|
2006-05-20 17:28:50 +00:00
|
|
|
x->get_widget("boldNameLabel", boldNameLabel);
|
2006-04-21 14:51:33 +00:00
|
|
|
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), "<b>%s</b>", 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);
|
|
|
|
}
|
|
|
|
|
2006-05-20 17:28:50 +00:00
|
|
|
void StreamWidget::setVolume(const pa_cvolume &v, bool force = false) {
|
2006-04-21 14:51:33 +00:00
|
|
|
g_assert(v.channels == channelMap.channels);
|
|
|
|
|
|
|
|
volume = v;
|
2006-05-20 17:28:50 +00:00
|
|
|
|
2006-08-07 16:45:30 +00:00
|
|
|
if (timeoutConnection.empty() || force) { /* do not update the volume when a volume change is still in flux */
|
2006-05-20 17:28:50 +00:00
|
|
|
for (int i = 0; i < volume.channels; i++)
|
|
|
|
channelWidgets[i]->setVolume(volume.values[i]);
|
2006-08-07 16:45:30 +00:00
|
|
|
}
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void StreamWidget::updateChannelVolume(int channel, pa_volume_t v) {
|
2006-05-20 17:28:50 +00:00
|
|
|
pa_cvolume n;
|
2006-04-21 14:51:33 +00:00
|
|
|
g_assert(channel < volume.channels);
|
|
|
|
|
2006-05-20 17:28:50 +00:00
|
|
|
n = volume;
|
2006-04-21 14:51:33 +00:00
|
|
|
if (lockToggleButton->get_active()) {
|
2006-05-20 17:28:50 +00:00
|
|
|
for (int i = 0; i < n.channels; i++)
|
|
|
|
n.values[i] = v;
|
2006-04-21 14:51:33 +00:00
|
|
|
} else
|
2006-05-20 17:28:50 +00:00
|
|
|
n.values[channel] = v;
|
2006-05-16 21:13:41 +00:00
|
|
|
|
2006-05-20 17:28:50 +00:00
|
|
|
setVolume(n, true);
|
2006-05-16 21:13:41 +00:00
|
|
|
|
|
|
|
if (timeoutConnection.empty())
|
2006-08-07 16:45:30 +00:00
|
|
|
timeoutConnection = Glib::signal_timeout().connect(sigc::mem_fun(*this, &StreamWidget::timeoutEvent), 100);
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void StreamWidget::onMuteToggleButton() {
|
|
|
|
lockToggleButton->set_sensitive(!muteToggleButton->get_active());
|
|
|
|
|
|
|
|
for (int i = 0; i < channelMap.channels; i++)
|
|
|
|
channelWidgets[i]->set_sensitive(!muteToggleButton->get_active());
|
|
|
|
}
|
|
|
|
|
2006-05-16 21:13:41 +00:00
|
|
|
bool StreamWidget::timeoutEvent() {
|
|
|
|
executeVolumeUpdate();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void StreamWidget::executeVolumeUpdate() {
|
|
|
|
}
|
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
SinkWidget::SinkWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
|
|
|
|
StreamWidget(cobject, x) {
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2006-05-16 21:13:41 +00:00
|
|
|
void SinkWidget::executeVolumeUpdate() {
|
2006-04-21 14:51:33 +00:00
|
|
|
pa_operation* o;
|
2006-05-16 21:13:41 +00:00
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
if (!(o = pa_context_set_sink_volume_by_index(context, index, &volume, NULL, NULL))) {
|
2006-04-21 19:24:32 +00:00
|
|
|
show_error("pa_context_set_sink_volume_by_index() failed");
|
|
|
|
return;
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pa_operation_unref(o);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SinkWidget::onMuteToggleButton() {
|
|
|
|
StreamWidget::onMuteToggleButton();
|
|
|
|
|
|
|
|
pa_operation* o;
|
|
|
|
if (!(o = pa_context_set_sink_mute_by_index(context, index, muteToggleButton->get_active(), NULL, NULL))) {
|
2006-04-21 19:24:32 +00:00
|
|
|
show_error("pa_context_set_sink_mute_by_index() failed");
|
|
|
|
return;
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pa_operation_unref(o);
|
|
|
|
}
|
|
|
|
|
|
|
|
SourceWidget::SourceWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
|
|
|
|
StreamWidget(cobject, x) {
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2006-05-16 21:13:41 +00:00
|
|
|
void SourceWidget::executeVolumeUpdate() {
|
2006-04-21 14:51:33 +00:00
|
|
|
pa_operation* o;
|
2006-05-16 21:13:41 +00:00
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
if (!(o = pa_context_set_source_volume_by_index(context, index, &volume, NULL, NULL))) {
|
2006-04-21 19:24:32 +00:00
|
|
|
show_error("pa_context_set_source_volume_by_index() failed");
|
|
|
|
return;
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pa_operation_unref(o);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SourceWidget::onMuteToggleButton() {
|
|
|
|
StreamWidget::onMuteToggleButton();
|
|
|
|
|
|
|
|
pa_operation* o;
|
|
|
|
if (!(o = pa_context_set_source_mute_by_index(context, index, muteToggleButton->get_active(), NULL, NULL))) {
|
2006-04-21 19:24:32 +00:00
|
|
|
show_error("pa_context_set_source_mute_by_index() failed");
|
|
|
|
return;
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
2006-05-16 21:13:41 +00:00
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
pa_operation_unref(o);
|
|
|
|
}
|
|
|
|
|
|
|
|
SinkInputWidget::SinkInputWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
|
2006-08-07 15:26:52 +00:00
|
|
|
StreamWidget(cobject, x),
|
|
|
|
mainWindow(NULL),
|
|
|
|
titleMenuItem("Move Stream...") {
|
|
|
|
|
|
|
|
add_events(Gdk::BUTTON_PRESS_MASK);
|
|
|
|
|
|
|
|
menu.append(titleMenuItem);
|
|
|
|
titleMenuItem.set_submenu(submenu);
|
|
|
|
}
|
|
|
|
|
|
|
|
SinkInputWidget::~SinkInputWidget() {
|
|
|
|
clearMenu();
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2006-05-16 21:13:41 +00:00
|
|
|
void SinkInputWidget::executeVolumeUpdate() {
|
2006-04-21 14:51:33 +00:00
|
|
|
pa_operation* o;
|
2006-08-07 16:45:30 +00:00
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
if (!(o = pa_context_set_sink_input_volume(context, index, &volume, NULL, NULL))) {
|
2006-04-21 19:24:32 +00:00
|
|
|
show_error("pa_context_set_sink_input_volume() failed");
|
|
|
|
return;
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pa_operation_unref(o);
|
|
|
|
}
|
|
|
|
|
2006-08-07 15:26:52 +00:00
|
|
|
bool SinkInputWidget::on_button_press_event(GdkEventButton* event) {
|
|
|
|
if (StreamWidget::on_button_press_event(event))
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
|
|
|
|
clearMenu();
|
|
|
|
buildMenu();
|
|
|
|
menu.popup(event->button, event->time);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
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->name.c_str(), i->second->index, i->second->index == sinkIndex);
|
|
|
|
submenu.append(m->menuItem);
|
|
|
|
}
|
|
|
|
|
|
|
|
menu.show_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
void SinkInputWidget::SinkMenuItem::onToggle() {
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
/*** MainWindow ***/
|
|
|
|
|
|
|
|
MainWindow::MainWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
|
2006-08-07 13:29:46 +00:00
|
|
|
Gtk::Window(cobject),
|
|
|
|
showSinkType(SINK_HARDWARE),
|
|
|
|
showSourceType(SOURCE_HARDWARE) {
|
2006-04-21 14:51:33 +00:00
|
|
|
|
|
|
|
x->get_widget("streamsVBox", streamsVBox);
|
|
|
|
x->get_widget("sinksVBox", sinksVBox);
|
|
|
|
x->get_widget("sourcesVBox", sourcesVBox);
|
|
|
|
x->get_widget("titleEventBox", titleEventBox);
|
|
|
|
x->get_widget("noStreamsLabel", noStreamsLabel);
|
|
|
|
x->get_widget("noSinksLabel", noSinksLabel);
|
|
|
|
x->get_widget("noSourcesLabel", noSourcesLabel);
|
2006-08-07 13:29:46 +00:00
|
|
|
x->get_widget("sinkTypeComboBox", sinkTypeComboBox);
|
|
|
|
x->get_widget("sourceTypeComboBox", sourceTypeComboBox);
|
2006-04-21 14:51:33 +00:00
|
|
|
|
|
|
|
sourcesVBox->set_reallocate_redraws(true);
|
2006-06-01 13:39:47 +00:00
|
|
|
streamsVBox->set_reallocate_redraws(true);
|
|
|
|
sinksVBox->set_reallocate_redraws(true);
|
2006-08-07 13:29:46 +00:00
|
|
|
|
|
|
|
sinkTypeComboBox->set_active((int) showSinkType);
|
|
|
|
sourceTypeComboBox->set_active((int) showSourceType);
|
|
|
|
|
|
|
|
sinkTypeComboBox->signal_changed().connect(sigc::mem_fun(*this, &MainWindow::onSinkTypeComboBoxChanged));
|
|
|
|
sourceTypeComboBox->signal_changed().connect(sigc::mem_fun(*this, &MainWindow::onSourceTypeComboBoxChanged));
|
2006-08-07 15:26:52 +00:00
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
Gdk::Color c("white");
|
|
|
|
titleEventBox->modify_bg(Gtk::STATE_NORMAL, c);
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2006-08-07 13:50:02 +00:00
|
|
|
void MainWindow::on_realize() {
|
|
|
|
Gtk::Window::on_realize();
|
|
|
|
|
|
|
|
get_window()->set_cursor(Gdk::Cursor(Gdk::WATCH));
|
|
|
|
}
|
|
|
|
|
2006-05-20 17:28:50 +00:00
|
|
|
MainWindow::~MainWindow() {
|
2006-08-07 15:26:52 +00:00
|
|
|
while (!clientNames.empty()) {
|
|
|
|
std::map<uint32_t, char*>::iterator i = clientNames.begin();
|
2006-05-20 17:28:50 +00:00
|
|
|
g_free(i->second);
|
2006-08-07 15:26:52 +00:00
|
|
|
clientNames.erase(i);
|
|
|
|
}
|
2006-05-20 17:28:50 +00:00
|
|
|
}
|
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
void MainWindow::updateSink(const pa_sink_info &info) {
|
|
|
|
SinkWidget *w;
|
2006-08-07 16:45:30 +00:00
|
|
|
bool is_new = false;
|
2006-04-21 14:51:33 +00:00
|
|
|
|
|
|
|
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;
|
2006-08-07 16:45:30 +00:00
|
|
|
is_new = true;
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
|
2006-08-07 13:29:46 +00:00
|
|
|
w->type = info.flags & PA_SINK_HARDWARE ? SINK_HARDWARE : SINK_VIRTUAL;
|
2006-08-07 15:26:52 +00:00
|
|
|
w->name = info.name;
|
2006-08-07 13:29:46 +00:00
|
|
|
|
2006-08-11 17:25:55 +00:00
|
|
|
w->boldNameLabel->set_text("");
|
2006-05-20 17:28:50 +00:00
|
|
|
gchar *txt;
|
2006-08-11 17:25:55 +00:00
|
|
|
w->nameLabel->set_markup(txt = g_markup_printf_escaped("%s", info.description));
|
2006-05-20 17:28:50 +00:00
|
|
|
g_free(txt);
|
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
w->setVolume(info.volume);
|
|
|
|
w->muteToggleButton->set_active(info.mute);
|
|
|
|
|
2006-08-07 16:45:30 +00:00
|
|
|
if (is_new)
|
|
|
|
updateDeviceVisibility();
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::updateSource(const pa_source_info &info) {
|
|
|
|
SourceWidget *w;
|
2006-08-07 16:45:30 +00:00
|
|
|
bool is_new = false;
|
2006-04-21 14:51:33 +00:00
|
|
|
|
2006-08-07 13:29:46 +00:00
|
|
|
if (sourceWidgets.count(info.index))
|
|
|
|
w = sourceWidgets[info.index];
|
2006-04-21 14:51:33 +00:00
|
|
|
else {
|
2006-08-07 13:29:46 +00:00
|
|
|
sourceWidgets[info.index] = w = SourceWidget::create();
|
2006-04-21 14:51:33 +00:00
|
|
|
w->setChannelMap(info.channel_map);
|
2006-08-07 13:29:46 +00:00
|
|
|
sourcesVBox->pack_start(*w, false, false, 0);
|
2006-04-21 14:51:33 +00:00
|
|
|
w->index = info.index;
|
2006-08-07 16:45:30 +00:00
|
|
|
is_new = true;
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
|
2006-08-07 13:29:46 +00:00
|
|
|
w->type = info.monitor_of_sink != PA_INVALID_INDEX ? SOURCE_MONITOR : (info.flags & PA_SOURCE_HARDWARE ? SOURCE_HARDWARE : SOURCE_VIRTUAL);
|
|
|
|
|
2006-08-11 17:25:55 +00:00
|
|
|
w->boldNameLabel->set_text("");
|
2006-05-20 17:28:50 +00:00
|
|
|
gchar *txt;
|
2006-08-11 17:25:55 +00:00
|
|
|
w->nameLabel->set_markup(txt = g_markup_printf_escaped("%s", info.description));
|
2006-05-20 17:28:50 +00:00
|
|
|
g_free(txt);
|
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
w->setVolume(info.volume);
|
|
|
|
w->muteToggleButton->set_active(info.mute);
|
|
|
|
|
2006-08-07 16:45:30 +00:00
|
|
|
if (is_new)
|
|
|
|
updateDeviceVisibility();
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2006-05-20 17:28:50 +00:00
|
|
|
w->clientIndex = info.client;
|
2006-08-07 15:26:52 +00:00
|
|
|
w->mainWindow = this;
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
|
2006-08-07 15:26:52 +00:00
|
|
|
w->sinkIndex = info.sink;
|
|
|
|
|
2006-05-20 17:28:50 +00:00
|
|
|
char *txt;
|
2006-07-21 20:17:49 +00:00
|
|
|
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 {
|
2006-08-11 17:25:55 +00:00
|
|
|
w->boldNameLabel->set_text("");
|
2006-07-21 20:17:49 +00:00
|
|
|
w->nameLabel->set_label(info.name);
|
|
|
|
}
|
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
w->setVolume(info.volume);
|
|
|
|
|
2006-08-07 13:29:46 +00:00
|
|
|
w->show();
|
|
|
|
updateDeviceVisibility();
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
|
2006-05-20 17:28:50 +00:00
|
|
|
void MainWindow::updateClient(const pa_client_info &info) {
|
|
|
|
|
|
|
|
g_free(clientNames[info.index]);
|
|
|
|
clientNames[info.index] = g_strdup(info.name);
|
|
|
|
|
2006-08-07 15:26:52 +00:00
|
|
|
for (std::map<uint32_t, SinkInputWidget*>::iterator i = streamWidgets.begin(); i != streamWidgets.end(); ++i) {
|
2006-05-20 17:28:50 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-08-07 13:29:46 +00:00
|
|
|
void MainWindow::updateDeviceVisibility() {
|
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
if (streamWidgets.empty())
|
|
|
|
noStreamsLabel->show();
|
|
|
|
else
|
|
|
|
noStreamsLabel->hide();
|
2006-08-07 13:29:46 +00:00
|
|
|
|
|
|
|
sourcesVBox->hide_all();
|
|
|
|
sinksVBox->hide_all();
|
|
|
|
|
|
|
|
bool is_empty = true;
|
|
|
|
|
2006-08-07 15:26:52 +00:00
|
|
|
for (std::map<uint32_t, SinkWidget*>::iterator i = sinkWidgets.begin(); i != sinkWidgets.end(); ++i) {
|
2006-08-07 13:29:46 +00:00
|
|
|
SinkWidget* w = i->second;
|
|
|
|
|
|
|
|
if (showSinkType == SINK_ALL || w->type == showSinkType) {
|
|
|
|
w->show_all();
|
|
|
|
is_empty = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_empty)
|
2006-04-21 14:51:33 +00:00
|
|
|
noSinksLabel->show();
|
2006-08-07 13:29:46 +00:00
|
|
|
|
|
|
|
is_empty = true;
|
2006-04-21 14:51:33 +00:00
|
|
|
|
2006-08-07 15:26:52 +00:00
|
|
|
for (std::map<uint32_t, SourceWidget*>::iterator i = sourceWidgets.begin(); i != sourceWidgets.end(); ++i) {
|
2006-08-07 13:29:46 +00:00
|
|
|
SourceWidget* w = i->second;
|
|
|
|
|
|
|
|
if (showSourceType == SOURCE_ALL || w->type == showSourceType) {
|
|
|
|
w->show_all();
|
|
|
|
is_empty = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_empty)
|
2006-04-21 14:51:33 +00:00
|
|
|
noSourcesLabel->show();
|
|
|
|
|
2006-08-07 13:29:46 +00:00
|
|
|
sourcesVBox->show();
|
|
|
|
sinksVBox->show();
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::removeSink(uint32_t index) {
|
2006-08-07 13:29:46 +00:00
|
|
|
if (!sinkWidgets.count(index))
|
2006-04-21 14:51:33 +00:00
|
|
|
return;
|
|
|
|
|
2006-08-07 13:29:46 +00:00
|
|
|
delete sinkWidgets[index];
|
2006-04-21 14:51:33 +00:00
|
|
|
sinkWidgets.erase(index);
|
2006-08-07 13:29:46 +00:00
|
|
|
updateDeviceVisibility();
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::removeSource(uint32_t index) {
|
2006-08-07 13:29:46 +00:00
|
|
|
if (!sourceWidgets.count(index))
|
2006-04-21 14:51:33 +00:00
|
|
|
return;
|
|
|
|
|
2006-08-07 13:29:46 +00:00
|
|
|
delete sourceWidgets[index];
|
|
|
|
sourceWidgets.erase(index);
|
|
|
|
updateDeviceVisibility();
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::removeSinkInput(uint32_t index) {
|
2006-08-07 13:29:46 +00:00
|
|
|
if (!streamWidgets.count(index))
|
2006-04-21 14:51:33 +00:00
|
|
|
return;
|
2006-08-07 13:29:46 +00:00
|
|
|
|
|
|
|
delete streamWidgets[index];
|
2006-04-21 14:51:33 +00:00
|
|
|
streamWidgets.erase(index);
|
2006-08-07 13:29:46 +00:00
|
|
|
updateDeviceVisibility();
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
|
2006-05-20 17:28:50 +00:00
|
|
|
void MainWindow::removeClient(uint32_t index) {
|
|
|
|
g_free(clientNames[index]);
|
|
|
|
clientNames.erase(index);
|
|
|
|
}
|
|
|
|
|
2006-08-07 13:29:46 +00:00
|
|
|
void MainWindow::onSinkTypeComboBoxChanged() {
|
|
|
|
showSinkType = (SinkType) sinkTypeComboBox->get_active_row_number();
|
|
|
|
|
|
|
|
if (showSinkType == (SinkType) -1)
|
|
|
|
sinkTypeComboBox->set_active((int) SINK_HARDWARE);
|
|
|
|
|
|
|
|
updateDeviceVisibility();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::onSourceTypeComboBoxChanged() {
|
|
|
|
showSourceType = (SourceType) sourceTypeComboBox->get_active_row_number();
|
|
|
|
|
|
|
|
if (showSourceType == (SourceType) -1)
|
|
|
|
sourceTypeComboBox->set_active((int) SOURCE_HARDWARE);
|
|
|
|
|
|
|
|
updateDeviceVisibility();
|
|
|
|
}
|
|
|
|
|
2006-08-07 13:50:02 +00:00
|
|
|
static void dec_outstanding(MainWindow *w) {
|
2006-08-07 15:26:52 +00:00
|
|
|
if (n_outstanding <= 0)
|
|
|
|
return;
|
2006-08-07 13:50:02 +00:00
|
|
|
|
|
|
|
if (--n_outstanding <= 0)
|
|
|
|
w->get_window()->set_cursor();
|
|
|
|
}
|
|
|
|
|
2006-04-21 19:24:32 +00:00
|
|
|
void sink_cb(pa_context *, const pa_sink_info *i, int eol, void *userdata) {
|
2006-04-21 14:51:33 +00:00
|
|
|
MainWindow *w = static_cast<MainWindow*>(userdata);
|
|
|
|
|
2006-08-07 13:50:02 +00:00
|
|
|
if (eol) {
|
|
|
|
dec_outstanding(w);
|
2006-04-21 14:51:33 +00:00
|
|
|
return;
|
2006-08-07 13:50:02 +00:00
|
|
|
}
|
2006-04-21 14:51:33 +00:00
|
|
|
|
|
|
|
if (!i) {
|
2006-04-21 19:24:32 +00:00
|
|
|
show_error("Sink callback failure");
|
2006-04-21 14:51:33 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
w->updateSink(*i);
|
|
|
|
}
|
|
|
|
|
2006-04-21 19:24:32 +00:00
|
|
|
void source_cb(pa_context *, const pa_source_info *i, int eol, void *userdata) {
|
2006-04-21 14:51:33 +00:00
|
|
|
MainWindow *w = static_cast<MainWindow*>(userdata);
|
|
|
|
|
2006-08-07 13:50:02 +00:00
|
|
|
if (eol) {
|
|
|
|
dec_outstanding(w);
|
2006-04-21 14:51:33 +00:00
|
|
|
return;
|
2006-08-07 13:50:02 +00:00
|
|
|
}
|
2006-04-21 14:51:33 +00:00
|
|
|
|
|
|
|
if (!i) {
|
2006-04-21 19:24:32 +00:00
|
|
|
show_error("Source callback failure");
|
2006-04-21 14:51:33 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
w->updateSource(*i);
|
|
|
|
}
|
|
|
|
|
2006-04-21 19:24:32 +00:00
|
|
|
void sink_input_cb(pa_context *, const pa_sink_input_info *i, int eol, void *userdata) {
|
2006-04-21 14:51:33 +00:00
|
|
|
MainWindow *w = static_cast<MainWindow*>(userdata);
|
|
|
|
|
2006-08-07 13:50:02 +00:00
|
|
|
if (eol) {
|
|
|
|
dec_outstanding(w);
|
2006-04-21 14:51:33 +00:00
|
|
|
return;
|
2006-08-07 13:50:02 +00:00
|
|
|
}
|
2006-04-21 14:51:33 +00:00
|
|
|
|
|
|
|
if (!i) {
|
2006-04-21 19:24:32 +00:00
|
|
|
show_error("Sink input callback failure");
|
2006-04-21 14:51:33 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
w->updateSinkInput(*i);
|
|
|
|
}
|
|
|
|
|
2006-05-20 17:28:50 +00:00
|
|
|
void client_cb(pa_context *, const pa_client_info *i, int eol, void *userdata) {
|
|
|
|
MainWindow *w = static_cast<MainWindow*>(userdata);
|
|
|
|
|
2006-08-07 13:50:02 +00:00
|
|
|
if (eol) {
|
|
|
|
dec_outstanding(w);
|
2006-05-20 17:28:50 +00:00
|
|
|
return;
|
2006-08-07 13:50:02 +00:00
|
|
|
}
|
2006-05-20 17:28:50 +00:00
|
|
|
|
|
|
|
if (!i) {
|
|
|
|
show_error("Client callback failure");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
w->updateClient(*i);
|
|
|
|
}
|
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
void subscribe_cb(pa_context *c, pa_subscription_event_type_t t, uint32_t index, void *userdata) {
|
|
|
|
MainWindow *w = static_cast<MainWindow*>(userdata);
|
|
|
|
|
|
|
|
switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
|
|
|
|
case PA_SUBSCRIPTION_EVENT_SINK:
|
|
|
|
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
|
|
|
|
w->removeSink(index);
|
|
|
|
else {
|
|
|
|
pa_operation *o;
|
|
|
|
if (!(o = pa_context_get_sink_info_by_index(c, index, sink_cb, w))) {
|
2006-04-21 19:24:32 +00:00
|
|
|
show_error("pa_context_get_sink_info_by_index() failed");
|
|
|
|
return;
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
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))) {
|
2006-04-21 19:24:32 +00:00
|
|
|
show_error("pa_context_get_source_info_by_index() failed");
|
|
|
|
return;
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
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))) {
|
2006-04-21 19:24:32 +00:00
|
|
|
show_error("pa_context_get_sink_input_info() failed");
|
|
|
|
return;
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
pa_operation_unref(o);
|
|
|
|
}
|
|
|
|
break;
|
2006-05-20 17:28:50 +00:00
|
|
|
case PA_SUBSCRIPTION_EVENT_CLIENT:
|
|
|
|
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
|
|
|
|
w->removeClient(index);
|
|
|
|
else {
|
|
|
|
pa_operation *o;
|
|
|
|
if (!(o = pa_context_get_client_info(c, index, client_cb, w))) {
|
|
|
|
show_error("pa_context_get_client_info() failed");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
pa_operation_unref(o);
|
|
|
|
}
|
|
|
|
break;
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void context_state_callback(pa_context *c, void *userdata) {
|
|
|
|
MainWindow *w = static_cast<MainWindow*>(userdata);
|
|
|
|
|
|
|
|
g_assert(c);
|
|
|
|
|
|
|
|
switch (pa_context_get_state(c)) {
|
|
|
|
case PA_CONTEXT_UNCONNECTED:
|
|
|
|
case PA_CONTEXT_CONNECTING:
|
|
|
|
case PA_CONTEXT_AUTHORIZING:
|
|
|
|
case PA_CONTEXT_SETTING_NAME:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PA_CONTEXT_READY: {
|
|
|
|
pa_operation *o;
|
|
|
|
|
|
|
|
pa_context_set_subscribe_callback(c, subscribe_cb, w);
|
|
|
|
|
2006-05-20 17:28:50 +00:00
|
|
|
if (!(o = pa_context_subscribe(c, (pa_subscription_mask_t) (PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE|PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_CLIENT), NULL, NULL))) {
|
2006-04-21 19:24:32 +00:00
|
|
|
show_error("pa_context_subscribe() failed");
|
|
|
|
return;
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
pa_operation_unref(o);
|
|
|
|
|
2006-05-20 17:28:50 +00:00
|
|
|
if (!(o = pa_context_get_client_info_list(c, client_cb, w))) {
|
|
|
|
show_error("pa_context_client_info_list() failed");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
pa_operation_unref(o);
|
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
if (!(o = pa_context_get_sink_info_list(c, sink_cb, w))) {
|
2006-04-21 19:24:32 +00:00
|
|
|
show_error("pa_context_get_sink_info_list() failed");
|
|
|
|
return;
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
pa_operation_unref(o);
|
|
|
|
|
|
|
|
if (!(o = pa_context_get_source_info_list(c, source_cb, w))) {
|
2006-04-21 19:24:32 +00:00
|
|
|
show_error("pa_context_get_source_info_list() failed");
|
|
|
|
return;
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
pa_operation_unref(o);
|
|
|
|
|
|
|
|
if (!(o = pa_context_get_sink_input_info_list(c, sink_input_cb, w))) {
|
2006-04-21 19:24:32 +00:00
|
|
|
show_error("pa_context_get_sink_input_info_list() failed");
|
|
|
|
return;
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
pa_operation_unref(o);
|
2006-08-07 13:50:02 +00:00
|
|
|
|
|
|
|
n_outstanding = 4;
|
2006-04-21 14:51:33 +00:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case PA_CONTEXT_FAILED:
|
2006-04-21 19:24:32 +00:00
|
|
|
show_error("Connection failed");
|
|
|
|
return;
|
2006-04-21 14:51:33 +00:00
|
|
|
|
|
|
|
case PA_CONTEXT_TERMINATED:
|
|
|
|
default:
|
2006-04-21 19:24:32 +00:00
|
|
|
Gtk::Main::quit();
|
|
|
|
return;
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char *argv[]) {
|
2006-04-22 18:21:12 +00:00
|
|
|
signal(SIGPIPE, SIG_IGN);
|
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
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);
|
2006-06-20 23:48:27 +00:00
|
|
|
context = pa_context_new(api, "PulseAudio Volume Control");
|
2006-04-21 14:51:33 +00:00
|
|
|
g_assert(context);
|
|
|
|
|
|
|
|
pa_context_set_state_callback(context, context_state_callback, mainWindow);
|
2006-04-21 19:24:32 +00:00
|
|
|
|
|
|
|
if (pa_context_connect(context, NULL, (pa_context_flags_t) 0, NULL) < 0) {
|
|
|
|
show_error("Connection failed");
|
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
Gtk::Main::run(*mainWindow);
|
|
|
|
delete mainWindow;
|
|
|
|
|
2006-04-21 19:24:32 +00:00
|
|
|
finish:
|
2006-04-21 14:51:33 +00:00
|
|
|
pa_context_unref(context);
|
|
|
|
pa_glib_mainloop_free(m);
|
|
|
|
}
|