cardwidget: add flag to lock profile from switching automatically

Part-of: <https://gitlab.freedesktop.org/pulseaudio/pavucontrol/-/merge_requests/60>
This commit is contained in:
Igor V. Kovalenko 2020-12-25 17:38:01 +03:00
parent b874058ae8
commit a1b4622ad5
7 changed files with 146 additions and 0 deletions

View File

@ -35,6 +35,7 @@ CardWidget::CardWidget(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>
x->get_widget("cardIconImage", iconImage); x->get_widget("cardIconImage", iconImage);
x->get_widget("codecBox", codecBox); x->get_widget("codecBox", codecBox);
x->get_widget("codecList", codecList); x->get_widget("codecList", codecList);
x->get_widget("profileLockToggleButton", profileLockToggleButton);
profileListStore = Gtk::ListStore::create(profileModel); profileListStore = Gtk::ListStore::create(profileModel);
profileList->set_model(profileListStore); profileList->set_model(profileListStore);
@ -49,6 +50,12 @@ CardWidget::CardWidget(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>
codecList->pack_start(codecModel.desc); codecList->pack_start(codecModel.desc);
codecList->signal_changed().connect( sigc::mem_fun(*this, &CardWidget::onCodecChange)); codecList->signal_changed().connect( sigc::mem_fun(*this, &CardWidget::onCodecChange));
hasProfileLock = false;
profileLockToggleButton->signal_clicked().connect(sigc::mem_fun(*this, &CardWidget::onProfileLockToggleButton));
profileLockToggleButton->set_sensitive(true);
profileLockToggleButton->set_visible(hasProfileLock);
} }
CardWidget* CardWidget::create() { CardWidget* CardWidget::create() {
@ -96,6 +103,8 @@ void CardWidget::prepareMenu() {
codecBox->show(); codecBox->show();
else else
codecBox->hide(); codecBox->hide();
profileLockToggleButton->set_visible(hasProfileLock);
} }
void CardWidget::onProfileChange() { void CardWidget::onProfileChange() {
@ -150,3 +159,31 @@ void CardWidget::onCodecChange() {
} }
#endif #endif
} }
void CardWidget::onProfileLockToggleButton() {
if (updating)
return;
#ifdef HAVE_PULSE_MESSAGING_API
Gtk::TreeModel::iterator iter = profileList->get_active();
if (iter)
{
Gtk::TreeModel::Row row = *iter;
if (row)
{
pa_operation* o;
Glib::ustring profile = row[profileModel.name];
bool profileIsLocked = profileLockToggleButton->get_active();
if (!(o = pa_context_send_message_to_object(get_context(), card_message_handler_path(pulse_card_name).c_str(),
"set-profile-sticky", profileIsLocked ? "true" : "false", NULL, NULL))) {
g_debug(_("pa_context_send_message_to_object() failed: %s"), pa_strerror(pa_context_errno(get_context())));
return;
}
pa_operation_unref(o);
}
}
#endif
}

View File

@ -45,6 +45,7 @@ public:
Glib::ustring name; Glib::ustring name;
std::string pulse_card_name; std::string pulse_card_name;
Gtk::Box *codecBox; Gtk::Box *codecBox;
Gtk::ToggleButton *profileLockToggleButton;
uint32_t index; uint32_t index;
bool updating; bool updating;
@ -59,11 +60,14 @@ public:
std::vector<std::pair<Glib::ustring, Glib::ustring>> codecs; std::vector<std::pair<Glib::ustring, Glib::ustring>> codecs;
Glib::ustring activeCodec; Glib::ustring activeCodec;
bool hasProfileLock;
void prepareMenu(); void prepareMenu();
protected: protected:
virtual void onProfileChange(); virtual void onProfileChange();
virtual void onCodecChange(); virtual void onCodecChange();
virtual void onProfileLockToggleButton();
/* Tree model columns */ /* Tree model columns */
class ModelColumns : public Gtk::TreeModel::ColumnRecord class ModelColumns : public Gtk::TreeModel::ColumnRecord

View File

@ -522,6 +522,28 @@ void MainWindow::setActiveCodec(const std::string& card_name, const std::string&
w->updating = false; w->updating = false;
} }
void MainWindow::setCardProfileIsSticky(const std::string& card_name, gboolean profile_is_sticky) {
CardWidget *w = NULL;
for (auto c : cardWidgets) {
if (card_name.compare(c.second->pulse_card_name) == 0)
w = c.second;
}
if (!w)
return;
w->updating = true;
/* make sure that profile lock toggle button is visible */
w->hasProfileLock = true;
w->profileLockToggleButton->set_active(profile_is_sticky);
w->prepareMenu();
w->updating = false;
}
bool MainWindow::updateSink(const pa_sink_info &info) { bool MainWindow::updateSink(const pa_sink_info &info) {
SinkWidget *w; SinkWidget *w;
bool is_new = false; bool is_new = false;

View File

@ -59,6 +59,8 @@ public:
void updateCardCodecs(const std::string& card_name, const std::unordered_map<std::string, std::string>& codecs); void updateCardCodecs(const std::string& card_name, const std::unordered_map<std::string, std::string>& codecs);
void setActiveCodec(const std::string& card_name, const std::string& codec); void setActiveCodec(const std::string& card_name, const std::string& codec);
void setCardProfileIsSticky(const std::string& card_name, gboolean profile_is_sticky);
void removeCard(uint32_t index); void removeCard(uint32_t index);
void removeSink(uint32_t index); void removeSink(uint32_t index);
void removeSource(uint32_t index); void removeSource(uint32_t index);

View File

@ -81,6 +81,10 @@ static void dec_outstanding(MainWindow *w) {
#ifdef HAVE_PULSE_MESSAGING_API #ifdef HAVE_PULSE_MESSAGING_API
std::string card_message_handler_path(const std::string& name) {
return "/card/" + name;
}
std::string card_bluez_message_handler_path(const std::string& name) { std::string card_bluez_message_handler_path(const std::string& name) {
return "/card/" + name + "/bluez"; return "/card/" + name + "/bluez";
} }
@ -201,6 +205,39 @@ static void context_bluetooth_card_active_codec_cb(pa_context *c, int success, c
g_object_unref(parser); g_object_unref(parser);
} }
static void context_card_profile_is_sticky_cb(pa_context *c, int success, char *response, void *userdata) {
auto u = std::unique_ptr<WindowAndCardName>(reinterpret_cast<WindowAndCardName*>(userdata));
if (!success)
return;
gboolean profile_is_sticky;
GError *gerror = NULL;
JsonParser *parser = json_parser_new();
if (!json_parser_load_from_data(parser, response, strlen(response), &gerror)) {
g_debug(_("could not read JSON from get-profile-sticky message response: %s"), gerror->message);
g_error_free(gerror);
g_object_unref(parser);
return;
}
JsonNode *root = json_parser_get_root(parser);
if (!root || JSON_NODE_TYPE(root) != JSON_NODE_VALUE) {
g_debug(_("get-profile-sticky message response is not a JSON value"));
g_object_unref(parser);
return;
}
profile_is_sticky = json_node_get_boolean(root);
u->first->setCardProfileIsSticky(u->second, profile_is_sticky);
g_object_unref(parser);
}
template<typename U> void send_message(pa_context *c, const char *target, const char *request, pa_context_string_cb_t cb, const U& u) template<typename U> void send_message(pa_context *c, const char *target, const char *request, pa_context_string_cb_t cb, const U& u)
{ {
auto send_message_userdata = new U(u); auto send_message_userdata = new U(u);
@ -300,6 +337,14 @@ static void context_message_handlers_cb(pa_context *c, int success, char *respon
/* list-codecs: retrieve list of codecs */ /* list-codecs: retrieve list of codecs */
send_message(c, e->first.c_str(), "list-codecs", context_bluetooth_card_codec_list_cb, *u); send_message(c, e->first.c_str(), "list-codecs", context_bluetooth_card_codec_list_cb, *u);
} }
/* send requests to card if card message handler is registered */
e = message_handler_map.find(card_message_handler_path(u->second));
if (e != message_handler_map.end()) {
/* get-profile-sticky: retrieve active codec name */
send_message(c, e->first.c_str(), "get-profile-sticky", context_card_profile_is_sticky_cb, *u);
}
} }
#endif #endif

View File

@ -134,6 +134,41 @@
<property name="position">1</property> <property name="position">1</property>
</packing> </packing>
</child> </child>
<child>
<object class="GtkHBox" id="hbox14">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">3</property>
<child>
<object class="GtkToggleButton" id="profileLockToggleButton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">Lock card to this profile</property>
<property name="relief">none</property>
<property name="active">True</property>
<child>
<object class="GtkImage" id="image4">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">changes-prevent</property>
<property name="icon_size">1</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>

View File

@ -79,6 +79,7 @@ void show_error(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);
#ifdef HAVE_PULSE_MESSAGING_API #ifdef HAVE_PULSE_MESSAGING_API
std::string card_message_handler_path(const std::string& name);
std::string card_bluez_message_handler_path(const std::string& name); std::string card_bluez_message_handler_path(const std::string& name);
#endif #endif