Skip to content

Commit

Permalink
Add MSVC support for Bluetooth
Browse files Browse the repository at this point in the history
Qt5 doen't implement some bluetooth functions in the WinRT backend. I
guess it's caused by the C++ minimum standard requirement. I have to
port some functions from Qt6 to make the Bluetooth function on MSVC
builds.

Limitations:
1. BLE Central doesn't work well on MinGW builds.
2. Bluetooth Client discovery doesn't work well on MSVC builds. Users
can still specify the Bluetooth address and connect to a server
manually.
3. Bluetooth Client reconnect might causes crashes on MSVC builds.
  • Loading branch information
wh201906 committed Apr 5, 2024
1 parent 511ec17 commit 9760ccc
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 8 deletions.
11 changes: 10 additions & 1 deletion src/SerialTest.pro
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
30 changes: 23 additions & 7 deletions src/devicetab.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
#include <QtAndroid>
#include <QAndroidJniEnvironment>
#endif
#ifdef _MSC_VER
#include <winrtbluetooth.h>
#endif

const QMap<QString, QString> DeviceTab::m_historyPrefix =
{
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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");
Expand Down
90 changes: 90 additions & 0 deletions src/winrtbluetooth.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#include "winrtbluetooth.h"

#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.Devices.Enumeration.h>
#include <winrt/Windows.Devices.Bluetooth.h>
#include <winrt/Windows.Devices.Radios.h>

#include <QCoreApplication>
#include <QElapsedTimer>

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 <typename T>
static bool await(IAsyncOperation<T> &&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<QBluetoothHostInfo> WinRTBluetooth::allLocalDevices(bool PoweredOnOnly)
{
QList<QBluetoothHostInfo> 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;
}
17 changes: 17 additions & 0 deletions src/winrtbluetooth.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#ifndef WINRTBLUETOOTH_H
#define WINRTBLUETOOTH_H

#include <QObject>
#include <QBluetoothHostInfo>

class WinRTBluetooth : public QObject
{
Q_OBJECT
public:
explicit WinRTBluetooth(QObject *parent = nullptr);
static QList<QBluetoothHostInfo> allLocalDevices(bool PoweredOnOnly = false);
signals:

};

#endif // WINRTBLUETOOTH_H

0 comments on commit 9760ccc

Please sign in to comment.