Merge patch from sjoerd that adds a new panel for listing recording streams, and makes the menu more visible by adding a button for it

git-svn-id: file:///home/lennart/svn/public/pavucontrol/trunk@71 c17c95f2-f111-0410-90bf-f30a9569010c
This commit is contained in:
Lennart Poettering 2008-04-20 22:13:20 +00:00
parent 3d45d8bf53
commit d8b6b58fe3
2 changed files with 574 additions and 66 deletions

View File

@ -3,6 +3,9 @@
/*** /***
This file is part of pavucontrol. This file is part of pavucontrol.
Copyright 2006-2008 Lennart Poettering <mzcnihpbageby (at) 0pointer (dot) de>
Copyright 2008 Sjoerd Simons <sjoerd@luon.net>
pavucontrol is free software; you can redistribute it and/or modify pavucontrol is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published it under the terms of the GNU General Public License as published
by the Free Software Foundation; either version 2 of the License, by the Free Software Foundation; either version 2 of the License,
@ -50,6 +53,12 @@ enum SinkType {
SINK_VIRTUAL, SINK_VIRTUAL,
}; };
enum SourceOutputType {
SOURCE_OUTPUT_ALL,
SOURCE_OUTPUT_CLIENT,
SOURCE_OUTPUT_VIRTUAL
};
enum SourceType{ enum SourceType{
SOURCE_ALL, SOURCE_ALL,
SOURCE_NO_MONITOR, SOURCE_NO_MONITOR,
@ -82,7 +91,27 @@ public:
virtual void set_sensitive(bool enabled); virtual void set_sensitive(bool enabled);
}; };
class StreamWidget : public Gtk::VBox { class MinimalStreamWidget : public Gtk::VBox {
public:
MinimalStreamWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
Gtk::Label *nameLabel, *boldNameLabel;
Gtk::ToggleButton *streamToggleButton;
Gtk::Menu menu;
bool updating;
void onStreamToggleButton();
void onMenuDeactivated();
void popupMenuPosition(int& x, int& y, bool& push_in);
virtual void prepareMenu(void);
protected:
virtual bool on_button_press_event(GdkEventButton* event);
};
class StreamWidget : public MinimalStreamWidget {
public: public:
StreamWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x); StreamWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
@ -90,12 +119,9 @@ public:
void setVolume(const pa_cvolume &volume, bool force); void setVolume(const pa_cvolume &volume, bool force);
virtual void updateChannelVolume(int channel, pa_volume_t v); virtual void updateChannelVolume(int channel, pa_volume_t v);
Gtk::Label *nameLabel, *boldNameLabel;
Gtk::VBox *channelsVBox; Gtk::VBox *channelsVBox;
Gtk::ToggleButton *lockToggleButton, *muteToggleButton; Gtk::ToggleButton *lockToggleButton, *muteToggleButton;
bool updating;
pa_channel_map channelMap; pa_channel_map channelMap;
pa_cvolume volume; pa_cvolume volume;
@ -120,15 +146,11 @@ public:
Glib::ustring name; Glib::ustring name;
uint32_t index; uint32_t index;
Gtk::Menu menu;
Gtk::CheckMenuItem defaultMenuItem; Gtk::CheckMenuItem defaultMenuItem;
virtual void onMuteToggleButton(); virtual void onMuteToggleButton();
virtual void executeVolumeUpdate(); virtual void executeVolumeUpdate();
virtual void onDefaultToggle(); virtual void onDefaultToggle();
protected:
virtual bool on_button_press_event(GdkEventButton* event);
}; };
class SourceWidget : public StreamWidget { class SourceWidget : public StreamWidget {
@ -138,17 +160,14 @@ public:
SourceType type; SourceType type;
Glib::ustring name; Glib::ustring name;
Glib::ustring description;
uint32_t index; uint32_t index;
Gtk::Menu menu;
Gtk::CheckMenuItem defaultMenuItem; Gtk::CheckMenuItem defaultMenuItem;
virtual void onMuteToggleButton(); virtual void onMuteToggleButton();
virtual void executeVolumeUpdate(); virtual void executeVolumeUpdate();
virtual void onDefaultToggle(); virtual void onDefaultToggle();
protected:
virtual bool on_button_press_event(GdkEventButton* event);
}; };
class SinkInputWidget : public StreamWidget { class SinkInputWidget : public StreamWidget {
@ -163,9 +182,10 @@ public:
virtual void executeVolumeUpdate(); virtual void executeVolumeUpdate();
virtual void onMuteToggleButton(); virtual void onMuteToggleButton();
virtual void onKill(); virtual void onKill();
virtual void prepareMenu();
MainWindow *mainWindow; MainWindow *mainWindow;
Gtk::Menu menu, submenu; Gtk::Menu submenu;
Gtk::MenuItem titleMenuItem, killMenuItem; Gtk::MenuItem titleMenuItem, killMenuItem;
struct SinkMenuItem { struct SinkMenuItem {
@ -188,9 +208,44 @@ public:
void clearMenu(); void clearMenu();
void buildMenu(); void buildMenu();
};
protected: class SourceOutputWidget : public MinimalStreamWidget {
virtual bool on_button_press_event(GdkEventButton* event); 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 MainWindow : public Gtk::Window { class MainWindow : public Gtk::Window {
@ -202,29 +257,34 @@ public:
void updateSink(const pa_sink_info &info); void updateSink(const pa_sink_info &info);
void updateSource(const pa_source_info &info); void updateSource(const pa_source_info &info);
void updateSinkInput(const pa_sink_input_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 updateClient(const pa_client_info &info);
void updateServer(const pa_server_info &info); void updateServer(const pa_server_info &info);
void removeSink(uint32_t index); void removeSink(uint32_t index);
void removeSource(uint32_t index); void removeSource(uint32_t index);
void removeSinkInput(uint32_t index); void removeSinkInput(uint32_t index);
void removeSourceOutput(uint32_t index);
void removeClient(uint32_t index); void removeClient(uint32_t index);
Gtk::VBox *streamsVBox, *sinksVBox, *sourcesVBox; Gtk::VBox *streamsVBox, *recsVBox, *sinksVBox, *sourcesVBox;
Gtk::EventBox *titleEventBox; Gtk::EventBox *titleEventBox;
Gtk::Label *noStreamsLabel, *noSinksLabel, *noSourcesLabel; Gtk::Label *noStreamsLabel, *noRecsLabel, *noSinksLabel, *noSourcesLabel;
Gtk::ComboBox *sinkInputTypeComboBox, *sinkTypeComboBox, *sourceTypeComboBox; Gtk::ComboBox *sinkInputTypeComboBox, *sourceOutputTypeComboBox, *sinkTypeComboBox, *sourceTypeComboBox;
std::map<uint32_t, SinkWidget*> sinkWidgets; std::map<uint32_t, SinkWidget*> sinkWidgets;
std::map<uint32_t, SourceWidget*> sourceWidgets; std::map<uint32_t, SourceWidget*> sourceWidgets;
std::map<uint32_t, SinkInputWidget*> sinkInputWidgets; std::map<uint32_t, SinkInputWidget*> sinkInputWidgets;
std::map<uint32_t, SourceOutputWidget*> sourceOutputWidgets;
std::map<uint32_t, char*> clientNames; std::map<uint32_t, char*> clientNames;
SinkInputType showSinkInputType; SinkInputType showSinkInputType;
SinkType showSinkType; SinkType showSinkType;
SourceOutputType showSourceOutputType;
SourceType showSourceType; SourceType showSourceType;
virtual void onSinkInputTypeComboBoxChanged(); virtual void onSinkInputTypeComboBoxChanged();
virtual void onSourceOutputTypeComboBoxChanged();
virtual void onSinkTypeComboBoxChanged(); virtual void onSinkTypeComboBoxChanged();
virtual void onSourceTypeComboBoxChanged(); virtual void onSourceTypeComboBoxChanged();
@ -302,15 +362,70 @@ void ChannelWidget::set_sensitive(bool enabled) {
volumeScale->set_sensitive(enabled); volumeScale->set_sensitive(enabled);
} }
/*** StreamWidget ***/ /*** MinimalStreamWidget ***/
MinimalStreamWidget::MinimalStreamWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
StreamWidget::StreamWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
Gtk::VBox(cobject), Gtk::VBox(cobject),
updating(false) { updating(false) {
x->get_widget("channelsVBox", channelsVBox);
x->get_widget("nameLabel", nameLabel); x->get_widget("nameLabel", nameLabel);
x->get_widget("boldNameLabel", boldNameLabel); x->get_widget("boldNameLabel", boldNameLabel);
x->get_widget("streamToggle", streamToggleButton);
streamToggleButton->set_active(false);
streamToggleButton->signal_clicked().connect(sigc::mem_fun(*this, &MinimalStreamWidget::onStreamToggleButton));
menu.signal_deactivate().connect(sigc::mem_fun(*this, &MinimalStreamWidget::onMenuDeactivated));
}
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) {
int menu_width, menu_height;
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;
}
/*** StreamWidget ***/
StreamWidget::StreamWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
MinimalStreamWidget(cobject, x) {
x->get_widget("channelsVBox", channelsVBox);
x->get_widget("lockToggleButton", lockToggleButton); x->get_widget("lockToggleButton", lockToggleButton);
x->get_widget("muteToggleButton", muteToggleButton); x->get_widget("muteToggleButton", muteToggleButton);
@ -425,19 +540,6 @@ void SinkWidget::onMuteToggleButton() {
pa_operation_unref(o); pa_operation_unref(o);
} }
bool SinkWidget::on_button_press_event(GdkEventButton* event) {
if (StreamWidget::on_button_press_event(event))
return TRUE;
if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
menu.popup(0, event->time);
return TRUE;
}
return FALSE;
}
void SinkWidget::onDefaultToggle() { void SinkWidget::onDefaultToggle() {
pa_operation* o; pa_operation* o;
@ -496,19 +598,6 @@ void SourceWidget::onMuteToggleButton() {
pa_operation_unref(o); pa_operation_unref(o);
} }
bool SourceWidget::on_button_press_event(GdkEventButton* event) {
if (StreamWidget::on_button_press_event(event))
return TRUE;
if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
menu.popup(0, event->time);
return TRUE;
}
return FALSE;
}
void SourceWidget::onDefaultToggle() { void SourceWidget::onDefaultToggle() {
pa_operation* o; pa_operation* o;
@ -574,18 +663,9 @@ void SinkInputWidget::onMuteToggleButton() {
pa_operation_unref(o); pa_operation_unref(o);
} }
bool SinkInputWidget::on_button_press_event(GdkEventButton* event) { void SinkInputWidget::prepareMenu() {
if (StreamWidget::on_button_press_event(event)) clearMenu();
return TRUE; buildMenu();
if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
clearMenu();
buildMenu();
menu.popup(0, event->time);
return TRUE;
}
return FALSE;
} }
void SinkInputWidget::clearMenu() { void SinkInputWidget::clearMenu() {
@ -634,6 +714,83 @@ void SinkInputWidget::SinkMenuItem::onToggle() {
pa_operation_unref(o); 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, "minimalStreamWidget");
x->get_widget_derived("minimalStreamWidget", 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);
}
/*** MainWindow ***/ /*** MainWindow ***/
MainWindow::MainWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) : MainWindow::MainWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
@ -643,13 +800,16 @@ MainWindow::MainWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade:
showSourceType(SOURCE_NO_MONITOR) { showSourceType(SOURCE_NO_MONITOR) {
x->get_widget("streamsVBox", streamsVBox); x->get_widget("streamsVBox", streamsVBox);
x->get_widget("recsVBox", recsVBox);
x->get_widget("sinksVBox", sinksVBox); x->get_widget("sinksVBox", sinksVBox);
x->get_widget("sourcesVBox", sourcesVBox); x->get_widget("sourcesVBox", sourcesVBox);
x->get_widget("titleEventBox", titleEventBox); x->get_widget("titleEventBox", titleEventBox);
x->get_widget("noStreamsLabel", noStreamsLabel); x->get_widget("noStreamsLabel", noStreamsLabel);
x->get_widget("noRecsLabel", noRecsLabel);
x->get_widget("noSinksLabel", noSinksLabel); x->get_widget("noSinksLabel", noSinksLabel);
x->get_widget("noSourcesLabel", noSourcesLabel); x->get_widget("noSourcesLabel", noSourcesLabel);
x->get_widget("sinkInputTypeComboBox", sinkInputTypeComboBox); x->get_widget("sinkInputTypeComboBox", sinkInputTypeComboBox);
x->get_widget("sourceOutputTypeComboBox", sourceOutputTypeComboBox);
x->get_widget("sinkTypeComboBox", sinkTypeComboBox); x->get_widget("sinkTypeComboBox", sinkTypeComboBox);
x->get_widget("sourceTypeComboBox", sourceTypeComboBox); x->get_widget("sourceTypeComboBox", sourceTypeComboBox);
@ -658,10 +818,12 @@ MainWindow::MainWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade:
sinksVBox->set_reallocate_redraws(true); sinksVBox->set_reallocate_redraws(true);
sinkInputTypeComboBox->set_active((int) showSinkInputType); sinkInputTypeComboBox->set_active((int) showSinkInputType);
sourceOutputTypeComboBox->set_active((int) showSourceOutputType);
sinkTypeComboBox->set_active((int) showSinkType); sinkTypeComboBox->set_active((int) showSinkType);
sourceTypeComboBox->set_active((int) showSourceType); sourceTypeComboBox->set_active((int) showSourceType);
sinkInputTypeComboBox->signal_changed().connect(sigc::mem_fun(*this, &MainWindow::onSinkInputTypeComboBoxChanged)); 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)); sinkTypeComboBox->signal_changed().connect(sigc::mem_fun(*this, &MainWindow::onSinkTypeComboBoxChanged));
sourceTypeComboBox->signal_changed().connect(sigc::mem_fun(*this, &MainWindow::onSourceTypeComboBoxChanged)); sourceTypeComboBox->signal_changed().connect(sigc::mem_fun(*this, &MainWindow::onSourceTypeComboBoxChanged));
@ -743,6 +905,7 @@ void MainWindow::updateSource(const pa_source_info &info) {
w->updating = true; w->updating = true;
w->name = info.name; 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->type = info.monitor_of_sink != PA_INVALID_INDEX ? SOURCE_MONITOR : (info.flags & PA_SOURCE_HARDWARE ? SOURCE_HARDWARE : SOURCE_VIRTUAL);
w->boldNameLabel->set_text(""); w->boldNameLabel->set_text("");
@ -803,6 +966,45 @@ void MainWindow::updateSinkInput(const pa_sink_input_info &info) {
w->updating = false; w->updating = false;
} }
void MainWindow::updateSourceOutput(const pa_source_output_info &info) {
SourceOutputWidget *w;
bool is_new = false;
if (sourceOutputWidgets.count(info.index))
w = sourceOutputWidgets[info.index];
else {
sourceOutputWidgets[info.index] = w = SourceOutputWidget::create();
//w->setChannelMap(info.channel_map);
recsVBox->pack_start(*w, false, false, 0);
w->index = info.index;
w->clientIndex = info.client;
w->mainWindow = this;
is_new = true;
}
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);
}
if (is_new)
updateDeviceVisibility();
w->updating = false;
}
void MainWindow::updateClient(const pa_client_info &info) { void MainWindow::updateClient(const pa_client_info &info) {
g_free(clientNames[info.index]); g_free(clientNames[info.index]);
@ -851,8 +1053,8 @@ void MainWindow::updateServer(const pa_server_info &info) {
} }
void MainWindow::updateDeviceVisibility() { void MainWindow::updateDeviceVisibility() {
streamsVBox->hide_all(); streamsVBox->hide_all();
recsVBox->hide_all();
sourcesVBox->hide_all(); sourcesVBox->hide_all();
sinksVBox->hide_all(); sinksVBox->hide_all();
@ -870,6 +1072,18 @@ void MainWindow::updateDeviceVisibility() {
if (is_empty) if (is_empty)
noStreamsLabel->show(); noStreamsLabel->show();
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_all();
is_empty = false;
}
}
if (is_empty)
noRecsLabel->show();
is_empty = true; is_empty = true;
for (std::map<uint32_t, SinkWidget*>::iterator i = sinkWidgets.begin(); i != sinkWidgets.end(); ++i) { for (std::map<uint32_t, SinkWidget*>::iterator i = sinkWidgets.begin(); i != sinkWidgets.end(); ++i) {
@ -901,6 +1115,7 @@ void MainWindow::updateDeviceVisibility() {
noSourcesLabel->show(); noSourcesLabel->show();
sourcesVBox->show(); sourcesVBox->show();
recsVBox->show();
sinksVBox->show(); sinksVBox->show();
streamsVBox->show(); streamsVBox->show();
} }
@ -932,6 +1147,15 @@ void MainWindow::removeSinkInput(uint32_t index) {
updateDeviceVisibility(); 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) { void MainWindow::removeClient(uint32_t index) {
g_free(clientNames[index]); g_free(clientNames[index]);
clientNames.erase(index); clientNames.erase(index);
@ -964,6 +1188,15 @@ void MainWindow::onSinkInputTypeComboBoxChanged() {
updateDeviceVisibility(); 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) { static void dec_outstanding(MainWindow *w) {
if (n_outstanding <= 0) if (n_outstanding <= 0)
return; return;
@ -1020,6 +1253,22 @@ void sink_input_cb(pa_context *, const pa_sink_input_info *i, int eol, void *use
w->updateSinkInput(*i); w->updateSinkInput(*i);
} }
void source_output_cb(pa_context *, const pa_source_output_info *i, int eol, void *userdata) {
MainWindow *w = static_cast<MainWindow*>(userdata);
if (eol) {
dec_outstanding(w);
return;
}
if (!i) {
show_error("Source output callback failure");
return;
}
w->updateSourceOutput(*i);
}
void client_cb(pa_context *, const pa_client_info *i, int eol, void *userdata) { void client_cb(pa_context *, const pa_client_info *i, int eol, void *userdata) {
MainWindow *w = static_cast<MainWindow*>(userdata); MainWindow *w = static_cast<MainWindow*>(userdata);
@ -1091,6 +1340,19 @@ void subscribe_cb(pa_context *c, pa_subscription_event_type_t t, uint32_t index,
} }
break; break;
case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT:
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
w->removeSourceOutput(index);
else {
pa_operation *o;
if (!(o = pa_context_get_source_output_info(c, index, source_output_cb, w))) {
show_error("pa_context_get_sink_input_info() failed");
return;
}
pa_operation_unref(o);
}
break;
case PA_SUBSCRIPTION_EVENT_CLIENT: case PA_SUBSCRIPTION_EVENT_CLIENT:
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
w->removeClient(index); w->removeClient(index);
@ -1136,6 +1398,7 @@ void context_state_callback(pa_context *c, void *userdata) {
(PA_SUBSCRIPTION_MASK_SINK| (PA_SUBSCRIPTION_MASK_SINK|
PA_SUBSCRIPTION_MASK_SOURCE| PA_SUBSCRIPTION_MASK_SOURCE|
PA_SUBSCRIPTION_MASK_SINK_INPUT| PA_SUBSCRIPTION_MASK_SINK_INPUT|
PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT|
PA_SUBSCRIPTION_MASK_CLIENT| PA_SUBSCRIPTION_MASK_CLIENT|
PA_SUBSCRIPTION_MASK_SERVER), NULL, NULL))) { PA_SUBSCRIPTION_MASK_SERVER), NULL, NULL))) {
show_error("pa_context_subscribe() failed"); show_error("pa_context_subscribe() failed");
@ -1173,7 +1436,13 @@ void context_state_callback(pa_context *c, void *userdata) {
} }
pa_operation_unref(o); pa_operation_unref(o);
n_outstanding = 5; if (!(o = pa_context_get_source_output_info_list(c, source_output_cb, w))) {
show_error("pa_context_get_source_output_info_list() failed");
return;
}
pa_operation_unref(o);
n_outstanding = 6;
break; break;
} }

View File

@ -212,6 +212,135 @@ Virtual Streams</property>
<property name="tab_fill">False</property> <property name="tab_fill">False</property>
</packing> </packing>
</child> </child>
<child>
<widget class="GtkVBox" id="vbox2">
<property name="visible">True</property>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="border_width">12</property>
<property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<child>
<widget class="GtkViewport" id="viewport5">
<property name="visible">True</property>
<property name="shadow_type">GTK_SHADOW_NONE</property>
<child>
<widget class="GtkVBox" id="recsVBox">
<property name="visible">True</property>
<child>
<widget class="GtkLabel" id="noRecsLabel">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="xalign">0</property>
<property name="xpad">16</property>
<property name="ypad">16</property>
<property name="label" translatable="yes">&lt;i&gt;No Streams Available&lt;/i&gt;</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkAlignment" id="alignment9">
<property name="visible">True</property>
<property name="bottom_padding">12</property>
<property name="left_padding">12</property>
<property name="right_padding">12</property>
<child>
<widget class="GtkHBox" id="hbox7">
<property name="visible">True</property>
<property name="spacing">6</property>
<child>
<widget class="GtkImage" id="image3">
<property name="visible">True</property>
<property name="xalign">1</property>
<property name="stock">gtk-dialog-info</property>
</widget>
<packing>
<property name="expand">False</property>
</packing>
</child>
<child>
<widget class="GtkAlignment" id="alignment10">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="right_padding">12</property>
<child>
<widget class="GtkLabel" id="label7">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">&lt;b&gt;Hint:&lt;/b&gt; &lt;i&gt;Right click on a recording stream to move it to another input device.&lt;/i&gt;</property>
<property name="use_markup">True</property>
</widget>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label8">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="xalign">1</property>
<property name="label" translatable="yes">&lt;b&gt;_Show:&lt;/b&gt;</property>
<property name="use_markup">True</property>
<property name="use_underline">True</property>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
<child>
<widget class="GtkComboBox" id="sourceOutputTypeComboBox">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="items" translatable="yes" comments="Applications&#10;All streams">All Streams
Applications
Virtual Streams</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">3</property>
</packing>
</child>
</widget>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="label" translatable="yes">_Recording</property>
<property name="use_underline">True</property>
</widget>
<packing>
<property name="type">tab</property>
<property name="position">1</property>
<property name="tab_fill">False</property>
</packing>
</child>
<child> <child>
<widget class="GtkVBox" id="vbox30"> <widget class="GtkVBox" id="vbox30">
<property name="visible">True</property> <property name="visible">True</property>
@ -324,7 +453,7 @@ Virtual Output Devices</property>
</child> </child>
</widget> </widget>
<packing> <packing>
<property name="position">1</property> <property name="position">2</property>
</packing> </packing>
</child> </child>
<child> <child>
@ -335,7 +464,7 @@ Virtual Output Devices</property>
</widget> </widget>
<packing> <packing>
<property name="type">tab</property> <property name="type">tab</property>
<property name="position">1</property> <property name="position">2</property>
<property name="tab_fill">False</property> <property name="tab_fill">False</property>
</packing> </packing>
</child> </child>
@ -453,7 +582,7 @@ Monitors</property>
</child> </child>
</widget> </widget>
<packing> <packing>
<property name="position">2</property> <property name="position">3</property>
</packing> </packing>
</child> </child>
<child> <child>
@ -464,7 +593,7 @@ Monitors</property>
</widget> </widget>
<packing> <packing>
<property name="type">tab</property> <property name="type">tab</property>
<property name="position">2</property> <property name="position">3</property>
<property name="tab_fill">False</property> <property name="tab_fill">False</property>
</packing> </packing>
</child> </child>
@ -518,6 +647,27 @@ Monitors</property>
<property name="position">1</property> <property name="position">1</property>
</packing> </packing>
</child> </child>
<child>
<widget class="GtkToggleButton" id="streamToggle">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="response_id">0</property>
<child>
<widget class="GtkArrow" id="arrow3">
<property name="visible">True</property>
<property name="arrow_type">GTK_ARROW_DOWN</property>
</widget>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="padding">2</property>
<property name="pack_type">GTK_PACK_END</property>
<property name="position">2</property>
</packing>
</child>
</widget> </widget>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
@ -673,4 +823,93 @@ Monitors</property>
</widget> </widget>
</child> </child>
</widget> </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>
<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="fill">False</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="wrap_mode">PANGO_WRAP_CHAR</property>
<property name="selectable">True</property>
<property name="ellipsize">PANGO_ELLIPSIZE_MIDDLE</property>
</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">True</property>
<property name="response_id">0</property>
<child>
<widget class="GtkArrow" id="arrow1">
<property name="visible">True</property>
<property name="arrow_type">GTK_ARROW_DOWN</property>
</widget>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="padding">2</property>
<property name="pack_type">GTK_PACK_END</property>
<property name="position">2</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</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>
</glade-interface> </glade-interface>