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',
version : '5.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')
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)
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)
libpulsemlglib_dep = dependency('libpulse-mainloop-glib', version : '>= 0.9.16', required : true)

View File

@ -28,14 +28,14 @@
/*** CardWidget ***/
CardWidget::CardWidget(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& x) :
Gtk::VBox(cobject) {
Gtk::Box(cobject) {
x->get_widget("cardNameLabel", nameLabel);
x->get_widget("profileList", profileList);
x->get_widget("cardIconImage", iconImage);
x->get_widget("codecBox", codecBox);
x->get_widget("codecList", codecList);
x->get_widget("profileLockToggleButton", profileLockToggleButton);
nameLabel = x->get_widget<Gtk::Label>("cardNameLabel");
profileList = x->get_widget<Gtk::ComboBox>("profileList");
iconImage = x->get_widget<Gtk::Image>("cardIconImage");
codecBox = x->get_widget<Gtk::Box>("codecBox");
codecList = x->get_widget<Gtk::ComboBox>("codecList");
profileLockToggleButton = x->get_widget<Gtk::ToggleButton>("profileLockToggleButton");
profileListStore = Gtk::ListStore::create(profileModel);
profileList->set_model(profileListStore);
@ -61,7 +61,7 @@ CardWidget::CardWidget(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>
CardWidget* CardWidget::create() {
CardWidget* w;
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();
return w;
}
@ -123,7 +123,7 @@ void CardWidget::onProfileChange() {
Glib::ustring profile = row[profileModel.name];
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;
}

View File

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

View File

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

View File

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

View File

@ -37,29 +37,45 @@ DeviceWidget::DeviceWidget(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Buil
mDigital(false) {
/* MinimalStreamWidget member variables. */
x->get_widget("deviceChannelsVBox", channelsVBox);
x->get_widget("deviceNameLabel", nameLabel);
x->get_widget("deviceBoldNameLabel", boldNameLabel);
x->get_widget("deviceIconImage", iconImage);
channelsVBox = x->get_widget<Gtk::Box>("deviceChannelsVBox");
nameLabel = x->get_widget<Gtk::Label>("deviceNameLabel");
boldNameLabel = x->get_widget<Gtk::Label>("deviceBoldNameLabel");
iconImage= x->get_widget<Gtk::Image>("deviceIconImage");
x->get_widget("deviceLockToggleButton", lockToggleButton);
x->get_widget("deviceMuteToggleButton", muteToggleButton);
x->get_widget("defaultToggleButton", defaultToggleButton);
x->get_widget("portSelect", portSelect);
x->get_widget("portList", portList);
x->get_widget("advancedOptions", advancedOptions);
x->get_widget("offsetSelect", offsetSelect);
x->get_widget("offsetButton", offsetButton);
lockToggleButton = x->get_widget<Gtk::ToggleButton>("deviceLockToggleButton");
muteToggleButton = x->get_widget<Gtk::ToggleButton>("deviceMuteToggleButton");
defaultToggleButton= x->get_widget<Gtk::ToggleButton>("defaultToggleButton");
portSelect = x->get_widget<Gtk::Box>("portSelect");
portList = x->get_widget<Gtk::ComboBox>("portList");
advancedOptions = x->get_widget<Gtk::Expander>("advancedOptions");
offsetSelect = x->get_widget<Gtk::Box>("offsetSelect");
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));
lockToggleButton->signal_clicked().connect(sigc::mem_fun(*this, &DeviceWidget::onLockToggleButton));
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();
auto gesture = Gtk::GestureClick::create();
gesture->set_button(3);
gesture->set_exclusive(true);
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);
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++) {
ChannelWidget *cw = channelWidgets[i];
channelsVBox->pack_start(*cw, false, false, 0);
channelsVBox->prepend(*cw);
cw->unreference();
}
@ -160,7 +176,7 @@ void DeviceWidget::onOffsetChange() {
if (!(o = pa_context_set_port_latency_offset(get_context(),
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;
}
pa_operation_unref(o);
@ -225,55 +241,64 @@ void DeviceWidget::prepareMenu() {
updateAdvancedOptionsVisibility();
}
bool DeviceWidget::onContextTriggerEvent(GdkEventButton* event) {
if (GDK_BUTTON_PRESS == event->type && 3 == event->button) {
contextMenu.popup_at_pointer((GdkEvent*)event);
return true;
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();
}
}
return false;
}
void DeviceWidget::renamePopup() {
void DeviceWidget::openRenamePopup(const Glib::VariantBase& parameter) {
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();
auto dialog = Gtk::AlertDialog::create(_("Sorry, but device renaming is not supported."));
dialog->set_modal(true);
dialog->set_detail(_("You need to load module-device-manager in the PulseAudio server in order to rename devices"));
dialog->show(*mpMainWindow);
return;
}
Gtk::Dialog* dialog;
Gtk::Entry* renameText;
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());
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))) {
show_error(_("pa_ext_device_manager_write() failed"));
renameDialog->present();
}
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;
}
pa_operation_unref(o);
g_free(key);
}
delete dialog;
g_free((char*)deviceKey);
delete this;
}
void DeviceWidget::updateAdvancedOptionsVisibility() {

View File

@ -57,7 +57,7 @@ public:
virtual void onLockToggleButton();
virtual void onDefaultToggleButton();
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);
void onOffsetChange();
@ -73,7 +73,7 @@ public:
void prepareMenu();
void renamePopup();
void openRenamePopup(const Glib::VariantBase& parameter);
protected:
MainWindow *mpMainWindow;
@ -85,8 +85,7 @@ protected:
virtual void onPortChange() = 0;
Gtk::Menu contextMenu;
Gtk::MenuItem rename;
Gtk::PopoverMenu contextMenu;
/* Tree model columns */
class ModelColumns : public Gtk::TreeModel::ColumnRecord
@ -103,7 +102,7 @@ protected:
ModelColumns portModel;
Gtk::Expander *advancedOptions;
Gtk::HBox *portSelect, *offsetSelect;
Gtk::Box *portSelect, *offsetSelect;
Gtk::ComboBox *portList;
Glib::RefPtr<Gtk::ListStore> treeModel;
Glib::RefPtr<Gtk::Adjustment> offsetAdjustment;
@ -114,7 +113,15 @@ protected:
private:
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

View File

@ -76,30 +76,26 @@ MainWindow::MainWindow(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>
canRenameDevices(false),
m_connected(false),
m_config_filename(NULL) {
ca_context_create (&canberraContext);
ca_context_set_driver(canberraContext, "pulse");
x->get_widget("cardsVBox", cardsVBox);
x->get_widget("streamsVBox", streamsVBox);
x->get_widget("recsVBox", recsVBox);
x->get_widget("sinksVBox", sinksVBox);
x->get_widget("sourcesVBox", sourcesVBox);
x->get_widget("noCardsLabel", noCardsLabel);
x->get_widget("noStreamsLabel", noStreamsLabel);
x->get_widget("noRecsLabel", noRecsLabel);
x->get_widget("noSinksLabel", noSinksLabel);
x->get_widget("noSourcesLabel", noSourcesLabel);
x->get_widget("connectingLabel", connectingLabel);
x->get_widget("sinkInputTypeComboBox", sinkInputTypeComboBox);
x->get_widget("sourceOutputTypeComboBox", sourceOutputTypeComboBox);
x->get_widget("sinkTypeComboBox", sinkTypeComboBox);
x->get_widget("sourceTypeComboBox", sourceTypeComboBox);
x->get_widget("notebook", notebook);
x->get_widget("showVolumeMetersCheckButton", 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(); });
cardsVBox = x->get_widget<Gtk::Box>("cardsVBox");
streamsVBox = x->get_widget<Gtk::Box>("streamsVBox");
recsVBox = x->get_widget<Gtk::Box>("recsVBox");
sinksVBox = x->get_widget<Gtk::Box>("sinksVBox");
sourcesVBox = x->get_widget<Gtk::Box>("sourcesVBox");
noCardsLabel = x->get_widget<Gtk::Label>("noCardsLabel");
noStreamsLabel = x->get_widget<Gtk::Label>("noStreamsLabel");
noRecsLabel = x->get_widget<Gtk::Label>("noRecsLabel");
noSinksLabel = x->get_widget<Gtk::Label>("noSinksLabel");
noSourcesLabel = x->get_widget<Gtk::Label>("noSourcesLabel");
connectingLabel = x->get_widget<Gtk::Label>("connectingLabel");
sinkInputTypeComboBox = x->get_widget<Gtk::ComboBox>("sinkInputTypeComboBox");
sourceOutputTypeComboBox = x->get_widget<Gtk::ComboBox>("sourceOutputTypeComboBox");
sinkTypeComboBox = x->get_widget<Gtk::ComboBox>("sinkTypeComboBox");
sourceTypeComboBox = x->get_widget<Gtk::ComboBox>("sourceTypeComboBox");
notebook = x->get_widget<Gtk::Notebook>("notebook");
showVolumeMetersCheckButton = x->get_widget<Gtk::CheckButton>("showVolumeMetersCheckButton");
sinkInputTypeComboBox->set_active((int) showSinkInputType);
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));
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();
g_assert(config);
@ -134,7 +133,7 @@ MainWindow::MainWindow(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>
int default_width, default_height;
get_default_size(default_width, 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);
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, "liststore4");
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");
if (w && maximize)
w->maximize();
@ -197,26 +196,26 @@ MainWindow* MainWindow::create(bool maximize) {
void MainWindow::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) {
switch (event->keyval) {
if ((state & Gdk::ModifierType::CONTROL_MASK) == Gdk::ModifierType::CONTROL_MASK) {
switch (keyval) {
case GDK_KEY_KP_1:
case GDK_KEY_KP_2:
case GDK_KEY_KP_3:
case GDK_KEY_KP_4:
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;
case GDK_KEY_1:
case GDK_KEY_2:
case GDK_KEY_3:
case GDK_KEY_4:
case GDK_KEY_5:
notebook->set_current_page(event->keyval - GDK_KEY_1);
notebook->set_current_page(keyval - GDK_KEY_1);
return true;
case GDK_KEY_W:
case GDK_KEY_Q:
@ -226,7 +225,7 @@ bool MainWindow::on_key_press_event(GdkEventKey* event) {
return true;
}
}
return Gtk::Window::on_key_press_event(event);
return false;
}
MainWindow::~MainWindow() {
@ -234,7 +233,7 @@ MainWindow::~MainWindow() {
g_assert(config);
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", "height", height);
g_key_file_set_integer(config, "window", "sinkInputType", sinkInputTypeComboBox->get_active_row_number());
@ -247,7 +246,7 @@ MainWindow::~MainWindow() {
GError *err = NULL;
gchar *filedata = g_key_file_to_data(config, &filelen, &err);
if (err) {
show_error(_("Error saving preferences"));
show_error(this, _("Error saving preferences"));
g_error_free(err);
goto finish;
}
@ -256,7 +255,7 @@ MainWindow::~MainWindow() {
g_free(filedata);
if (err) {
gchar* msg = g_strconcat(_("Error writing config file %s"), m_config_filename, NULL);
show_error(msg);
show_error(this, msg);
g_free(msg);
g_error_free(err);
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<Gdk::Pixbuf> pixbuf;
gint width = 24, height = 24;
Gtk::IconSize::lookup(size, width, height);
theme = Gtk::IconTheme::get_default();
try {
pixbuf = theme->load_icon(name, width, Gtk::ICON_LOOKUP_GENERIC_FALLBACK | Gtk::ICON_LOOKUP_FORCE_SIZE);
} catch (Glib::Error &e) {
/* Ignore errors. */
theme = Gtk::IconTheme::get_for_display(Gdk::Display::get_default());
std::string iconName(name);
while (!theme->has_icon(iconName.c_str())) {
size_t lastDashIndex = iconName.find_last_of("-");
if (lastDashIndex == std::string::npos) {
iconName = "gtk-missing-image";
break;
}
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);
iconName = iconName.substr(0,lastDashIndex);
}
i->set_from_icon_name(iconName.c_str());
}
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];
else {
cardWidgets[info.index] = w = CardWidget::create();
cardsVBox->pack_start(*w, false, false, 0);
cardsVBox->append(*w);
w->unreference();
w->index = info.index;
is_new = true;
@ -382,7 +367,7 @@ void MainWindow::updateCard(const pa_card_info &info) {
g_free(txt);
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;
profile_priorities.clear();
@ -556,7 +541,7 @@ bool MainWindow::updateSink(const pa_sink_info &info) {
else {
sinkWidgets[info.index] = w = SinkWidget::create(this);
w->setChannelMap(info.channel_map, !!(info.flags & PA_SINK_DECIBEL_VOLUME));
sinksVBox->pack_start(*w, false, false, 0);
sinksVBox->append(*w);
w->unreference();
w->index = info.index;
w->monitor_index = info.monitor_source;
@ -580,7 +565,7 @@ bool MainWindow::updateSink(const pa_sink_info &info) {
g_free(txt);
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->muteToggleButton->set_active(info.mute);
@ -630,7 +615,7 @@ static void read_callback(pa_stream *s, size_t length, void *userdata) {
double v;
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;
}
@ -675,7 +660,7 @@ pa_stream* MainWindow::createMonitorStreamForSource(uint32_t source_idx, uint32_
snprintf(t, sizeof(t), "%u", source_idx);
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;
}
@ -690,7 +675,7 @@ pa_stream* MainWindow::createMonitorStreamForSource(uint32_t source_idx, uint32_
(!showVolumeMetersCheckButton->get_active() ? PA_STREAM_START_CORKED : PA_STREAM_NOFLAGS));
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);
return NULL;
}
@ -722,7 +707,7 @@ void MainWindow::updateSource(const pa_source_info &info) {
else {
sourceWidgets[info.index] = w = SourceWidget::create(this);
w->setChannelMap(info.channel_map, !!(info.flags & PA_SOURCE_DECIBEL_VOLUME));
sourcesVBox->pack_start(*w, false, false, 0);
sourcesVBox->append(*w);
w->unreference();
w->index = info.index;
is_new = true;
@ -748,7 +733,7 @@ void MainWindow::updateSource(const pa_source_info &info) {
g_free(txt);
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->muteToggleButton->set_active(info.mute);
@ -817,7 +802,7 @@ void MainWindow::setIconFromProplist(Gtk::Image *icon, pa_proplist *l, const cha
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) {
@ -840,7 +825,7 @@ void MainWindow::updateSinkInput(const pa_sink_input_info &info) {
} else {
sinkInputWidgets[info.index] = w = SinkInputWidget::create(this);
w->setChannelMap(info.channel_map, true);
streamsVBox->pack_start(*w, false, false, 0);
streamsVBox->append(*w);
w->unreference();
w->index = info.index;
w->clientIndex = info.client;
@ -899,7 +884,7 @@ void MainWindow::updateSourceOutput(const pa_source_output_info &info) {
#if HAVE_SOURCE_OUTPUT_VOLUMES
w->setChannelMap(info.channel_map, true);
#endif
recsVBox->pack_start(*w, false, false, 0);
recsVBox->append(*w);
w->unreference();
w->index = info.index;
w->clientIndex = info.client;
@ -996,7 +981,7 @@ bool MainWindow::createEventRoleWidget() {
};
eventRoleWidget = RoleWidget::create();
streamsVBox->pack_start(*eventRoleWidget, false, false, 0);
streamsVBox->append(*eventRoleWidget);
eventRoleWidget->unreference();
eventRoleWidget->role = "sink-input-by-media-role:event";
eventRoleWidget->setChannelMap(cm, true);
@ -1004,7 +989,7 @@ bool MainWindow::createEventRoleWidget() {
eventRoleWidget->boldNameLabel->set_text("");
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 = "";
@ -1292,7 +1277,7 @@ void MainWindow::removeSink(uint32_t index) {
if (!sinkWidgets.count(index))
return;
delete sinkWidgets[index];
sinksVBox->remove(*sinkWidgets[index]);
sinkWidgets.erase(index);
updateDeviceVisibility();
}
@ -1301,7 +1286,7 @@ void MainWindow::removeSource(uint32_t index) {
if (!sourceWidgets.count(index))
return;
delete sourceWidgets[index];
sourcesVBox->remove(*sourceWidgets[index]);
sourceWidgets.erase(index);
updateDeviceVisibility();
}
@ -1310,7 +1295,7 @@ void MainWindow::removeSinkInput(uint32_t index) {
if (!sinkInputWidgets.count(index))
return;
delete sinkInputWidgets[index];
streamsVBox->remove(*sinkInputWidgets[index]);
sinkInputWidgets.erase(index);
updateDeviceVisibility();
}
@ -1319,7 +1304,7 @@ void MainWindow::removeSourceOutput(uint32_t index) {
if (!sourceOutputWidgets.count(index))
return;
delete sourceOutputWidgets[index];
recsVBox->remove(*sourceOutputWidgets[index]);
sourceOutputWidgets.erase(index);
updateDeviceVisibility();
}

View File

@ -29,6 +29,8 @@ class MainWindow;
# include <pulse/ext-device-restore.h>
#endif
#include <canberra.h>
#include <unordered_map>
class CardWidget;
@ -76,7 +78,7 @@ public:
void setConnectingMessage(const char *string = NULL);
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::ComboBox *sinkInputTypeComboBox, *sourceOutputTypeComboBox, *sinkTypeComboBox, *sourceTypeComboBox;
Gtk::CheckButton *showVolumeMetersCheckButton;
@ -116,9 +118,11 @@ public:
bool canRenameDevices;
ca_context *canberraContext;
protected:
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:
gboolean m_connected;

View File

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

View File

@ -26,7 +26,7 @@
/*** MinimalStreamWidget ***/
MinimalStreamWidget::MinimalStreamWidget(BaseObjectType* cobject) :
Gtk::VBox(cobject),
Gtk::Box(cobject),
channelsVBox(NULL),
nameLabel(NULL),
boldNameLabel(NULL),
@ -53,7 +53,7 @@ void MinimalStreamWidget::init() {
* in the constructor. */
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
* 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)
void MinimalStreamWidget::updatePeak(double v) {
if (lastPeak >= DECAY_STEP)
if (v < lastPeak - DECAY_STEP)
v = lastPeak - DECAY_STEP;

View File

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

View File

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

View File

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

View File

@ -29,8 +29,6 @@
#include <json-glib/json-glib.h>
#endif
#include <canberra-gtk.h>
#include "pavucontrol.h"
#include "i18n.h"
#include "minimalstreamwidget.h"
@ -58,15 +56,27 @@ static int tab_number = 0;
static bool retry = false;
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];
snprintf(buf, sizeof(buf), "%s: %s", txt, pa_strerror(pa_context_errno(context)));
Gtk::MessageDialog dialog(buf, false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
dialog.run();
PavuApplication::get_instance().quit();
auto dialog = Gtk::AlertDialog::create(buf);
dialog->set_modal(true);
if (GTK_IS_WINDOW(root->gobj())) {
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) {
@ -74,7 +84,7 @@ static void dec_outstanding(MainWindow *w) {
return;
if (--n_outstanding <= 0) {
w->get_window()->set_cursor();
w->set_cursor(Gdk::Cursor::create("default"));;
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)
return;
show_error(_("Card callback failure"));
show_error(w, _("Card callback failure"));
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)
return;
show_error(_("Sink callback failure"));
show_error(w, _("Sink callback failure"));
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)
return;
show_error(_("Source callback failure"));
show_error(w, _("Source callback failure"));
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)
return;
show_error(_("Sink input callback failure"));
show_error(w, _("Sink input callback failure"));
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)
return;
show_error(_("Source output callback failure"));
show_error(w, _("Source output callback failure"));
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)
return;
show_error(_("Client callback failure"));
show_error(w, _("Client callback failure"));
return;
}
@ -491,7 +501,7 @@ void server_info_cb(pa_context *, const pa_server_info *i, void *userdata) {
MainWindow *w = static_cast<MainWindow*>(userdata);
if (!i) {
show_error(_("Server info callback failure"));
show_error(w, _("Server info callback failure"));
return;
}
@ -527,7 +537,7 @@ static void ext_stream_restore_subscribe_cb(pa_context *c, void *userdata) {
pa_operation *o;
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;
}
@ -566,7 +576,7 @@ static void ext_device_restore_subscribe_cb(pa_context *c, pa_device_type_t type
return;
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;
}
@ -603,7 +613,7 @@ static void ext_device_manager_subscribe_cb(pa_context *c, void *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"));
show_error(w, _("pa_ext_device_manager_read() failed"));
return;
}
@ -620,7 +630,7 @@ void subscribe_cb(pa_context *c, pa_subscription_event_type_t t, uint32_t index,
else {
pa_operation *o;
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;
}
pa_operation_unref(o);
@ -633,7 +643,7 @@ void subscribe_cb(pa_context *c, pa_subscription_event_type_t t, uint32_t index,
else {
pa_operation *o;
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;
}
pa_operation_unref(o);
@ -646,7 +656,7 @@ void subscribe_cb(pa_context *c, pa_subscription_event_type_t t, uint32_t index,
else {
pa_operation *o;
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;
}
pa_operation_unref(o);
@ -659,7 +669,7 @@ void subscribe_cb(pa_context *c, pa_subscription_event_type_t t, uint32_t index,
else {
pa_operation *o;
if (!(o = pa_context_get_source_output_info(c, index, source_output_cb, w))) {
show_error(_("pa_context_get_sink_input_info() failed"));
show_error(w, _("pa_context_get_sink_input_info() failed"));
return;
}
pa_operation_unref(o);
@ -672,7 +682,7 @@ void subscribe_cb(pa_context *c, pa_subscription_event_type_t t, uint32_t index,
else {
pa_operation *o;
if (!(o = pa_context_get_client_info(c, index, client_cb, w))) {
show_error(_("pa_context_get_client_info() failed"));
show_error(w, _("pa_context_get_client_info() failed"));
return;
}
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: {
pa_operation *o;
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;
}
pa_operation_unref(o);
@ -695,7 +705,7 @@ void subscribe_cb(pa_context *c, pa_subscription_event_type_t t, uint32_t index,
else {
pa_operation *o;
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;
}
pa_operation_unref(o);
@ -738,7 +748,7 @@ void context_state_callback(pa_context *c, void *userdata) {
PA_SUBSCRIPTION_MASK_CLIENT|
PA_SUBSCRIPTION_MASK_SERVER|
PA_SUBSCRIPTION_MASK_CARD), NULL, NULL))) {
show_error(_("pa_context_subscribe() failed"));
show_error(w, _("pa_context_subscribe() failed"));
return;
}
pa_operation_unref(o);
@ -747,49 +757,49 @@ void context_state_callback(pa_context *c, void *userdata) {
n_outstanding = 0;
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;
}
pa_operation_unref(o);
n_outstanding++;
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;
}
pa_operation_unref(o);
n_outstanding++;
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;
}
pa_operation_unref(o);
n_outstanding++;
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;
}
pa_operation_unref(o);
n_outstanding++;
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;
}
pa_operation_unref(o);
n_outstanding++;
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;
}
pa_operation_unref(o);
n_outstanding++;
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;
}
pa_operation_unref(o);
@ -916,8 +926,6 @@ MainWindow* pavucontrol_get_window(pa_glib_mainloop *m, bool maximize, bool _ret
tab_number = _tab_number;
retry = _retry;
ca_context_set_driver(ca_gtk_context_get(), "pulse");
mainWindow = MainWindow::create(maximize);
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 <gtkmm.h>
#include <gtkmm/buildable.h>
#include <pulse/pulseaudio.h>
#include <pulse/glib-mainloop.h>
@ -74,7 +75,7 @@ enum SourceType {
#include "mainwindow.h"
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);

View File

@ -39,15 +39,11 @@ RoleWidget::RoleWidget(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>
RoleWidget* RoleWidget::create() {
RoleWidget* w;
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();
return w;
}
bool RoleWidget::onContextTriggerEvent(GdkEventButton*) {
return false;
}
void RoleWidget::onMuteToggleButton() {
StreamWidget::onMuteToggleButton();
@ -69,7 +65,7 @@ void RoleWidget::executeVolumeUpdate() {
pa_operation* o;
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;
}

View File

@ -35,7 +35,6 @@ public:
virtual void onMuteToggleButton();
virtual void executeVolumeUpdate();
virtual bool onContextTriggerEvent(GdkEventButton*);
};
#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")));
g_free(txt);
terminate.set_label(_("Terminate Playback"));
addKillMenu(_("Terminate Playback"));
}
SinkInputWidget* SinkInputWidget::create(MainWindow* mainWindow) {
SinkInputWidget* w;
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->reference();
@ -84,7 +84,7 @@ void SinkInputWidget::executeVolumeUpdate() {
pa_operation* o;
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;
}
@ -99,17 +99,17 @@ void SinkInputWidget::onMuteToggleButton() {
pa_operation* o;
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;
}
pa_operation_unref(o);
}
void SinkInputWidget::onKill() {
void SinkInputWidget::onKill(const Glib::VariantBase& parameter) {
pa_operation* o;
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;
}

View File

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

View File

@ -24,7 +24,7 @@
#include "sinkwidget.h"
#include <canberra-gtk.h>
#include <canberra.h>
#if HAVE_EXT_DEVICE_RESTORE_API
# include <pulse/format.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
uint8_t i = 0;
x->get_widget("encodingSelect", encodingSelect);
encodingSelect = x->get_widget<Gtk::Grid>("encodingSelect");
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));
++i;
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));
++i;
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));
++i;
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));
++i;
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));
++i;
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);
#ifdef PA_ENCODING_MPEG2_AAC_IEC61937
if (pa_context_get_server_protocol_version(get_context()) >= 28) {
@ -76,7 +76,7 @@ SinkWidget::SinkWidget(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>
#endif
++i;
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);
#ifdef PA_ENCODING_TRUEHD_IEC61937
if (pa_context_get_server_protocol_version(get_context()) >= 33) {
@ -87,7 +87,7 @@ SinkWidget::SinkWidget(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>
#endif
++i;
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);
#ifdef PA_ENCODING_DTSHD_IEC61937
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* w;
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->reference();
return w;
@ -114,20 +114,21 @@ void SinkWidget::executeVolumeUpdate() {
int playing = 0;
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;
}
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)
return;
snprintf(dev, sizeof(dev), "%lu", (unsigned long) index);
ca_context_change_device(ca_gtk_context_get(), dev);
ca_context_change_device(mpMainWindow->canberraContext, dev);
ca_gtk_play_for_widget(GTK_WIDGET(gobj()),
ca_context_play(mpMainWindow->canberraContext,
2,
CA_PROP_EVENT_DESCRIPTION, _("Volume Control Feedback Sound"),
CA_PROP_EVENT_ID, "audio-volume-change",
@ -135,7 +136,7 @@ void SinkWidget::executeVolumeUpdate() {
CA_PROP_CANBERRA_ENABLE, "1",
NULL);
ca_context_change_device(ca_gtk_context_get(), NULL);
ca_context_change_device(mpMainWindow->canberraContext, NULL);
}
void SinkWidget::onMuteToggleButton() {
@ -146,7 +147,7 @@ void SinkWidget::onMuteToggleButton() {
pa_operation* o;
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;
}
@ -160,7 +161,7 @@ void SinkWidget::onDefaultToggleButton() {
return;
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;
}
pa_operation_unref(o);
@ -180,7 +181,7 @@ void SinkWidget::onPortChange() {
Glib::ustring port = row[portModel.name];
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;
}
@ -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))) {
show_error(_("pa_ext_device_restore_save_sink_formats() failed"));
show_error(this, _("pa_ext_device_restore_save_sink_formats() failed"));
free(formats);
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")));
g_free(txt);
terminate.set_label(_("Terminate Recording"));
addKillMenu(_("Terminate Recording"));
#if !HAVE_SOURCE_OUTPUT_VOLUMES
/* 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* w;
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->reference();
return w;
@ -90,7 +90,7 @@ void SourceOutputWidget::executeVolumeUpdate() {
pa_operation* o;
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;
}
@ -105,7 +105,7 @@ void SourceOutputWidget::onMuteToggleButton() {
pa_operation* o;
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;
}
@ -113,10 +113,10 @@ void SourceOutputWidget::onMuteToggleButton() {
}
#endif
void SourceOutputWidget::onKill() {
void SourceOutputWidget::onKill(const Glib::VariantBase& parameter) {
pa_operation* o;
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;
}

View File

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

View File

@ -33,7 +33,7 @@ SourceWidget::SourceWidget(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Buil
SourceWidget* SourceWidget::create(MainWindow* mainWindow) {
SourceWidget* w;
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->reference();
return w;
@ -43,7 +43,7 @@ void SourceWidget::executeVolumeUpdate() {
pa_operation* o;
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;
}
@ -58,7 +58,7 @@ void SourceWidget::onMuteToggleButton() {
pa_operation* o;
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;
}
@ -72,7 +72,7 @@ void SourceWidget::onDefaultToggleButton() {
return;
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;
}
pa_operation_unref(o);
@ -94,7 +94,7 @@ void SourceWidget::onPortChange() {
Glib::ustring port = row[portModel.name];
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;
}

View File

@ -34,26 +34,20 @@ StreamWidget::StreamWidget(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Buil
mpMainWindow(NULL) {
/* MinimalStreamWidget member variables. */
x->get_widget("streamChannelsVBox", channelsVBox);
x->get_widget("streamNameLabel", nameLabel);
x->get_widget("streamBoldNameLabel", boldNameLabel);
x->get_widget("streamIconImage", iconImage);
channelsVBox = x->get_widget<Gtk::Box>("streamChannelsVBox");
nameLabel = x->get_widget<Gtk::Label>("streamNameLabel");
boldNameLabel = x->get_widget<Gtk::Label>("streamBoldNameLabel");
iconImage = x->get_widget<Gtk::Image>("streamIconImage");
x->get_widget("streamLockToggleButton", lockToggleButton);
x->get_widget("streamMuteToggleButton", muteToggleButton);
x->get_widget("directionLabel", directionLabel);
x->get_widget("deviceComboBox", deviceComboBox);
lockToggleButton = x->get_widget<Gtk::ToggleButton>("streamLockToggleButton");
muteToggleButton = x->get_widget<Gtk::ToggleButton>("streamMuteToggleButton");
directionLabel = x->get_widget<Gtk::Label>("directionLabel");
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));
lockToggleButton->signal_clicked().connect(sigc::mem_fun(*this, &StreamWidget::onLockToggleButton));
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++)
channelWidgets[i] = NULL;
}
@ -64,12 +58,35 @@ void StreamWidget::init(MainWindow* mainWindow) {
MinimalStreamWidget::init();
}
bool StreamWidget::onContextTriggerEvent(GdkEventButton* event) {
if (GDK_BUTTON_PRESS == event->type && 3 == event->button) {
contextMenu.popup_at_pointer((GdkEvent*)event);
return true;
void StreamWidget::addKillMenu(const char* killLabel) {
auto gesture = Gtk::GestureClick::create();
gesture->set_button(3);
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) {
@ -79,7 +96,7 @@ void StreamWidget::setChannelMap(const pa_channel_map &m, bool can_decibel) {
for (int i = 0; i < m.channels; i++) {
ChannelWidget *cw = channelWidgets[i];
channelsVBox->pack_start(*cw, false, false, 0);
channelsVBox->prepend(*cw);
cw->unreference();
}
@ -144,7 +161,7 @@ bool StreamWidget::timeoutEvent() {
void StreamWidget::executeVolumeUpdate() {
}
void StreamWidget::onKill() {
void StreamWidget::onKill(const Glib::VariantBase& parameter) {
}
void StreamWidget::onDeviceComboBoxChanged() {

View File

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