Add support for the device-manager module.

Currently this module only really allows for devices to be renamed, so we add a new
dialog that can be activated by right clicking on devices (i.e. sinks/sources).
This dialog allows you to enter a new name which will be set via the extension
provided by the device-manager module.

Future work will allow you to manage (i.e. rename, delete etc) offline devices too.
This commit is contained in:
Colin Guthrie 2009-06-28 15:57:59 +01:00
parent 400c69acf4
commit 375c2c2d62
10 changed files with 203 additions and 13 deletions

View File

@ -22,9 +22,14 @@
#include <config.h> #include <config.h>
#endif #endif
#include <pulse/ext-device-manager.h>
#include "mainwindow.h"
#include "devicewidget.h" #include "devicewidget.h"
#include "channelwidget.h" #include "channelwidget.h"
#include "i18n.h"
/*** DeviceWidget ***/ /*** DeviceWidget ***/
DeviceWidget::DeviceWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) : DeviceWidget::DeviceWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
MinimalStreamWidget(cobject, x) { MinimalStreamWidget(cobject, x) {
@ -35,9 +40,15 @@ DeviceWidget::DeviceWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Gl
x->get_widget("portSelect", portSelect); x->get_widget("portSelect", portSelect);
x->get_widget("portList", portList); x->get_widget("portList", portList);
this->signal_button_press_event().connect(sigc::mem_fun(*this, &DeviceWidget::onContextTriggerEvent));
muteToggleButton->signal_clicked().connect(sigc::mem_fun(*this, &DeviceWidget::onMuteToggleButton)); muteToggleButton->signal_clicked().connect(sigc::mem_fun(*this, &DeviceWidget::onMuteToggleButton));
defaultToggleButton->signal_clicked().connect(sigc::mem_fun(*this, &DeviceWidget::onDefaultToggleButton)); defaultToggleButton->signal_clicked().connect(sigc::mem_fun(*this, &DeviceWidget::onDefaultToggleButton));
rename.set_label(_("Rename Device..."));
rename.signal_activate().connect(sigc::mem_fun(*this, &DeviceWidget::renamePopup));
contextMenu.append(rename);
contextMenu.show_all();
treeModel = Gtk::ListStore::create(portModel); treeModel = Gtk::ListStore::create(portModel);
portList->set_model(treeModel); portList->set_model(treeModel);
portList->pack_start(portModel.desc); portList->pack_start(portModel.desc);
@ -48,6 +59,11 @@ DeviceWidget::DeviceWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Gl
channelWidgets[i] = NULL; channelWidgets[i] = NULL;
} }
void DeviceWidget::init(MainWindow* mainWindow, Glib::ustring deviceType) {
mpMainWindow = mainWindow;
mDeviceType = deviceType;
}
void DeviceWidget::setChannelMap(const pa_channel_map &m, bool can_decibel) { void DeviceWidget::setChannelMap(const pa_channel_map &m, bool can_decibel) {
channelMap = m; channelMap = m;
@ -151,3 +167,57 @@ void DeviceWidget::prepareMenu() {
else else
portSelect->hide(); portSelect->hide();
} }
bool DeviceWidget::onContextTriggerEvent(GdkEventButton* event) {
if (GDK_BUTTON_PRESS == event->type && 3 == event->button) {
contextMenu.popup(event->button, event->time);
return true;
}
return false;
}
void DeviceWidget::renamePopup() {
if (updating)
return;
if (!mpMainWindow->canRenameDevices) {
Gtk::MessageDialog dialog(
*mpMainWindow,
_("Sorry, but device renaming is not supported."),
false,
Gtk::MESSAGE_WARNING,
Gtk::BUTTONS_OK,
true);
dialog.set_secondary_text(_("You need to load module-device-manager in the PulseAudio server in order to rename devices"));
dialog.run();
return;
}
Gtk::Dialog* dialog;
Gtk::Entry* renameText;
Glib::RefPtr<Gnome::Glade::Xml> x = Gnome::Glade::Xml::create(GLADE_FILE, "renameDialog");
x->get_widget("renameDialog", dialog);
x->get_widget("renameText", renameText);
renameText->set_text(description);
dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
dialog->add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
dialog->set_default_response(Gtk::RESPONSE_OK);
if (Gtk::RESPONSE_OK == dialog->run()) {
pa_operation* o;
pa_ext_device_manager_info info[1];
gchar *key = g_markup_printf_escaped("%s:%s", mDeviceType.c_str(), name.c_str());
info[0].name = key;
info[0].description = renameText->get_text().c_str();
if (!(o = pa_ext_device_manager_write(get_context(), PA_UPDATE_MERGE, info, 1, 1, NULL, NULL))) {
show_error(_("pa_ext_device_manager_write() failed"));
return;
}
pa_operation_unref(o);
g_free(key);
}
delete dialog;
}

View File

@ -25,16 +25,21 @@
#include "minimalstreamwidget.h" #include "minimalstreamwidget.h"
class MainWindow;
class ChannelWidget; class ChannelWidget;
class DeviceWidget : public MinimalStreamWidget { class DeviceWidget : public MinimalStreamWidget {
public: public:
DeviceWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x); DeviceWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
void init(MainWindow* mainWindow, Glib::ustring);
void setChannelMap(const pa_channel_map &m, bool can_decibel); void setChannelMap(const pa_channel_map &m, bool can_decibel);
void setVolume(const pa_cvolume &volume, bool force = false); void setVolume(const pa_cvolume &volume, bool force = false);
virtual void updateChannelVolume(int channel, pa_volume_t v); virtual void updateChannelVolume(int channel, pa_volume_t v);
Glib::ustring name;
Glib::ustring description;
Gtk::ToggleButton *lockToggleButton, *muteToggleButton, *defaultToggleButton; Gtk::ToggleButton *lockToggleButton, *muteToggleButton, *defaultToggleButton;
pa_channel_map channelMap; pa_channel_map channelMap;
@ -45,6 +50,7 @@ public:
virtual void onMuteToggleButton(); virtual void onMuteToggleButton();
virtual void onDefaultToggleButton(); virtual void onDefaultToggleButton();
virtual void setDefault(bool isDefault); virtual void setDefault(bool isDefault);
virtual bool onContextTriggerEvent(GdkEventButton*);
sigc::connection timeoutConnection; sigc::connection timeoutConnection;
@ -59,9 +65,17 @@ public:
void prepareMenu(); void prepareMenu();
void renamePopup();
protected: protected:
MainWindow *mpMainWindow;
virtual void onPortChange() = 0; virtual void onPortChange() = 0;
Gtk::Menu contextMenu;
Gtk::MenuItem rename;
/* Tree model columns */ /* Tree model columns */
class ModelColumns : public Gtk::TreeModel::ColumnRecord class ModelColumns : public Gtk::TreeModel::ColumnRecord
{ {
@ -79,6 +93,10 @@ protected:
Gtk::HBox *portSelect; Gtk::HBox *portSelect;
Gtk::ComboBox *portList; Gtk::ComboBox *portList;
Glib::RefPtr<Gtk::ListStore> treeModel; Glib::RefPtr<Gtk::ListStore> treeModel;
private:
Glib::ustring mDeviceType;
}; };
#endif #endif

View File

@ -72,7 +72,8 @@ MainWindow::MainWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade:
showSinkType(SINK_ALL), showSinkType(SINK_ALL),
showSourceOutputType(SOURCE_OUTPUT_CLIENT), showSourceOutputType(SOURCE_OUTPUT_CLIENT),
showSourceType(SOURCE_NO_MONITOR), showSourceType(SOURCE_NO_MONITOR),
eventRoleWidget(NULL){ eventRoleWidget(NULL),
canRenameDevices(false) {
x->get_widget("cardsVBox", cardsVBox); x->get_widget("cardsVBox", cardsVBox);
x->get_widget("streamsVBox", streamsVBox); x->get_widget("streamsVBox", streamsVBox);
@ -203,7 +204,7 @@ void MainWindow::updateSink(const pa_sink_info &info) {
if (sinkWidgets.count(info.index)) if (sinkWidgets.count(info.index))
w = sinkWidgets[info.index]; w = sinkWidgets[info.index];
else { else {
sinkWidgets[info.index] = w = SinkWidget::create(); sinkWidgets[info.index] = w = SinkWidget::create(this);
w->setChannelMap(info.channel_map, !!(info.flags & PA_SINK_DECIBEL_VOLUME)); w->setChannelMap(info.channel_map, !!(info.flags & PA_SINK_DECIBEL_VOLUME));
sinksVBox->pack_start(*w, false, false, 0); sinksVBox->pack_start(*w, false, false, 0);
w->index = info.index; w->index = info.index;
@ -363,7 +364,7 @@ void MainWindow::updateSource(const pa_source_info &info) {
if (sourceWidgets.count(info.index)) if (sourceWidgets.count(info.index))
w = sourceWidgets[info.index]; w = sourceWidgets[info.index];
else { else {
sourceWidgets[info.index] = w = SourceWidget::create(); sourceWidgets[info.index] = w = SourceWidget::create(this);
w->setChannelMap(info.channel_map, !!(info.flags & PA_SOURCE_DECIBEL_VOLUME)); w->setChannelMap(info.channel_map, !!(info.flags & PA_SOURCE_DECIBEL_VOLUME));
sourcesVBox->pack_start(*w, false, false, 0); sourcesVBox->pack_start(*w, false, false, 0);
w->index = info.index; w->index = info.index;

View File

@ -91,6 +91,8 @@ public:
Glib::ustring defaultSinkName, defaultSourceName; Glib::ustring defaultSinkName, defaultSourceName;
bool canRenameDevices;
protected: protected:
virtual void on_realize(); virtual void on_realize();
}; };

View File

@ -25,6 +25,7 @@
#include <pulse/pulseaudio.h> #include <pulse/pulseaudio.h>
#include <pulse/glib-mainloop.h> #include <pulse/glib-mainloop.h>
#include <pulse/ext-stream-restore.h> #include <pulse/ext-stream-restore.h>
#include <pulse/ext-device-manager.h>
#include <canberra-gtk.h> #include <canberra-gtk.h>
@ -239,6 +240,42 @@ static void ext_stream_restore_subscribe_cb(pa_context *c, void *userdata) {
pa_operation_unref(o); pa_operation_unref(o);
} }
void ext_device_manager_read_cb(
pa_context *,
const pa_ext_device_manager_info *,
int eol,
void *userdata) {
MainWindow *w = static_cast<MainWindow*>(userdata);
if (eol < 0) {
dec_outstanding(w);
g_debug(_("Failed to initialize device manager extension: %s"), pa_strerror(pa_context_errno(context)));
return;
}
w->canRenameDevices = true;
if (eol > 0) {
dec_outstanding(w);
return;
}
/* Do something with a widget when this part is written */
}
static void ext_device_manager_subscribe_cb(pa_context *c, void *userdata) {
MainWindow *w = static_cast<MainWindow*>(userdata);
pa_operation *o;
if (!(o = pa_ext_device_manager_read(c, ext_device_manager_read_cb, w))) {
show_error(_("pa_ext_device_manager_read() failed"));
return;
}
pa_operation_unref(o);
}
void subscribe_cb(pa_context *c, pa_subscription_event_type_t t, uint32_t index, void *userdata) { void subscribe_cb(pa_context *c, pa_subscription_event_type_t t, uint32_t index, void *userdata) {
MainWindow *w = static_cast<MainWindow*>(userdata); MainWindow *w = static_cast<MainWindow*>(userdata);
@ -416,7 +453,7 @@ void context_state_callback(pa_context *c, void *userdata) {
pa_operation_unref(o); pa_operation_unref(o);
n_outstanding++; n_outstanding++;
/* This call is not always supported */ /* These calls are not always supported */
if ((o = pa_ext_stream_restore_read(c, ext_stream_restore_read_cb, w))) { if ((o = pa_ext_stream_restore_read(c, ext_stream_restore_read_cb, w))) {
pa_operation_unref(o); pa_operation_unref(o);
n_outstanding++; n_outstanding++;
@ -429,6 +466,18 @@ void context_state_callback(pa_context *c, void *userdata) {
} else } else
g_debug(_("Failed to initialize stream_restore extension: %s"), pa_strerror(pa_context_errno(context))); g_debug(_("Failed to initialize stream_restore extension: %s"), pa_strerror(pa_context_errno(context)));
if ((o = pa_ext_device_manager_read(c, ext_device_manager_read_cb, w))) {
pa_operation_unref(o);
n_outstanding++;
pa_ext_device_manager_set_subscribe_cb(c, ext_device_manager_subscribe_cb, w);
if ((o = pa_ext_device_manager_subscribe(c, 1, NULL, NULL)))
pa_operation_unref(o);
} else
g_debug(_("Failed to initialize device manager extension: %s"), pa_strerror(pa_context_errno(context)));
break; break;
} }

View File

@ -1024,4 +1024,56 @@ Monitors</property>
</widget> </widget>
</child> </child>
</widget> </widget>
<widget class="GtkDialog" id="renameDialog">
<property name="border_width">5</property>
<property name="type_hint">normal</property>
<property name="has_separator">False</property>
<child internal-child="vbox">
<widget class="GtkVBox" id="dialog-vbox1">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="spacing">2</property>
<child>
<widget class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">&lt;b&gt;Rename device to:&lt;/b&gt;</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="renameText">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">&#x25CF;</property>
<property name="activates_default">True</property>
<property name="width_chars">60</property>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
<child internal-child="action_area">
<widget class="GtkHButtonBox" id="dialog-action_area1">
<property name="visible">True</property>
<property name="layout_style">end</property>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="pack_type">end</property>
<property name="position">0</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface> </glade-interface>

View File

@ -32,10 +32,11 @@ SinkWidget::SinkWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade:
DeviceWidget(cobject, x) { DeviceWidget(cobject, x) {
} }
SinkWidget* SinkWidget::create() { SinkWidget* SinkWidget::create(MainWindow* mainWindow) {
SinkWidget* w; SinkWidget* w;
Glib::RefPtr<Gnome::Glade::Xml> x = Gnome::Glade::Xml::create(GLADE_FILE, "deviceWidget"); Glib::RefPtr<Gnome::Glade::Xml> x = Gnome::Glade::Xml::create(GLADE_FILE, "deviceWidget");
x->get_widget_derived("deviceWidget", w); x->get_widget_derived("deviceWidget", w);
w->init(mainWindow, "sink");
return w; return w;
} }

View File

@ -28,11 +28,9 @@
class SinkWidget : public DeviceWidget { class SinkWidget : public DeviceWidget {
public: public:
SinkWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x); SinkWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
static SinkWidget* create(); static SinkWidget* create(MainWindow* mainWindow);
SinkType type; SinkType type;
Glib::ustring description;
Glib::ustring name;
uint32_t index, monitor_index, card_index; uint32_t index, monitor_index, card_index;
bool can_decibel; bool can_decibel;

View File

@ -30,10 +30,11 @@ SourceWidget::SourceWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Gl
DeviceWidget(cobject, x) { DeviceWidget(cobject, x) {
} }
SourceWidget* SourceWidget::create() { SourceWidget* SourceWidget::create(MainWindow* mainWindow) {
SourceWidget* w; SourceWidget* w;
Glib::RefPtr<Gnome::Glade::Xml> x = Gnome::Glade::Xml::create(GLADE_FILE, "deviceWidget"); Glib::RefPtr<Gnome::Glade::Xml> x = Gnome::Glade::Xml::create(GLADE_FILE, "deviceWidget");
x->get_widget_derived("deviceWidget", w); x->get_widget_derived("deviceWidget", w);
w->init(mainWindow, "source");
return w; return w;
} }
@ -99,4 +100,4 @@ void SourceWidget::onPortChange() {
pa_operation_unref(o); pa_operation_unref(o);
} }
} }
} }

View File

@ -28,11 +28,9 @@
class SourceWidget : public DeviceWidget { class SourceWidget : public DeviceWidget {
public: public:
SourceWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x); SourceWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
static SourceWidget* create(); static SourceWidget* create(MainWindow* mainWindow);
SourceType type; SourceType type;
Glib::ustring name;
Glib::ustring description;
uint32_t index, card_index; uint32_t index, card_index;
bool can_decibel; bool can_decibel;