diff --git a/Makefile.common b/Makefile.common index b1ae43d02c..60f99ef8bb 100644 --- a/Makefile.common +++ b/Makefile.common @@ -567,6 +567,16 @@ ifeq ($(HAVE_EMSCRIPTEN), 1) camera/drivers/rwebcam.o endif +ifeq ($(HAVE_BLUETOOTH), 1) + OBJ += bluetooth/drivers/bluetoothctl.o +endif + +ifeq ($(HAVE_BLUETOOTH), 1) + ifeq ($(HAVE_DBUS), 1) + OBJ += bluetooth/drivers/bluez.o + endif +endif + ifeq ($(HAVE_LAKKA), 1) OBJ += wifi/drivers/connmanctl.o endif @@ -809,6 +819,10 @@ ifeq ($(HAVE_MENU), 1) endif endif +ifeq ($(HAVE_BLUETOOTH), 1) + DEFINES += -DHAVE_BLUETOOTH +endif + ifeq ($(HAVE_LAKKA), 1) DEFINES += -DHAVE_LAKKA endif @@ -1793,6 +1807,7 @@ ifeq ($(HAVE_NETWORKING), 1) tasks/task_http.o \ tasks/task_netplay_lan_scan.o \ tasks/task_netplay_nat_traversal.o \ + tasks/task_bluetooth.o \ tasks/task_wifi.o \ tasks/task_pl_thumbnail_download.o \ tasks/task_netplay_find_content.o diff --git a/bluetooth/bluetooth_driver.h b/bluetooth/bluetooth_driver.h new file mode 100644 index 0000000000..840315e463 --- /dev/null +++ b/bluetooth/bluetooth_driver.h @@ -0,0 +1,91 @@ +/* RetroArch - A frontend for libretro. + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#ifndef __BLUETOOTH_DRIVER__H +#define __BLUETOOTH_DRIVER__H + +#include + +#include +#include +#include + +RETRO_BEGIN_DECLS + +enum rarch_bluetooth_ctl_state +{ + RARCH_BLUETOOTH_CTL_NONE = 0, + RARCH_BLUETOOTH_CTL_DESTROY, + RARCH_BLUETOOTH_CTL_DEINIT, + RARCH_BLUETOOTH_CTL_SET_ACTIVE, + RARCH_BLUETOOTH_CTL_UNSET_ACTIVE, + RARCH_BLUETOOTH_CTL_IS_ACTIVE, + RARCH_BLUETOOTH_CTL_FIND_DRIVER, + RARCH_BLUETOOTH_CTL_SET_CB, + RARCH_BLUETOOTH_CTL_STOP, + RARCH_BLUETOOTH_CTL_START, + RARCH_BLUETOOTH_CTL_INIT +}; + +typedef struct bluetooth_driver +{ + void *(*init)(void); + + void (*free)(void *data); + + bool (*start)(void *data); + void (*stop)(void *data); + + void (*scan)(void); + void (*get_devices)(struct string_list *list); + bool (*device_is_connected)(unsigned i); + void (*device_get_sublabel)(char *s, unsigned i, size_t len); + bool (*connect_device)(unsigned i); + + const char *ident; +} bluetooth_driver_t; + +extern bluetooth_driver_t bluetooth_bluetoothctl; +extern bluetooth_driver_t bluetooth_bluez; + +/** + * config_get_bluetooth_driver_options: + * + * Get an enumerated list of all bluetooth driver names, + * separated by '|'. + * + * Returns: string listing of all bluetooth driver names, + * separated by '|'. + **/ +const char* config_get_bluetooth_driver_options(void); + +void driver_bluetooth_stop(void); + +bool driver_bluetooth_start(void); + +void driver_bluetooth_scan(void); + +void driver_bluetooth_get_devices(struct string_list *list); + +bool driver_bluetooth_device_is_connected(unsigned i); + +void driver_bluetooth_device_get_sublabel(char *s, unsigned i, size_t len); + +bool driver_bluetooth_connect_device(unsigned i); + +bool bluetooth_driver_ctl(enum rarch_bluetooth_ctl_state state, void *data); + +RETRO_END_DECLS + +#endif diff --git a/bluetooth/drivers/bluetoothctl.c b/bluetooth/drivers/bluetoothctl.c new file mode 100644 index 0000000000..5cc021b7d6 --- /dev/null +++ b/bluetooth/drivers/bluetoothctl.c @@ -0,0 +1,214 @@ +/* RetroArch - A frontend for libretro. + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include +#include + +#include "../bluetooth_driver.h" +#include "../../retroarch.h" + +static bool bluetoothctl_cache[256] = {0}; +static unsigned bluetoothctl_counter[256] = {0}; +static struct string_list* lines = NULL; +static char command[256] = {0}; + +static void *bluetoothctl_init(void) +{ + return (void*)-1; +} + +static void bluetoothctl_free(void *data) +{ + (void)data; +} + +static bool bluetoothctl_start(void *data) +{ + (void)data; + return true; +} + +static void bluetoothctl_stop(void *data) +{ + (void)data; +} + +static void bluetoothctl_scan(void) +{ + char line[512]; + union string_list_elem_attr attr; + FILE *dev_file = NULL; + + attr.i = 0; + if (lines) + free(lines); + lines = string_list_new(); + + pclose(popen("bluetoothctl -- power on", "r")); + + pclose(popen("bluetoothctl --timeout 15 scan on", "r")); + + runloop_msg_queue_push(msg_hash_to_str(MSG_BLUETOOTH_SCAN_COMPLETE), + 1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, + MESSAGE_QUEUE_CATEGORY_INFO); + + dev_file = popen("bluetoothctl -- devices", "r"); + while (fgets(line, 512, dev_file)) + { + size_t len = strlen(line); + if (len > 0 && line[len-1] == '\n') + line[--len] = '\0'; + + string_list_append(lines, line, attr); + } + pclose(dev_file); +} + +static void bluetoothctl_get_devices(struct string_list* devices) +{ + unsigned i; + union string_list_elem_attr attr; + attr.i = 0; + + if (!lines) + return; + + for (i = 0; i < lines->size; i++) + { + char device[64]; + const char *line = lines->elems[i].data; + + /* bluetoothctl devices outputs lines of the format: + * $ bluetoothctl devices + * 'Device (mac address) (device name)' + */ + strlcpy(device, line+24, sizeof(device)); + string_list_append(devices, device, attr); + } +} + +static bool bluetoothctl_device_is_connected(unsigned i) +{ + char ln[512] = {0}; + char device[18] = {0}; + const char *line = lines->elems[i].data; + FILE *command_file = NULL; + + if (bluetoothctl_counter[i] == 60) + { + static struct string_list* list = NULL; + bluetoothctl_counter[i] = 0; + list = string_split(line, " "); + if (!list) + return false; + + if (list->size == 0) + { + string_list_free(list); + return false; + } + + strlcpy(device, list->elems[1].data, sizeof(device)); + string_list_free(list); + + snprintf(command, sizeof(command), "\ + bluetoothctl -- info %s | grep 'Connected: yes'", + device); + + command_file = popen(command, "r"); + + while (fgets(ln, 512, command_file)) + { + bluetoothctl_cache[i] = true; + return true; + } + pclose(command_file); + bluetoothctl_cache[i] = false; + } + else + { + bluetoothctl_counter[i]++; + return bluetoothctl_cache[i]; + } + + return false; +} + +static bool bluetoothctl_connect_device(unsigned idx) +{ + unsigned i; + char device[18] = {0}; + const char *line = lines->elems[idx].data; + static struct string_list* list = NULL; + /* bluetoothctl devices outputs lines of the format: + * $ bluetoothctl devices + * 'Device (mac address) (device name)' + */ + list = string_split(line, " "); + if (!list) + return false; + + if (list->size == 0) + { + string_list_free(list); + return false; + } + + strlcpy(device, list->elems[1].data, sizeof(device)); + string_list_free(list); + + snprintf(command, sizeof(command), "\ + bluetoothctl -- trust %s", + device); + + pclose(popen(command, "r")); + + snprintf(command, sizeof(command), "\ + bluetoothctl -- pair %s", + device); + + pclose(popen(command, "r")); + + snprintf(command, sizeof(command), "\ + bluetoothctl -- connect %s", + device); + + pclose(popen(command, "r")); + + bluetoothctl_counter[idx] = 0; + return true; +} + +void bluetoothctl_device_get_sublabel (char *s, unsigned i, size_t len) +{ + /* bluetoothctl devices outputs lines of the format: + * $ bluetoothctl devices + * 'Device (mac address) (device name)' + */ + const char *line = lines->elems[i].data; + strlcpy(s, line+7, 18); +} + +bluetooth_driver_t bluetooth_bluetoothctl = { + bluetoothctl_init, + bluetoothctl_free, + bluetoothctl_start, + bluetoothctl_stop, + bluetoothctl_scan, + bluetoothctl_get_devices, + bluetoothctl_device_is_connected, + bluetoothctl_device_get_sublabel, + bluetoothctl_connect_device, + "bluetoothctl", +}; diff --git a/bluetooth/drivers/bluez.c b/bluetooth/drivers/bluez.c new file mode 100644 index 0000000000..328eebe325 --- /dev/null +++ b/bluetooth/drivers/bluez.c @@ -0,0 +1,593 @@ +/* RetroArch - A frontend for libretro. + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include +#include +#include +#include + +#include "../bluetooth_driver.h" +#include "../../retroarch.h" + +typedef struct { + /* object path. usually looks like /org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF + * technically unlimited, but should be enough */ + char path[128]; + + /* for display purposes 64 bytes should be enough */ + char name[64]; + + /* MAC address, 17 bytes */ + char address[18]; + + /* freedesktop.org icon name + * See bluez/src/dbus-common.c + * Can be NULL */ + char icon[64]; + + int connected; + int paired; + int trusted; +} device_info_t; + +#define VECTOR_LIST_TYPE device_info_t +#define VECTOR_LIST_NAME device_info +#include "../../libretro-common/lists/vector_list.c" +#undef VECTOR_LIST_TYPE +#undef VECTOR_LIST_NAME + +static struct device_info_vector_list *devices = NULL; +static char adapter[256] = {0}; +static DBusConnection* dbus_connection = NULL; +static bool bluez_cache[256] = {0}; +static int bluez_cache_counter[256] = {0}; + +static void *bluez_init (void) +{ + return (void*)-1; +} + +static void bluez_free (void *data) +{ + (void)data; +} + +static bool bluez_start (void *data) +{ + (void)data; + return true; +} + +static void bluez_stop (void *data) +{ + (void)data; +} + +static int +set_bool_property ( + const char *path, + const char *arg_adapter, + const char *arg_property, + int value) +{ + DBusMessage *message, *reply; + DBusError err; + + dbus_error_init(&err); + + message = dbus_message_new_method_call( + "org.bluez", + path, + "org.freedesktop.DBus.Properties", + "Set" + ); + if (!message) + return 1; + + DBusMessageIter req_iter, req_subiter; + dbus_message_iter_init_append(message, &req_iter); + if (!dbus_message_iter_append_basic(&req_iter, DBUS_TYPE_STRING, &arg_adapter)) + goto fault; + if (!dbus_message_iter_append_basic(&req_iter, DBUS_TYPE_STRING, &arg_property)) + goto fault; + if (!dbus_message_iter_open_container(&req_iter, DBUS_TYPE_VARIANT, + DBUS_TYPE_BOOLEAN_AS_STRING, &req_subiter)) + { + goto fault; + } + if (!dbus_message_iter_append_basic(&req_subiter, DBUS_TYPE_BOOLEAN, &value)) + goto fault; + if (!dbus_message_iter_close_container(&req_iter, &req_subiter)) + goto fault; + + reply = dbus_connection_send_with_reply_and_block(dbus_connection, + message, 1000, &err); + if (!reply) + goto fault; + dbus_message_unref(reply); + dbus_message_unref(message); + return 0; + +fault: + dbus_message_iter_abandon_container_if_open(&req_iter, &req_subiter); + dbus_message_unref(message); + return 1; +} + +static int +get_bool_property ( + const char *path, + const char *arg_adapter, + const char *arg_property, + int *value) +{ + DBusMessage *message, *reply; + DBusError err; + DBusMessageIter root_iter, variant_iter; + + dbus_error_init(&err); + + message = dbus_message_new_method_call( "org.bluez", path, + "org.freedesktop.DBus.Properties", "Get"); + if (!message) + return 1; + + if (!dbus_message_append_args(message, + DBUS_TYPE_STRING, &arg_adapter, + DBUS_TYPE_STRING, &arg_property, + DBUS_TYPE_INVALID)) + { + return 1; + } + + reply = dbus_connection_send_with_reply_and_block(dbus_connection, + message, 1000, &err); + + dbus_message_unref(message); + + if (!reply) + return 1; + + if (!dbus_message_iter_init(reply, &root_iter)) + return 1; + if (DBUS_TYPE_VARIANT != dbus_message_iter_get_arg_type(&root_iter)) + return 1; + dbus_message_iter_recurse(&root_iter, &variant_iter); + dbus_message_iter_get_basic(&variant_iter, value); + + dbus_message_unref(reply); + return 0; +} + +static int +adapter_discovery (const char *method) +{ + DBusMessage *message; + + message = dbus_message_new_method_call( "org.bluez", adapter, + "org.bluez.Adapter1", method); + if (!message) + return 1; + + if (!dbus_connection_send(dbus_connection, message, NULL)) + return 1; + + dbus_connection_flush(dbus_connection); + dbus_message_unref(message); + + return 0; +} + +static int +get_managed_objects (DBusMessage **reply) +{ + DBusMessage *message; + DBusError err; + + dbus_error_init(&err); + + message = dbus_message_new_method_call( "org.bluez", "/", + "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); + if (!message) + return 1; + + *reply = dbus_connection_send_with_reply_and_block(dbus_connection, + message, -1, &err); + /* if (!reply) is done by the caller in this one */ + + dbus_message_unref(message); + return 0; +} + +static int +device_method (const char *path, const char *method) +{ + DBusMessage *message, *reply; + DBusError err; + + dbus_error_init(&err); + + message = dbus_message_new_method_call( "org.bluez", path, + "org.bluez.Device1", method); + if (!message) + return 1; + + reply = dbus_connection_send_with_reply_and_block(dbus_connection, + message, 10000, &err); + if (!reply) + return 1; + + dbus_connection_flush(dbus_connection); + dbus_message_unref(message); + + return 0; +} + +static int +device_remove (const char *path) +{ + DBusMessage *message, *reply; + DBusError err; + + dbus_error_init(&err); + + message = dbus_message_new_method_call( "org.bluez", adapter, + "org.bluez.Adapter11", "RemoveDevice"); + if (!message) + return 1; + + if (!dbus_message_append_args(message, + DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID)) + { + return 1; + } + + reply = dbus_connection_send_with_reply_and_block(dbus_connection, + message, 10000, &err); + if (!reply) + return 1; + + dbus_connection_flush(dbus_connection); + dbus_message_unref(message); + + return 0; +} + +static int +get_default_adapter (DBusMessage *reply) +{ + /* "...an application would discover the available adapters by + * performing a ObjectManager.GetManagedObjects call and look for any + * returned objects with an “org.bluez.Adapter1″ interface. + * The concept of a default adapter was always a bit fuzzy and the + * value could’t be changed, so if applications need something like it + * they could e.g. just pick the first adapter they encounter in the + * GetManagedObjects reply." + * -- http://www.bluez.org/bluez-5-api-introduction-and-porting-guide/ + */ + + DBusMessageIter root_iter; + DBusMessageIter dict_1_iter, dict_2_iter; + DBusMessageIter array_1_iter, array_2_iter; + + char *obj_path, *interface_name; + + /* a{oa{sa{sv}}} */ + if (!dbus_message_iter_init(reply, &root_iter)) + return 1; + + /* a */ + if (DBUS_TYPE_ARRAY != dbus_message_iter_get_arg_type(&root_iter)) + return 1; + dbus_message_iter_recurse(&root_iter, &array_1_iter); + do { + /* a{...} */ + if (DBUS_TYPE_DICT_ENTRY != dbus_message_iter_get_arg_type(&array_1_iter)) + return 1; + dbus_message_iter_recurse(&array_1_iter, &dict_1_iter); + + /* a{o...} */ + if (DBUS_TYPE_OBJECT_PATH != dbus_message_iter_get_arg_type(&dict_1_iter)) + return 1; + dbus_message_iter_get_basic(&dict_1_iter, &obj_path); + + if (!dbus_message_iter_next(&dict_1_iter)) + return 1; + /* a{oa} */ + if (DBUS_TYPE_ARRAY != dbus_message_iter_get_arg_type(&dict_1_iter)) + return 1; + dbus_message_iter_recurse(&dict_1_iter, &array_2_iter); + do { + /* empty array? */ + if (DBUS_TYPE_INVALID == dbus_message_iter_get_arg_type(&array_2_iter)) + continue; + + /* a{oa{...}} */ + if (DBUS_TYPE_DICT_ENTRY != dbus_message_iter_get_arg_type(&array_2_iter)) + return 1; + dbus_message_iter_recurse(&array_2_iter, &dict_2_iter); + + /* a{oa{s...}} */ + if (DBUS_TYPE_STRING != dbus_message_iter_get_arg_type(&dict_2_iter)) + return 1; + dbus_message_iter_get_basic(&dict_2_iter, &interface_name); + if (strcmp(interface_name, "org.bluez.Adapter1") == 0) { + strlcpy(adapter, obj_path, 256); + return 0; + } + } while (dbus_message_iter_next(&array_2_iter)); + } while (dbus_message_iter_next(&array_1_iter)); + + /* Couldn't find an adapter */ + return 1; +} + +static int +read_scanned_devices (DBusMessage *reply) +{ + DBusMessageIter root_iter; + DBusMessageIter dict_1_iter, dict_2_iter, dict_3_iter; + DBusMessageIter array_1_iter, array_2_iter, array_3_iter; + DBusMessageIter variant_iter; + + device_info_t device; + + char *obj_path, *interface_name, *interface_property_name; + char *found_device_address, *found_device_name, *found_device_icon; + + /* a{oa{sa{sv}}} */ + if (!dbus_message_iter_init(reply, &root_iter)) + return 1; + + /* a */ + if (DBUS_TYPE_ARRAY != dbus_message_iter_get_arg_type(&root_iter)) + return 1; + dbus_message_iter_recurse(&root_iter, &array_1_iter); + do { + /* a{...} */ + if (DBUS_TYPE_DICT_ENTRY != dbus_message_iter_get_arg_type(&array_1_iter)) + return 1; + dbus_message_iter_recurse(&array_1_iter, &dict_1_iter); + + /* a{o...} */ + if (DBUS_TYPE_OBJECT_PATH != dbus_message_iter_get_arg_type(&dict_1_iter)) + return 1; + dbus_message_iter_get_basic(&dict_1_iter, &obj_path); + + if (!dbus_message_iter_next(&dict_1_iter)) + return 1; + /* a{oa} */ + if (DBUS_TYPE_ARRAY != dbus_message_iter_get_arg_type(&dict_1_iter)) + return 1; + dbus_message_iter_recurse(&dict_1_iter, &array_2_iter); + do { + /* empty array? */ + if (DBUS_TYPE_INVALID == dbus_message_iter_get_arg_type(&array_2_iter)) + continue; + + /* a{oa{...}} */ + if (DBUS_TYPE_DICT_ENTRY != dbus_message_iter_get_arg_type(&array_2_iter)) + return 1; + dbus_message_iter_recurse(&array_2_iter, &dict_2_iter); + + /* a{oa{s...}} */ + if (DBUS_TYPE_STRING != dbus_message_iter_get_arg_type(&dict_2_iter)) + return 1; + dbus_message_iter_get_basic(&dict_2_iter, &interface_name); + if (strcmp(interface_name, "org.bluez.Device1") != 0) + continue; + memset(&device, 0, sizeof(device)); + strlcpy(device.path, obj_path, 128); + + if (!dbus_message_iter_next(&dict_2_iter)) + return 1; + /* a{oa{sa}} */ + if (DBUS_TYPE_ARRAY != dbus_message_iter_get_arg_type(&dict_2_iter)) + return 1; + dbus_message_iter_recurse(&dict_2_iter, &array_3_iter); + + do { + /* empty array? */ + if (DBUS_TYPE_INVALID == dbus_message_iter_get_arg_type(&array_3_iter)) + continue; + + /* a{oa{sa{...}}} */ + if (DBUS_TYPE_DICT_ENTRY != dbus_message_iter_get_arg_type(&array_3_iter)) + return 1; + dbus_message_iter_recurse(&array_3_iter, &dict_3_iter); + + /* a{oa{sa{s...}}} */ + if (DBUS_TYPE_STRING != dbus_message_iter_get_arg_type(&dict_3_iter)) + return 1; + dbus_message_iter_get_basic(&dict_3_iter, &interface_property_name); + + if (!dbus_message_iter_next(&dict_3_iter)) + return 1; + /* a{oa{sa{sv}}} */ + if (DBUS_TYPE_VARIANT != dbus_message_iter_get_arg_type(&dict_3_iter)) + return 1; + + /* Below, "Alias" property is used instead of "Name". + * "This value ("Name") is only present for + * completeness. It is better to always use + * the Alias property when displaying the + * devices name." + * -- bluez/doc/device-api.txt + */ + + /* DBUS_TYPE_VARIANT is a container type */ + dbus_message_iter_recurse(&dict_3_iter, &variant_iter); + if (strcmp(interface_property_name, "Address") == 0) { + dbus_message_iter_get_basic(&variant_iter, &found_device_address); + strlcpy(device.address, found_device_address, 18); + } else if (strcmp(interface_property_name, "Alias") == 0) { + dbus_message_iter_get_basic(&variant_iter, &found_device_name); + strlcpy(device.name, found_device_name, 64); + } else if (strcmp(interface_property_name, "Icon") == 0) { + dbus_message_iter_get_basic(&variant_iter, &found_device_icon); + strlcpy(device.icon, found_device_icon, 64); + } else if (strcmp(interface_property_name, "Connected") == 0) { + dbus_message_iter_get_basic(&variant_iter, &device.connected); + } else if (strcmp(interface_property_name, "Paired") == 0) { + dbus_message_iter_get_basic(&variant_iter, &device.paired); + } else if (strcmp(interface_property_name, "Trusted") == 0) { + dbus_message_iter_get_basic(&variant_iter, &device.trusted); + } + } while (dbus_message_iter_next(&array_3_iter)); + if (!device_info_vector_list_append(devices, device)) + return 1; + } while (dbus_message_iter_next(&array_2_iter)); + } while (dbus_message_iter_next(&array_1_iter)); + + return 0; +} + +static void bluez_dbus_connect (void) +{ + DBusError err; + dbus_error_init(&err); + dbus_connection = dbus_bus_get_private(DBUS_BUS_SYSTEM, &err); +} + +static void bluez_dbus_disconnect (void) +{ + if (!dbus_connection) + return; + + dbus_connection_close(dbus_connection); + dbus_connection_unref(dbus_connection); + dbus_connection = NULL; +} + +static void bluez_scan (void) +{ + DBusError err; + DBusMessage *reply; + + bluez_dbus_connect(); + + if (get_managed_objects(&reply)) + return; + if (!reply) + return; + + /* Get default adapter */ + if (get_default_adapter(reply)) + return; + dbus_message_unref(reply); + + /* Power device on */ + if (set_bool_property(adapter, "org.bluez.Adapter1", "Powered", 1)) + return; + + /* Start discovery */ + if (adapter_discovery("StartDiscovery")) + return; + + retro_sleep(10000); + + /* Stop discovery */ + if (adapter_discovery("StopDiscovery")) + return; + + /* Get scanned devices */ + if (get_managed_objects(&reply)) + return; + if (!reply) + return; + + if (devices) + device_info_vector_list_free(devices); + devices = device_info_vector_list_new(); + + read_scanned_devices(reply); + dbus_message_unref(reply); + bluez_dbus_disconnect(); +} + +static void bluez_get_devices (struct string_list* devices_string_list) +{ + unsigned i; + union string_list_elem_attr attr; + attr.i = 0; + + if (!devices) + return; + + for (i = 0; i < devices->count; i++) + { + char device[64]; + strlcpy(device, devices->data[i].name, 64); + string_list_append(devices_string_list, device, attr); + } +} + +static bool bluez_device_is_connected (unsigned i) +{ + int value; + + if (bluez_cache_counter[i] == 60) { + bluez_cache_counter[i] = 0; + bluez_dbus_connect(); + get_bool_property(devices->data[i].path, "org.bluez.Device1", + "Connected", &value); + bluez_dbus_disconnect(); + + bluez_cache[i] = value; + return value; + } else { + bluez_cache_counter[i]++; + return bluez_cache[i]; + } +} + +static void bluez_device_get_sublabel (char *s, unsigned i, size_t len) +{ + strlcpy(s, devices->data[i].address, len); +} + +static bool bluez_connect_device (unsigned i) +{ + bluez_dbus_connect(); + + /* Remove the device */ + device_remove(devices->data[i].path); + /* Trust the device */ + if (set_bool_property(devices->data[i].path, "org.bluez.Device1", "Trusted", 1)) + return false; + /* Pair the device */ + if (device_method(devices->data[i].path, "Pair")) + return false; + /* Connect the device */ + if (device_method(devices->data[i].path, "Connect")) + return false; + + bluez_dbus_disconnect(); + bluez_cache_counter[i] = 0; + return true; +} + +bluetooth_driver_t bluetooth_bluez = { + bluez_init, + bluez_free, + bluez_start, + bluez_stop, + bluez_scan, + bluez_get_devices, + bluez_device_is_connected, + bluez_device_get_sublabel, + bluez_connect_device, + "bluez", +}; diff --git a/configuration.c b/configuration.c index 3bd4f6904f..062085d892 100644 --- a/configuration.c +++ b/configuration.c @@ -271,9 +271,16 @@ enum camera_driver_enum CAMERA_NULL }; +enum bluetooth_driver_enum +{ + BLUETOOTH_BLUETOOTHCTL = CAMERA_NULL + 1, + BLUETOOTH_BLUEZ, + BLUETOOTH_NULL +}; + enum wifi_driver_enum { - WIFI_CONNMANCTL = CAMERA_NULL + 1, + WIFI_CONNMANCTL = BLUETOOTH_NULL + 1, WIFI_NULL }; @@ -559,6 +566,16 @@ static const enum camera_driver_enum CAMERA_DEFAULT_DRIVER = CAMERA_ANDROID; static const enum camera_driver_enum CAMERA_DEFAULT_DRIVER = CAMERA_NULL; #endif +#if defined(HAVE_BLUETOOTH) +# if defined(HAVE_DBUS) +static const enum bluetooth_driver_enum BLUETOOTH_DEFAULT_DRIVER = BLUETOOTH_BLUEZ; +# else +static const enum bluetooth_driver_enum BLUETOOTH_DEFAULT_DRIVER = BLUETOOTH_BLUETOOTHCTL; +# endif +#else +static const enum bluetooth_driver_enum BLUETOOTH_DEFAULT_DRIVER = BLUETOOTH_NULL; +#endif + #if defined(HAVE_LAKKA) static const enum wifi_driver_enum WIFI_DEFAULT_DRIVER = WIFI_CONNMANCTL; #else @@ -1014,6 +1031,30 @@ const char *config_get_default_camera(void) return "null"; } +/** + * config_get_default_bluetooth: + * + * Gets default bluetooth driver. + * + * Returns: Default bluetooth driver. + **/ +const char *config_get_default_bluetooth(void) +{ + enum bluetooth_driver_enum default_driver = BLUETOOTH_DEFAULT_DRIVER; + + switch (default_driver) + { + case BLUETOOTH_BLUETOOTHCTL: + return "bluetoothctl"; + case BLUETOOTH_BLUEZ: + return "bluez"; + case BLUETOOTH_NULL: + break; + } + + return "null"; +} + /** * config_get_default_wifi: * @@ -1148,6 +1189,7 @@ static struct config_array_setting *populate_settings_array(settings_t *settings SETTING_ARRAY("video_driver", settings->arrays.video_driver, false, NULL, true); SETTING_ARRAY("record_driver", settings->arrays.record_driver, false, NULL, true); SETTING_ARRAY("camera_driver", settings->arrays.camera_driver, false, NULL, true); + SETTING_ARRAY("bluetooth_driver", settings->arrays.bluetooth_driver, false, NULL, true); SETTING_ARRAY("wifi_driver", settings->arrays.wifi_driver, false, NULL, true); SETTING_ARRAY("location_driver", settings->arrays.location_driver,false, NULL, true); #ifdef HAVE_MENU @@ -2021,6 +2063,7 @@ void config_set_defaults(void *data) const char *def_menu = config_get_default_menu(); #endif const char *def_camera = config_get_default_camera(); + const char *def_bluetooth = config_get_default_bluetooth(); const char *def_wifi = config_get_default_wifi(); const char *def_led = config_get_default_led(); const char *def_location = config_get_default_location(); @@ -2092,6 +2135,10 @@ void config_set_defaults(void *data) configuration_set_string(settings, settings->arrays.camera_driver, def_camera); + if (def_bluetooth) + configuration_set_string(settings, + settings->arrays.bluetooth_driver, + def_bluetooth); if (def_wifi) configuration_set_string(settings, settings->arrays.wifi_driver, diff --git a/configuration.h b/configuration.h index e09459bfa2..3d21ae781e 100644 --- a/configuration.h +++ b/configuration.h @@ -310,6 +310,9 @@ typedef struct settings /* Camera */ bool camera_allow; + /* Bluetooth */ + bool bluetooth_allow; + /* WiFi */ bool wifi_allow; @@ -640,6 +643,7 @@ typedef struct settings char video_driver[32]; char record_driver[32]; char camera_driver[32]; + char bluetooth_driver[32]; char wifi_driver[32]; char led_driver[32]; char location_driver[32]; @@ -760,6 +764,15 @@ typedef struct settings **/ const char *config_get_default_camera(void); +/** + * config_get_default_bluetooth: + * + * Gets default bluetooth driver. + * + * Returns: Default bluetooth driver. + **/ +const char *config_get_default_bluetooth(void); + /** * config_get_default_wifi: * diff --git a/driver.h b/driver.h index dd116ab142..18abf3078e 100644 --- a/driver.h +++ b/driver.h @@ -35,6 +35,7 @@ enum DRIVER_LOCATION, DRIVER_MENU, DRIVERS_VIDEO_INPUT, + DRIVER_BLUETOOTH, DRIVER_WIFI, DRIVER_LED, DRIVER_MIDI @@ -49,6 +50,7 @@ enum DRIVER_LOCATION_MASK = 1 << DRIVER_LOCATION, DRIVER_MENU_MASK = 1 << DRIVER_MENU, DRIVERS_VIDEO_INPUT_MASK = 1 << DRIVERS_VIDEO_INPUT, + DRIVER_BLUETOOTH_MASK = 1 << DRIVER_BLUETOOTH, DRIVER_WIFI_MASK = 1 << DRIVER_WIFI, DRIVER_LED_MASK = 1 << DRIVER_LED, DRIVER_MIDI_MASK = 1 << DRIVER_MIDI diff --git a/griffin/griffin.c b/griffin/griffin.c index 05bf7d7bcf..1ad799e485 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -1152,6 +1152,16 @@ RETROARCH #include "../intl/msg_hash_us.c" +/*============================================================ +BLUETOOTH +============================================================ */ +#ifdef HAVE_BLUETOOTH +#include "../bluetooth/drivers/bluetoothctl.c" +#ifdef HAVE_DBUS +#include "../bluetooth/drivers/bluez.c" +#endif +#endif + /*============================================================ WIFI ============================================================ */ @@ -1226,6 +1236,7 @@ NETPLAY #include "../tasks/task_http.c" #include "../tasks/task_netplay_lan_scan.c" #include "../tasks/task_netplay_nat_traversal.c" +#include "../tasks/task_bluetooth.c" #include "../tasks/task_wifi.c" #include "../tasks/task_netplay_find_content.c" #include "../tasks/task_pl_thumbnail_download.c" diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index a2f64849e8..fcd0a01617 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -204,6 +204,10 @@ MSG_HASH( MENU_ENUM_LABEL_CAMERA_DRIVER, "camera_driver" ) +MSG_HASH( + MENU_ENUM_LABEL_BLUETOOTH_DRIVER, + "bluetooth_driver" + ) MSG_HASH( MENU_ENUM_LABEL_CB_CORE_CONTENT_DIRS_LIST, "cb_core_content_dirs_list" @@ -988,6 +992,10 @@ MSG_HASH( MENU_ENUM_LABEL_DEFERRED_CRT_SWITCHRES_SETTINGS_LIST, "deferred_crt_switchres_settings_list" ) +MSG_HASH( + MENU_ENUM_LABEL_DEFERRED_BLUETOOTH_SETTINGS_LIST, + "deferred_bluetooth_settings_list" + ) MSG_HASH( MENU_ENUM_LABEL_DEFERRED_WIFI_SETTINGS_LIST, "deferred_wifi_settings_list" @@ -1854,6 +1862,10 @@ MSG_HASH( MENU_ENUM_LABEL_NO_NETPLAY_HOSTS_FOUND, "no_netplay_hosts_found" ) +MSG_HASH( + MENU_ENUM_LABEL_NO_BT_DEVICES_FOUND, + "no_bt_devices_found" + ) MSG_HASH( MENU_ENUM_LABEL_NO_NETWORKS_FOUND, "no_networks_found" @@ -3218,6 +3230,10 @@ MSG_HASH( MENU_ENUM_LABEL_WIFI_SETTINGS, "wifi_settings" ) +MSG_HASH( + MENU_ENUM_LABEL_BLUETOOTH_SETTINGS, + "bluetooth_settings" + ) MSG_HASH( MENU_ENUM_LABEL_XMB_ALPHA_FACTOR, "xmb_alpha_factor" diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index b02c2647d9..0fe0e0f121 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -1193,6 +1193,14 @@ MSG_HASH( MENU_ENUM_SUBLABEL_CAMERA_DRIVER, "Camera driver to use." ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_BLUETOOTH_DRIVER, + "Bluetooth" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_BLUETOOTH_DRIVER, + "Bluetooth driver to use." + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_WIFI_DRIVER, "Wi-Fi" @@ -6372,6 +6380,10 @@ MSG_HASH( MENU_ENUM_LABEL_VALUE_NO_SETTINGS_FOUND, "No Settings Found" ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_NO_BT_DEVICES_FOUND, + "No Bluetooth Devices Found" + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_NO_NETWORKS_FOUND, "No Networks Found" @@ -8962,6 +8974,10 @@ MSG_HASH( MENU_ENUM_LABEL_VALUE_NO_PLAYLISTS, "No playlists." ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_BT_CONNECTED, + "Connected" + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_ONLINE, "Online" @@ -10630,10 +10646,18 @@ MSG_HASH( MSG_DEVICE_NOT_CONFIGURED_FALLBACK, "not configured, using fallback" ) +MSG_HASH( + MSG_BLUETOOTH_SCAN_COMPLETE, + "Bluetooth scan complete." + ) MSG_HASH( MSG_WIFI_SCAN_COMPLETE, "Wi-Fi scan complete." ) +MSG_HASH( + MSG_SCANNING_BLUETOOTH_DEVICES, + "Scanning bluetooth devices..." + ) MSG_HASH( MSG_SCANNING_WIRELESS_NETWORKS, "Scanning wireless networks..." @@ -11044,6 +11068,14 @@ MSG_HASH( MENU_ENUM_LABEL_VALUE_VIDEO_SOFT_FILTER, "Soft Filter" ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_BLUETOOTH_SETTINGS, + "Bluetooth" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_BLUETOOTH_SETTINGS, + "Scans for bluetooth devices and connects them." + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_WIFI_SETTINGS, "Wi-Fi" diff --git a/list_special.h b/list_special.h index a0e4b106a6..cbdbf0b877 100644 --- a/list_special.h +++ b/list_special.h @@ -44,6 +44,7 @@ enum string_list_type STRING_LIST_NONE = 0, STRING_LIST_MENU_DRIVERS, STRING_LIST_CAMERA_DRIVERS, + STRING_LIST_BLUETOOTH_DRIVERS, STRING_LIST_WIFI_DRIVERS, STRING_LIST_LOCATION_DRIVERS, STRING_LIST_AUDIO_DRIVERS, diff --git a/menu/cbs/menu_cbs_deferred_push.c b/menu/cbs/menu_cbs_deferred_push.c index f1f340bfad..190df8f1a7 100644 --- a/menu/cbs/menu_cbs_deferred_push.c +++ b/menu/cbs/menu_cbs_deferred_push.c @@ -198,6 +198,7 @@ GENERIC_DEFERRED_PUSH(deferred_push_user_interface_settings_list, DISPLAYLIST_ GENERIC_DEFERRED_PUSH(deferred_push_power_management_settings_list, DISPLAYLIST_POWER_MANAGEMENT_SETTINGS_LIST) GENERIC_DEFERRED_PUSH(deferred_push_retro_achievements_settings_list,DISPLAYLIST_RETRO_ACHIEVEMENTS_SETTINGS_LIST) GENERIC_DEFERRED_PUSH(deferred_push_updater_settings_list, DISPLAYLIST_UPDATER_SETTINGS_LIST) +GENERIC_DEFERRED_PUSH(deferred_push_bluetooth_settings_list, DISPLAYLIST_BLUETOOTH_SETTINGS_LIST) GENERIC_DEFERRED_PUSH(deferred_push_wifi_settings_list, DISPLAYLIST_WIFI_SETTINGS_LIST) GENERIC_DEFERRED_PUSH(deferred_push_network_settings_list, DISPLAYLIST_NETWORK_SETTINGS_LIST) GENERIC_DEFERRED_PUSH(deferred_push_subsystem_settings_list, DISPLAYLIST_SUBSYSTEM_SETTINGS_LIST) @@ -741,6 +742,7 @@ static int menu_cbs_init_bind_deferred_push_compare_label( {MENU_ENUM_LABEL_DEFERRED_NETWORK_SETTINGS_LIST, deferred_push_network_settings_list}, {MENU_ENUM_LABEL_DEFERRED_SUBSYSTEM_SETTINGS_LIST, deferred_push_subsystem_settings_list}, {MENU_ENUM_LABEL_DEFERRED_NETWORK_HOSTING_SETTINGS_LIST, deferred_push_network_hosting_settings_list}, + {MENU_ENUM_LABEL_DEFERRED_BLUETOOTH_SETTINGS_LIST, deferred_push_bluetooth_settings_list}, {MENU_ENUM_LABEL_DEFERRED_WIFI_SETTINGS_LIST, deferred_push_wifi_settings_list}, {MENU_ENUM_LABEL_DEFERRED_LAKKA_SERVICES_LIST, deferred_push_lakka_services_list}, {MENU_ENUM_LABEL_DEFERRED_USER_SETTINGS_LIST, deferred_push_user_settings_list}, diff --git a/menu/cbs/menu_cbs_get_value.c b/menu/cbs/menu_cbs_get_value.c index c8e13c7cab..4f4c8095e5 100644 --- a/menu/cbs/menu_cbs_get_value.c +++ b/menu/cbs/menu_cbs_get_value.c @@ -40,6 +40,7 @@ #include "../../performance_counters.h" #include "../../paths.h" #include "../../verbosity.h" +#include "../../bluetooth/bluetooth_driver.h" #include "../../wifi/wifi_driver.h" #include "../../playlist.h" #include "../../manual_content_scan.h" @@ -836,6 +837,21 @@ static void menu_action_setting_disp_set_label_entry( strlcpy(s2, path, len2); } +static void menu_action_setting_disp_set_label_bluetooth_is_connected( + file_list_t* list, + unsigned *w, unsigned type, unsigned i, + const char *label, + char *s, size_t len, + const char *path, + char *s2, size_t len2) +{ + strlcpy(s2, path, len2); + *w = 19; + + if (driver_bluetooth_device_is_connected(i)) + strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_BT_CONNECTED), len); +} + static void menu_action_setting_disp_set_label_wifi_is_online( file_list_t* list, unsigned *w, unsigned type, unsigned i, @@ -1568,10 +1584,15 @@ static int menu_cbs_init_bind_get_string_representation_compare_label( case MENU_ENUM_LABEL_MIDI_DRIVER: case MENU_ENUM_LABEL_LOCATION_DRIVER: case MENU_ENUM_LABEL_CAMERA_DRIVER: + case MENU_ENUM_LABEL_BLUETOOTH_DRIVER: case MENU_ENUM_LABEL_WIFI_DRIVER: case MENU_ENUM_LABEL_MENU_DRIVER: BIND_ACTION_GET_VALUE(cbs, menu_action_setting_disp_set_label); break; + case MENU_ENUM_LABEL_CONNECT_BLUETOOTH: + BIND_ACTION_GET_VALUE(cbs, + menu_action_setting_disp_set_label_bluetooth_is_connected); + break; case MENU_ENUM_LABEL_CONNECT_WIFI: BIND_ACTION_GET_VALUE(cbs, menu_action_setting_disp_set_label_wifi_is_online); diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index 8b7b70558c..722c5500ac 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -72,6 +72,7 @@ #include "../../retroarch.h" #include "../../verbosity.h" #include "../../lakka.h" +#include "../../bluetooth/bluetooth_driver.h" #include "../../wifi/wifi_driver.h" #include "../../gfx/video_display_server.h" #include "../../manual_content_scan.h" @@ -371,6 +372,8 @@ static enum msg_hash_enums action_ok_dl_to_enum(unsigned lbl) return MENU_ENUM_LABEL_DEFERRED_SUBSYSTEM_SETTINGS_LIST; case ACTION_OK_DL_NETWORK_SETTINGS_LIST: return MENU_ENUM_LABEL_DEFERRED_NETWORK_SETTINGS_LIST; + case ACTION_OK_DL_BLUETOOTH_SETTINGS_LIST: + return MENU_ENUM_LABEL_DEFERRED_BLUETOOTH_SETTINGS_LIST; case ACTION_OK_DL_WIFI_SETTINGS_LIST: return MENU_ENUM_LABEL_DEFERRED_WIFI_SETTINGS_LIST; case ACTION_OK_DL_NETPLAY: @@ -1233,6 +1236,7 @@ int generic_action_ok_displaylist_push(const char *path, case ACTION_OK_DL_NETWORK_SETTINGS_LIST: case ACTION_OK_DL_NETWORK_HOSTING_SETTINGS_LIST: case ACTION_OK_DL_SUBSYSTEM_SETTINGS_LIST: + case ACTION_OK_DL_BLUETOOTH_SETTINGS_LIST: case ACTION_OK_DL_WIFI_SETTINGS_LIST: case ACTION_OK_DL_NETPLAY: case ACTION_OK_DL_NETPLAY_LAN_SCAN_SETTINGS_LIST: @@ -2570,6 +2574,14 @@ int generic_action_ok_help(const char *path, entry_idx, ACTION_OK_DL_HELP); } +static int action_ok_bluetooth(const char *path, const char *label, + unsigned type, size_t idx, size_t entry_idx) +{ + driver_bluetooth_connect_device(idx); + + return 0; +} + static void menu_input_wifi_cb(void *userdata, const char *passphrase) { unsigned idx = menu_input_dialog_get_kb_idx(); @@ -4854,6 +4866,7 @@ DEFAULT_ACTION_OK_FUNC(action_ok_network_list, ACTION_OK_DL_NETWORK_SETTINGS_LIS DEFAULT_ACTION_OK_FUNC(action_ok_network_hosting_list, ACTION_OK_DL_NETWORK_HOSTING_SETTINGS_LIST) DEFAULT_ACTION_OK_FUNC(action_ok_subsystem_list, ACTION_OK_DL_SUBSYSTEM_SETTINGS_LIST) DEFAULT_ACTION_OK_FUNC(action_ok_database_manager_list, ACTION_OK_DL_DATABASE_MANAGER_LIST) +DEFAULT_ACTION_OK_FUNC(action_ok_bluetooth_list, ACTION_OK_DL_BLUETOOTH_SETTINGS_LIST) DEFAULT_ACTION_OK_FUNC(action_ok_wifi_list, ACTION_OK_DL_WIFI_SETTINGS_LIST) DEFAULT_ACTION_OK_FUNC(action_ok_cursor_manager_list, ACTION_OK_DL_CURSOR_MANAGER_LIST) DEFAULT_ACTION_OK_FUNC(action_ok_compressed_archive_push, ACTION_OK_DL_COMPRESSED_ARCHIVE_PUSH) @@ -6936,6 +6949,7 @@ static int menu_cbs_init_bind_ok_compare_label(menu_file_list_cbs_t *cbs, {MENU_ENUM_LABEL_FILE_BROWSER_OPEN_PICKER, action_ok_open_picker}, {MENU_ENUM_LABEL_RETRO_ACHIEVEMENTS_SETTINGS, action_ok_retro_achievements_list}, {MENU_ENUM_LABEL_UPDATER_SETTINGS, action_ok_updater_list}, + {MENU_ENUM_LABEL_BLUETOOTH_SETTINGS, action_ok_bluetooth_list}, {MENU_ENUM_LABEL_WIFI_SETTINGS, action_ok_wifi_list}, {MENU_ENUM_LABEL_NETWORK_HOSTING_SETTINGS, action_ok_network_hosting_list}, {MENU_ENUM_LABEL_SUBSYSTEM_SETTINGS, action_ok_subsystem_list}, @@ -7358,6 +7372,9 @@ static int menu_cbs_init_bind_ok_compare_type(menu_file_list_cbs_t *cbs, case FILE_TYPE_RDB_ENTRY: BIND_ACTION_OK(cbs, action_ok_rdb_entry); break; + case MENU_BLUETOOTH: + BIND_ACTION_OK(cbs, action_ok_bluetooth); + break; case MENU_WIFI: BIND_ACTION_OK(cbs, action_ok_wifi); break; diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index 986bc2d270..ad7d179d02 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -30,6 +30,7 @@ #endif #include "../../core_info.h" #include "../../verbosity.h" +#include "../../bluetooth/bluetooth_driver.h" #ifdef HAVE_NETWORKING #include "../../network/netplay/netplay.h" @@ -192,6 +193,7 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_settings_list, MENU_ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_menu_settings_list, MENU_ENUM_SUBLABEL_INPUT_MENU_SETTINGS) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_haptic_feedback_settings_list, MENU_ENUM_SUBLABEL_INPUT_HAPTIC_FEEDBACK_SETTINGS) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_latency_settings_list, MENU_ENUM_SUBLABEL_LATENCY_SETTINGS) +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_bluetooth_settings_list, MENU_ENUM_SUBLABEL_BLUETOOTH_SETTINGS) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_wifi_settings_list, MENU_ENUM_SUBLABEL_WIFI_SETTINGS) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_netplay_lan_scan_settings_list,MENU_ENUM_SUBLABEL_NETPLAY_LAN_SCAN_SETTINGS) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_help_list, MENU_ENUM_SUBLABEL_HELP_LIST) @@ -452,6 +454,7 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_location_driver, MENU_ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_menu_driver, MENU_ENUM_SUBLABEL_MENU_DRIVER) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_record_driver, MENU_ENUM_SUBLABEL_RECORD_DRIVER) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_midi_driver, MENU_ENUM_SUBLABEL_MIDI_DRIVER) +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_bluetooth_driver, MENU_ENUM_SUBLABEL_BLUETOOTH_DRIVER) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_wifi_driver, MENU_ENUM_SUBLABEL_WIFI_DRIVER) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_filter_supported_extensions, MENU_ENUM_SUBLABEL_NAVIGATION_BROWSER_FILTER_SUPPORTED_EXTENSIONS_ENABLE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_wallpaper, MENU_ENUM_SUBLABEL_MENU_WALLPAPER) @@ -874,6 +877,16 @@ static int action_bind_sublabel_systeminfo_controller_entry( return 0; } +static int action_bind_sublabel_bluetooth_list( + file_list_t *list, + unsigned type, unsigned i, + const char *label, const char *path, + char *s, size_t len) +{ + driver_bluetooth_device_get_sublabel(s, i, len); + return 0; +} + static int action_bind_sublabel_cheevos_entry( file_list_t *list, unsigned type, unsigned i, @@ -2600,6 +2613,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_NAVIGATION_BROWSER_FILTER_SUPPORTED_EXTENSIONS_ENABLE: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_filter_supported_extensions); break; + case MENU_ENUM_LABEL_BLUETOOTH_DRIVER: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_bluetooth_driver); + break; case MENU_ENUM_LABEL_WIFI_DRIVER: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_wifi_driver); break; @@ -3041,6 +3057,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_VIDEO_SHARED_CONTEXT: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_video_shared_context); break; + case MENU_ENUM_LABEL_CONNECT_BLUETOOTH: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_bluetooth_list); + break; case MENU_ENUM_LABEL_CHEEVOS_UNLOCKED_ENTRY: case MENU_ENUM_LABEL_CHEEVOS_UNLOCKED_ENTRY_HARDCORE: case MENU_ENUM_LABEL_CHEEVOS_LOCKED_ENTRY: @@ -3417,6 +3436,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_INPUT_HAPTIC_FEEDBACK_SETTINGS: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_input_haptic_feedback_settings_list); break; + case MENU_ENUM_LABEL_BLUETOOTH_SETTINGS: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_bluetooth_settings_list); + break; case MENU_ENUM_LABEL_WIFI_SETTINGS: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_wifi_settings_list); break; diff --git a/menu/cbs/menu_cbs_title.c b/menu/cbs/menu_cbs_title.c index e94df630f9..122d4f6d90 100644 --- a/menu/cbs/menu_cbs_title.c +++ b/menu/cbs/menu_cbs_title.c @@ -485,6 +485,7 @@ DEFAULT_TITLE_MACRO(action_get_power_management_settings_list, MENU_ENUM_LABEL_ DEFAULT_TITLE_MACRO(action_get_menu_sounds_list, MENU_ENUM_LABEL_VALUE_MENU_SOUNDS) DEFAULT_TITLE_MACRO(action_get_menu_file_browser_settings_list, MENU_ENUM_LABEL_VALUE_MENU_FILE_BROWSER_SETTINGS) DEFAULT_TITLE_MACRO(action_get_retro_achievements_settings_list,MENU_ENUM_LABEL_VALUE_RETRO_ACHIEVEMENTS_SETTINGS) +DEFAULT_TITLE_MACRO(action_get_bluetooth_settings_list, MENU_ENUM_LABEL_VALUE_BLUETOOTH_SETTINGS) DEFAULT_TITLE_MACRO(action_get_wifi_settings_list, MENU_ENUM_LABEL_VALUE_WIFI_SETTINGS) DEFAULT_TITLE_MACRO(action_get_network_hosting_settings_list, MENU_ENUM_LABEL_VALUE_NETWORK_HOSTING_SETTINGS) DEFAULT_TITLE_MACRO(action_get_subsystem_settings_list, MENU_ENUM_LABEL_VALUE_SUBSYSTEM_SETTINGS) @@ -783,6 +784,7 @@ static int menu_cbs_init_bind_title_compare_label(menu_file_list_cbs_t *cbs, {MENU_ENUM_LABEL_DEFERRED_MENU_SOUNDS_LIST, action_get_menu_sounds_list}, {MENU_ENUM_LABEL_DEFERRED_MENU_FILE_BROWSER_SETTINGS_LIST, action_get_menu_file_browser_settings_list}, {MENU_ENUM_LABEL_DEFERRED_RETRO_ACHIEVEMENTS_SETTINGS_LIST, action_get_retro_achievements_settings_list}, + {MENU_ENUM_LABEL_DEFERRED_BLUETOOTH_SETTINGS_LIST, action_get_bluetooth_settings_list}, {MENU_ENUM_LABEL_DEFERRED_WIFI_SETTINGS_LIST, action_get_wifi_settings_list}, {MENU_ENUM_LABEL_DEFERRED_UPDATER_SETTINGS_LIST, action_get_updater_settings_list}, {MENU_ENUM_LABEL_DEFERRED_NETWORK_HOSTING_SETTINGS_LIST, action_get_network_hosting_settings_list}, diff --git a/menu/drivers/materialui.c b/menu/drivers/materialui.c index 89dea6f19b..9d4a94fb44 100644 --- a/menu/drivers/materialui.c +++ b/menu/drivers/materialui.c @@ -9558,6 +9558,7 @@ static void materialui_list_insert( string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_RETRO_ACHIEVEMENTS_SETTINGS)) || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_ACCOUNTS_YOUTUBE)) || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_ACCOUNTS_TWITCH)) || + string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_BLUETOOTH_SETTINGS)) || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_WIFI_SETTINGS)) || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_NETWORK_SETTINGS)) || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_NETPLAY_LAN_SCAN_SETTINGS)) || diff --git a/menu/drivers/ozone/ozone_texture.c b/menu/drivers/ozone/ozone_texture.c index 7665e71ce2..3b91ada157 100644 --- a/menu/drivers/ozone/ozone_texture.c +++ b/menu/drivers/ozone/ozone_texture.c @@ -263,6 +263,7 @@ uintptr_t ozone_entries_icon_get_texture(ozone_handle_t *ozone, return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_ACHIEVEMENTS]; case MENU_ENUM_LABEL_NETWORK_INFORMATION: case MENU_ENUM_LABEL_NETWORK_SETTINGS: + case MENU_ENUM_LABEL_BLUETOOTH_SETTINGS: case MENU_ENUM_LABEL_WIFI_SETTINGS: case MENU_ENUM_LABEL_NETWORK_INFO_ENTRY: case MENU_ENUM_LABEL_NETWORK_HOSTING_SETTINGS: @@ -411,6 +412,8 @@ uintptr_t ozone_entries_icon_get_texture(ozone_handle_t *ozone, #endif case MENU_INFO_MESSAGE: return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CORE_INFO]; + case MENU_BLUETOOTH: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_BLUETOOTH]; case MENU_WIFI: return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_WIFI]; #ifdef HAVE_NETWORKING @@ -593,6 +596,8 @@ switch (id) return "undo.png"; case OZONE_ENTRIES_ICONS_TEXTURE_CORE_INFO: return "core-infos.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_BLUETOOTH: + return "bluetooth.png"; case OZONE_ENTRIES_ICONS_TEXTURE_WIFI: return "wifi.png"; case OZONE_ENTRIES_ICONS_TEXTURE_CORE_OPTIONS: diff --git a/menu/drivers/ozone/ozone_texture.h b/menu/drivers/ozone/ozone_texture.h index 68c727b3f1..d2c3f328b9 100644 --- a/menu/drivers/ozone/ozone_texture.h +++ b/menu/drivers/ozone/ozone_texture.h @@ -90,6 +90,7 @@ enum OZONE_ENTRIES_ICONS_TEXTURE_LOADSTATE, OZONE_ENTRIES_ICONS_TEXTURE_UNDO, OZONE_ENTRIES_ICONS_TEXTURE_CORE_INFO, + OZONE_ENTRIES_ICONS_TEXTURE_BLUETOOTH, OZONE_ENTRIES_ICONS_TEXTURE_WIFI, OZONE_ENTRIES_ICONS_TEXTURE_CORE_OPTIONS, OZONE_ENTRIES_ICONS_TEXTURE_INPUT_REMAPPING_OPTIONS, diff --git a/menu/drivers/stripes.c b/menu/drivers/stripes.c index 30b765f7bf..be19e5c9be 100644 --- a/menu/drivers/stripes.c +++ b/menu/drivers/stripes.c @@ -123,6 +123,7 @@ enum STRIPES_TEXTURE_LOADSTATE, STRIPES_TEXTURE_UNDO, STRIPES_TEXTURE_CORE_INFO, + STRIPES_TEXTURE_BLUETOOTH, STRIPES_TEXTURE_WIFI, STRIPES_TEXTURE_CORE_OPTIONS, STRIPES_TEXTURE_INPUT_REMAPPING_OPTIONS, @@ -2280,6 +2281,8 @@ static uintptr_t stripes_icon_get_id(stripes_handle_t *stripes, return stripes->textures.list[STRIPES_TEXTURE_SETTING]; case MENU_INFO_MESSAGE: return stripes->textures.list[STRIPES_TEXTURE_CORE_INFO]; + case MENU_BLUETOOTH: + return stripes->textures.list[STRIPES_TEXTURE_BLUETOOTH]; case MENU_WIFI: return stripes->textures.list[STRIPES_TEXTURE_WIFI]; #ifdef HAVE_NETWORKING @@ -3620,6 +3623,8 @@ static const char *stripes_texture_path(unsigned id) return "undo.png"; case STRIPES_TEXTURE_CORE_INFO: return "core-infos.png"; + case STRIPES_TEXTURE_BLUETOOTH: + return "bluetooth.png"; case STRIPES_TEXTURE_WIFI: return "wifi.png"; case STRIPES_TEXTURE_CORE_OPTIONS: diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index e9ceeb5278..f10e78478d 100644 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -131,6 +131,7 @@ enum XMB_TEXTURE_LOADSTATE, XMB_TEXTURE_UNDO, XMB_TEXTURE_CORE_INFO, + XMB_TEXTURE_BLUETOOTH, XMB_TEXTURE_WIFI, XMB_TEXTURE_CORE_OPTIONS, XMB_TEXTURE_INPUT_REMAPPING_OPTIONS, @@ -2690,6 +2691,7 @@ static uintptr_t xmb_icon_get_id(xmb_handle_t *xmb, return xmb->textures.list[XMB_TEXTURE_RELOAD]; case MENU_ENUM_LABEL_NETWORK_INFORMATION: case MENU_ENUM_LABEL_NETWORK_SETTINGS: + case MENU_ENUM_LABEL_BLUETOOTH_SETTINGS: case MENU_ENUM_LABEL_WIFI_SETTINGS: case MENU_ENUM_LABEL_NETWORK_INFO_ENTRY: case MENU_ENUM_LABEL_NETWORK_HOSTING_SETTINGS: @@ -2810,6 +2812,8 @@ static uintptr_t xmb_icon_get_id(xmb_handle_t *xmb, return xmb->textures.list[XMB_TEXTURE_SETTING]; case MENU_INFO_MESSAGE: return xmb->textures.list[XMB_TEXTURE_CORE_INFO]; + case MENU_BLUETOOTH: + return xmb->textures.list[XMB_TEXTURE_BLUETOOTH]; case MENU_WIFI: return xmb->textures.list[XMB_TEXTURE_WIFI]; #ifdef HAVE_NETWORKING @@ -5628,6 +5632,8 @@ static const char *xmb_texture_path(unsigned id) return "undo.png"; case XMB_TEXTURE_CORE_INFO: return "core-infos.png"; + case XMB_TEXTURE_BLUETOOTH: + return "bluetooth.png"; case XMB_TEXTURE_WIFI: return "wifi.png"; case XMB_TEXTURE_CORE_OPTIONS: diff --git a/menu/menu_cbs.h b/menu/menu_cbs.h index 61057efc42..5ad4c96837 100644 --- a/menu/menu_cbs.h +++ b/menu/menu_cbs.h @@ -175,6 +175,7 @@ enum ACTION_OK_DL_MENU_FILE_BROWSER_SETTINGS_LIST, ACTION_OK_DL_RETRO_ACHIEVEMENTS_SETTINGS_LIST, ACTION_OK_DL_UPDATER_SETTINGS_LIST, + ACTION_OK_DL_BLUETOOTH_SETTINGS_LIST, ACTION_OK_DL_WIFI_SETTINGS_LIST, ACTION_OK_DL_NETWORK_SETTINGS_LIST, ACTION_OK_DL_SUBSYSTEM_SETTINGS_LIST, diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 63f24f9aaf..44b68b0d0b 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -94,6 +94,7 @@ #include "../list_special.h" #include "../performance_counters.h" #include "../core_info.h" +#include "../bluetooth/bluetooth_driver.h" #include "../wifi/wifi_driver.h" #include "../tasks/task_content.h" #include "../tasks/tasks_internal.h" @@ -3902,6 +3903,47 @@ static void menu_displaylist_parse_playlist_generic( playlist, playlist_name, is_collection); } +#ifdef HAVE_BLUETOOTH +static void bluetooth_scan_callback(retro_task_t *task, + void *task_data, + void *user_data, const char *error) +{ + unsigned i; + file_list_t *file_list = NULL; + struct string_list *device_list = NULL; + + const char *path = NULL; + const char *label = NULL; + unsigned menu_type = 0; + + menu_entries_get_last_stack(&path, &label, &menu_type, NULL, NULL); + + /* Don't push the results if we left the bluetooth menu */ + if (!string_is_equal(label, + msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_BLUETOOTH_SETTINGS_LIST))) + return; + + file_list = menu_entries_get_selection_buf_ptr(0); + menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, file_list); + + device_list = string_list_new(); + + driver_bluetooth_get_devices(device_list); + + for (i = 0; i < device_list->size; i++) + { + const char *device = device_list->elems[i].data; + menu_entries_append_enum(file_list, + device, + msg_hash_to_str(MENU_ENUM_LABEL_CONNECT_BLUETOOTH), + MENU_ENUM_LABEL_CONNECT_BLUETOOTH, + MENU_BLUETOOTH, 0, 0); + } + + string_list_free(device_list); +} +#endif + #ifdef HAVE_NETWORKING static void wifi_scan_callback(retro_task_t *task, void *task_data, @@ -4784,6 +4826,35 @@ unsigned menu_displaylist_build_list( #endif } break; + case DISPLAYLIST_BLUETOOTH_SETTINGS_LIST: +#ifdef HAVE_BLUETOOTH + { + settings_t *settings = config_get_ptr(); + if (!string_is_equal(settings->arrays.bluetooth_driver, "null")) + { + struct string_list *device_list = string_list_new(); + driver_bluetooth_get_devices(device_list); + + if (device_list->size == 0) + task_push_bluetooth_scan(bluetooth_scan_callback); + else + { + unsigned i; + for (i = 0; i < device_list->size; i++) + { + const char *device = device_list->elems[i].data; + if (menu_entries_append_enum(list, + device, + msg_hash_to_str(MENU_ENUM_LABEL_CONNECT_BLUETOOTH), + MENU_ENUM_LABEL_CONNECT_BLUETOOTH, + MENU_BLUETOOTH, 0, 0)) + count++; + } + } + } + } +#endif + break; case DISPLAYLIST_WIFI_SETTINGS_LIST: #ifdef HAVE_NETWORKING { @@ -7213,6 +7284,7 @@ unsigned menu_displaylist_build_list( {MENU_ENUM_LABEL_ACCESSIBILITY_SETTINGS, PARSE_ACTION, true}, {MENU_ENUM_LABEL_POWER_MANAGEMENT_SETTINGS,PARSE_ACTION, true}, {MENU_ENUM_LABEL_RETRO_ACHIEVEMENTS_SETTINGS,PARSE_ACTION, true}, + {MENU_ENUM_LABEL_BLUETOOTH_SETTINGS,PARSE_ACTION, true}, {MENU_ENUM_LABEL_WIFI_SETTINGS,PARSE_ACTION, true}, {MENU_ENUM_LABEL_NETWORK_SETTINGS,PARSE_ACTION, true}, {MENU_ENUM_LABEL_NETPLAY_LAN_SCAN_SETTINGS,PARSE_ACTION, true}, @@ -7292,6 +7364,7 @@ unsigned menu_displaylist_build_list( build_list[i].checked = settings->bools.settings_show_directory; break; /* MISSING: + * MENU_ENUM_LABEL_BLUETOOTH_SETTINGS * MENU_ENUM_LABEL_WIFI_SETTINGS * MENU_ENUM_LABEL_NETPLAY_LAN_SCAN_SETTINGS * MENU_ENUM_LABEL_LAKKA_SERVICES @@ -7602,6 +7675,9 @@ unsigned menu_displaylist_build_list( {MENU_ENUM_LABEL_MENU_DRIVER, PARSE_ONLY_STRING_OPTIONS}, {MENU_ENUM_LABEL_RECORD_DRIVER, PARSE_ONLY_STRING_OPTIONS}, {MENU_ENUM_LABEL_MIDI_DRIVER, PARSE_ONLY_STRING_OPTIONS}, +#ifdef HAVE_BLUETOOTH + {MENU_ENUM_LABEL_BLUETOOTH_DRIVER, PARSE_ONLY_STRING_OPTIONS}, +#endif #ifdef HAVE_LAKKA {MENU_ENUM_LABEL_WIFI_DRIVER, PARSE_ONLY_STRING_OPTIONS}, #endif @@ -10002,6 +10078,7 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, case DISPLAYLIST_INFORMATION_LIST: case DISPLAYLIST_SCAN_DIRECTORY_LIST: case DISPLAYLIST_SYSTEM_INFO: + case DISPLAYLIST_BLUETOOTH_SETTINGS_LIST: case DISPLAYLIST_WIFI_SETTINGS_LIST: case DISPLAYLIST_AUDIO_MIXER_SETTINGS_LIST: case DISPLAYLIST_BROWSE_URL_START: @@ -10056,6 +10133,13 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, MENU_ENUM_LABEL_NO_PERFORMANCE_COUNTERS, 0, 0, 0); break; + case DISPLAYLIST_BLUETOOTH_SETTINGS_LIST: + menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_BT_DEVICES_FOUND), + msg_hash_to_str(MENU_ENUM_LABEL_NO_BT_DEVICES_FOUND), + MENU_ENUM_LABEL_NO_BT_DEVICES_FOUND, + 0, 0, 0); + break; case DISPLAYLIST_WIFI_SETTINGS_LIST: menu_entries_append_enum(info->list, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_NETWORKS_FOUND), diff --git a/menu/menu_displaylist.h b/menu/menu_displaylist.h index 1e599c3d73..936a88cd10 100644 --- a/menu/menu_displaylist.h +++ b/menu/menu_displaylist.h @@ -186,6 +186,7 @@ enum menu_displaylist_ctl_state DISPLAYLIST_MENU_SOUNDS_LIST, DISPLAYLIST_RETRO_ACHIEVEMENTS_SETTINGS_LIST, DISPLAYLIST_UPDATER_SETTINGS_LIST, + DISPLAYLIST_BLUETOOTH_SETTINGS_LIST, DISPLAYLIST_WIFI_SETTINGS_LIST, DISPLAYLIST_NETWORK_SETTINGS_LIST, DISPLAYLIST_NETWORK_HOSTING_SETTINGS_LIST, diff --git a/menu/menu_driver.h b/menu/menu_driver.h index 28e2d61825..eecda685b7 100644 --- a/menu/menu_driver.h +++ b/menu/menu_driver.h @@ -121,6 +121,7 @@ enum menu_settings_type MENU_SETTING_PLAYLIST_MANAGER_RIGHT_THUMBNAIL_MODE, MENU_SETTING_PLAYLIST_MANAGER_LEFT_THUMBNAIL_MODE, MENU_SETTING_PLAYLIST_MANAGER_SORT_MODE, + MENU_BLUETOOTH, MENU_WIFI, MENU_ROOM, MENU_ROOM_LAN, diff --git a/menu/menu_setting.c b/menu/menu_setting.c index fe79fee08a..ea2a6b74fa 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -82,6 +82,7 @@ #include "../paths.h" #include "../dynamic.h" #include "../list_special.h" +#include "../bluetooth/bluetooth_driver.h" #include "../wifi/wifi_driver.h" #include "../midi/midi_driver.h" #include "../tasks/tasks_internal.h" @@ -8372,6 +8373,19 @@ static bool setting_append_list( SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_ADVANCED); #endif +#ifdef HAVE_BLUETOOTH + if (string_is_not_equal(settings->arrays.bluetooth_driver, "null")) + { + CONFIG_ACTION( + list, list_info, + MENU_ENUM_LABEL_BLUETOOTH_SETTINGS, + MENU_ENUM_LABEL_VALUE_BLUETOOTH_SETTINGS, + &group_info, + &subgroup_info, + parent_group); + } +#endif + #ifdef HAVE_LAKKA if (string_is_not_equal(settings->arrays.wifi_driver, "null")) { @@ -8482,7 +8496,7 @@ static bool setting_append_list( case SETTINGS_LIST_DRIVERS: { unsigned i; - struct string_options_entry string_options_entries[11]; + struct string_options_entry string_options_entries[12]; START_GROUP(list, list_info, &group_info, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DRIVER_SETTINGS), parent_group); MENU_SETTINGS_LIST_CURRENT_ADD_ENUM_IDX_PTR(list, list_info, MENU_ENUM_LABEL_DRIVER_SETTINGS); @@ -8534,40 +8548,47 @@ static bool setting_append_list( string_options_entries[5].default_value = config_get_default_camera(); string_options_entries[5].values = config_get_camera_driver_options(); - string_options_entries[6].target = settings->arrays.wifi_driver; - string_options_entries[6].len = sizeof(settings->arrays.wifi_driver); - string_options_entries[6].name_enum_idx = MENU_ENUM_LABEL_WIFI_DRIVER; - string_options_entries[6].SHORT_enum_idx = MENU_ENUM_LABEL_VALUE_WIFI_DRIVER; - string_options_entries[6].default_value = config_get_default_wifi(); - string_options_entries[6].values = config_get_wifi_driver_options(); + string_options_entries[6].target = settings->arrays.bluetooth_driver; + string_options_entries[6].len = sizeof(settings->arrays.bluetooth_driver); + string_options_entries[6].name_enum_idx = MENU_ENUM_LABEL_BLUETOOTH_DRIVER; + string_options_entries[6].SHORT_enum_idx = MENU_ENUM_LABEL_VALUE_BLUETOOTH_DRIVER; + string_options_entries[6].default_value = config_get_default_bluetooth(); + string_options_entries[6].values = config_get_bluetooth_driver_options(); - string_options_entries[7].target = settings->arrays.location_driver; - string_options_entries[7].len = sizeof(settings->arrays.location_driver); - string_options_entries[7].name_enum_idx = MENU_ENUM_LABEL_LOCATION_DRIVER; - string_options_entries[7].SHORT_enum_idx = MENU_ENUM_LABEL_VALUE_LOCATION_DRIVER; - string_options_entries[7].default_value = config_get_default_location(); - string_options_entries[7].values = config_get_location_driver_options(); + string_options_entries[7].target = settings->arrays.wifi_driver; + string_options_entries[7].len = sizeof(settings->arrays.wifi_driver); + string_options_entries[7].name_enum_idx = MENU_ENUM_LABEL_WIFI_DRIVER; + string_options_entries[7].SHORT_enum_idx = MENU_ENUM_LABEL_VALUE_WIFI_DRIVER; + string_options_entries[7].default_value = config_get_default_wifi(); + string_options_entries[7].values = config_get_wifi_driver_options(); - string_options_entries[8].target = settings->arrays.menu_driver; - string_options_entries[8].len = sizeof(settings->arrays.menu_driver); - string_options_entries[8].name_enum_idx = MENU_ENUM_LABEL_MENU_DRIVER; - string_options_entries[8].SHORT_enum_idx = MENU_ENUM_LABEL_VALUE_MENU_DRIVER; - string_options_entries[8].default_value = config_get_default_menu(); - string_options_entries[8].values = config_get_menu_driver_options(); + string_options_entries[8].target = settings->arrays.location_driver; + string_options_entries[8].len = sizeof(settings->arrays.location_driver); + string_options_entries[8].name_enum_idx = MENU_ENUM_LABEL_LOCATION_DRIVER; + string_options_entries[8].SHORT_enum_idx = MENU_ENUM_LABEL_VALUE_LOCATION_DRIVER; + string_options_entries[8].default_value = config_get_default_location(); + string_options_entries[8].values = config_get_location_driver_options(); - string_options_entries[9].target = settings->arrays.record_driver; - string_options_entries[9].len = sizeof(settings->arrays.record_driver); - string_options_entries[9].name_enum_idx = MENU_ENUM_LABEL_RECORD_DRIVER; - string_options_entries[9].SHORT_enum_idx = MENU_ENUM_LABEL_VALUE_RECORD_DRIVER; - string_options_entries[9].default_value = config_get_default_record(); - string_options_entries[9].values = config_get_record_driver_options(); + string_options_entries[9].target = settings->arrays.menu_driver; + string_options_entries[9].len = sizeof(settings->arrays.menu_driver); + string_options_entries[9].name_enum_idx = MENU_ENUM_LABEL_MENU_DRIVER; + string_options_entries[9].SHORT_enum_idx = MENU_ENUM_LABEL_VALUE_MENU_DRIVER; + string_options_entries[9].default_value = config_get_default_menu(); + string_options_entries[9].values = config_get_menu_driver_options(); - string_options_entries[10].target = settings->arrays.midi_driver; - string_options_entries[10].len = sizeof(settings->arrays.midi_driver); - string_options_entries[10].name_enum_idx = MENU_ENUM_LABEL_MIDI_DRIVER; - string_options_entries[10].SHORT_enum_idx = MENU_ENUM_LABEL_VALUE_MIDI_DRIVER; - string_options_entries[10].default_value = config_get_default_midi(); - string_options_entries[10].values = config_get_midi_driver_options(); + string_options_entries[10].target = settings->arrays.record_driver; + string_options_entries[10].len = sizeof(settings->arrays.record_driver); + string_options_entries[10].name_enum_idx = MENU_ENUM_LABEL_RECORD_DRIVER; + string_options_entries[10].SHORT_enum_idx = MENU_ENUM_LABEL_VALUE_RECORD_DRIVER; + string_options_entries[10].default_value = config_get_default_record(); + string_options_entries[10].values = config_get_record_driver_options(); + + string_options_entries[11].target = settings->arrays.midi_driver; + string_options_entries[11].len = sizeof(settings->arrays.midi_driver); + string_options_entries[11].name_enum_idx = MENU_ENUM_LABEL_MIDI_DRIVER; + string_options_entries[11].SHORT_enum_idx = MENU_ENUM_LABEL_VALUE_MIDI_DRIVER; + string_options_entries[11].default_value = config_get_default_midi(); + string_options_entries[11].values = config_get_midi_driver_options(); for (i = 0; i < ARRAY_SIZE(string_options_entries); i++) { diff --git a/msg_hash.h b/msg_hash.h index cec0434c70..8acf79197f 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -373,7 +373,9 @@ enum msg_hash_enums MSG_TOGGLE_CONTENT_METADATA, MSG_NO_THUMBNAIL_AVAILABLE, MSG_PRESS_AGAIN_TO_QUIT, + MSG_BLUETOOTH_SCAN_COMPLETE, MSG_WIFI_SCAN_COMPLETE, + MSG_SCANNING_BLUETOOTH_DEVICES, MSG_SCANNING_WIRELESS_NETWORKS, MSG_FAILED_TO_TAKE_SCREENSHOT, MSG_CUSTOM_TIMING_GIVEN, @@ -1259,6 +1261,7 @@ enum msg_hash_enums MENU_LABEL(RETRO_ACHIEVEMENTS_SETTINGS), MENU_LABEL(MENU_FILE_BROWSER_SETTINGS), MENU_LABEL(UPDATER_SETTINGS), + MENU_LABEL(BLUETOOTH_SETTINGS), MENU_LABEL(WIFI_SETTINGS), MENU_LABEL(USER_SETTINGS), MENU_LABEL(DIRECTORY_SETTINGS), @@ -1269,6 +1272,7 @@ enum msg_hash_enums MENU_LABEL(NETWORK_SETTINGS), MENU_LABEL(NETPLAY_LAN_SCAN_SETTINGS), + MENU_ENUM_LABEL_CONNECT_BLUETOOTH, MENU_ENUM_LABEL_CONNECT_WIFI, MENU_ENUM_LABEL_CONNECT_NETPLAY_ROOM, MENU_ENUM_LABEL_CONNECT_NETPLAY_LAN, @@ -1429,6 +1433,7 @@ enum msg_hash_enums MENU_ENUM_LABEL_DEFERRED_MENU_FILE_BROWSER_SETTINGS_LIST, MENU_ENUM_LABEL_DEFERRED_RETRO_ACHIEVEMENTS_SETTINGS_LIST, MENU_ENUM_LABEL_DEFERRED_UPDATER_SETTINGS_LIST, + MENU_ENUM_LABEL_DEFERRED_BLUETOOTH_SETTINGS_LIST, MENU_ENUM_LABEL_DEFERRED_WIFI_SETTINGS_LIST, MENU_ENUM_LABEL_DEFERRED_SUBSYSTEM_SETTINGS_LIST, MENU_ENUM_LABEL_DEFERRED_NETWORK_HOSTING_SETTINGS_LIST, @@ -1509,6 +1514,7 @@ enum msg_hash_enums MENU_LABEL(LOAD_CONTENT_SPECIAL), MENU_LABEL(NO_SETTINGS_FOUND), MENU_LABEL(NO_PRESETS_FOUND), + MENU_LABEL(NO_BT_DEVICES_FOUND), MENU_LABEL(NO_NETWORKS_FOUND), MENU_LABEL(NO_PERFORMANCE_COUNTERS), MENU_LABEL(FRAME_THROTTLE_SETTINGS), @@ -1776,6 +1782,7 @@ enum msg_hash_enums MENU_LABEL(MENU_DRIVER), MENU_LABEL(LOCATION_DRIVER), MENU_LABEL(CAMERA_DRIVER), + MENU_LABEL(BLUETOOTH_DRIVER), MENU_LABEL(WIFI_DRIVER), MENU_LABEL(AUDIO_RESAMPLER_DRIVER), MENU_LABEL(RECORD_DRIVER), @@ -1979,6 +1986,7 @@ enum msg_hash_enums MENU_LABEL(SIDELOAD_CORE_ERROR), MENU_LABEL(SIDELOAD_CORE_SUCCESS), MENU_LABEL(MANAGEMENT), + MENU_LABEL(BT_CONNECTED), MENU_LABEL(ONLINE), MENU_LABEL(ONLINE_UPDATER), MENU_LABEL(NETPLAY), diff --git a/retroarch.c b/retroarch.c index 430f0c4445..5c6188bd2c 100644 --- a/retroarch.c +++ b/retroarch.c @@ -215,6 +215,7 @@ #endif #include "gfx/video_display_server.h" #include "gfx/video_crt_switch.h" +#include "bluetooth/bluetooth_driver.h" #include "wifi/wifi_driver.h" #include "led/led_driver.h" #include "midi/midi_driver.h" @@ -857,6 +858,30 @@ static hid_driver_t *hid_drivers[] = { }; #endif +static bluetooth_driver_t bluetooth_null = { + NULL, /* init */ + NULL, /* free */ + NULL, /* start */ + NULL, /* stop */ + NULL, /* scan */ + NULL, /* get_devices */ + NULL, /* device_is_connected */ + NULL, /* device_get_sublabel */ + NULL, /* connect_device */ + "null", +}; + +static const bluetooth_driver_t *bluetooth_drivers[] = { +#ifdef HAVE_BLUETOOTH + &bluetooth_bluetoothctl, +#ifdef HAVE_DBUS + &bluetooth_bluez, +#endif +#endif + &bluetooth_null, + NULL, +}; + static wifi_driver_t wifi_null = { NULL, /* init */ NULL, /* free */ @@ -1026,6 +1051,7 @@ static const camera_driver_t *camera_drivers[] = { | DRIVER_LOCATION_MASK \ | DRIVER_MENU_MASK \ | DRIVERS_VIDEO_INPUT_MASK \ + | DRIVER_BLUETOOTH_MASK \ | DRIVER_WIFI_MASK \ | DRIVER_LED_MASK \ | DRIVER_MIDI_MASK ) @@ -1037,6 +1063,7 @@ static const camera_driver_t *camera_drivers[] = { | DRIVER_CAMERA_MASK \ | DRIVER_LOCATION_MASK \ | DRIVERS_VIDEO_INPUT_MASK \ + | DRIVER_BLUETOOTH_MASK \ | DRIVER_WIFI_MASK \ | DRIVER_LED_MASK \ | DRIVER_MIDI_MASK ) @@ -1950,6 +1977,7 @@ struct rarch_state #endif bool location_driver_active; + bool bluetooth_driver_active; bool wifi_driver_active; bool video_driver_active; bool audio_driver_active; @@ -2356,6 +2384,9 @@ struct rarch_state const location_driver_t *location_driver; void *location_data; + const bluetooth_driver_t *bluetooth_driver; + void *bluetooth_data; + const wifi_driver_t *wifi_driver; void *wifi_data; @@ -10140,6 +10171,21 @@ struct string_list *string_list_new_special(enum string_list_type type, string_list_append(s, opt, attr); } break; + case STRING_LIST_BLUETOOTH_DRIVERS: +#ifdef HAVE_BLUETOOTH + for (i = 0; bluetooth_drivers[i]; i++) + { + const char *opt = bluetooth_drivers[i]->ident; + *len += strlen(opt) + 1; + + if (!add_null_entries) + add_null_entries = (i == 0) || !string_is_equal(opt, "null"); + + if (add_null_entries) + string_list_append(s, opt, attr); + } + break; +#endif case STRING_LIST_WIFI_DRIVERS: #ifdef HAVE_WIFI for (i = 0; wifi_drivers[i]; i++) @@ -20054,6 +20100,164 @@ static void clear_controller_port_map(struct rarch_state *p_rarch) { } #endif +/* BLUETOOTH DRIVER */ + +/** + * config_get_bluetooth_driver_options: + * + * Get an enumerated list of all bluetooth driver names, + * separated by '|'. + * + * Returns: string listing of all bluetooth driver names, + * separated by '|'. + **/ +const char* config_get_bluetooth_driver_options(void) +{ + return char_list_new_special(STRING_LIST_BLUETOOTH_DRIVERS, NULL); +} + +void driver_bluetooth_scan(void) +{ + struct rarch_state *p_rarch = &rarch_st; + p_rarch->bluetooth_driver->scan(); +} + +void driver_bluetooth_get_devices(struct string_list* devices) +{ + struct rarch_state *p_rarch = &rarch_st; + p_rarch->bluetooth_driver->get_devices(devices); +} + +bool driver_bluetooth_device_is_connected(unsigned i) +{ + struct rarch_state *p_rarch = &rarch_st; + return p_rarch->bluetooth_driver->device_is_connected(i); +} + +void driver_bluetooth_device_get_sublabel(char *s, unsigned i, size_t len) +{ + struct rarch_state *p_rarch = &rarch_st; + p_rarch->bluetooth_driver->device_get_sublabel(s, i, len); +} + +bool driver_bluetooth_connect_device(unsigned i) +{ + struct rarch_state *p_rarch = &rarch_st; + return p_rarch->bluetooth_driver->connect_device(i); +} + +bool bluetooth_driver_ctl(enum rarch_bluetooth_ctl_state state, void *data) +{ + struct rarch_state *p_rarch = &rarch_st; + settings_t *settings = p_rarch->configuration_settings; + + switch (state) + { + case RARCH_BLUETOOTH_CTL_DESTROY: + p_rarch->bluetooth_driver_active = false; + p_rarch->bluetooth_driver = NULL; + p_rarch->bluetooth_data = NULL; + break; + case RARCH_BLUETOOTH_CTL_SET_ACTIVE: + p_rarch->bluetooth_driver_active = true; + break; + case RARCH_BLUETOOTH_CTL_FIND_DRIVER: + { + int i; + driver_ctx_info_t drv; + + drv.label = "bluetooth_driver"; + drv.s = settings->arrays.bluetooth_driver; + + driver_ctl(RARCH_DRIVER_CTL_FIND_INDEX, &drv); + + i = (int)drv.len; + + if (i >= 0) + p_rarch->bluetooth_driver = (const bluetooth_driver_t*)bluetooth_drivers[i]; + else + { + if (verbosity_is_enabled()) + { + unsigned d; + RARCH_ERR("Couldn't find any bluetooth driver named \"%s\"\n", + settings->arrays.bluetooth_driver); + RARCH_LOG_OUTPUT("Available bluetooth drivers are:\n"); + for (d = 0; bluetooth_drivers[d]; d++) + RARCH_LOG_OUTPUT("\t%s\n", bluetooth_drivers[d]->ident); + + RARCH_WARN("Going to default to first bluetooth driver...\n"); + } + + p_rarch->bluetooth_driver = (const bluetooth_driver_t*)bluetooth_drivers[0]; + + if (!p_rarch->bluetooth_driver) + retroarch_fail(1, "find_bluetooth_driver()"); + } + } + break; + case RARCH_BLUETOOTH_CTL_UNSET_ACTIVE: + p_rarch->bluetooth_driver_active = false; + break; + case RARCH_BLUETOOTH_CTL_IS_ACTIVE: + return p_rarch->bluetooth_driver_active; + case RARCH_BLUETOOTH_CTL_DEINIT: + if (p_rarch->bluetooth_data && p_rarch->bluetooth_driver) + { + if (p_rarch->bluetooth_driver->free) + p_rarch->bluetooth_driver->free(p_rarch->bluetooth_data); + } + + p_rarch->bluetooth_data = NULL; + break; + case RARCH_BLUETOOTH_CTL_STOP: + if ( p_rarch->bluetooth_driver + && p_rarch->bluetooth_driver->stop + && p_rarch->bluetooth_data) + p_rarch->bluetooth_driver->stop(p_rarch->bluetooth_data); + break; + case RARCH_BLUETOOTH_CTL_START: + if ( p_rarch->bluetooth_driver + && p_rarch->bluetooth_data + && p_rarch->bluetooth_driver->start) + { + bool bluetooth_allow = settings->bools.bluetooth_allow; + if (bluetooth_allow) + return p_rarch->bluetooth_driver->start(p_rarch->bluetooth_data); + } + return false; + case RARCH_BLUETOOTH_CTL_SET_CB: + { + /*struct retro_bluetooth_callback *cb = + (struct retro_bluetooth_callback*)data; + bluetooth_cb = *cb;*/ + } + break; + case RARCH_BLUETOOTH_CTL_INIT: + /* Resource leaks will follow if bluetooth is initialized twice. */ + if (p_rarch->bluetooth_data) + return false; + + bluetooth_driver_ctl(RARCH_BLUETOOTH_CTL_FIND_DRIVER, NULL); + + p_rarch->bluetooth_data = p_rarch->bluetooth_driver->init(); + + if (!p_rarch->bluetooth_data) + { + RARCH_ERR("Failed to initialize bluetooth driver. Will continue without bluetooth.\n"); + bluetooth_driver_ctl(RARCH_BLUETOOTH_CTL_UNSET_ACTIVE, NULL); + } + + /*if (bluetooth_cb.initialized) + bluetooth_cb.initialized();*/ + break; + default: + break; + } + + return false; +} + /* WIFI DRIVER */ /** @@ -32937,6 +33141,21 @@ static const void *find_driver_nonempty( } } } + else if (string_is_equal(label, "bluetooth_driver")) + { + if (bluetooth_drivers[i]) + { + const char *ident = bluetooth_drivers[i]->ident; + if (!add_entry) + add_entry = i == 0 || !string_is_equal(ident, "null"); + + if (add_entry) + { + strlcpy(s, ident, len); + return bluetooth_drivers[i]; + } + } + } else if (string_is_equal(label, "wifi_driver")) { if (wifi_drivers[i]) @@ -33366,6 +33585,9 @@ static void driver_uninit(struct rarch_state *p_rarch, int flags) p_rarch->camera_data = NULL; } + if ((flags & DRIVER_BLUETOOTH_MASK)) + bluetooth_driver_ctl(RARCH_BLUETOOTH_CTL_DEINIT, NULL); + if ((flags & DRIVER_WIFI_MASK)) wifi_driver_ctl(RARCH_WIFI_CTL_DEINIT, NULL); @@ -33445,6 +33667,7 @@ static void retroarch_deinit_drivers(struct rarch_state *p_rarch) p_rarch->camera_driver = NULL; p_rarch->camera_data = NULL; + bluetooth_driver_ctl(RARCH_BLUETOOTH_CTL_DESTROY, NULL); wifi_driver_ctl(RARCH_WIFI_CTL_DESTROY, NULL); cbs->frame_cb = retro_frame_null; @@ -35402,6 +35625,7 @@ bool retroarch_main_init(int argc, char *argv[]) video_driver_find_driver(p_rarch); input_driver_find_driver(p_rarch); camera_driver_find_driver(p_rarch); + bluetooth_driver_ctl(RARCH_BLUETOOTH_CTL_FIND_DRIVER, NULL); wifi_driver_ctl(RARCH_WIFI_CTL_FIND_DRIVER, NULL); find_location_driver(p_rarch); #ifdef HAVE_MENU diff --git a/tasks/task_bluetooth.c b/tasks/task_bluetooth.c new file mode 100644 index 0000000000..845bcbb29e --- /dev/null +++ b/tasks/task_bluetooth.c @@ -0,0 +1,57 @@ +/* RetroArch - A frontend for libretro. + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include "tasks_internal.h" + +#include "../msg_hash.h" +#include "../verbosity.h" +#include "../bluetooth/bluetooth_driver.h" + +static void task_bluetooth_scan_handler(retro_task_t *task) +{ + driver_bluetooth_scan(); + + task_set_progress(task, 100); + task_free_title(task); + task_set_title(task, strdup(msg_hash_to_str(MSG_BLUETOOTH_SCAN_COMPLETE))); + task_set_finished(task, true); +} + +bool task_push_bluetooth_scan(retro_task_callback_t cb) +{ + retro_task_t *task = task_init(); + + if (!task) + return false; + + /* blocking means no other task can run while this one is running, + * which is the default */ + task->type = TASK_TYPE_BLOCKING; + task->state = NULL; + task->handler = task_bluetooth_scan_handler; + task->callback = cb; + task->title = strdup(msg_hash_to_str( + MSG_SCANNING_BLUETOOTH_DEVICES)); + + task_queue_push(task); + + return true; +} diff --git a/tasks/tasks_internal.h b/tasks/tasks_internal.h index ef2a95d4bd..e7910cd0c9 100644 --- a/tasks/tasks_internal.h +++ b/tasks/tasks_internal.h @@ -81,6 +81,8 @@ void *task_push_http_post_transfer_with_user_agent(const char* url, const char* task_retriever_info_t *http_task_get_transfer_list(void); +bool task_push_bluetooth_scan(retro_task_callback_t cb); + bool task_push_wifi_scan(retro_task_callback_t cb); bool task_push_netplay_lan_scan(retro_task_callback_t cb);