From 270eda63a97a11f45b94b0dd1673601b23b3b3c9 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sat, 3 May 2025 18:19:22 +0300 Subject: [PATCH] bluez5: different icon for A2DP & HFP output routes Set different icons for A2DP & HFP output routes, so that they look different (in Gnome). Don't call the non-HFP output route as "headset" or "handsfree" in this case, to be less ambiguous about microphone availability. Also set device.icon-name for the device too. --- spa/plugins/bluez5/bluez5-dbus.c | 10 ++++--- spa/plugins/bluez5/bluez5-device.c | 42 ++++++++++++++++++++++++++++-- spa/plugins/bluez5/defs.h | 25 ++++++++++++++++++ 3 files changed, 71 insertions(+), 6 deletions(-) diff --git a/spa/plugins/bluez5/bluez5-dbus.c b/spa/plugins/bluez5/bluez5-dbus.c index 2b12c05d0..ace1c2ed8 100644 --- a/spa/plugins/bluez5/bluez5-dbus.c +++ b/spa/plugins/bluez5/bluez5-dbus.c @@ -1721,8 +1721,9 @@ static void emit_device_info(struct spa_bt_monitor *monitor, { struct spa_device_object_info info; char dev[32], name[128], class[16], vendor_id[64], product_id[64], product_id_tot[67]; - struct spa_dict_item items[23]; + struct spa_dict_item items[24]; uint32_t n_items = 0; + enum spa_bt_form_factor ff; info = SPA_DEVICE_OBJECT_INFO_INIT(); info.type = SPA_TYPE_INTERFACE_Device; @@ -1731,6 +1732,8 @@ static void emit_device_info(struct spa_bt_monitor *monitor, SPA_DEVICE_OBJECT_CHANGE_MASK_PROPS; info.flags = 0; + ff = spa_bt_form_factor_from_class(device->bluetooth_class); + items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_API, "bluez5"); items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_BUS, "bluetooth"); items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_MEDIA_CLASS, "Audio/Device"); @@ -1745,9 +1748,8 @@ static void emit_device_info(struct spa_bt_monitor *monitor, items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_VENDOR_ID, vendor_id); items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_PRODUCT_ID, product_id_tot); } - items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_FORM_FACTOR, - spa_bt_form_factor_name( - spa_bt_form_factor_from_class(device->bluetooth_class))); + items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_FORM_FACTOR, spa_bt_form_factor_name(ff)); + items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_ICON_NAME, spa_bt_form_factor_icon_name(ff)); items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_STRING, device->address); items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_API_BLUEZ5_ICON, device->icon); items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_API_BLUEZ5_PATH, device->path); diff --git a/spa/plugins/bluez5/bluez5-device.c b/spa/plugins/bluez5/bluez5-device.c index d2c1031ac..92c824d27 100644 --- a/spa/plugins/bluez5/bluez5-device.c +++ b/spa/plugins/bluez5/bluez5-device.c @@ -534,7 +534,7 @@ static void emit_device_set_node(struct impl *this, uint32_t id) struct spa_bt_device *device = this->bt_dev; struct node *node = &this->nodes[id]; struct spa_device_object_info info; - struct spa_dict_item items[8]; + struct spa_dict_item items[9]; char str_id[32], members_json[8192], channels_json[512]; struct device_set_member *members; uint32_t n_members; @@ -2150,6 +2150,22 @@ static bool profile_has_route(uint32_t profile, uint32_t route) return false; } +static bool device_has_route(struct impl *this, uint32_t route) +{ + bool found = false; + + if (this->bt_dev->connected_profiles & SPA_BT_PROFILE_A2DP_DUPLEX) + found = found || profile_has_route(DEVICE_PROFILE_A2DP, route); + if (this->bt_dev->connected_profiles & SPA_BT_PROFILE_BAP_AUDIO) + found = found || profile_has_route(DEVICE_PROFILE_BAP, route); + if (this->bt_dev->connected_profiles & SPA_BT_PROFILE_HEADSET_HEAD_UNIT) + found = found || profile_has_route(DEVICE_PROFILE_HSP_HFP, route); + if (this->bt_dev->connected_profiles & SPA_BT_PROFILE_ASHA_SINK) + found = found || profile_has_route(DEVICE_PROFILE_ASHA, route); + + return found; +} + static struct spa_pod *build_route(struct impl *this, struct spa_pod_builder *b, uint32_t id, uint32_t route, uint32_t profile) { @@ -2157,6 +2173,7 @@ static struct spa_pod *build_route(struct impl *this, struct spa_pod_builder *b, struct spa_pod_frame f[2]; enum spa_direction direction; const char *name_prefix, *description, *hfp_description, *port_type; + const char *port_icon_name = NULL; enum spa_bt_form_factor ff; enum spa_bluetooth_audio_codec codec; enum spa_param_availability available; @@ -2250,6 +2267,20 @@ static struct spa_pod *build_route(struct impl *this, struct spa_pod_builder *b, dev = DEVICE_ID_SINK; available = this->device_set.sink_enabled ? SPA_PARAM_AVAILABILITY_no : SPA_PARAM_AVAILABILITY_yes; + + if (device_has_route(this, ROUTE_HF_OUTPUT)) { + /* Distinguish A2DP vs. HFP output routes */ + switch (ff) { + case SPA_BT_FORM_FACTOR_HEADSET: + case SPA_BT_FORM_FACTOR_HANDSFREE: + port_icon_name = spa_bt_form_factor_icon_name(SPA_BT_FORM_FACTOR_HEADPHONE); + /* Don't call it "headset", the HF one has the mic */ + description = _("Headphone"); + break; + default: + break; + } + } break; case ROUTE_HF_OUTPUT: direction = SPA_DIRECTION_OUTPUT; @@ -2257,6 +2288,8 @@ static struct spa_pod *build_route(struct impl *this, struct spa_pod_builder *b, description = hfp_description; dev = DEVICE_ID_SINK; available = SPA_PARAM_AVAILABILITY_yes; + if (device_has_route(this, ROUTE_OUTPUT)) + port_icon_name = spa_bt_form_factor_icon_name(SPA_BT_FORM_FACTOR_HEADSET); break; case ROUTE_SET_INPUT: if (!(this->device_set.source_enabled && this->device_set.leader)) @@ -2292,11 +2325,16 @@ static struct spa_pod *build_route(struct impl *this, struct spa_pod_builder *b, 0); spa_pod_builder_prop(b, SPA_PARAM_ROUTE_info, 0); spa_pod_builder_push_struct(b, &f[1]); - spa_pod_builder_int(b, 1); + spa_pod_builder_int(b, port_icon_name ? 2 : 1); spa_pod_builder_add(b, SPA_POD_String("port.type"), SPA_POD_String(port_type), NULL); + if (port_icon_name) + spa_pod_builder_add(b, + SPA_POD_String("device.icon-name"), + SPA_POD_String(port_icon_name), + NULL); spa_pod_builder_pop(b, &f[1]); spa_pod_builder_prop(b, SPA_PARAM_ROUTE_profiles, 0); spa_pod_builder_push_array(b, &f[1]); diff --git a/spa/plugins/bluez5/defs.h b/spa/plugins/bluez5/defs.h index a97b9ecb4..dd092a626 100644 --- a/spa/plugins/bluez5/defs.h +++ b/spa/plugins/bluez5/defs.h @@ -427,6 +427,31 @@ static inline const char *spa_bt_form_factor_name(enum spa_bt_form_factor ff) } } +static inline const char *spa_bt_form_factor_icon_name(enum spa_bt_form_factor ff) +{ + switch (ff) { + case SPA_BT_FORM_FACTOR_HEADSET: + return "audio-headset-bluetooth"; + case SPA_BT_FORM_FACTOR_HANDSFREE: + return "audio-handsfree-bluetooth"; + case SPA_BT_FORM_FACTOR_MICROPHONE: + return "audio-input-microphone-bluetooth"; + case SPA_BT_FORM_FACTOR_SPEAKER: + return "audio-speakers-bluetooth"; + case SPA_BT_FORM_FACTOR_HEADPHONE: + return "audio-headphones-bluetooth"; + case SPA_BT_FORM_FACTOR_PORTABLE: + return "multimedia-player-bluetooth"; + case SPA_BT_FORM_FACTOR_PHONE: + return "phone-bluetooth"; + case SPA_BT_FORM_FACTOR_CAR: + case SPA_BT_FORM_FACTOR_HIFI: + case SPA_BT_FORM_FACTOR_UNKNOWN: + default: + return "audio-card-bluetooth"; + } +} + static inline enum spa_bt_form_factor spa_bt_form_factor_from_class(uint32_t bluetooth_class) { uint32_t major, minor;