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);