diff --git a/src/SerialTest.pro b/src/SerialTest.pro index 0d0b995..f8abaae 100644 --- a/src/SerialTest.pro +++ b/src/SerialTest.pro @@ -6,7 +6,16 @@ android { greaterThan(QT_MAJOR_VERSION, 4): QT += widgets -CONFIG += c++11 +win32-msvc*: { + # For Bluetooth LE in WinRT + CONFIG += c++17 + SOURCES += winrtbluetooth.cpp + HEADERS += winrtbluetooth.h + LIBS += -lwindowsapp +} else { + CONFIG += c++11 +} + # The following define makes your compiler emit warnings if you use # any Qt feature that has been marked deprecated (the exact warnings diff --git a/src/devicetab.cpp b/src/devicetab.cpp index 456a74d..e5c50c4 100644 --- a/src/devicetab.cpp +++ b/src/devicetab.cpp @@ -13,6 +13,9 @@ #include #include #endif +#ifdef _MSC_VER +#include +#endif const QMap DeviceTab::m_historyPrefix = { @@ -424,19 +427,28 @@ qint64 DeviceTab::updateBTAdapterList() // need modify getRequiredPermission(); #endif +#ifdef _MSC_VER + // Only powered on adapters + auto BTAdapterList = WinRTBluetooth::allLocalDevices(true); +#else + // All adapters auto BTAdapterList = QBluetoothLocalDevice::allDevices(); +#endif for(auto it = BTAdapterList.cbegin(); it != BTAdapterList.cend(); ++it) { qDebug() << "dev:" << it->name() << it->address(); +#ifndef _MSC_VER QBluetoothLocalDevice dev(it->address()); - if(dev.isValid() && dev.hostMode() != QBluetoothLocalDevice::HostPoweredOff) + if(!dev.isValid() || dev.hostMode() == QBluetoothLocalDevice::HostPoweredOff) { - QString name = QString("%1:%2").arg(adapterID + 1).arg(it->name()); - ui->BTClient_adapterBox->addItem(name, it->address().toString()); - ui->BTServer_adapterBox->addItem(name, it->address().toString()); - ui->BLEC_adapterBox->addItem(name, it->address().toString()); - adapterID++; + continue; } +#endif + QString name = QString("%1:%2").arg(adapterID + 1).arg(it->name()); + ui->BTClient_adapterBox->addItem(name, it->address().toString()); + ui->BTServer_adapterBox->addItem(name, it->address().toString()); + ui->BLEC_adapterBox->addItem(name, it->address().toString()); + adapterID++; } return adapterID; // adapter count } @@ -1277,9 +1289,13 @@ void DeviceTab::on_refreshButton_clicked() void DeviceTab::on_BTClient_adapterBox_activated(int index) { + // This function is actually useless. + // According to the source code of Qt Connectivity (v5.9.0, v5.15.2 and v6.7.0), + // the constructor of QBluetoothDeviceDiscoveryAgent only checks if the adapter address exists + // in QBluetoothLocalDevice::allDevices(). It ignores null address and won't specify which adapter to be used. Q_UNUSED(index) ui->BTClient_localAddrLabel->setText(ui->BTClient_adapterBox->currentData().toString()); - setBTClientDiscoveryAgent(QBluetoothAddress(ui->BTClient_adapterBox->currentData().toString())); + setBTClientDiscoveryAgent(QBluetoothAddress()); #ifdef Q_OS_ANDROID // invalid MAC address, ignore ui->BTClient_localAddrLabel->setHidden(ui->BTClient_localAddrLabel->text() == "02:00:00:00:00:00"); diff --git a/src/winrtbluetooth.cpp b/src/winrtbluetooth.cpp new file mode 100644 index 0000000..ac9e544 --- /dev/null +++ b/src/winrtbluetooth.cpp @@ -0,0 +1,90 @@ +#include "winrtbluetooth.h" + +#include +#include +#include +#include + +#include +#include + +using namespace winrt::Windows::Foundation; +using namespace winrt::Windows::Foundation::Collections; +using namespace winrt::Windows::Devices::Enumeration; +using namespace winrt::Windows::Devices::Bluetooth; +using namespace winrt::Windows::Devices::Radios; + +template +static bool await(IAsyncOperation &&asyncInfo, T &result, uint timeout = 0) +{ + using WinRtAsyncStatus = winrt::Windows::Foundation::AsyncStatus; + WinRtAsyncStatus status; + QElapsedTimer timer; + if(timeout) + timer.start(); + do + { + QCoreApplication::processEvents(); + status = asyncInfo.Status(); + } + while(status == WinRtAsyncStatus::Started && (!timeout || !timer.hasExpired(timeout))); + if(status == WinRtAsyncStatus::Completed) + { + result = asyncInfo.GetResults(); + return true; + } + else + { + return false; + } +} + +static DeviceInformationCollection getAvailableAdapters() +{ + const auto btSelector = BluetoothAdapter::GetDeviceSelector(); + DeviceInformationCollection deviceInfoCollection(nullptr); + await(DeviceInformation::FindAllAsync(btSelector), deviceInfoCollection); + return deviceInfoCollection; +} + +static Radio getRadioFromAdapterId(winrt::hstring id) +{ + BluetoothAdapter a(nullptr); + bool res = await(BluetoothAdapter::FromIdAsync(id), a); + if(res && a) + { + Radio r(nullptr); + res = await(a.GetRadioAsync(), r); + if(res && r) + return r; + } + return nullptr; +} + +WinRTBluetooth::WinRTBluetooth(QObject *parent) + : QObject{parent} +{ + +} + +QList WinRTBluetooth::allLocalDevices(bool PoweredOnOnly) +{ + QList devices; + const auto deviceInfoCollection = getAvailableAdapters(); + if(deviceInfoCollection) + { + for(const auto &devInfo : deviceInfoCollection) + { + BluetoothAdapter adapter(nullptr); + const bool res = await(BluetoothAdapter::FromIdAsync(devInfo.Id()), adapter); + if(res && adapter && (!PoweredOnOnly || getRadioFromAdapterId(devInfo.Id()).State() == RadioState::On)) + { + QBluetoothHostInfo info; + info.setName(QString::fromStdString(winrt::to_string(devInfo.Name()))); + info.setAddress(QBluetoothAddress(adapter.BluetoothAddress())); + devices.push_back(std::move(info)); + } + } + } + return devices; +} diff --git a/src/winrtbluetooth.h b/src/winrtbluetooth.h new file mode 100644 index 0000000..72f5060 --- /dev/null +++ b/src/winrtbluetooth.h @@ -0,0 +1,17 @@ +#ifndef WINRTBLUETOOTH_H +#define WINRTBLUETOOTH_H + +#include +#include + +class WinRTBluetooth : public QObject +{ + Q_OBJECT +public: + explicit WinRTBluetooth(QObject *parent = nullptr); + static QList allLocalDevices(bool PoweredOnOnly = false); +signals: + +}; + +#endif // WINRTBLUETOOTH_H