From a1b4622ad5de0513361b8638152d5e5b1ac6980f Mon Sep 17 00:00:00 2001 From: "Igor V. Kovalenko" Date: Fri, 25 Dec 2020 17:38:01 +0300 Subject: [PATCH] cardwidget: add flag to lock profile from switching automatically Part-of: --- src/cardwidget.cc | 37 +++++++++++++++++++++++++++++++++++ src/cardwidget.h | 4 ++++ src/mainwindow.cc | 22 +++++++++++++++++++++ src/mainwindow.h | 2 ++ src/pavucontrol.cc | 45 +++++++++++++++++++++++++++++++++++++++++++ src/pavucontrol.glade | 35 +++++++++++++++++++++++++++++++++ src/pavucontrol.h | 1 + 7 files changed, 146 insertions(+) diff --git a/src/cardwidget.cc b/src/cardwidget.cc index b4fe378..d751fd2 100644 --- a/src/cardwidget.cc +++ b/src/cardwidget.cc @@ -35,6 +35,7 @@ CardWidget::CardWidget(BaseObjectType* cobject, const Glib::RefPtr x->get_widget("cardIconImage", iconImage); x->get_widget("codecBox", codecBox); x->get_widget("codecList", codecList); + x->get_widget("profileLockToggleButton", profileLockToggleButton); profileListStore = Gtk::ListStore::create(profileModel); profileList->set_model(profileListStore); @@ -49,6 +50,12 @@ CardWidget::CardWidget(BaseObjectType* cobject, const Glib::RefPtr codecList->pack_start(codecModel.desc); 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() { @@ -96,6 +103,8 @@ void CardWidget::prepareMenu() { codecBox->show(); else codecBox->hide(); + + profileLockToggleButton->set_visible(hasProfileLock); } void CardWidget::onProfileChange() { @@ -150,3 +159,31 @@ void CardWidget::onCodecChange() { } #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 +} diff --git a/src/cardwidget.h b/src/cardwidget.h index 98201a4..3c86659 100644 --- a/src/cardwidget.h +++ b/src/cardwidget.h @@ -45,6 +45,7 @@ public: Glib::ustring name; std::string pulse_card_name; Gtk::Box *codecBox; + Gtk::ToggleButton *profileLockToggleButton; uint32_t index; bool updating; @@ -59,11 +60,14 @@ public: std::vector> codecs; Glib::ustring activeCodec; + bool hasProfileLock; + void prepareMenu(); protected: virtual void onProfileChange(); virtual void onCodecChange(); + virtual void onProfileLockToggleButton(); /* Tree model columns */ class ModelColumns : public Gtk::TreeModel::ColumnRecord diff --git a/src/mainwindow.cc b/src/mainwindow.cc index ea290bd..bae4c7a 100644 --- a/src/mainwindow.cc +++ b/src/mainwindow.cc @@ -522,6 +522,28 @@ void MainWindow::setActiveCodec(const std::string& card_name, const std::string& 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) { SinkWidget *w; bool is_new = false; diff --git a/src/mainwindow.h b/src/mainwindow.h index 50f3985..5015144 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -59,6 +59,8 @@ public: void updateCardCodecs(const std::string& card_name, const std::unordered_map& codecs); 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 removeSink(uint32_t index); void removeSource(uint32_t index); diff --git a/src/pavucontrol.cc b/src/pavucontrol.cc index 3f5c218..18d5400 100644 --- a/src/pavucontrol.cc +++ b/src/pavucontrol.cc @@ -81,6 +81,10 @@ static void dec_outstanding(MainWindow *w) { #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) { 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); } +static void context_card_profile_is_sticky_cb(pa_context *c, int success, char *response, void *userdata) { + auto u = std::unique_ptr(reinterpret_cast(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 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); @@ -300,6 +337,14 @@ static void context_message_handlers_cb(pa_context *c, int success, char *respon /* list-codecs: retrieve list of codecs */ 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 diff --git a/src/pavucontrol.glade b/src/pavucontrol.glade index e5cf31a..253534e 100644 --- a/src/pavucontrol.glade +++ b/src/pavucontrol.glade @@ -134,6 +134,41 @@ 1 + + + True + False + 3 + + + True + True + False + Lock card to this profile + none + True + + + True + False + changes-prevent + 1 + + + + + False + False + 1 + + + + + False + True + 2 + + False diff --git a/src/pavucontrol.h b/src/pavucontrol.h index 7bf7f49..c8696e6 100644 --- a/src/pavucontrol.h +++ b/src/pavucontrol.h @@ -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); #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); #endif