diff options
-rw-r--r-- | include/bluez-glib.h | 14 | ||||
-rw-r--r-- | src/api.c | 134 | ||||
-rw-r--r-- | src/bluez-call.c | 8 | ||||
-rw-r--r-- | src/meson.build | 3 |
4 files changed, 149 insertions, 10 deletions
diff --git a/include/bluez-glib.h b/include/bluez-glib.h index 25abecb..257effb 100644 --- a/include/bluez-glib.h +++ b/include/bluez-glib.h @@ -75,11 +75,17 @@ typedef void (*bluez_device_event_cb_t)(gchar *adapter, typedef void (*bluez_media_control_event_cb_t)(gchar *adapter, gchar *device, - gchar *endpoint, bluez_event_t event, GVariant *properties, gpointer user_data); +typedef void (*bluez_media_transport_event_cb_t)(gchar *adapter, + gchar *device, + gchar *endpoint, + bluez_event_t event, + GVariant *properties, + gpointer user_data); + typedef void (*bluez_media_player_event_cb_t)(gchar *adapter, gchar *device, gchar *player, @@ -104,6 +110,8 @@ void bluez_add_media_control_event_callback(bluez_media_control_event_cb_t cb, g void bluez_add_media_player_event_callback(bluez_media_player_event_cb_t cb, gpointer user_data); +void bluez_add_media_transport_event_callback(bluez_media_transport_event_cb_t cb, gpointer user_data); + void bluez_add_agent_event_callback(bluez_agent_event_cb_t cb, gpointer user_data); void bluez_set_log_level(bluez_log_level_t level); @@ -154,6 +162,10 @@ gboolean bluez_device_avrcp_controls(const char *device, bluez_media_control_t a gboolean bluez_set_pincode(const char *pincode); +gboolean bluez_get_media_control_properties(const char *device, GVariant **reply); + +gboolean bluez_get_media_player_properties(const char *device, GVariant **reply); + #ifdef __cplusplus } // extern "C" #endif @@ -51,6 +51,7 @@ callback_list_t bluez_adapter_callbacks; callback_list_t bluez_device_callbacks; callback_list_t bluez_media_control_callbacks; callback_list_t bluez_media_player_callbacks; +callback_list_t bluez_media_transport_callbacks; // The global handler thread and BlueZ state static GThread *g_bluez_thread; @@ -175,7 +176,7 @@ static void run_media_callbacks(callback_list_t *callbacks, { GSList *list; - if (!(endpoint || player)) + if (!(adapter || device)) return; g_mutex_lock(&callbacks->mutex); @@ -185,9 +186,12 @@ static void run_media_callbacks(callback_list_t *callbacks, if (player) { bluez_media_player_event_cb_t cb = (bluez_media_player_event_cb_t) entry->callback; (*cb)(adapter, device, player, event, properties, entry->user_data); + } else if (endpoint) { + bluez_media_transport_event_cb_t cb = (bluez_media_transport_event_cb_t) entry->callback; + (*cb)(adapter, device, endpoint, event, properties, entry->user_data); } else { bluez_media_control_event_cb_t cb = (bluez_media_control_event_cb_t) entry->callback; - (*cb)(adapter, device, endpoint, event, properties, entry->user_data); + (*cb)(adapter, device, event, properties, entry->user_data); } } } @@ -226,6 +230,14 @@ EXPORT void bluez_add_media_player_event_callback(bluez_media_player_event_cb_t callback_add(&bluez_media_player_callbacks, cb, user_data); } +EXPORT void bluez_add_media_transport_event_callback(bluez_media_player_event_cb_t cb, gpointer user_data) +{ + if (!cb) + return; + + callback_add(&bluez_media_transport_callbacks, cb, user_data); +} + static void mediaplayer1_set_path(struct bluez_state *ns, const char *path) { if (ns->mediaplayer_path) @@ -254,6 +266,18 @@ static void bluez_devices_signal_callback(GDBusConnection *connection, DEBUG("parameters: %s", g_variant_print(parameters, TRUE)); #endif + // Be paranoid to avoid any potential issues from unexpected signals, + // as glib seems to do some unexpected reuse of the D-Bus signal + // mechanism if there is more than one subscriber in the same process, + // and we will see signals we did not register for. :( + if (!((g_str_has_prefix(object_path, "/org/bluez/") && + g_strcmp0(interface_name, "org.freedesktop.DBus.Properties") == 0) || + (g_strcmp0(object_path, "/") == 0 && + g_strcmp0(interface_name, "org.freedesktop.DBus.ObjectManager") == 0))) { + // Not an expected signal + return; + } + if (!g_strcmp0(signal_name, "InterfacesAdded")) { g_variant_get(parameters, "(&oa{sa{sv}})", &path, &array); @@ -284,7 +308,7 @@ static void bluez_devices_signal_callback(GDBusConnection *connection, DEBUG("media endpoint added!"); DEBUG("media endpoint path %s, key %s, endpoint %s", path, key, endpoint); - run_media_callbacks(&bluez_media_control_callbacks, + run_media_callbacks(&bluez_media_transport_callbacks, adapter, device, endpoint, @@ -299,7 +323,7 @@ static void bluez_devices_signal_callback(GDBusConnection *connection, gchar *adapter = bluez_return_adapter(path); gchar *device = bluez_return_device(path); gchar *player = find_index(path, 5); - DEBUG("media player removed!"); + DEBUG("media player added!"); DEBUG("media player = %s", player); run_media_callbacks(&bluez_media_player_callbacks, adapter, @@ -388,6 +412,20 @@ static void bluez_devices_signal_callback(GDBusConnection *connection, run_callbacks(&bluez_adapter_callbacks, adapter, NULL, BLUEZ_EVENT_CHANGE, var); g_free(adapter); + } else if (!g_strcmp0(path, BLUEZ_MEDIACONTROL_INTERFACE)) { + gchar *adapter = bluez_return_adapter(object_path); + gchar *device = bluez_return_device(object_path); + DEBUG("media control changed!"); + run_media_callbacks(&bluez_media_control_callbacks, + adapter, + device, + NULL, + NULL, + BLUEZ_EVENT_CHANGE, + var); + g_free(adapter); + g_free(device); + } else if (!g_strcmp0(path, BLUEZ_MEDIAPLAYER_INTERFACE)) { gchar *adapter = bluez_return_adapter(object_path); gchar *device = bluez_return_device(object_path); @@ -411,12 +449,12 @@ static void bluez_devices_signal_callback(GDBusConnection *connection, gchar *endpoint = bluez_return_endpoint(object_path); DEBUG("media endpoint changed!"); DEBUG("media endpoint %s", endpoint); - run_media_callbacks(&bluez_media_control_callbacks, + run_media_callbacks(&bluez_media_transport_callbacks, adapter, device, endpoint, NULL, - BLUEZ_EVENT_REMOVE, + BLUEZ_EVENT_CHANGE, var); g_free(adapter); g_free(device); @@ -1541,3 +1579,87 @@ EXPORT gboolean bluez_set_pincode(const char *pincode) return rc; } + +EXPORT gboolean bluez_get_media_control_properties(const char *device, GVariant **reply) +{ + struct bluez_state *ns = bluez_get_state(); + GVariant *properties = NULL; + GError *error = NULL; + + if (!ns) { + ERROR("Invalid state"); + return FALSE; + } + if (!reply) + return FALSE; + + gchar *device_path = get_bluez_path(NULL, device); + properties = bluez_get_properties(ns, + BLUEZ_AT_MEDIACONTROL, + device_path, + &error); + if (error) { + ERROR("bluez_get_properties error: %s", error->message); + g_clear_error(&error); + *reply = NULL; + } + + // Pull properties out of tuple so caller does not have to + g_variant_get(properties, "(@a{sv})", reply); + g_variant_unref(properties); + + return TRUE; +} + +EXPORT gboolean bluez_get_media_player_properties(const char *device, GVariant **reply) +{ + struct bluez_state *ns = bluez_get_state(); + GVariant *properties = NULL; + GError *error = NULL; + + if (!ns) { + ERROR("Invalid state"); + return FALSE; + } + if (!reply) + return FALSE; + + gchar *player = NULL; + gchar *device_path = get_bluez_path(NULL, device); + if (device_path) { + // TODO: handle multiple players per device + GVariant *val = bluez_get_property(ns, BLUEZ_AT_MEDIACONTROL, device_path, "Player", NULL); + if (val) { + player = g_variant_get_string(val, NULL); + g_variant_unref(val); + } + if (!player) + player = g_strconcat(device_path, "/", BLUEZ_DEFAULT_PLAYER, NULL); + + g_free(device_path); + } else { + player = g_strdup(ns->mediaplayer_path); + } + + if (!player) { + ERROR("No path given"); + return FALSE; + } + + DEBUG("Using player %s", player); + properties = bluez_get_properties(ns, + BLUEZ_AT_MEDIAPLAYER, + player, + &error); + if (error) { + ERROR("bluez_get_properties error: %s", error->message); + g_clear_error(&error); + *reply = NULL; + } + + // Pull properties out of tuple so caller does not have to + g_variant_get(properties, "(@a{sv})", reply); + g_variant_unref(properties); + + return TRUE; +} diff --git a/src/bluez-call.c b/src/bluez-call.c index 86f9b9c..5c0fc77 100644 --- a/src/bluez-call.c +++ b/src/bluez-call.c @@ -229,6 +229,8 @@ bluez_call_async(struct bluez_state *ns, } cpw->callback = callback; + DEBUG("calling %s:%s:%s on %s:%s", + interface, method, params, BLUEZ_SERVICE, path); g_dbus_connection_call(ns->conn, BLUEZ_SERVICE, path, interface, method, params, NULL, /* reply type */ @@ -281,6 +283,8 @@ GVariant *bluez_get_properties(struct bluez_state *ns, return NULL; } + DEBUG("calling %s:%s:%s on %s:%s", + interface, method, interface2, BLUEZ_SERVICE, path); reply = g_dbus_connection_call_sync(ns->conn, BLUEZ_SERVICE, path, interface, method, interface2 ? g_variant_new("(s)", interface2) : NULL, @@ -443,8 +447,8 @@ gboolean bluez_autoconnect(gpointer data) gboolean paired = FALSE; if (g_variant_dict_lookup(props_dict, "Paired", "b", &paired) && paired) { - gchar *path = g_strconcat("/org/bluez/", adapter, "/", key, NULL); - GVariant *connect_reply = bluez_call(ns, "device", path, "Connect", NULL, NULL); + gchar *path = g_strconcat(adapter, "/", key, NULL); + GVariant *connect_reply = bluez_call(ns, BLUEZ_AT_DEVICE, path, "Connect", NULL, NULL); g_free(path); if (!connect_reply) continue; diff --git a/src/meson.build b/src/meson.build index 2ce6cb0..e774d21 100644 --- a/src/meson.build +++ b/src/meson.build @@ -14,6 +14,7 @@ if get_option('build-tester') executable('bluez-glib-test', 'test.c', include_directories: inc, - dependencies: [systemd_dep, glib_deps, lib_dep]) + dependencies: [systemd_dep, glib_deps, lib_dep], + install: true) endif |