summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIvan Solovev <ivan.solovev@qt.io>2022-06-22 18:57:41 +0200
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2022-07-11 12:18:50 +0000
commitf7453b9ac69fd177ead408c22eb580a21797bef3 (patch)
tree58dde6f22a6c281985a1f3db37cf4bc7c41dbbf6
parent23804f42fb201c0495d28b67095482012b672c48 (diff)
Windows Bluetooth: move DeviceWatcher wrapper into a separate header
The DeviceWatcher wrapper seems to be useful not only in QBluetoothLocalDevice implementation, but also for device discovery. This patch moves it to a separate header, so that it can be reused. The class' API is also refactored to suit for more general usecases. This patch also applies the changes to QBluetoothLocalDevice. As a drive-by: clean-up some includes and namespaces in QBluetoothLocalDevice implementation. Task-number: QTBUG-103263 Change-Id: I470c6eab4810065c03d5905032f4288fa9d6de8e Reviewed-by: Juha Vuolle <juha.vuolle@insta.fi> Reviewed-by: Oliver Wolff <oliver.wolff@qt.io> (cherry picked from commit 761a059d5a5ef97e97039f3a34b3a7f92944f1f0) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r--src/bluetooth/CMakeLists.txt1
-rw-r--r--src/bluetooth/qbluetoothdevicewatcher_winrt.cpp109
-rw-r--r--src/bluetooth/qbluetoothdevicewatcher_winrt_p.h69
-rw-r--r--src/bluetooth/qbluetoothlocaldevice_winrt.cpp90
4 files changed, 207 insertions, 62 deletions
diff --git a/src/bluetooth/CMakeLists.txt b/src/bluetooth/CMakeLists.txt
index 909a26f4..865bf154 100644
--- a/src/bluetooth/CMakeLists.txt
+++ b/src/bluetooth/CMakeLists.txt
@@ -223,6 +223,7 @@ elseif(QT_FEATURE_winrt_bt)
qbluetoothsocket_winrt.cpp qbluetoothsocket_winrt_p.h
qbluetoothutils_winrt.cpp qbluetoothutils_winrt_p.h
qlowenergycontroller_winrt.cpp qlowenergycontroller_winrt_p.h
+ qbluetoothdevicewatcher_winrt.cpp qbluetoothdevicewatcher_winrt_p.h
DEFINES
QT_WINRT_BLUETOOTH
LIBRARIES
diff --git a/src/bluetooth/qbluetoothdevicewatcher_winrt.cpp b/src/bluetooth/qbluetoothdevicewatcher_winrt.cpp
new file mode 100644
index 00000000..a61cde87
--- /dev/null
+++ b/src/bluetooth/qbluetoothdevicewatcher_winrt.cpp
@@ -0,0 +1,109 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qbluetoothdevicewatcher_winrt_p.h"
+
+#include <winrt/Windows.Foundation.Collections.h>
+
+using namespace winrt::Windows::Foundation;
+using namespace winrt::Windows::Devices::Enumeration;
+
+QT_BEGIN_NAMESPACE
+
+QBluetoothDeviceWatcherWinRT::QBluetoothDeviceWatcherWinRT(int id, winrt::hstring selector)
+ : m_id(id),
+ m_watcher(DeviceInformation::CreateWatcher(selector))
+{
+ qRegisterMetaType<winrt::hstring>("winrt::hstring");
+}
+
+QBluetoothDeviceWatcherWinRT::QBluetoothDeviceWatcherWinRT(int id, winrt::hstring selector,
+ winrt::Windows::Devices::Enumeration::DeviceInformationKind kind)
+ : m_id(id)
+{
+ qRegisterMetaType<winrt::hstring>("winrt::hstring");
+ const winrt::param::iterable<winrt::hstring> extra {};
+ m_watcher = DeviceInformation::CreateWatcher(selector, extra, kind);
+}
+
+QBluetoothDeviceWatcherWinRT::~QBluetoothDeviceWatcherWinRT()
+{
+ if (m_watcher && m_initialized) {
+ stop();
+ unsubscribeFromEvents();
+ m_initialized = false;
+ }
+}
+
+bool QBluetoothDeviceWatcherWinRT::init()
+{
+ if (!m_watcher) {
+ qWarning("Windows failed to create an instance of DeviceWatcher. "
+ "Detection of Bluetooth devices might not work correctly.");
+ return false;
+ }
+ if (!m_initialized) {
+ subscribeToEvents();
+ m_initialized = true;
+ }
+ return true;
+}
+
+void QBluetoothDeviceWatcherWinRT::start()
+{
+ if (m_watcher)
+ m_watcher.Start();
+}
+
+void QBluetoothDeviceWatcherWinRT::stop()
+{
+ if (m_watcher && canStop())
+ m_watcher.Stop();
+}
+
+void QBluetoothDeviceWatcherWinRT::subscribeToEvents()
+{
+ Q_ASSERT(m_watcher.Status() == DeviceWatcherStatus::Created);
+ // The callbacks are triggered from separate threads. So we capture
+ // thisPtr to make sure that the object is valid.
+ auto thisPtr = shared_from_this();
+ m_addedToken = m_watcher.Added([thisPtr](DeviceWatcher, const DeviceInformation &info) {
+ emit thisPtr->deviceAdded(info.Id(), thisPtr->m_id);
+ });
+ m_removedToken =
+ m_watcher.Removed([thisPtr](DeviceWatcher, const DeviceInformationUpdate &upd) {
+ emit thisPtr->deviceRemoved(upd.Id(), thisPtr->m_id);
+ });
+ m_updatedToken =
+ m_watcher.Updated([thisPtr](DeviceWatcher, const DeviceInformationUpdate &upd) {
+ emit thisPtr->deviceUpdated(upd.Id(), thisPtr->m_id);
+ });
+ // because of ambiguous declaration
+ using WinRtInspectable = winrt::Windows::Foundation::IInspectable;
+ m_enumerationToken =
+ m_watcher.EnumerationCompleted([thisPtr](DeviceWatcher, const WinRtInspectable &) {
+ emit thisPtr->enumerationCompleted(thisPtr->m_id);
+ });
+ m_stoppedToken = m_watcher.Stopped([thisPtr](DeviceWatcher, const WinRtInspectable &) {
+ emit thisPtr->watcherStopped(thisPtr->m_id);
+ });
+}
+
+void QBluetoothDeviceWatcherWinRT::unsubscribeFromEvents()
+{
+ m_watcher.Added(m_addedToken);
+ m_watcher.Removed(m_removedToken);
+ m_watcher.Updated(m_updatedToken);
+ m_watcher.EnumerationCompleted(m_enumerationToken);
+ m_watcher.Stopped(m_stoppedToken);
+}
+
+bool QBluetoothDeviceWatcherWinRT::canStop() const
+{
+ const auto status = m_watcher.Status();
+ // Also 'Aborted', but calling Stop() there is a no-op
+ return status == DeviceWatcherStatus::Started
+ || status == DeviceWatcherStatus::EnumerationCompleted;
+}
+
+QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothdevicewatcher_winrt_p.h b/src/bluetooth/qbluetoothdevicewatcher_winrt_p.h
new file mode 100644
index 00000000..c0551036
--- /dev/null
+++ b/src/bluetooth/qbluetoothdevicewatcher_winrt_p.h
@@ -0,0 +1,69 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QBLUETOOTHDEVICEWATCHER_WINRT_P_H
+#define QBLUETOOTHDEVICEWATCHER_WINRT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/private/qglobal_p.h>
+#include <QtCore/QObject>
+
+#include <private/qbluetoothutils_winrt_p.h>
+
+#include <winrt/base.h>
+#include <winrt/Windows.Devices.Enumeration.h>
+
+QT_BEGIN_NAMESPACE
+
+class QBluetoothDeviceWatcherWinRT : public QObject,
+ public std::enable_shared_from_this<QBluetoothDeviceWatcherWinRT>
+{
+ Q_OBJECT
+public:
+ QBluetoothDeviceWatcherWinRT(int id, winrt::hstring selector);
+ QBluetoothDeviceWatcherWinRT(int id, winrt::hstring selector,
+ winrt::Windows::Devices::Enumeration::DeviceInformationKind kind);
+ ~QBluetoothDeviceWatcherWinRT();
+
+ bool init();
+ void start();
+ void stop();
+
+signals:
+ // The signals will be emitted from a separate thread,
+ // so we need to use Qt::QueuedConnection
+ void deviceAdded(winrt::hstring deviceId, int id);
+ void deviceRemoved(winrt::hstring deviceId, int id);
+ void deviceUpdated(winrt::hstring deviceId, int id);
+ void enumerationCompleted(int id);
+ void watcherStopped(int id);
+
+private:
+ void subscribeToEvents();
+ void unsubscribeFromEvents();
+ bool canStop() const;
+
+ const int m_id; // used to uniquely identify the wrapper
+ winrt::Windows::Devices::Enumeration::DeviceWatcher m_watcher = nullptr;
+ bool m_initialized = false;
+
+ winrt::event_token m_addedToken;
+ winrt::event_token m_removedToken;
+ winrt::event_token m_updatedToken;
+ winrt::event_token m_enumerationToken;
+ winrt::event_token m_stoppedToken;
+};
+
+QT_END_NAMESPACE
+
+#endif // QBLUETOOTHDEVICEWATCHER_WINRT_P_H
diff --git a/src/bluetooth/qbluetoothlocaldevice_winrt.cpp b/src/bluetooth/qbluetoothlocaldevice_winrt.cpp
index d97ba507..06fb5340 100644
--- a/src/bluetooth/qbluetoothlocaldevice_winrt.cpp
+++ b/src/bluetooth/qbluetoothlocaldevice_winrt.cpp
@@ -1,11 +1,12 @@
-// Copyright (C) 2019 The Qt Company Ltd.
+// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#include <qbluetoothutils_winrt_p.h>
+#include <private/qbluetoothutils_winrt_p.h>
#include "qbluetoothlocaldevice.h"
#include "qbluetoothaddress.h"
+#include "qbluetoothdevicewatcher_winrt_p.h"
#include "qbluetoothlocaldevice_p.h"
#include "qbluetoothutils_winrt_p.h"
@@ -14,20 +15,18 @@
#include <winrt/Windows.Devices.Enumeration.h>
#include <winrt/Windows.Devices.Bluetooth.h>
#include <winrt/Windows.Devices.Radios.h>
-#include <wrl.h>
-#include <QtCore/private/qfunctions_winrt_p.h>
+#include <QtCore/QCoreApplication>
+#include <QtCore/QElapsedTimer>
+#include <QtCore/QLoggingCategory>
+#include <QtCore/QMutex>
#include <QtCore/QPointer>
#include <QtCore/QTimer>
-#include <QtCore/QMutex>
-#include <QtCore/QLoggingCategory>
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 Microsoft::WRL;
-using namespace Microsoft::WRL::Wrappers;
using namespace winrt::Windows::Devices::Radios;
QT_BEGIN_NAMESPACE
@@ -75,66 +74,35 @@ static RadioState windowsStateFromMode(QBluetoothLocalDevice::HostMode mode)
class AdapterManager;
-class WatcherWrapper : public QObject, public std::enable_shared_from_this<WatcherWrapper>
+class WatcherWrapper
{
- Q_OBJECT
public:
- WatcherWrapper(winrt::hstring selector) : mWatcher(DeviceInformation::CreateWatcher(selector))
+ // we do not really care about unique ids here
+ WatcherWrapper(winrt::hstring selector) :
+ mWatcher(std::make_shared<QBluetoothDeviceWatcherWinRT>(0, selector))
{
}
- ~WatcherWrapper()
- {
- if (mWatcher && mInitialized) {
- mWatcher.Stop();
- unsubscribeFromEvents();
- }
- }
template<typename AddedSlot, typename RemovedSlot>
- bool init(AdapterManager *manager, AddedSlot onAdded, RemovedSlot onRemoved)
+ void init(AdapterManager *manager, AddedSlot onAdded, RemovedSlot onRemoved)
{
if (!mWatcher) {
- qWarning() << "Windows failed to create an instance of DeviceWatcher. "
- << "QBluetoothLocalDevice might fail to provide some information.";
- return false;
+ qWarning("QBluetoothLocalDevice: failed to create device watcher!");
+ return;
}
- subscribeToEvents();
- connect(this, &WatcherWrapper::deviceAdded, manager, onAdded, Qt::QueuedConnection);
- connect(this, &WatcherWrapper::deviceRemoved, manager, onRemoved, Qt::QueuedConnection);
- mInitialized = true;
- mWatcher.Start();
- return true;
- }
-
-signals:
- void deviceAdded(winrt::hstring id);
- void deviceRemoved(winrt::hstring id);
+ QObject::connect(mWatcher.get(), &QBluetoothDeviceWatcherWinRT::deviceAdded,
+ manager, onAdded, Qt::QueuedConnection);
+ QObject::connect(mWatcher.get(), &QBluetoothDeviceWatcherWinRT::deviceRemoved,
+ manager, onRemoved, Qt::QueuedConnection);
-private:
- void subscribeToEvents()
- {
- // The callbacks are triggered from separate threads. So we capture
- // thisPtr to make sure that the object is valid.
- auto thisPtr = shared_from_this();
- mAddedToken = mWatcher.Added([thisPtr](DeviceWatcher, const DeviceInformation &info) {
- emit thisPtr->deviceAdded(info.Id());
- });
- mRemovedToken =
- mWatcher.Removed([thisPtr](DeviceWatcher, const DeviceInformationUpdate &upd) {
- emit thisPtr->deviceRemoved(upd.Id());
- });
- }
- void unsubscribeFromEvents()
- {
- mWatcher.Added(mAddedToken);
- mWatcher.Removed(mRemovedToken);
+ // This will also print a warning on failure.
+ if (mWatcher->init())
+ mWatcher->start();
}
- bool mInitialized = false;
- DeviceWatcher mWatcher = nullptr;
- winrt::event_token mAddedToken;
- winrt::event_token mRemovedToken;
+private:
+ std::shared_ptr<QBluetoothDeviceWatcherWinRT> mWatcher = nullptr;
};
/*
@@ -193,9 +161,9 @@ private:
Q_SLOT void onDeviceAdded(winrt::hstring id);
Q_SLOT void onDeviceRemoved(winrt::hstring id);
- std::shared_ptr<WatcherWrapper> mAdapterWatcher = nullptr;
- std::shared_ptr<WatcherWrapper> mLeDevicesWatcher = nullptr;
- std::shared_ptr<WatcherWrapper> mClassicDevicesWatcher = nullptr;
+ std::unique_ptr<WatcherWrapper> mAdapterWatcher = nullptr;
+ std::unique_ptr<WatcherWrapper> mLeDevicesWatcher = nullptr;
+ std::unique_ptr<WatcherWrapper> mClassicDevicesWatcher = nullptr;
QMutex mMutex;
// Key for this map is BluetoothAdapter Id, *not* Radio Id.
QMap<winrt::hstring, RadioInfo> mRadios;
@@ -204,10 +172,8 @@ private:
AdapterManager::AdapterManager() : QObject()
{
- qRegisterMetaType<winrt::hstring>("winrt::hstring");
-
const auto adapterSelector = BluetoothAdapter::GetDeviceSelector();
- mAdapterWatcher = std::make_shared<WatcherWrapper>(adapterSelector);
+ mAdapterWatcher = std::make_unique<WatcherWrapper>(adapterSelector);
mAdapterWatcher->init(this, &AdapterManager::onAdapterAdded, &AdapterManager::onAdapterRemoved);
// Once created, device watchers will also populate the initial list of
@@ -215,7 +181,7 @@ AdapterManager::AdapterManager() : QObject()
const auto leSelector = BluetoothLEDevice::GetDeviceSelectorFromConnectionStatus(
BluetoothConnectionStatus::Connected);
- mLeDevicesWatcher = std::make_shared<WatcherWrapper>(leSelector);
+ mLeDevicesWatcher = std::make_unique<WatcherWrapper>(leSelector);
mLeDevicesWatcher->init(this, &AdapterManager::onDeviceAdded, &AdapterManager::onDeviceRemoved);
const auto classicSelector = BluetoothDevice::GetDeviceSelectorFromConnectionStatus(