Use Gtk4 instead of Gtk3

This commit is contained in:
JA 2023-11-30 16:44:52 +01:00 committed by Arun Raghavan
parent 04655fae70
commit 52aef7e81d
28 changed files with 1046 additions and 1665 deletions

View File

@ -1,16 +1,16 @@
project('pavucontrol', 'cpp', project('pavucontrol', 'cpp',
version : '5.0', version : '5.0',
meson_version : '>= 0.50.0', meson_version : '>= 0.50.0',
default_options : [ 'c_std=gnu11', 'cpp_std=c++11' ] default_options : [ 'c_std=gnu11', 'cpp_std=c++17' ]
) )
with_lynx = get_option('lynx') with_lynx = get_option('lynx')
cpp = meson.get_compiler('cpp') cpp = meson.get_compiler('cpp')
gtkmm_dep = dependency('gtkmm-3.0', version : '>= 3.0', required : true) gtkmm_dep = dependency('gtkmm-4.0', version : '>= 4.0', required : true)
sigcpp_dep = dependency('sigc++-2.0', required : true) sigcpp_dep = dependency('sigc++-2.0', required : true)
canberragtk_dep = dependency('libcanberra-gtk3', version : '>= 0.16', required : true) canberragtk_dep = dependency('libcanberra', version : '>= 0.16', required : true)
libpulse_dep = dependency('libpulse', version : '>= 5.0', required : true) libpulse_dep = dependency('libpulse', version : '>= 5.0', required : true)
libpulsemlglib_dep = dependency('libpulse-mainloop-glib', version : '>= 0.9.16', required : true) libpulsemlglib_dep = dependency('libpulse-mainloop-glib', version : '>= 0.9.16', required : true)

View File

@ -28,14 +28,14 @@
/*** CardWidget ***/ /*** CardWidget ***/
CardWidget::CardWidget(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& x) : CardWidget::CardWidget(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& x) :
Gtk::VBox(cobject) { Gtk::Box(cobject) {
x->get_widget("cardNameLabel", nameLabel); nameLabel = x->get_widget<Gtk::Label>("cardNameLabel");
x->get_widget("profileList", profileList); profileList = x->get_widget<Gtk::ComboBox>("profileList");
x->get_widget("cardIconImage", iconImage); iconImage = x->get_widget<Gtk::Image>("cardIconImage");
x->get_widget("codecBox", codecBox); codecBox = x->get_widget<Gtk::Box>("codecBox");
x->get_widget("codecList", codecList); codecList = x->get_widget<Gtk::ComboBox>("codecList");
x->get_widget("profileLockToggleButton", profileLockToggleButton); profileLockToggleButton = x->get_widget<Gtk::ToggleButton>("profileLockToggleButton");
profileListStore = Gtk::ListStore::create(profileModel); profileListStore = Gtk::ListStore::create(profileModel);
profileList->set_model(profileListStore); profileList->set_model(profileListStore);
@ -61,7 +61,7 @@ CardWidget::CardWidget(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>
CardWidget* CardWidget::create() { CardWidget* CardWidget::create() {
CardWidget* w; CardWidget* w;
Glib::RefPtr<Gtk::Builder> x = Gtk::Builder::create_from_file(GLADE_FILE, "cardWidget"); Glib::RefPtr<Gtk::Builder> x = Gtk::Builder::create_from_file(GLADE_FILE, "cardWidget");
x->get_widget_derived("cardWidget", w); w = Gtk::Builder::get_widget_derived<CardWidget>(x, "cardWidget");
w->reference(); w->reference();
return w; return w;
} }
@ -123,7 +123,7 @@ void CardWidget::onProfileChange() {
Glib::ustring profile = row[profileModel.name]; Glib::ustring profile = row[profileModel.name];
if (!(o = pa_context_set_card_profile_by_index(get_context(), index, profile.c_str(), NULL, NULL))) { if (!(o = pa_context_set_card_profile_by_index(get_context(), index, profile.c_str(), NULL, NULL))) {
show_error(_("pa_context_set_card_profile_by_index() failed")); show_error(this, _("pa_context_set_card_profile_by_index() failed"));
return; return;
} }

View File

@ -34,13 +34,12 @@ public:
std::vector<Glib::ustring> profiles; std::vector<Glib::ustring> profiles;
}; };
class CardWidget : public Gtk::VBox { class CardWidget : public Gtk::Box {
public: public:
CardWidget(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& x); CardWidget(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& x);
static CardWidget* create(); static CardWidget* create();
Gtk::Label *nameLabel; Gtk::Label *nameLabel;
Gtk::Menu menu;
Gtk::Image *iconImage; Gtk::Image *iconImage;
Glib::ustring name; Glib::ustring name;
std::string pulse_card_name; std::string pulse_card_name;

View File

@ -30,14 +30,14 @@
/*** ChannelWidget ***/ /*** ChannelWidget ***/
ChannelWidget::ChannelWidget(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& x) : ChannelWidget::ChannelWidget(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& x) :
Gtk::EventBox(cobject), Gtk::Widget(cobject),
can_decibel(false), can_decibel(false),
volumeScaleEnabled(true), volumeScaleEnabled(true),
last(false) { last(false) {
x->get_widget("channelLabel", channelLabel); channelLabel = x->get_widget<Gtk::Label>("channelLabel");
x->get_widget("volumeLabel", volumeLabel); volumeLabel = x->get_widget<Gtk::Label>("volumeLabel");
x->get_widget("volumeScale", volumeScale); volumeScale = x->get_widget<Gtk::Scale>("volumeScale");
volumeScale->set_range((double)PA_VOLUME_MUTED, (double)PA_VOLUME_UI_MAX); volumeScale->set_range((double)PA_VOLUME_MUTED, (double)PA_VOLUME_UI_MAX);
volumeScale->set_value((double)PA_VOLUME_NORM); volumeScale->set_value((double)PA_VOLUME_NORM);
@ -52,7 +52,7 @@ ChannelWidget* ChannelWidget::createOne(MinimalStreamWidget *owner, int channelI
Glib::RefPtr<Gtk::Builder> x = Gtk::Builder::create(); Glib::RefPtr<Gtk::Builder> x = Gtk::Builder::create();
x->add_from_file(GLADE_FILE, "adjustment1"); x->add_from_file(GLADE_FILE, "adjustment1");
x->add_from_file(GLADE_FILE, "channelWidget"); x->add_from_file(GLADE_FILE, "channelWidget");
x->get_widget_derived("channelWidget", w); w = Gtk::Builder::get_widget_derived<ChannelWidget>(x, "channelWidget");
w->reference(); w->reference();
w->channel = channelIndex; w->channel = channelIndex;
@ -75,8 +75,8 @@ void ChannelWidget::create(MinimalStreamWidget *owner, const pa_channel_map &m,
Gtk::Requisition minimumSize; Gtk::Requisition minimumSize;
Gtk::Requisition naturalSize; Gtk::Requisition naturalSize;
widgets[i]->channelLabel->get_preferred_size(minimumSize, naturalSize); widgets[i]->channelLabel->get_preferred_size(minimumSize, naturalSize);
if (naturalSize.width > maxLabelWidth) if (naturalSize.get_width() > maxLabelWidth)
maxLabelWidth = naturalSize.width; maxLabelWidth = naturalSize.get_width();
} }
widgets[m.channels - 1]->last = true; widgets[m.channels - 1]->last = true;
@ -108,6 +108,7 @@ void ChannelWidget::setVolume(pa_volume_t volume) {
volumeScaleEnabled = false; volumeScaleEnabled = false;
volumeScale->set_value(volume > PA_VOLUME_UI_MAX ? PA_VOLUME_UI_MAX : volume); volumeScale->set_value(volume > PA_VOLUME_UI_MAX ? PA_VOLUME_UI_MAX : volume);
currentVolume = volumeScale->get_value();
volumeScaleEnabled = true; volumeScaleEnabled = true;
} }
@ -120,11 +121,12 @@ void ChannelWidget::onVolumeScaleValueChanged() {
return; return;
pa_volume_t volume = (pa_volume_t) volumeScale->get_value(); pa_volume_t volume = (pa_volume_t) volumeScale->get_value();
if (volume != currentVolume)
minimalStreamWidget->updateChannelVolume(channel, volume); minimalStreamWidget->updateChannelVolume(channel, volume);
} }
void ChannelWidget::set_sensitive(bool enabled) { void ChannelWidget::set_sensitive(bool enabled) {
Gtk::EventBox::set_sensitive(enabled); Gtk::Widget::set_sensitive(enabled);
channelLabel->set_sensitive(enabled); channelLabel->set_sensitive(enabled);
volumeLabel->set_sensitive(enabled); volumeLabel->set_sensitive(enabled);

View File

@ -25,7 +25,7 @@
class MinimalStreamWidget; class MinimalStreamWidget;
class ChannelWidget : public Gtk::EventBox { class ChannelWidget : public Gtk::Widget {
public: public:
ChannelWidget(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& x); ChannelWidget(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& x);
@ -53,6 +53,7 @@ public:
virtual void setBaseVolume(pa_volume_t); virtual void setBaseVolume(pa_volume_t);
private: private:
pa_volume_t currentVolume;
static ChannelWidget *createOne(MinimalStreamWidget *owner, int channelIndex, pa_channel_position channelPosition, static ChannelWidget *createOne(MinimalStreamWidget *owner, int channelIndex, pa_channel_position channelPosition,
bool can_decibel); bool can_decibel);
}; };

View File

@ -37,29 +37,45 @@ DeviceWidget::DeviceWidget(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Buil
mDigital(false) { mDigital(false) {
/* MinimalStreamWidget member variables. */ /* MinimalStreamWidget member variables. */
x->get_widget("deviceChannelsVBox", channelsVBox); channelsVBox = x->get_widget<Gtk::Box>("deviceChannelsVBox");
x->get_widget("deviceNameLabel", nameLabel); nameLabel = x->get_widget<Gtk::Label>("deviceNameLabel");
x->get_widget("deviceBoldNameLabel", boldNameLabel); boldNameLabel = x->get_widget<Gtk::Label>("deviceBoldNameLabel");
x->get_widget("deviceIconImage", iconImage); iconImage= x->get_widget<Gtk::Image>("deviceIconImage");
x->get_widget("deviceLockToggleButton", lockToggleButton); lockToggleButton = x->get_widget<Gtk::ToggleButton>("deviceLockToggleButton");
x->get_widget("deviceMuteToggleButton", muteToggleButton); muteToggleButton = x->get_widget<Gtk::ToggleButton>("deviceMuteToggleButton");
x->get_widget("defaultToggleButton", defaultToggleButton); defaultToggleButton= x->get_widget<Gtk::ToggleButton>("defaultToggleButton");
x->get_widget("portSelect", portSelect); portSelect = x->get_widget<Gtk::Box>("portSelect");
x->get_widget("portList", portList); portList = x->get_widget<Gtk::ComboBox>("portList");
x->get_widget("advancedOptions", advancedOptions); advancedOptions = x->get_widget<Gtk::Expander>("advancedOptions");
x->get_widget("offsetSelect", offsetSelect); offsetSelect = x->get_widget<Gtk::Box>("offsetSelect");
x->get_widget("offsetButton", offsetButton); offsetButton = x->get_widget<Gtk::SpinButton>("offsetButton");
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));
lockToggleButton->signal_clicked().connect(sigc::mem_fun(*this, &DeviceWidget::onLockToggleButton)); lockToggleButton->signal_clicked().connect(sigc::mem_fun(*this, &DeviceWidget::onLockToggleButton));
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...")); auto gesture = Gtk::GestureClick::create();
rename.signal_activate().connect(sigc::mem_fun(*this, &DeviceWidget::renamePopup)); gesture->set_button(3);
contextMenu.append(rename); gesture->set_exclusive(true);
contextMenu.show_all(); gesture->signal_pressed().connect(sigc::mem_fun(*this, &DeviceWidget::onContextTriggerEvent));
this->add_controller(gesture);
const std::string actionName = "rename", groupName="devicewidget";
auto action = Gio::SimpleAction::create(actionName);
action->set_enabled(true);
action->signal_activate().connect(sigc::mem_fun(*this, &DeviceWidget::openRenamePopup));
auto group = Gio::SimpleActionGroup::create();
group->add_action(action);
insert_action_group(groupName, group);
auto menuModel = Gio::Menu::create();
menuModel->append(_("Rename Device..."), groupName + "." + actionName);
contextMenu.set_menu_model(menuModel);
contextMenu.set_parent(*this);
treeModel = Gtk::ListStore::create(portModel); treeModel = Gtk::ListStore::create(portModel);
portList->set_model(treeModel); portList->set_model(treeModel);
@ -88,7 +104,7 @@ void DeviceWidget::setChannelMap(const pa_channel_map &m, bool can_decibel) {
for (int i = 0; i < m.channels; i++) { for (int i = 0; i < m.channels; i++) {
ChannelWidget *cw = channelWidgets[i]; ChannelWidget *cw = channelWidgets[i];
channelsVBox->pack_start(*cw, false, false, 0); channelsVBox->prepend(*cw);
cw->unreference(); cw->unreference();
} }
@ -160,7 +176,7 @@ void DeviceWidget::onOffsetChange() {
if (!(o = pa_context_set_port_latency_offset(get_context(), if (!(o = pa_context_set_port_latency_offset(get_context(),
card_name.c_str(), activePort.c_str(), offset, NULL, NULL))) { card_name.c_str(), activePort.c_str(), offset, NULL, NULL))) {
show_error(_("pa_context_set_port_latency_offset() failed")); show_error(this, _("pa_context_set_port_latency_offset() failed"));
return; return;
} }
pa_operation_unref(o); pa_operation_unref(o);
@ -225,55 +241,64 @@ void DeviceWidget::prepareMenu() {
updateAdvancedOptionsVisibility(); updateAdvancedOptionsVisibility();
} }
bool DeviceWidget::onContextTriggerEvent(GdkEventButton* event) {
if (GDK_BUTTON_PRESS == event->type && 3 == event->button) {
contextMenu.popup_at_pointer((GdkEvent*)event);
return true;
}
return false; void DeviceWidget::onContextTriggerEvent(gint n_press, gdouble x, gdouble y) {
if (n_press == 1) {
contextMenu.set_pointing_to(Gdk::Rectangle {(int) x, (int) y, 0 , 0});
contextMenu.popup();
}
} }
void DeviceWidget::renamePopup() { void DeviceWidget::openRenamePopup(const Glib::VariantBase& parameter) {
if (updating) if (updating)
return; return;
if (!mpMainWindow->canRenameDevices) { if (!mpMainWindow->canRenameDevices) {
Gtk::MessageDialog dialog( auto dialog = Gtk::AlertDialog::create(_("Sorry, but device renaming is not supported."));
*mpMainWindow, dialog->set_modal(true);
_("Sorry, but device renaming is not supported."), dialog->set_detail(_("You need to load module-device-manager in the PulseAudio server in order to rename devices"));
false, dialog->show(*mpMainWindow);
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; return;
} }
Gtk::Dialog* dialog;
Gtk::Entry* renameText;
Glib::RefPtr<Gtk::Builder> x = Gtk::Builder::create_from_file(GLADE_FILE, "renameDialog"); Glib::RefPtr<Gtk::Builder> x = Gtk::Builder::create_from_file(GLADE_FILE, "renameDialog");
x->get_widget("renameDialog", dialog);
x->get_widget("renameText", renameText);
renameText->set_text(description);
dialog->add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
dialog->add_button(_("_OK"), Gtk::RESPONSE_OK);
dialog->set_default_response(Gtk::RESPONSE_OK);
if (Gtk::RESPONSE_OK == dialog->run()) {
pa_operation* o;
gchar *key = g_markup_printf_escaped("%s:%s", mDeviceType.c_str(), name.c_str()); gchar *key = g_markup_printf_escaped("%s:%s", mDeviceType.c_str(), name.c_str());
RenameWindow* renameDialog = Gtk::Builder::get_widget_derived<RenameWindow>(x, "renameDialog", description.c_str(), key);
renameDialog->set_transient_for(*mpMainWindow);
if (!(o = pa_ext_device_manager_set_device_description(get_context(), key, renameText->get_text().c_str(), NULL, NULL))) { renameDialog->present();
show_error(_("pa_ext_device_manager_write() failed")); }
RenameWindow::RenameWindow(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& x, const gchar* name, const gchar* key) :
Gtk::ApplicationWindow(cobject),
deviceKey(key){
renameText = x->get_widget<Gtk::Entry>("renameText");
renameText->set_text(name);
Gtk::Button* renameButton = x->get_widget<Gtk::Button>("renameButton");
set_default_widget(*renameButton);
auto renameAction = Gio::SimpleAction::create("rename");
renameAction->set_enabled(true);
renameAction->signal_activate().connect(sigc::mem_fun(*this, &RenameWindow::renamePopup));
add_action(renameAction);
}
void RenameWindow::renamePopup(const Glib::VariantBase& parameter){
pa_operation* o;
auto name = renameText->get_text();
if (!(o = pa_ext_device_manager_set_device_description(get_context(), deviceKey, name.c_str(), NULL, NULL))) {
show_error(this, _("pa_ext_device_manager_write() failed"));
return; return;
} }
pa_operation_unref(o); pa_operation_unref(o);
g_free(key); g_free((char*)deviceKey);
} delete this;
delete dialog;
} }
void DeviceWidget::updateAdvancedOptionsVisibility() { void DeviceWidget::updateAdvancedOptionsVisibility() {

View File

@ -57,7 +57,7 @@ public:
virtual void onLockToggleButton(); virtual void onLockToggleButton();
virtual void onDefaultToggleButton(); virtual void onDefaultToggleButton();
virtual void setDefault(bool isDefault); virtual void setDefault(bool isDefault);
virtual bool onContextTriggerEvent(GdkEventButton*); virtual void onContextTriggerEvent(gint n_press, gdouble x, gdouble y);
virtual void setLatencyOffset(int64_t offset); virtual void setLatencyOffset(int64_t offset);
void onOffsetChange(); void onOffsetChange();
@ -73,7 +73,7 @@ public:
void prepareMenu(); void prepareMenu();
void renamePopup(); void openRenamePopup(const Glib::VariantBase& parameter);
protected: protected:
MainWindow *mpMainWindow; MainWindow *mpMainWindow;
@ -85,8 +85,7 @@ protected:
virtual void onPortChange() = 0; virtual void onPortChange() = 0;
Gtk::Menu contextMenu; Gtk::PopoverMenu contextMenu;
Gtk::MenuItem rename;
/* Tree model columns */ /* Tree model columns */
class ModelColumns : public Gtk::TreeModel::ColumnRecord class ModelColumns : public Gtk::TreeModel::ColumnRecord
@ -103,7 +102,7 @@ protected:
ModelColumns portModel; ModelColumns portModel;
Gtk::Expander *advancedOptions; Gtk::Expander *advancedOptions;
Gtk::HBox *portSelect, *offsetSelect; Gtk::Box *portSelect, *offsetSelect;
Gtk::ComboBox *portList; Gtk::ComboBox *portList;
Glib::RefPtr<Gtk::ListStore> treeModel; Glib::RefPtr<Gtk::ListStore> treeModel;
Glib::RefPtr<Gtk::Adjustment> offsetAdjustment; Glib::RefPtr<Gtk::Adjustment> offsetAdjustment;
@ -114,7 +113,15 @@ protected:
private: private:
Glib::ustring mDeviceType; Glib::ustring mDeviceType;
};
class RenameWindow : public Gtk::ApplicationWindow {
public:
RenameWindow(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& x, const gchar* name, const gchar* key);
Gtk::Entry* renameText;
const gchar* deviceKey;
private:
void renamePopup(const Glib::VariantBase& parameter);
}; };
#endif #endif

View File

@ -76,30 +76,26 @@ MainWindow::MainWindow(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>
canRenameDevices(false), canRenameDevices(false),
m_connected(false), m_connected(false),
m_config_filename(NULL) { m_config_filename(NULL) {
ca_context_create (&canberraContext);
ca_context_set_driver(canberraContext, "pulse");
x->get_widget("cardsVBox", cardsVBox); cardsVBox = x->get_widget<Gtk::Box>("cardsVBox");
x->get_widget("streamsVBox", streamsVBox); streamsVBox = x->get_widget<Gtk::Box>("streamsVBox");
x->get_widget("recsVBox", recsVBox); recsVBox = x->get_widget<Gtk::Box>("recsVBox");
x->get_widget("sinksVBox", sinksVBox); sinksVBox = x->get_widget<Gtk::Box>("sinksVBox");
x->get_widget("sourcesVBox", sourcesVBox); sourcesVBox = x->get_widget<Gtk::Box>("sourcesVBox");
x->get_widget("noCardsLabel", noCardsLabel); noCardsLabel = x->get_widget<Gtk::Label>("noCardsLabel");
x->get_widget("noStreamsLabel", noStreamsLabel); noStreamsLabel = x->get_widget<Gtk::Label>("noStreamsLabel");
x->get_widget("noRecsLabel", noRecsLabel); noRecsLabel = x->get_widget<Gtk::Label>("noRecsLabel");
x->get_widget("noSinksLabel", noSinksLabel); noSinksLabel = x->get_widget<Gtk::Label>("noSinksLabel");
x->get_widget("noSourcesLabel", noSourcesLabel); noSourcesLabel = x->get_widget<Gtk::Label>("noSourcesLabel");
x->get_widget("connectingLabel", connectingLabel); connectingLabel = x->get_widget<Gtk::Label>("connectingLabel");
x->get_widget("sinkInputTypeComboBox", sinkInputTypeComboBox); sinkInputTypeComboBox = x->get_widget<Gtk::ComboBox>("sinkInputTypeComboBox");
x->get_widget("sourceOutputTypeComboBox", sourceOutputTypeComboBox); sourceOutputTypeComboBox = x->get_widget<Gtk::ComboBox>("sourceOutputTypeComboBox");
x->get_widget("sinkTypeComboBox", sinkTypeComboBox); sinkTypeComboBox = x->get_widget<Gtk::ComboBox>("sinkTypeComboBox");
x->get_widget("sourceTypeComboBox", sourceTypeComboBox); sourceTypeComboBox = x->get_widget<Gtk::ComboBox>("sourceTypeComboBox");
x->get_widget("notebook", notebook); notebook = x->get_widget<Gtk::Notebook>("notebook");
x->get_widget("showVolumeMetersCheckButton", showVolumeMetersCheckButton); showVolumeMetersCheckButton = x->get_widget<Gtk::CheckButton>("showVolumeMetersCheckButton");
sourcesVBox->signal_size_allocate().connect([this](Gdk::Rectangle _unused){ sourcesVBox->queue_draw(); });
cardsVBox->signal_size_allocate().connect([this](Gdk::Rectangle _unused){ cardsVBox->queue_draw(); });
streamsVBox->signal_size_allocate().connect([this](Gdk::Rectangle _unused){ streamsVBox->queue_draw(); });
recsVBox->signal_size_allocate().connect([this](Gdk::Rectangle _unused){ recsVBox->queue_draw(); });
sinksVBox->signal_size_allocate().connect([this](Gdk::Rectangle _unused){ sinksVBox->queue_draw(); });
sinkInputTypeComboBox->set_active((int) showSinkInputType); sinkInputTypeComboBox->set_active((int) showSinkInputType);
sourceOutputTypeComboBox->set_active((int) showSourceOutputType); sourceOutputTypeComboBox->set_active((int) showSourceOutputType);
@ -112,6 +108,9 @@ MainWindow::MainWindow(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>
sourceTypeComboBox->signal_changed().connect(sigc::mem_fun(*this, &MainWindow::onSourceTypeComboBoxChanged)); sourceTypeComboBox->signal_changed().connect(sigc::mem_fun(*this, &MainWindow::onSourceTypeComboBoxChanged));
showVolumeMetersCheckButton->signal_toggled().connect(sigc::mem_fun(*this, &MainWindow::onShowVolumeMetersCheckButtonToggled)); showVolumeMetersCheckButton->signal_toggled().connect(sigc::mem_fun(*this, &MainWindow::onShowVolumeMetersCheckButtonToggled));
auto event_controller_key = Gtk::EventControllerKey::create();
event_controller_key->signal_key_pressed().connect(sigc::mem_fun(*this, &MainWindow::on_key_press_event), false);
this->add_controller(event_controller_key);
GKeyFile* config = g_key_file_new(); GKeyFile* config = g_key_file_new();
g_assert(config); g_assert(config);
@ -134,7 +133,7 @@ MainWindow::MainWindow(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>
int default_width, default_height; int default_width, default_height;
get_default_size(default_width, default_height); get_default_size(default_width, default_height);
if (width >= default_width && height >= default_height) if (width >= default_width && height >= default_height)
resize(width, height); set_default_size(width, height);
int sinkInputTypeSelection = g_key_file_get_integer(config, "window", "sinkInputType", &err); int sinkInputTypeSelection = g_key_file_get_integer(config, "window", "sinkInputType", &err);
if (err == NULL) if (err == NULL)
@ -187,7 +186,7 @@ MainWindow* MainWindow::create(bool maximize) {
x->add_from_file(GLADE_FILE, "liststore3"); x->add_from_file(GLADE_FILE, "liststore3");
x->add_from_file(GLADE_FILE, "liststore4"); x->add_from_file(GLADE_FILE, "liststore4");
x->add_from_file(GLADE_FILE, "mainWindow"); x->add_from_file(GLADE_FILE, "mainWindow");
x->get_widget_derived("mainWindow", w); w = Gtk::Builder::get_widget_derived<MainWindow>(x, "mainWindow");
w->get_style_context()->add_class("pavucontrol-window"); w->get_style_context()->add_class("pavucontrol-window");
if (w && maximize) if (w && maximize)
w->maximize(); w->maximize();
@ -197,26 +196,26 @@ MainWindow* MainWindow::create(bool maximize) {
void MainWindow::on_realize() { void MainWindow::on_realize() {
Gtk::Window::on_realize(); Gtk::Window::on_realize();
get_window()->set_cursor(Gdk::Cursor::create(Gdk::WATCH)); set_cursor(Gdk::Cursor::create("wait"));
} }
bool MainWindow::on_key_press_event(GdkEventKey* event) { bool MainWindow::on_key_press_event(guint keyval, guint keycode, Gdk::ModifierType state) {
if (event->state & GDK_CONTROL_MASK) { if ((state & Gdk::ModifierType::CONTROL_MASK) == Gdk::ModifierType::CONTROL_MASK) {
switch (event->keyval) { switch (keyval) {
case GDK_KEY_KP_1: case GDK_KEY_KP_1:
case GDK_KEY_KP_2: case GDK_KEY_KP_2:
case GDK_KEY_KP_3: case GDK_KEY_KP_3:
case GDK_KEY_KP_4: case GDK_KEY_KP_4:
case GDK_KEY_KP_5: case GDK_KEY_KP_5:
notebook->set_current_page(event->keyval - GDK_KEY_KP_1); notebook->set_current_page(keyval - GDK_KEY_KP_1);
return true; return true;
case GDK_KEY_1: case GDK_KEY_1:
case GDK_KEY_2: case GDK_KEY_2:
case GDK_KEY_3: case GDK_KEY_3:
case GDK_KEY_4: case GDK_KEY_4:
case GDK_KEY_5: case GDK_KEY_5:
notebook->set_current_page(event->keyval - GDK_KEY_1); notebook->set_current_page(keyval - GDK_KEY_1);
return true; return true;
case GDK_KEY_W: case GDK_KEY_W:
case GDK_KEY_Q: case GDK_KEY_Q:
@ -226,7 +225,7 @@ bool MainWindow::on_key_press_event(GdkEventKey* event) {
return true; return true;
} }
} }
return Gtk::Window::on_key_press_event(event); return false;
} }
MainWindow::~MainWindow() { MainWindow::~MainWindow() {
@ -234,7 +233,7 @@ MainWindow::~MainWindow() {
g_assert(config); g_assert(config);
int width, height; int width, height;
get_size(width, height); get_default_size(width, height);
g_key_file_set_integer(config, "window", "width", width); g_key_file_set_integer(config, "window", "width", width);
g_key_file_set_integer(config, "window", "height", height); g_key_file_set_integer(config, "window", "height", height);
g_key_file_set_integer(config, "window", "sinkInputType", sinkInputTypeComboBox->get_active_row_number()); g_key_file_set_integer(config, "window", "sinkInputType", sinkInputTypeComboBox->get_active_row_number());
@ -247,7 +246,7 @@ MainWindow::~MainWindow() {
GError *err = NULL; GError *err = NULL;
gchar *filedata = g_key_file_to_data(config, &filelen, &err); gchar *filedata = g_key_file_to_data(config, &filelen, &err);
if (err) { if (err) {
show_error(_("Error saving preferences")); show_error(this, _("Error saving preferences"));
g_error_free(err); g_error_free(err);
goto finish; goto finish;
} }
@ -256,7 +255,7 @@ MainWindow::~MainWindow() {
g_free(filedata); g_free(filedata);
if (err) { if (err) {
gchar* msg = g_strconcat(_("Error writing config file %s"), m_config_filename, NULL); gchar* msg = g_strconcat(_("Error writing config file %s"), m_config_filename, NULL);
show_error(msg); show_error(this, msg);
g_free(msg); g_free(msg);
g_error_free(err); g_error_free(err);
goto finish; goto finish;
@ -274,34 +273,20 @@ finish:
} }
} }
static void set_icon_name_default(Gtk::Image *i, const char *name, Gtk::IconSize size) { static void set_icon_name_default(Gtk::Image *i, const char *name) {
/* We emulate the behavior of the GTK_ICON_LOOKUP_GENERIC_FALLBACK flag from Gtk3 */
Glib::RefPtr<Gtk::IconTheme> theme; Glib::RefPtr<Gtk::IconTheme> theme;
Glib::RefPtr<Gdk::Pixbuf> pixbuf; theme = Gtk::IconTheme::get_for_display(Gdk::Display::get_default());
gint width = 24, height = 24; std::string iconName(name);
while (!theme->has_icon(iconName.c_str())) {
Gtk::IconSize::lookup(size, width, height); size_t lastDashIndex = iconName.find_last_of("-");
theme = Gtk::IconTheme::get_default(); if (lastDashIndex == std::string::npos) {
iconName = "gtk-missing-image";
try { break;
pixbuf = theme->load_icon(name, width, Gtk::ICON_LOOKUP_GENERIC_FALLBACK | Gtk::ICON_LOOKUP_FORCE_SIZE);
} catch (Glib::Error &e) {
/* Ignore errors. */
} }
iconName = iconName.substr(0,lastDashIndex);
if (!pixbuf) {
try {
pixbuf = Gdk::Pixbuf::create_from_file(name);
} catch (Glib::FileError &e) {
/* Ignore errors. */
} catch (Gdk::PixbufError &e) {
/* Ignore errors. */
}
}
if (pixbuf) {
pixbuf = pixbuf->scale_simple(width, height, Gdk::INTERP_BILINEAR);
i->set(pixbuf);
} }
i->set_from_icon_name(iconName.c_str());
} }
static void updatePorts(DeviceWidget *w, std::map<Glib::ustring, PortInfo> &ports) { static void updatePorts(DeviceWidget *w, std::map<Glib::ustring, PortInfo> &ports) {
@ -365,7 +350,7 @@ void MainWindow::updateCard(const pa_card_info &info) {
w = cardWidgets[info.index]; w = cardWidgets[info.index];
else { else {
cardWidgets[info.index] = w = CardWidget::create(); cardWidgets[info.index] = w = CardWidget::create();
cardsVBox->pack_start(*w, false, false, 0); cardsVBox->append(*w);
w->unreference(); w->unreference();
w->index = info.index; w->index = info.index;
is_new = true; is_new = true;
@ -382,7 +367,7 @@ void MainWindow::updateCard(const pa_card_info &info) {
g_free(txt); g_free(txt);
icon = pa_proplist_gets(info.proplist, PA_PROP_DEVICE_ICON_NAME); icon = pa_proplist_gets(info.proplist, PA_PROP_DEVICE_ICON_NAME);
set_icon_name_default(w->iconImage, icon ? icon : "audio-card", Gtk::ICON_SIZE_SMALL_TOOLBAR); set_icon_name_default(w->iconImage, icon ? icon : "audio-card");
w->hasSinks = w->hasSources = false; w->hasSinks = w->hasSources = false;
profile_priorities.clear(); profile_priorities.clear();
@ -556,7 +541,7 @@ bool MainWindow::updateSink(const pa_sink_info &info) {
else { else {
sinkWidgets[info.index] = w = SinkWidget::create(this); 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->append(*w);
w->unreference(); w->unreference();
w->index = info.index; w->index = info.index;
w->monitor_index = info.monitor_source; w->monitor_index = info.monitor_source;
@ -580,7 +565,7 @@ bool MainWindow::updateSink(const pa_sink_info &info) {
g_free(txt); g_free(txt);
icon = pa_proplist_gets(info.proplist, PA_PROP_DEVICE_ICON_NAME); icon = pa_proplist_gets(info.proplist, PA_PROP_DEVICE_ICON_NAME);
set_icon_name_default(w->iconImage, icon ? icon : "audio-card", Gtk::ICON_SIZE_SMALL_TOOLBAR); set_icon_name_default(w->iconImage, icon ? icon : "audio-card");
w->setVolume(info.volume); w->setVolume(info.volume);
w->muteToggleButton->set_active(info.mute); w->muteToggleButton->set_active(info.mute);
@ -630,7 +615,7 @@ static void read_callback(pa_stream *s, size_t length, void *userdata) {
double v; double v;
if (pa_stream_peek(s, &data, &length) < 0) { if (pa_stream_peek(s, &data, &length) < 0) {
show_error(_("Failed to read data from stream")); show_error(w, _("Failed to read data from stream"));
return; return;
} }
@ -675,7 +660,7 @@ pa_stream* MainWindow::createMonitorStreamForSource(uint32_t source_idx, uint32_
snprintf(t, sizeof(t), "%u", source_idx); snprintf(t, sizeof(t), "%u", source_idx);
if (!(s = pa_stream_new(get_context(), _("Peak detect"), &ss, NULL))) { if (!(s = pa_stream_new(get_context(), _("Peak detect"), &ss, NULL))) {
show_error(_("Failed to create monitoring stream")); show_error(this, _("Failed to create monitoring stream"));
return NULL; return NULL;
} }
@ -690,7 +675,7 @@ pa_stream* MainWindow::createMonitorStreamForSource(uint32_t source_idx, uint32_
(!showVolumeMetersCheckButton->get_active() ? PA_STREAM_START_CORKED : PA_STREAM_NOFLAGS)); (!showVolumeMetersCheckButton->get_active() ? PA_STREAM_START_CORKED : PA_STREAM_NOFLAGS));
if (pa_stream_connect_record(s, t, &attr, flags) < 0) { if (pa_stream_connect_record(s, t, &attr, flags) < 0) {
show_error(_("Failed to connect monitoring stream")); show_error(this, _("Failed to connect monitoring stream"));
pa_stream_unref(s); pa_stream_unref(s);
return NULL; return NULL;
} }
@ -722,7 +707,7 @@ void MainWindow::updateSource(const pa_source_info &info) {
else { else {
sourceWidgets[info.index] = w = SourceWidget::create(this); 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->append(*w);
w->unreference(); w->unreference();
w->index = info.index; w->index = info.index;
is_new = true; is_new = true;
@ -748,7 +733,7 @@ void MainWindow::updateSource(const pa_source_info &info) {
g_free(txt); g_free(txt);
icon = pa_proplist_gets(info.proplist, PA_PROP_DEVICE_ICON_NAME); icon = pa_proplist_gets(info.proplist, PA_PROP_DEVICE_ICON_NAME);
set_icon_name_default(w->iconImage, icon ? icon : "audio-input-microphone", Gtk::ICON_SIZE_SMALL_TOOLBAR); set_icon_name_default(w->iconImage, icon ? icon : "audio-input-microphone");
w->setVolume(info.volume); w->setVolume(info.volume);
w->muteToggleButton->set_active(info.mute); w->muteToggleButton->set_active(info.mute);
@ -817,7 +802,7 @@ void MainWindow::setIconFromProplist(Gtk::Image *icon, pa_proplist *l, const cha
finish: finish:
set_icon_name_default(icon, t, Gtk::ICON_SIZE_SMALL_TOOLBAR); set_icon_name_default(icon, t);
} }
void MainWindow::updateSinkInput(const pa_sink_input_info &info) { void MainWindow::updateSinkInput(const pa_sink_input_info &info) {
@ -840,7 +825,7 @@ void MainWindow::updateSinkInput(const pa_sink_input_info &info) {
} else { } else {
sinkInputWidgets[info.index] = w = SinkInputWidget::create(this); sinkInputWidgets[info.index] = w = SinkInputWidget::create(this);
w->setChannelMap(info.channel_map, true); w->setChannelMap(info.channel_map, true);
streamsVBox->pack_start(*w, false, false, 0); streamsVBox->append(*w);
w->unreference(); w->unreference();
w->index = info.index; w->index = info.index;
w->clientIndex = info.client; w->clientIndex = info.client;
@ -899,7 +884,7 @@ void MainWindow::updateSourceOutput(const pa_source_output_info &info) {
#if HAVE_SOURCE_OUTPUT_VOLUMES #if HAVE_SOURCE_OUTPUT_VOLUMES
w->setChannelMap(info.channel_map, true); w->setChannelMap(info.channel_map, true);
#endif #endif
recsVBox->pack_start(*w, false, false, 0); recsVBox->append(*w);
w->unreference(); w->unreference();
w->index = info.index; w->index = info.index;
w->clientIndex = info.client; w->clientIndex = info.client;
@ -996,7 +981,7 @@ bool MainWindow::createEventRoleWidget() {
}; };
eventRoleWidget = RoleWidget::create(); eventRoleWidget = RoleWidget::create();
streamsVBox->pack_start(*eventRoleWidget, false, false, 0); streamsVBox->append(*eventRoleWidget);
eventRoleWidget->unreference(); eventRoleWidget->unreference();
eventRoleWidget->role = "sink-input-by-media-role:event"; eventRoleWidget->role = "sink-input-by-media-role:event";
eventRoleWidget->setChannelMap(cm, true); eventRoleWidget->setChannelMap(cm, true);
@ -1004,7 +989,7 @@ bool MainWindow::createEventRoleWidget() {
eventRoleWidget->boldNameLabel->set_text(""); eventRoleWidget->boldNameLabel->set_text("");
eventRoleWidget->nameLabel->set_label(_("System Sounds")); eventRoleWidget->nameLabel->set_label(_("System Sounds"));
eventRoleWidget->iconImage->set_from_icon_name("multimedia-volume-control", Gtk::ICON_SIZE_SMALL_TOOLBAR); eventRoleWidget->iconImage->set_from_icon_name("multimedia-volume-control");
eventRoleWidget->device = ""; eventRoleWidget->device = "";
@ -1292,7 +1277,7 @@ void MainWindow::removeSink(uint32_t index) {
if (!sinkWidgets.count(index)) if (!sinkWidgets.count(index))
return; return;
delete sinkWidgets[index]; sinksVBox->remove(*sinkWidgets[index]);
sinkWidgets.erase(index); sinkWidgets.erase(index);
updateDeviceVisibility(); updateDeviceVisibility();
} }
@ -1301,7 +1286,7 @@ void MainWindow::removeSource(uint32_t index) {
if (!sourceWidgets.count(index)) if (!sourceWidgets.count(index))
return; return;
delete sourceWidgets[index]; sourcesVBox->remove(*sourceWidgets[index]);
sourceWidgets.erase(index); sourceWidgets.erase(index);
updateDeviceVisibility(); updateDeviceVisibility();
} }
@ -1310,7 +1295,7 @@ void MainWindow::removeSinkInput(uint32_t index) {
if (!sinkInputWidgets.count(index)) if (!sinkInputWidgets.count(index))
return; return;
delete sinkInputWidgets[index]; streamsVBox->remove(*sinkInputWidgets[index]);
sinkInputWidgets.erase(index); sinkInputWidgets.erase(index);
updateDeviceVisibility(); updateDeviceVisibility();
} }
@ -1319,7 +1304,7 @@ void MainWindow::removeSourceOutput(uint32_t index) {
if (!sourceOutputWidgets.count(index)) if (!sourceOutputWidgets.count(index))
return; return;
delete sourceOutputWidgets[index]; recsVBox->remove(*sourceOutputWidgets[index]);
sourceOutputWidgets.erase(index); sourceOutputWidgets.erase(index);
updateDeviceVisibility(); updateDeviceVisibility();
} }

View File

@ -29,6 +29,8 @@ class MainWindow;
# include <pulse/ext-device-restore.h> # include <pulse/ext-device-restore.h>
#endif #endif
#include <canberra.h>
#include <unordered_map> #include <unordered_map>
class CardWidget; class CardWidget;
@ -76,7 +78,7 @@ public:
void setConnectingMessage(const char *string = NULL); void setConnectingMessage(const char *string = NULL);
Gtk::Notebook *notebook; Gtk::Notebook *notebook;
Gtk::VBox *streamsVBox, *recsVBox, *sinksVBox, *sourcesVBox, *cardsVBox; Gtk::Box *streamsVBox, *recsVBox, *sinksVBox, *sourcesVBox, *cardsVBox;
Gtk::Label *noStreamsLabel, *noRecsLabel, *noSinksLabel, *noSourcesLabel, *noCardsLabel, *connectingLabel; Gtk::Label *noStreamsLabel, *noRecsLabel, *noSinksLabel, *noSourcesLabel, *noCardsLabel, *connectingLabel;
Gtk::ComboBox *sinkInputTypeComboBox, *sourceOutputTypeComboBox, *sinkTypeComboBox, *sourceTypeComboBox; Gtk::ComboBox *sinkInputTypeComboBox, *sourceOutputTypeComboBox, *sinkTypeComboBox, *sourceTypeComboBox;
Gtk::CheckButton *showVolumeMetersCheckButton; Gtk::CheckButton *showVolumeMetersCheckButton;
@ -116,9 +118,11 @@ public:
bool canRenameDevices; bool canRenameDevices;
ca_context *canberraContext;
protected: protected:
virtual void on_realize(); virtual void on_realize();
virtual bool on_key_press_event(GdkEventKey* event); virtual bool on_key_press_event(guint keyval, guint keycode, Gdk::ModifierType state);
private: private:
gboolean m_connected; gboolean m_connected;

View File

@ -33,8 +33,8 @@ executable('pavucontrol',
install_data('pavucontrol.glade') install_data('pavucontrol.glade')
desktop_file = i18n.merge_file( desktop_file = i18n.merge_file(
input : 'pavucontrol.desktop.in', input : 'org.pulseaudio.pavucontrol.desktop.in',
output : 'pavucontrol.desktop', output : 'org.pulseaudio.pavucontrol.desktop',
po_dir : po_dir, po_dir : po_dir,
type : 'desktop', type : 'desktop',
install : true, install : true,

View File

@ -26,7 +26,7 @@
/*** MinimalStreamWidget ***/ /*** MinimalStreamWidget ***/
MinimalStreamWidget::MinimalStreamWidget(BaseObjectType* cobject) : MinimalStreamWidget::MinimalStreamWidget(BaseObjectType* cobject) :
Gtk::VBox(cobject), Gtk::Box(cobject),
channelsVBox(NULL), channelsVBox(NULL),
nameLabel(NULL), nameLabel(NULL),
boldNameLabel(NULL), boldNameLabel(NULL),
@ -53,7 +53,7 @@ void MinimalStreamWidget::init() {
* in the constructor. */ * in the constructor. */
peakProgressBar.set_size_request(-1, 10); peakProgressBar.set_size_request(-1, 10);
channelsVBox->pack_end(peakProgressBar, false, false); channelsVBox->append(peakProgressBar);
/* XXX: Why is the peak meter hidden by default? Maybe the idea is that if /* XXX: Why is the peak meter hidden by default? Maybe the idea is that if
* setting up the monitoring stream fails for whatever reason, then we * setting up the monitoring stream fails for whatever reason, then we
@ -64,7 +64,6 @@ void MinimalStreamWidget::init() {
#define DECAY_STEP (1.0 / PEAKS_RATE) #define DECAY_STEP (1.0 / PEAKS_RATE)
void MinimalStreamWidget::updatePeak(double v) { void MinimalStreamWidget::updatePeak(double v) {
if (lastPeak >= DECAY_STEP) if (lastPeak >= DECAY_STEP)
if (v < lastPeak - DECAY_STEP) if (v < lastPeak - DECAY_STEP)
v = lastPeak - DECAY_STEP; v = lastPeak - DECAY_STEP;

View File

@ -25,7 +25,7 @@
#define PEAKS_RATE 144 #define PEAKS_RATE 144
class MinimalStreamWidget : public Gtk::VBox { class MinimalStreamWidget : public Gtk::Box {
public: public:
MinimalStreamWidget(BaseObjectType* cobject); MinimalStreamWidget(BaseObjectType* cobject);
virtual ~MinimalStreamWidget(); virtual ~MinimalStreamWidget();
@ -33,7 +33,7 @@ public:
/* Subclass constructors are expected to initialize these variables. /* Subclass constructors are expected to initialize these variables.
* MinimalStreamWidget can't initialize these, because the glade object * MinimalStreamWidget can't initialize these, because the glade object
* id's depend on the subclass type. */ * id's depend on the subclass type. */
Gtk::VBox *channelsVBox; Gtk::Box *channelsVBox;
Gtk::Label *nameLabel, *boldNameLabel; Gtk::Label *nameLabel, *boldNameLabel;
Gtk::Image *iconImage; Gtk::Image *iconImage;

View File

@ -24,8 +24,6 @@
#include "i18n.h" #include "i18n.h"
#include <canberra-gtk.h>
#include "pavuapplication.h" #include "pavuapplication.h"
#include "pavucontrol.h" #include "pavucontrol.h"
#include "mainwindow.h" #include "mainwindow.h"
@ -39,7 +37,7 @@ PavuApplication::get_instance()
} }
PavuApplication::PavuApplication() : PavuApplication::PavuApplication() :
Gtk::Application("org.pulseaudio.pavucontrol", Gio::ApplicationFlags::APPLICATION_HANDLES_COMMAND_LINE), Gtk::Application("org.pulseaudio.pavucontrol", Gio::Application::Flags::HANDLES_COMMAND_LINE),
mainWindow(NULL), mainWindow(NULL),
retry(false), retry(false),
maximize(false), maximize(false),
@ -59,9 +57,8 @@ MainWindow* PavuApplication::create_window()
MainWindow* pavucontrol_window = pavucontrol_get_window(m, maximize, retry, tab); MainWindow* pavucontrol_window = pavucontrol_get_window(m, maximize, retry, tab);
pavucontrol_window->signal_hide().connect( pavucontrol_window->signal_close_request().connect(sigc::mem_fun(*this,
sigc::bind<Gtk::Window*>(sigc::mem_fun(*this, &PavuApplication::on_close_window), true);
&PavuApplication::on_hide_window), pavucontrol_window));
return pavucontrol_window; return pavucontrol_window;
} }
@ -94,9 +91,9 @@ void PavuApplication::on_activate()
* exiting : when the last registered window of Gtk::Application is closed, * exiting : when the last registered window of Gtk::Application is closed,
* the application's run() function returns. * the application's run() function returns.
*/ */
void PavuApplication::on_hide_window(Gtk::Window* window) bool PavuApplication::on_close_window()
{ {
delete window; delete mainWindow;
mainWindow = NULL; mainWindow = NULL;
if (get_context()) { if (get_context()) {
@ -104,6 +101,7 @@ void PavuApplication::on_hide_window(Gtk::Window* window)
} }
pa_glib_mainloop_free(m); pa_glib_mainloop_free(m);
m = NULL; m = NULL;
return true;
} }
template <typename T_ArgType> template <typename T_ArgType>
@ -155,23 +153,23 @@ int main(int argc, char *argv[]) {
/* Add command-line options */ /* Add command-line options */
globalInstance.add_main_option_entry( globalInstance.add_main_option_entry(
Gio::Application::OptionType::OPTION_TYPE_INT, Gio::Application::OptionType::INT,
"tab", 't', "tab", 't',
_("Select a specific tab on load."), _("Select a specific tab on load."),
_("number")); _("number"));
globalInstance.add_main_option_entry( globalInstance.add_main_option_entry(
Gio::Application::OptionType::OPTION_TYPE_BOOL, Gio::Application::OptionType::BOOL,
"retry", 'r', "retry", 'r',
_("Retry forever if pa quits (every 5 seconds).")); _("Retry forever if pa quits (every 5 seconds)."));
globalInstance.add_main_option_entry( globalInstance.add_main_option_entry(
Gio::Application::OptionType::OPTION_TYPE_BOOL, Gio::Application::OptionType::BOOL,
"maximize", 'm', "maximize", 'm',
_("Maximize the window.")); _("Maximize the window."));
globalInstance.add_main_option_entry( globalInstance.add_main_option_entry(
Gio::Application::OptionType::OPTION_TYPE_BOOL, Gio::Application::OptionType::BOOL,
"version", 'v', "version", 'v',
_("Show version.")); _("Show version."));

View File

@ -45,7 +45,8 @@ protected:
private: private:
MainWindow* create_window(); MainWindow* create_window();
void on_hide_window(Gtk::Window* window); void on_hide_window();
bool on_close_window();
pa_glib_mainloop *m; pa_glib_mainloop *m;
}; };

View File

@ -29,8 +29,6 @@
#include <json-glib/json-glib.h> #include <json-glib/json-glib.h>
#endif #endif
#include <canberra-gtk.h>
#include "pavucontrol.h" #include "pavucontrol.h"
#include "i18n.h" #include "i18n.h"
#include "minimalstreamwidget.h" #include "minimalstreamwidget.h"
@ -58,15 +56,27 @@ static int tab_number = 0;
static bool retry = false; static bool retry = false;
static int reconnect_timeout = 1; static int reconnect_timeout = 1;
void show_error(const char *txt) { void show_error_finish (const Glib::RefPtr<Gio::AsyncResult>& result) {
PavuApplication::get_instance().quit();
}
void show_error(Gtk::Widget* widget, const char *txt) {
Gtk::Root *root = widget->get_root();
char buf[256]; char buf[256];
snprintf(buf, sizeof(buf), "%s: %s", txt, pa_strerror(pa_context_errno(context))); 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); auto dialog = Gtk::AlertDialog::create(buf);
dialog.run(); dialog->set_modal(true);
if (GTK_IS_WINDOW(root->gobj())) {
PavuApplication::get_instance().quit(); GtkWindow* w = (GtkWindow*) root->gobj();
Gtk::Window* window = Glib::wrap(w);
window->present();
dialog->choose(*window, sigc::ptr_fun(show_error_finish));
}
else {
dialog->choose(sigc::ptr_fun(show_error_finish));
}
} }
static void dec_outstanding(MainWindow *w) { static void dec_outstanding(MainWindow *w) {
@ -74,7 +84,7 @@ static void dec_outstanding(MainWindow *w) {
return; return;
if (--n_outstanding <= 0) { if (--n_outstanding <= 0) {
w->get_window()->set_cursor(); w->set_cursor(Gdk::Cursor::create("default"));;
w->setConnectionState(true); w->setConnectionState(true);
} }
} }
@ -355,7 +365,7 @@ void card_cb(pa_context *c, const pa_card_info *i, int eol, void *userdata) {
if (pa_context_errno(context) == PA_ERR_NOENTITY) if (pa_context_errno(context) == PA_ERR_NOENTITY)
return; return;
show_error(_("Card callback failure")); show_error(w, _("Card callback failure"));
return; return;
} }
@ -383,7 +393,7 @@ void sink_cb(pa_context *c, const pa_sink_info *i, int eol, void *userdata) {
if (pa_context_errno(context) == PA_ERR_NOENTITY) if (pa_context_errno(context) == PA_ERR_NOENTITY)
return; return;
show_error(_("Sink callback failure")); show_error(w, _("Sink callback failure"));
return; return;
} }
@ -407,7 +417,7 @@ void source_cb(pa_context *, const pa_source_info *i, int eol, void *userdata) {
if (pa_context_errno(context) == PA_ERR_NOENTITY) if (pa_context_errno(context) == PA_ERR_NOENTITY)
return; return;
show_error(_("Source callback failure")); show_error(w, _("Source callback failure"));
return; return;
} }
@ -426,7 +436,7 @@ void sink_input_cb(pa_context *, const pa_sink_input_info *i, int eol, void *use
if (pa_context_errno(context) == PA_ERR_NOENTITY) if (pa_context_errno(context) == PA_ERR_NOENTITY)
return; return;
show_error(_("Sink input callback failure")); show_error(w, _("Sink input callback failure"));
return; return;
} }
@ -445,7 +455,7 @@ void source_output_cb(pa_context *, const pa_source_output_info *i, int eol, voi
if (pa_context_errno(context) == PA_ERR_NOENTITY) if (pa_context_errno(context) == PA_ERR_NOENTITY)
return; return;
show_error(_("Source output callback failure")); show_error(w, _("Source output callback failure"));
return; return;
} }
@ -475,7 +485,7 @@ void client_cb(pa_context *, const pa_client_info *i, int eol, void *userdata) {
if (pa_context_errno(context) == PA_ERR_NOENTITY) if (pa_context_errno(context) == PA_ERR_NOENTITY)
return; return;
show_error(_("Client callback failure")); show_error(w, _("Client callback failure"));
return; return;
} }
@ -491,7 +501,7 @@ void server_info_cb(pa_context *, const pa_server_info *i, void *userdata) {
MainWindow *w = static_cast<MainWindow*>(userdata); MainWindow *w = static_cast<MainWindow*>(userdata);
if (!i) { if (!i) {
show_error(_("Server info callback failure")); show_error(w, _("Server info callback failure"));
return; return;
} }
@ -527,7 +537,7 @@ static void ext_stream_restore_subscribe_cb(pa_context *c, void *userdata) {
pa_operation *o; pa_operation *o;
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))) {
show_error(_("pa_ext_stream_restore_read() failed")); show_error(w, _("pa_ext_stream_restore_read() failed"));
return; return;
} }
@ -566,7 +576,7 @@ static void ext_device_restore_subscribe_cb(pa_context *c, pa_device_type_t type
return; return;
if (!(o = pa_ext_device_restore_read_formats(c, type, idx, ext_device_restore_read_cb, w))) { if (!(o = pa_ext_device_restore_read_formats(c, type, idx, ext_device_restore_read_cb, w))) {
show_error(_("pa_ext_device_restore_read_sink_formats() failed")); show_error(w, _("pa_ext_device_restore_read_sink_formats() failed"));
return; return;
} }
@ -603,7 +613,7 @@ static void ext_device_manager_subscribe_cb(pa_context *c, void *userdata) {
pa_operation *o; pa_operation *o;
if (!(o = pa_ext_device_manager_read(c, ext_device_manager_read_cb, w))) { if (!(o = pa_ext_device_manager_read(c, ext_device_manager_read_cb, w))) {
show_error(_("pa_ext_device_manager_read() failed")); show_error(w, _("pa_ext_device_manager_read() failed"));
return; return;
} }
@ -620,7 +630,7 @@ void subscribe_cb(pa_context *c, pa_subscription_event_type_t t, uint32_t index,
else { else {
pa_operation *o; pa_operation *o;
if (!(o = pa_context_get_sink_info_by_index(c, index, sink_cb, w))) { if (!(o = pa_context_get_sink_info_by_index(c, index, sink_cb, w))) {
show_error(_("pa_context_get_sink_info_by_index() failed")); show_error(w, _("pa_context_get_sink_info_by_index() failed"));
return; return;
} }
pa_operation_unref(o); pa_operation_unref(o);
@ -633,7 +643,7 @@ void subscribe_cb(pa_context *c, pa_subscription_event_type_t t, uint32_t index,
else { else {
pa_operation *o; pa_operation *o;
if (!(o = pa_context_get_source_info_by_index(c, index, source_cb, w))) { if (!(o = pa_context_get_source_info_by_index(c, index, source_cb, w))) {
show_error(_("pa_context_get_source_info_by_index() failed")); show_error(w, _("pa_context_get_source_info_by_index() failed"));
return; return;
} }
pa_operation_unref(o); pa_operation_unref(o);
@ -646,7 +656,7 @@ void subscribe_cb(pa_context *c, pa_subscription_event_type_t t, uint32_t index,
else { else {
pa_operation *o; pa_operation *o;
if (!(o = pa_context_get_sink_input_info(c, index, sink_input_cb, w))) { if (!(o = pa_context_get_sink_input_info(c, index, sink_input_cb, w))) {
show_error(_("pa_context_get_sink_input_info() failed")); show_error(w, _("pa_context_get_sink_input_info() failed"));
return; return;
} }
pa_operation_unref(o); pa_operation_unref(o);
@ -659,7 +669,7 @@ void subscribe_cb(pa_context *c, pa_subscription_event_type_t t, uint32_t index,
else { else {
pa_operation *o; pa_operation *o;
if (!(o = pa_context_get_source_output_info(c, index, source_output_cb, w))) { if (!(o = pa_context_get_source_output_info(c, index, source_output_cb, w))) {
show_error(_("pa_context_get_sink_input_info() failed")); show_error(w, _("pa_context_get_sink_input_info() failed"));
return; return;
} }
pa_operation_unref(o); pa_operation_unref(o);
@ -672,7 +682,7 @@ void subscribe_cb(pa_context *c, pa_subscription_event_type_t t, uint32_t index,
else { else {
pa_operation *o; pa_operation *o;
if (!(o = pa_context_get_client_info(c, index, client_cb, w))) { if (!(o = pa_context_get_client_info(c, index, client_cb, w))) {
show_error(_("pa_context_get_client_info() failed")); show_error(w, _("pa_context_get_client_info() failed"));
return; return;
} }
pa_operation_unref(o); pa_operation_unref(o);
@ -682,7 +692,7 @@ void subscribe_cb(pa_context *c, pa_subscription_event_type_t t, uint32_t index,
case PA_SUBSCRIPTION_EVENT_SERVER: { case PA_SUBSCRIPTION_EVENT_SERVER: {
pa_operation *o; pa_operation *o;
if (!(o = pa_context_get_server_info(c, server_info_cb, w))) { if (!(o = pa_context_get_server_info(c, server_info_cb, w))) {
show_error(_("pa_context_get_server_info() failed")); show_error(w, _("pa_context_get_server_info() failed"));
return; return;
} }
pa_operation_unref(o); pa_operation_unref(o);
@ -695,7 +705,7 @@ void subscribe_cb(pa_context *c, pa_subscription_event_type_t t, uint32_t index,
else { else {
pa_operation *o; pa_operation *o;
if (!(o = pa_context_get_card_info_by_index(c, index, card_cb, w))) { if (!(o = pa_context_get_card_info_by_index(c, index, card_cb, w))) {
show_error(_("pa_context_get_card_info_by_index() failed")); show_error(w, _("pa_context_get_card_info_by_index() failed"));
return; return;
} }
pa_operation_unref(o); pa_operation_unref(o);
@ -738,7 +748,7 @@ void context_state_callback(pa_context *c, void *userdata) {
PA_SUBSCRIPTION_MASK_CLIENT| PA_SUBSCRIPTION_MASK_CLIENT|
PA_SUBSCRIPTION_MASK_SERVER| PA_SUBSCRIPTION_MASK_SERVER|
PA_SUBSCRIPTION_MASK_CARD), NULL, NULL))) { PA_SUBSCRIPTION_MASK_CARD), NULL, NULL))) {
show_error(_("pa_context_subscribe() failed")); show_error(w, _("pa_context_subscribe() failed"));
return; return;
} }
pa_operation_unref(o); pa_operation_unref(o);
@ -747,49 +757,49 @@ void context_state_callback(pa_context *c, void *userdata) {
n_outstanding = 0; n_outstanding = 0;
if (!(o = pa_context_get_server_info(c, server_info_cb, w))) { if (!(o = pa_context_get_server_info(c, server_info_cb, w))) {
show_error(_("pa_context_get_server_info() failed")); show_error(w, _("pa_context_get_server_info() failed"));
return; return;
} }
pa_operation_unref(o); pa_operation_unref(o);
n_outstanding++; n_outstanding++;
if (!(o = pa_context_get_client_info_list(c, client_cb, w))) { if (!(o = pa_context_get_client_info_list(c, client_cb, w))) {
show_error(_("pa_context_client_info_list() failed")); show_error(w, _("pa_context_client_info_list() failed"));
return; return;
} }
pa_operation_unref(o); pa_operation_unref(o);
n_outstanding++; n_outstanding++;
if (!(o = pa_context_get_card_info_list(c, card_cb, w))) { if (!(o = pa_context_get_card_info_list(c, card_cb, w))) {
show_error(_("pa_context_get_card_info_list() failed")); show_error(w, _("pa_context_get_card_info_list() failed"));
return; return;
} }
pa_operation_unref(o); pa_operation_unref(o);
n_outstanding++; n_outstanding++;
if (!(o = pa_context_get_sink_info_list(c, sink_cb, w))) { if (!(o = pa_context_get_sink_info_list(c, sink_cb, w))) {
show_error(_("pa_context_get_sink_info_list() failed")); show_error(w, _("pa_context_get_sink_info_list() failed"));
return; return;
} }
pa_operation_unref(o); pa_operation_unref(o);
n_outstanding++; n_outstanding++;
if (!(o = pa_context_get_source_info_list(c, source_cb, w))) { if (!(o = pa_context_get_source_info_list(c, source_cb, w))) {
show_error(_("pa_context_get_source_info_list() failed")); show_error(w, _("pa_context_get_source_info_list() failed"));
return; return;
} }
pa_operation_unref(o); pa_operation_unref(o);
n_outstanding++; n_outstanding++;
if (!(o = pa_context_get_sink_input_info_list(c, sink_input_cb, w))) { if (!(o = pa_context_get_sink_input_info_list(c, sink_input_cb, w))) {
show_error(_("pa_context_get_sink_input_info_list() failed")); show_error(w, _("pa_context_get_sink_input_info_list() failed"));
return; return;
} }
pa_operation_unref(o); pa_operation_unref(o);
n_outstanding++; n_outstanding++;
if (!(o = pa_context_get_source_output_info_list(c, source_output_cb, w))) { if (!(o = pa_context_get_source_output_info_list(c, source_output_cb, w))) {
show_error(_("pa_context_get_source_output_info_list() failed")); show_error(w, _("pa_context_get_source_output_info_list() failed"));
return; return;
} }
pa_operation_unref(o); pa_operation_unref(o);
@ -916,8 +926,6 @@ MainWindow* pavucontrol_get_window(pa_glib_mainloop *m, bool maximize, bool _ret
tab_number = _tab_number; tab_number = _tab_number;
retry = _retry; retry = _retry;
ca_context_set_driver(ca_gtk_context_get(), "pulse");
mainWindow = MainWindow::create(maximize); mainWindow = MainWindow::create(maximize);
api = pa_glib_mainloop_get_api(m); api = pa_glib_mainloop_get_api(m);

File diff suppressed because it is too large Load Diff

View File

@ -29,6 +29,7 @@
#include <libintl.h> #include <libintl.h>
#include <gtkmm.h> #include <gtkmm.h>
#include <gtkmm/buildable.h>
#include <pulse/pulseaudio.h> #include <pulse/pulseaudio.h>
#include <pulse/glib-mainloop.h> #include <pulse/glib-mainloop.h>
@ -74,7 +75,7 @@ enum SourceType {
#include "mainwindow.h" #include "mainwindow.h"
pa_context* get_context(void); pa_context* get_context(void);
void show_error(const char *txt); void show_error(Gtk::Widget *w, const char *txt);
MainWindow* pavucontrol_get_window(pa_glib_mainloop *m, bool maximize, bool retry, int tab_number); MainWindow* pavucontrol_get_window(pa_glib_mainloop *m, bool maximize, bool retry, int tab_number);

View File

@ -39,15 +39,11 @@ RoleWidget::RoleWidget(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>
RoleWidget* RoleWidget::create() { RoleWidget* RoleWidget::create() {
RoleWidget* w; RoleWidget* w;
Glib::RefPtr<Gtk::Builder> x = Gtk::Builder::create_from_file(GLADE_FILE, "streamWidget"); Glib::RefPtr<Gtk::Builder> x = Gtk::Builder::create_from_file(GLADE_FILE, "streamWidget");
x->get_widget_derived("streamWidget", w); w = Gtk::Builder::get_widget_derived<RoleWidget>(x, "streamWidget");
w->reference(); w->reference();
return w; return w;
} }
bool RoleWidget::onContextTriggerEvent(GdkEventButton*) {
return false;
}
void RoleWidget::onMuteToggleButton() { void RoleWidget::onMuteToggleButton() {
StreamWidget::onMuteToggleButton(); StreamWidget::onMuteToggleButton();
@ -69,7 +65,7 @@ void RoleWidget::executeVolumeUpdate() {
pa_operation* o; pa_operation* o;
if (!(o = pa_ext_stream_restore_write(get_context(), PA_UPDATE_REPLACE, &info, 1, TRUE, NULL, NULL))) { if (!(o = pa_ext_stream_restore_write(get_context(), PA_UPDATE_REPLACE, &info, 1, TRUE, NULL, NULL))) {
show_error(_("pa_ext_stream_restore_write() failed")); show_error(this, _("pa_ext_stream_restore_write() failed"));
return; return;
} }

View File

@ -35,7 +35,6 @@ public:
virtual void onMuteToggleButton(); virtual void onMuteToggleButton();
virtual void executeVolumeUpdate(); virtual void executeVolumeUpdate();
virtual bool onContextTriggerEvent(GdkEventButton*);
}; };
#endif #endif

View File

@ -35,13 +35,13 @@ SinkInputWidget::SinkInputWidget(BaseObjectType* cobject, const Glib::RefPtr<Gtk
directionLabel->set_label(txt = g_markup_printf_escaped("<i>%s</i>", _("on"))); directionLabel->set_label(txt = g_markup_printf_escaped("<i>%s</i>", _("on")));
g_free(txt); g_free(txt);
terminate.set_label(_("Terminate Playback")); addKillMenu(_("Terminate Playback"));
} }
SinkInputWidget* SinkInputWidget::create(MainWindow* mainWindow) { SinkInputWidget* SinkInputWidget::create(MainWindow* mainWindow) {
SinkInputWidget* w; SinkInputWidget* w;
Glib::RefPtr<Gtk::Builder> x = Gtk::Builder::create_from_file(GLADE_FILE, "streamWidget"); Glib::RefPtr<Gtk::Builder> x = Gtk::Builder::create_from_file(GLADE_FILE, "streamWidget");
x->get_widget_derived("streamWidget", w); w = Gtk::Builder::get_widget_derived<SinkInputWidget>(x, "streamWidget");
w->init(mainWindow); w->init(mainWindow);
w->reference(); w->reference();
@ -84,7 +84,7 @@ void SinkInputWidget::executeVolumeUpdate() {
pa_operation* o; pa_operation* o;
if (!(o = pa_context_set_sink_input_volume(get_context(), index, &volume, NULL, NULL))) { if (!(o = pa_context_set_sink_input_volume(get_context(), index, &volume, NULL, NULL))) {
show_error(_("pa_context_set_sink_input_volume() failed")); show_error(this, _("pa_context_set_sink_input_volume() failed"));
return; return;
} }
@ -99,17 +99,17 @@ void SinkInputWidget::onMuteToggleButton() {
pa_operation* o; pa_operation* o;
if (!(o = pa_context_set_sink_input_mute(get_context(), index, muteToggleButton->get_active(), NULL, NULL))) { if (!(o = pa_context_set_sink_input_mute(get_context(), index, muteToggleButton->get_active(), NULL, NULL))) {
show_error(_("pa_context_set_sink_input_mute() failed")); show_error(this, _("pa_context_set_sink_input_mute() failed"));
return; return;
} }
pa_operation_unref(o); pa_operation_unref(o);
} }
void SinkInputWidget::onKill() { void SinkInputWidget::onKill(const Glib::VariantBase& parameter) {
pa_operation* o; pa_operation* o;
if (!(o = pa_context_kill_sink_input(get_context(), index, NULL, NULL))) { if (!(o = pa_context_kill_sink_input(get_context(), index, NULL, NULL))) {
show_error(_("pa_context_kill_sink_input() failed")); show_error(this, _("pa_context_kill_sink_input() failed"));
return; return;
} }

View File

@ -41,7 +41,7 @@ public:
void updateDeviceComboBox(); void updateDeviceComboBox();
virtual void executeVolumeUpdate(); virtual void executeVolumeUpdate();
virtual void onMuteToggleButton(); virtual void onMuteToggleButton();
virtual void onKill(); virtual void onKill(const Glib::VariantBase& parameter);
virtual void onDeviceComboBoxChanged(); virtual void onDeviceComboBoxChanged();
private: private:

View File

@ -24,7 +24,7 @@
#include "sinkwidget.h" #include "sinkwidget.h"
#include <canberra-gtk.h> #include <canberra.h>
#if HAVE_EXT_DEVICE_RESTORE_API #if HAVE_EXT_DEVICE_RESTORE_API
# include <pulse/format.h> # include <pulse/format.h>
# include <pulse/ext-device-restore.h> # include <pulse/ext-device-restore.h>
@ -37,35 +37,35 @@ SinkWidget::SinkWidget(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>
#if HAVE_EXT_DEVICE_RESTORE_API #if HAVE_EXT_DEVICE_RESTORE_API
uint8_t i = 0; uint8_t i = 0;
x->get_widget("encodingSelect", encodingSelect); encodingSelect = x->get_widget<Gtk::Grid>("encodingSelect");
encodings[i].encoding = PA_ENCODING_PCM; encodings[i].encoding = PA_ENCODING_PCM;
x->get_widget("encodingFormatPCM", encodings[i].widget); encodings[i].widget = x->get_widget<Gtk::CheckButton>("encodingFormatPCM");
encodings[i].widget->signal_toggled().connect(sigc::mem_fun(*this, &SinkWidget::onEncodingsChange)); encodings[i].widget->signal_toggled().connect(sigc::mem_fun(*this, &SinkWidget::onEncodingsChange));
++i; ++i;
encodings[i].encoding = PA_ENCODING_AC3_IEC61937; encodings[i].encoding = PA_ENCODING_AC3_IEC61937;
x->get_widget("encodingFormatAC3", encodings[i].widget); encodings[i].widget = x->get_widget<Gtk::CheckButton>("encodingFormatAC3");
encodings[i].widget->signal_toggled().connect(sigc::mem_fun(*this, &SinkWidget::onEncodingsChange)); encodings[i].widget->signal_toggled().connect(sigc::mem_fun(*this, &SinkWidget::onEncodingsChange));
++i; ++i;
encodings[i].encoding = PA_ENCODING_EAC3_IEC61937; encodings[i].encoding = PA_ENCODING_EAC3_IEC61937;
x->get_widget("encodingFormatEAC3", encodings[i].widget); encodings[i].widget = x->get_widget<Gtk::CheckButton>("encodingFormatEAC3");
encodings[i].widget->signal_toggled().connect(sigc::mem_fun(*this, &SinkWidget::onEncodingsChange)); encodings[i].widget->signal_toggled().connect(sigc::mem_fun(*this, &SinkWidget::onEncodingsChange));
++i; ++i;
encodings[i].encoding = PA_ENCODING_MPEG_IEC61937; encodings[i].encoding = PA_ENCODING_MPEG_IEC61937;
x->get_widget("encodingFormatMPEG", encodings[i].widget); encodings[i].widget = x->get_widget<Gtk::CheckButton>("encodingFormatMPEG");
encodings[i].widget->signal_toggled().connect(sigc::mem_fun(*this, &SinkWidget::onEncodingsChange)); encodings[i].widget->signal_toggled().connect(sigc::mem_fun(*this, &SinkWidget::onEncodingsChange));
++i; ++i;
encodings[i].encoding = PA_ENCODING_DTS_IEC61937; encodings[i].encoding = PA_ENCODING_DTS_IEC61937;
x->get_widget("encodingFormatDTS", encodings[i].widget); encodings[i].widget = x->get_widget<Gtk::CheckButton>("encodingFormatDTS");
encodings[i].widget->signal_toggled().connect(sigc::mem_fun(*this, &SinkWidget::onEncodingsChange)); encodings[i].widget->signal_toggled().connect(sigc::mem_fun(*this, &SinkWidget::onEncodingsChange));
++i; ++i;
encodings[i].encoding = PA_ENCODING_INVALID; encodings[i].encoding = PA_ENCODING_INVALID;
x->get_widget("encodingFormatAAC", encodings[i].widget); encodings[i].widget = x->get_widget<Gtk::CheckButton>("encodingFormatAAC");
encodings[i].widget->set_sensitive(false); encodings[i].widget->set_sensitive(false);
#ifdef PA_ENCODING_MPEG2_AAC_IEC61937 #ifdef PA_ENCODING_MPEG2_AAC_IEC61937
if (pa_context_get_server_protocol_version(get_context()) >= 28) { if (pa_context_get_server_protocol_version(get_context()) >= 28) {
@ -76,7 +76,7 @@ SinkWidget::SinkWidget(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>
#endif #endif
++i; ++i;
encodings[i].encoding = PA_ENCODING_INVALID; encodings[i].encoding = PA_ENCODING_INVALID;
x->get_widget("encodingFormatTRUEHD", encodings[i].widget); encodings[i].widget = x->get_widget<Gtk::CheckButton>("encodingFormatTRUEHD");
encodings[i].widget->set_sensitive(false); encodings[i].widget->set_sensitive(false);
#ifdef PA_ENCODING_TRUEHD_IEC61937 #ifdef PA_ENCODING_TRUEHD_IEC61937
if (pa_context_get_server_protocol_version(get_context()) >= 33) { if (pa_context_get_server_protocol_version(get_context()) >= 33) {
@ -87,7 +87,7 @@ SinkWidget::SinkWidget(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>
#endif #endif
++i; ++i;
encodings[i].encoding = PA_ENCODING_INVALID; encodings[i].encoding = PA_ENCODING_INVALID;
x->get_widget("encodingFormatDTSHD", encodings[i].widget); encodings[i].widget = x->get_widget<Gtk::CheckButton>("encodingFormatDTSHD");
encodings[i].widget->set_sensitive(false); encodings[i].widget->set_sensitive(false);
#ifdef PA_ENCODING_DTSHD_IEC61937 #ifdef PA_ENCODING_DTSHD_IEC61937
if (pa_context_get_server_protocol_version(get_context()) >= 33) { if (pa_context_get_server_protocol_version(get_context()) >= 33) {
@ -102,7 +102,7 @@ SinkWidget::SinkWidget(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>
SinkWidget* SinkWidget::create(MainWindow* mainWindow) { SinkWidget* SinkWidget::create(MainWindow* mainWindow) {
SinkWidget* w; SinkWidget* w;
Glib::RefPtr<Gtk::Builder> x = Gtk::Builder::create_from_file(GLADE_FILE, "deviceWidget"); Glib::RefPtr<Gtk::Builder> x = Gtk::Builder::create_from_file(GLADE_FILE, "deviceWidget");
x->get_widget_derived("deviceWidget", w); w = Gtk::Builder::get_widget_derived<SinkWidget>(x, "deviceWidget");
w->init(mainWindow, "sink"); w->init(mainWindow, "sink");
w->reference(); w->reference();
return w; return w;
@ -114,20 +114,21 @@ void SinkWidget::executeVolumeUpdate() {
int playing = 0; int playing = 0;
if (!(o = pa_context_set_sink_volume_by_index(get_context(), index, &volume, NULL, NULL))) { if (!(o = pa_context_set_sink_volume_by_index(get_context(), index, &volume, NULL, NULL))) {
show_error(_("pa_context_set_sink_volume_by_index() failed")); show_error(this, _("pa_context_set_sink_volume_by_index() failed"));
return; return;
} }
pa_operation_unref(o); pa_operation_unref(o);
ca_context_playing(ca_gtk_context_get(), 2, &playing); snprintf(dev, sizeof(dev), "%lu", (unsigned long) index);
ca_context_playing(mpMainWindow->canberraContext, 2, &playing);
if (playing) if (playing)
return; return;
snprintf(dev, sizeof(dev), "%lu", (unsigned long) index); ca_context_change_device(mpMainWindow->canberraContext, dev);
ca_context_change_device(ca_gtk_context_get(), dev);
ca_gtk_play_for_widget(GTK_WIDGET(gobj()), ca_context_play(mpMainWindow->canberraContext,
2, 2,
CA_PROP_EVENT_DESCRIPTION, _("Volume Control Feedback Sound"), CA_PROP_EVENT_DESCRIPTION, _("Volume Control Feedback Sound"),
CA_PROP_EVENT_ID, "audio-volume-change", CA_PROP_EVENT_ID, "audio-volume-change",
@ -135,7 +136,7 @@ void SinkWidget::executeVolumeUpdate() {
CA_PROP_CANBERRA_ENABLE, "1", CA_PROP_CANBERRA_ENABLE, "1",
NULL); NULL);
ca_context_change_device(ca_gtk_context_get(), NULL); ca_context_change_device(mpMainWindow->canberraContext, NULL);
} }
void SinkWidget::onMuteToggleButton() { void SinkWidget::onMuteToggleButton() {
@ -146,7 +147,7 @@ void SinkWidget::onMuteToggleButton() {
pa_operation* o; pa_operation* o;
if (!(o = pa_context_set_sink_mute_by_index(get_context(), index, muteToggleButton->get_active(), NULL, NULL))) { if (!(o = pa_context_set_sink_mute_by_index(get_context(), index, muteToggleButton->get_active(), NULL, NULL))) {
show_error(_("pa_context_set_sink_mute_by_index() failed")); show_error(this, _("pa_context_set_sink_mute_by_index() failed"));
return; return;
} }
@ -160,7 +161,7 @@ void SinkWidget::onDefaultToggleButton() {
return; return;
if (!(o = pa_context_set_default_sink(get_context(), name.c_str(), NULL, NULL))) { if (!(o = pa_context_set_default_sink(get_context(), name.c_str(), NULL, NULL))) {
show_error(_("pa_context_set_default_sink() failed")); show_error(this, _("pa_context_set_default_sink() failed"));
return; return;
} }
pa_operation_unref(o); pa_operation_unref(o);
@ -180,7 +181,7 @@ void SinkWidget::onPortChange() {
Glib::ustring port = row[portModel.name]; Glib::ustring port = row[portModel.name];
if (!(o = pa_context_set_sink_port_by_index(get_context(), index, port.c_str(), NULL, NULL))) { if (!(o = pa_context_set_sink_port_by_index(get_context(), index, port.c_str(), NULL, NULL))) {
show_error(_("pa_context_set_sink_port_by_index() failed")); show_error(this, _("pa_context_set_sink_port_by_index() failed"));
return; return;
} }
@ -222,7 +223,7 @@ void SinkWidget::onEncodingsChange() {
} }
if (!(o = pa_ext_device_restore_save_formats(get_context(), PA_DEVICE_TYPE_SINK, index, n_formats, formats, NULL, NULL))) { if (!(o = pa_ext_device_restore_save_formats(get_context(), PA_DEVICE_TYPE_SINK, index, n_formats, formats, NULL, NULL))) {
show_error(_("pa_ext_device_restore_save_sink_formats() failed")); show_error(this, _("pa_ext_device_restore_save_sink_formats() failed"));
free(formats); free(formats);
return; return;
} }

View File

@ -35,7 +35,7 @@ SourceOutputWidget::SourceOutputWidget(BaseObjectType* cobject, const Glib::RefP
directionLabel->set_label(txt = g_markup_printf_escaped("<i>%s</i>", _("from"))); directionLabel->set_label(txt = g_markup_printf_escaped("<i>%s</i>", _("from")));
g_free(txt); g_free(txt);
terminate.set_label(_("Terminate Recording")); addKillMenu(_("Terminate Recording"));
#if !HAVE_SOURCE_OUTPUT_VOLUMES #if !HAVE_SOURCE_OUTPUT_VOLUMES
/* Source Outputs do not have volume controls in versions of PA < 1.0 */ /* Source Outputs do not have volume controls in versions of PA < 1.0 */
@ -47,7 +47,7 @@ SourceOutputWidget::SourceOutputWidget(BaseObjectType* cobject, const Glib::RefP
SourceOutputWidget* SourceOutputWidget::create(MainWindow* mainWindow) { SourceOutputWidget* SourceOutputWidget::create(MainWindow* mainWindow) {
SourceOutputWidget* w; SourceOutputWidget* w;
Glib::RefPtr<Gtk::Builder> x = Gtk::Builder::create_from_file(GLADE_FILE, "streamWidget"); Glib::RefPtr<Gtk::Builder> x = Gtk::Builder::create_from_file(GLADE_FILE, "streamWidget");
x->get_widget_derived("streamWidget", w); w = Gtk::Builder::get_widget_derived<SourceOutputWidget>(x, "streamWidget");
w->init(mainWindow); w->init(mainWindow);
w->reference(); w->reference();
return w; return w;
@ -90,7 +90,7 @@ void SourceOutputWidget::executeVolumeUpdate() {
pa_operation* o; pa_operation* o;
if (!(o = pa_context_set_source_output_volume(get_context(), index, &volume, NULL, NULL))) { if (!(o = pa_context_set_source_output_volume(get_context(), index, &volume, NULL, NULL))) {
show_error(_("pa_context_set_source_output_volume() failed")); show_error(this, _("pa_context_set_source_output_volume() failed"));
return; return;
} }
@ -105,7 +105,7 @@ void SourceOutputWidget::onMuteToggleButton() {
pa_operation* o; pa_operation* o;
if (!(o = pa_context_set_source_output_mute(get_context(), index, muteToggleButton->get_active(), NULL, NULL))) { if (!(o = pa_context_set_source_output_mute(get_context(), index, muteToggleButton->get_active(), NULL, NULL))) {
show_error(_("pa_context_set_source_output_mute() failed")); show_error(this, _("pa_context_set_source_output_mute() failed"));
return; return;
} }
@ -113,10 +113,10 @@ void SourceOutputWidget::onMuteToggleButton() {
} }
#endif #endif
void SourceOutputWidget::onKill() { void SourceOutputWidget::onKill(const Glib::VariantBase& parameter) {
pa_operation* o; pa_operation* o;
if (!(o = pa_context_kill_source_output(get_context(), index, NULL, NULL))) { if (!(o = pa_context_kill_source_output(get_context(), index, NULL, NULL))) {
show_error(_("pa_context_kill_source_output() failed")); show_error(this, _("pa_context_kill_source_output() failed"));
return; return;
} }

View File

@ -43,7 +43,7 @@ public:
virtual void executeVolumeUpdate(); virtual void executeVolumeUpdate();
virtual void onMuteToggleButton(); virtual void onMuteToggleButton();
#endif #endif
virtual void onKill(); virtual void onKill(const Glib::VariantBase& parameter);
virtual void onDeviceComboBoxChanged(); virtual void onDeviceComboBoxChanged();
private: private:

View File

@ -33,7 +33,7 @@ SourceWidget::SourceWidget(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Buil
SourceWidget* SourceWidget::create(MainWindow* mainWindow) { SourceWidget* SourceWidget::create(MainWindow* mainWindow) {
SourceWidget* w; SourceWidget* w;
Glib::RefPtr<Gtk::Builder> x = Gtk::Builder::create_from_file(GLADE_FILE, "deviceWidget"); Glib::RefPtr<Gtk::Builder> x = Gtk::Builder::create_from_file(GLADE_FILE, "deviceWidget");
x->get_widget_derived("deviceWidget", w); w = Gtk::Builder::get_widget_derived<SourceWidget>(x, "deviceWidget");
w->init(mainWindow, "source"); w->init(mainWindow, "source");
w->reference(); w->reference();
return w; return w;
@ -43,7 +43,7 @@ void SourceWidget::executeVolumeUpdate() {
pa_operation* o; pa_operation* o;
if (!(o = pa_context_set_source_volume_by_index(get_context(), index, &volume, NULL, NULL))) { if (!(o = pa_context_set_source_volume_by_index(get_context(), index, &volume, NULL, NULL))) {
show_error(_("pa_context_set_source_volume_by_index() failed")); show_error(this, _("pa_context_set_source_volume_by_index() failed"));
return; return;
} }
@ -58,7 +58,7 @@ void SourceWidget::onMuteToggleButton() {
pa_operation* o; pa_operation* o;
if (!(o = pa_context_set_source_mute_by_index(get_context(), index, muteToggleButton->get_active(), NULL, NULL))) { if (!(o = pa_context_set_source_mute_by_index(get_context(), index, muteToggleButton->get_active(), NULL, NULL))) {
show_error(_("pa_context_set_source_mute_by_index() failed")); show_error(this, _("pa_context_set_source_mute_by_index() failed"));
return; return;
} }
@ -72,7 +72,7 @@ void SourceWidget::onDefaultToggleButton() {
return; return;
if (!(o = pa_context_set_default_source(get_context(), name.c_str(), NULL, NULL))) { if (!(o = pa_context_set_default_source(get_context(), name.c_str(), NULL, NULL))) {
show_error(_("pa_context_set_default_source() failed")); show_error(this, _("pa_context_set_default_source() failed"));
return; return;
} }
pa_operation_unref(o); pa_operation_unref(o);
@ -94,7 +94,7 @@ void SourceWidget::onPortChange() {
Glib::ustring port = row[portModel.name]; Glib::ustring port = row[portModel.name];
if (!(o = pa_context_set_source_port_by_index(get_context(), index, port.c_str(), NULL, NULL))) { if (!(o = pa_context_set_source_port_by_index(get_context(), index, port.c_str(), NULL, NULL))) {
show_error(_("pa_context_set_source_port_by_index() failed")); show_error(this, _("pa_context_set_source_port_by_index() failed"));
return; return;
} }

View File

@ -34,26 +34,20 @@ StreamWidget::StreamWidget(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Buil
mpMainWindow(NULL) { mpMainWindow(NULL) {
/* MinimalStreamWidget member variables. */ /* MinimalStreamWidget member variables. */
x->get_widget("streamChannelsVBox", channelsVBox); channelsVBox = x->get_widget<Gtk::Box>("streamChannelsVBox");
x->get_widget("streamNameLabel", nameLabel); nameLabel = x->get_widget<Gtk::Label>("streamNameLabel");
x->get_widget("streamBoldNameLabel", boldNameLabel); boldNameLabel = x->get_widget<Gtk::Label>("streamBoldNameLabel");
x->get_widget("streamIconImage", iconImage); iconImage = x->get_widget<Gtk::Image>("streamIconImage");
x->get_widget("streamLockToggleButton", lockToggleButton); lockToggleButton = x->get_widget<Gtk::ToggleButton>("streamLockToggleButton");
x->get_widget("streamMuteToggleButton", muteToggleButton); muteToggleButton = x->get_widget<Gtk::ToggleButton>("streamMuteToggleButton");
x->get_widget("directionLabel", directionLabel); directionLabel = x->get_widget<Gtk::Label>("directionLabel");
x->get_widget("deviceComboBox", deviceComboBox); deviceComboBox = x->get_widget<Gtk::ComboBoxText>("deviceComboBox");
this->signal_button_press_event().connect(sigc::mem_fun(*this, &StreamWidget::onContextTriggerEvent));
muteToggleButton->signal_clicked().connect(sigc::mem_fun(*this, &StreamWidget::onMuteToggleButton)); muteToggleButton->signal_clicked().connect(sigc::mem_fun(*this, &StreamWidget::onMuteToggleButton));
lockToggleButton->signal_clicked().connect(sigc::mem_fun(*this, &StreamWidget::onLockToggleButton)); lockToggleButton->signal_clicked().connect(sigc::mem_fun(*this, &StreamWidget::onLockToggleButton));
deviceComboBox->signal_changed().connect(sigc::mem_fun(*this, &StreamWidget::onDeviceComboBoxChanged)); deviceComboBox->signal_changed().connect(sigc::mem_fun(*this, &StreamWidget::onDeviceComboBoxChanged));
terminate.set_label(_("Terminate"));
terminate.signal_activate().connect(sigc::mem_fun(*this, &StreamWidget::onKill));
contextMenu.append(terminate);
contextMenu.show_all();
for (unsigned i = 0; i < PA_CHANNELS_MAX; i++) for (unsigned i = 0; i < PA_CHANNELS_MAX; i++)
channelWidgets[i] = NULL; channelWidgets[i] = NULL;
} }
@ -64,12 +58,35 @@ void StreamWidget::init(MainWindow* mainWindow) {
MinimalStreamWidget::init(); MinimalStreamWidget::init();
} }
bool StreamWidget::onContextTriggerEvent(GdkEventButton* event) { void StreamWidget::addKillMenu(const char* killLabel) {
if (GDK_BUTTON_PRESS == event->type && 3 == event->button) { auto gesture = Gtk::GestureClick::create();
contextMenu.popup_at_pointer((GdkEvent*)event); gesture->set_button(3);
return true; gesture->set_exclusive(true);
gesture->signal_pressed().connect(sigc::mem_fun(*this, &StreamWidget::onContextTriggerEvent));
this->add_controller(gesture);
const std::string actionName = "kill", groupName="streamwidget";
auto action = Gio::SimpleAction::create(actionName);
action->set_enabled(true);
action->signal_activate().connect(sigc::mem_fun(*this, &StreamWidget::onKill));
auto group = Gio::SimpleActionGroup::create();
group->add_action(action);
insert_action_group(groupName, group);
auto menuModel = Gio::Menu::create();
menuModel->append(killLabel, groupName + "." + actionName);
contextMenu.set_menu_model(menuModel);
contextMenu.set_parent(*this);
}
void StreamWidget::onContextTriggerEvent(gint n_press, gdouble x, gdouble y) {
if (n_press == 1) {
contextMenu.set_pointing_to(Gdk::Rectangle {(int) x, (int) y, 0 , 0});
contextMenu.popup();
} }
return false;
} }
void StreamWidget::setChannelMap(const pa_channel_map &m, bool can_decibel) { void StreamWidget::setChannelMap(const pa_channel_map &m, bool can_decibel) {
@ -79,7 +96,7 @@ void StreamWidget::setChannelMap(const pa_channel_map &m, bool can_decibel) {
for (int i = 0; i < m.channels; i++) { for (int i = 0; i < m.channels; i++) {
ChannelWidget *cw = channelWidgets[i]; ChannelWidget *cw = channelWidgets[i];
channelsVBox->pack_start(*cw, false, false, 0); channelsVBox->prepend(*cw);
cw->unreference(); cw->unreference();
} }
@ -144,7 +161,7 @@ bool StreamWidget::timeoutEvent() {
void StreamWidget::executeVolumeUpdate() { void StreamWidget::executeVolumeUpdate() {
} }
void StreamWidget::onKill() { void StreamWidget::onKill(const Glib::VariantBase& parameter) {
} }
void StreamWidget::onDeviceComboBoxChanged() { void StreamWidget::onDeviceComboBoxChanged() {

View File

@ -53,21 +53,21 @@ public:
virtual void onMuteToggleButton(); virtual void onMuteToggleButton();
virtual void onLockToggleButton(); virtual void onLockToggleButton();
virtual bool onContextTriggerEvent(GdkEventButton*); virtual void onContextTriggerEvent(gint n_press, gdouble x, gdouble y);
sigc::connection timeoutConnection; sigc::connection timeoutConnection;
bool timeoutEvent(); bool timeoutEvent();
virtual void executeVolumeUpdate(); virtual void executeVolumeUpdate();
virtual void onKill(); virtual void onKill(const Glib::VariantBase& parameter);
virtual void onDeviceComboBoxChanged(); virtual void onDeviceComboBoxChanged();
protected: protected:
MainWindow* mpMainWindow; MainWindow* mpMainWindow;
Gtk::Menu contextMenu; Gtk::PopoverMenu contextMenu;
Gtk::MenuItem terminate; void addKillMenu(const char* killLabel);
}; };
#endif #endif