#include "BluetoothDevices.h" #include "System.h" #include #include #include #include #ifdef HAS_BLUEZ namespace BluetoothDevices { namespace DynCtx { enum class DeviceState { Invalid = BIT(0), Failed = BIT(1), // Request failed RequestConnect = BIT(2), // If device is requested to connect RequestDisconnect = BIT(3), // If device is requested to disconnect Connected = BIT(4), // BlueZ says to us, that it is connected Disconnected = BIT(5), // BlueZ says to us, that it is disconnected }; DEFINE_ENUM_FLAGS(DeviceState); struct BTDeviceWithState { System::BluetoothDevice device{}; DeviceState state{}; }; std::mutex deviceMutex; std::vector devices; Box* deviceListBox; Window* win; bool scanning = false; void OnClick(Button& button, BTDeviceWithState& device) { DeviceState& state = device.state; // Clear failed bit state &= ~DeviceState::Failed; button.RemoveClass("failed"); // Only try to connect, if we know we're already disconnected and we haven't requested before(Same for disconnect) if (FLAG_CHECK(state, DeviceState::Disconnected) && !FLAG_CHECK(state, DeviceState::RequestConnect)) { button.AddClass("active"); button.RemoveClass("inactive"); state |= DeviceState::RequestConnect; System::Connect(device.device, [&dev = device, &but = button](bool success, System::BluetoothDevice&) { deviceMutex.lock(); if (!success) { dev.state &= ~DeviceState::RequestConnect; dev.state |= DeviceState::Failed; but.AddClass("failed"); } deviceMutex.unlock(); }); } else if (FLAG_CHECK(state, DeviceState::Connected) && !FLAG_CHECK(state, DeviceState::RequestDisconnect)) { button.AddClass("inactive"); button.RemoveClass("active"); state |= DeviceState::RequestDisconnect; System::Disconnect(device.device, [&dev = device, &but = button](bool success, System::BluetoothDevice&) { deviceMutex.lock(); if (!success) { dev.state &= ~DeviceState::RequestDisconnect; dev.state |= DeviceState::Failed; but.AddClass("failed"); } deviceMutex.unlock(); }); } } void UpdateDeviceUIElem(Button& button, BTDeviceWithState& device) { if (device.device.name.size()) { button.SetText(device.device.name); } else { button.SetText(device.device.mac); } bool requestConnect = FLAG_CHECK(device.state, DeviceState::RequestConnect); bool requestDisconnect = FLAG_CHECK(device.state, DeviceState::RequestDisconnect); if (requestConnect || (!requestDisconnect && FLAG_CHECK(device.state, DeviceState::Connected))) { button.AddClass("active"); button.RemoveClass("inactive"); } else if (requestDisconnect || (!requestConnect && FLAG_CHECK(device.state, DeviceState::Disconnected))) { button.AddClass("inactive"); button.RemoveClass("active"); } if (FLAG_CHECK(device.state, DeviceState::Failed)) { button.AddClass("failed"); } else { button.RemoveClass("failed"); } button.OnClick( [&dev = device](Button& button) { OnClick(button, dev); }); } void InvalidateDeviceUI() { // Shrink if (deviceListBox->GetChilds().size() > devices.size()) { for (size_t i = deviceListBox->GetChilds().size() - 1; i >= devices.size(); i--) { deviceListBox->RemoveChild(i); } } size_t idx = 0; for (auto& device : devices) { if (idx >= deviceListBox->GetChilds().size()) { // Create new auto button = Widget::Create