mirror of
https://github.com/subsurface/subsurface.git
synced 2024-11-28 21:20:19 +00:00
26e610c3f4
Creating it from an address is a) deprecated and b) impossible on Mac or iOS. Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
682 lines
23 KiB
C++
682 lines
23 KiB
C++
// SPDX-License-Identifier: GPL-2.0
|
|
#include <QShortcut>
|
|
#include <QDebug>
|
|
#include <QMessageBox>
|
|
#include <QMenu>
|
|
#include "core/btdiscovery.h"
|
|
|
|
#include <QBluetoothUuid>
|
|
|
|
#include "ui_btdeviceselectiondialog.h"
|
|
#include "btdeviceselectiondialog.h"
|
|
|
|
#if defined(Q_OS_WIN)
|
|
Q_DECLARE_METATYPE(QBluetoothDeviceDiscoveryAgent::Error)
|
|
#endif
|
|
#if QT_VERSION < 0x050500
|
|
Q_DECLARE_METATYPE(QBluetoothDeviceInfo)
|
|
#endif
|
|
|
|
BtDeviceSelectionDialog::BtDeviceSelectionDialog(QWidget *parent) :
|
|
QDialog(parent),
|
|
ui(new Ui::BtDeviceSelectionDialog),
|
|
remoteDeviceDiscoveryAgent(0)
|
|
{
|
|
ui->setupUi(this);
|
|
|
|
// Quit button callbacks
|
|
QShortcut *quit = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q), this);
|
|
connect(quit, SIGNAL(activated()), this, SLOT(reject()));
|
|
connect(ui->quit, SIGNAL(clicked()), this, SLOT(reject()));
|
|
|
|
// Translate the UI labels
|
|
ui->localDeviceDetails->setTitle(tr("Local Bluetooth device details"));
|
|
ui->selectDeviceLabel->setText(tr("Select device:"));
|
|
ui->deviceAddressLabel->setText(tr("Address:"));
|
|
ui->deviceNameLabel->setText(tr("Name:"));
|
|
ui->deviceState->setText(tr("Bluetooth powered on"));
|
|
ui->changeDeviceState->setText(tr("Turn on/off"));
|
|
ui->discoveredDevicesLabel->setText(tr("Discovered devices"));
|
|
ui->scan->setText(tr("Scan"));
|
|
ui->clear->setText(tr("Clear"));
|
|
ui->save->setText(tr("Save"));
|
|
ui->quit->setText(tr("Quit"));
|
|
|
|
// Disable the save button because there is no device selected
|
|
ui->save->setEnabled(false);
|
|
|
|
// Add event for item selection
|
|
connect(ui->discoveredDevicesList, SIGNAL(itemClicked(QListWidgetItem*)),
|
|
this, SLOT(itemClicked(QListWidgetItem*)));
|
|
|
|
#if defined(Q_OS_WIN)
|
|
ULONG ulRetCode = SUCCESS;
|
|
WSADATA WSAData = { 0 };
|
|
|
|
// Initialize WinSock and ask for version 2.2.
|
|
ulRetCode = WSAStartup(MAKEWORD(2, 2), &WSAData);
|
|
if (ulRetCode != SUCCESS) {
|
|
QMessageBox::critical(this, "Bluetooth",
|
|
tr("Could not initialize Winsock version 2.2"), QMessageBox::Ok);
|
|
return;
|
|
}
|
|
|
|
// Initialize the device discovery agent
|
|
initializeDeviceDiscoveryAgent();
|
|
|
|
// On Windows we cannot select a device or show information about the local device
|
|
ui->localDeviceDetails->hide();
|
|
#else
|
|
// Initialize the local Bluetooth device
|
|
localDevice = new QBluetoothLocalDevice();
|
|
|
|
// Populate the list with local bluetooth devices
|
|
QList<QBluetoothHostInfo> localAvailableDevices = localDevice->allDevices();
|
|
int availableDevicesSize = localAvailableDevices.size();
|
|
|
|
if (availableDevicesSize > 1) {
|
|
int defaultDeviceIndex = -1;
|
|
|
|
for (int it = 0; it < availableDevicesSize; it++) {
|
|
QBluetoothHostInfo localAvailableDevice = localAvailableDevices.at(it);
|
|
ui->localSelectedDevice->addItem(localAvailableDevice.name(),
|
|
QVariant::fromValue(localAvailableDevice.address()));
|
|
|
|
if (localDevice->address() == localAvailableDevice.address())
|
|
defaultDeviceIndex = it;
|
|
}
|
|
|
|
// Positionate the current index to the default device and register to index changes events
|
|
ui->localSelectedDevice->setCurrentIndex(defaultDeviceIndex);
|
|
connect(ui->localSelectedDevice, SIGNAL(currentIndexChanged(int)),
|
|
this, SLOT(localDeviceChanged(int)));
|
|
} else {
|
|
// If there is only one local Bluetooth adapter hide the combobox and the label
|
|
ui->selectDeviceLabel->hide();
|
|
ui->localSelectedDevice->hide();
|
|
}
|
|
|
|
// Update the UI information about the local device
|
|
updateLocalDeviceInformation();
|
|
|
|
// Initialize the device discovery agent
|
|
if (localDevice->isValid())
|
|
initializeDeviceDiscoveryAgent();
|
|
#endif
|
|
}
|
|
|
|
BtDeviceSelectionDialog::~BtDeviceSelectionDialog()
|
|
{
|
|
delete ui;
|
|
|
|
#if defined(Q_OS_WIN)
|
|
// Terminate the use of Winsock 2 DLL
|
|
WSACleanup();
|
|
#else
|
|
// Clean the local device
|
|
delete localDevice;
|
|
#endif
|
|
if (remoteDeviceDiscoveryAgent) {
|
|
// Clean the device discovery agent
|
|
if (remoteDeviceDiscoveryAgent->isActive()) {
|
|
remoteDeviceDiscoveryAgent->stop();
|
|
#if defined(Q_OS_WIN)
|
|
remoteDeviceDiscoveryAgent->wait();
|
|
#endif
|
|
}
|
|
|
|
delete remoteDeviceDiscoveryAgent;
|
|
}
|
|
}
|
|
|
|
void BtDeviceSelectionDialog::on_changeDeviceState_clicked()
|
|
{
|
|
#if defined(Q_OS_WIN)
|
|
// TODO add implementation
|
|
#else
|
|
if (localDevice->hostMode() == QBluetoothLocalDevice::HostPoweredOff) {
|
|
ui->dialogStatus->setText(tr("Trying to turn on the local Bluetooth device..."));
|
|
localDevice->powerOn();
|
|
} else {
|
|
ui->dialogStatus->setText(tr("Trying to turn off the local Bluetooth device..."));
|
|
localDevice->setHostMode(QBluetoothLocalDevice::HostPoweredOff);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void BtDeviceSelectionDialog::on_save_clicked()
|
|
{
|
|
// Get the selected device. There will be always a selected device if the save button is enabled.
|
|
QListWidgetItem *currentItem = ui->discoveredDevicesList->currentItem();
|
|
QBluetoothDeviceInfo remoteDeviceInfo = currentItem->data(Qt::UserRole).value<QBluetoothDeviceInfo>();
|
|
|
|
// Save the selected device
|
|
selectedRemoteDeviceInfo = QSharedPointer<QBluetoothDeviceInfo>(new QBluetoothDeviceInfo(remoteDeviceInfo));
|
|
QString address = remoteDeviceInfo.address().isNull() ? remoteDeviceInfo.deviceUuid().toString() :
|
|
remoteDeviceInfo.address().toString();
|
|
saveBtDeviceInfo(address.toUtf8().constData(), remoteDeviceInfo);
|
|
if (remoteDeviceDiscoveryAgent->isActive()) {
|
|
// Stop the SDP agent if the clear button is pressed and enable the Scan button
|
|
remoteDeviceDiscoveryAgent->stop();
|
|
#if defined(Q_OS_WIN)
|
|
remoteDeviceDiscoveryAgent->wait();
|
|
#endif
|
|
ui->scan->setEnabled(true);
|
|
}
|
|
|
|
// Close the device selection dialog and set the result code to Accepted
|
|
accept();
|
|
}
|
|
|
|
void BtDeviceSelectionDialog::on_clear_clicked()
|
|
{
|
|
ui->dialogStatus->setText(tr("Remote devices list was cleared."));
|
|
ui->discoveredDevicesList->clear();
|
|
ui->save->setEnabled(false);
|
|
|
|
if (remoteDeviceDiscoveryAgent->isActive()) {
|
|
// Stop the SDP agent if the clear button is pressed and enable the Scan button
|
|
remoteDeviceDiscoveryAgent->stop();
|
|
#if defined(Q_OS_WIN)
|
|
remoteDeviceDiscoveryAgent->wait();
|
|
#endif
|
|
ui->scan->setEnabled(true);
|
|
}
|
|
}
|
|
|
|
void BtDeviceSelectionDialog::on_scan_clicked()
|
|
{
|
|
ui->dialogStatus->setText(tr("Scanning for remote devices..."));
|
|
ui->discoveredDevicesList->clear();
|
|
remoteDeviceDiscoveryAgent->start();
|
|
ui->scan->setEnabled(false);
|
|
}
|
|
|
|
void BtDeviceSelectionDialog::remoteDeviceScanFinished()
|
|
{
|
|
if (remoteDeviceDiscoveryAgent->error() == QBluetoothDeviceDiscoveryAgent::NoError) {
|
|
ui->dialogStatus->setText(tr("Scanning finished successfully."));
|
|
} else {
|
|
deviceDiscoveryError(remoteDeviceDiscoveryAgent->error());
|
|
}
|
|
|
|
ui->scan->setEnabled(true);
|
|
}
|
|
|
|
void BtDeviceSelectionDialog::hostModeStateChanged(QBluetoothLocalDevice::HostMode mode)
|
|
{
|
|
#if defined(Q_OS_WIN)
|
|
// TODO add implementation
|
|
#else
|
|
bool on = !(mode == QBluetoothLocalDevice::HostPoweredOff);
|
|
|
|
//: %1 will be replaced with "turned on" or "turned off"
|
|
ui->dialogStatus->setText(tr("The local Bluetooth device was %1.")
|
|
.arg(on? tr("turned on") : tr("turned off")));
|
|
ui->deviceState->setChecked(on);
|
|
ui->scan->setEnabled(on);
|
|
#endif
|
|
}
|
|
|
|
void BtDeviceSelectionDialog::addRemoteDevice(const QBluetoothDeviceInfo &remoteDeviceInfo)
|
|
{
|
|
#if defined(Q_OS_WIN)
|
|
// On Windows we cannot obtain the pairing status so we set only the name and the address of the device
|
|
QString deviceLabel = QString("%1 (%2)").arg(remoteDeviceInfo.name(),
|
|
remoteDeviceInfo.address().toString());
|
|
QColor pairingColor = QColor(Qt::white);
|
|
#else
|
|
// By default we use the status label and the color for the UNPAIRED state
|
|
QColor pairingColor = QColor("#F1A9A0");
|
|
QString pairingStatusLabel = tr("UNPAIRED");
|
|
QBluetoothLocalDevice::Pairing pairingStatus = localDevice->pairingStatus(remoteDeviceInfo.address());
|
|
|
|
if (pairingStatus == QBluetoothLocalDevice::Paired) {
|
|
pairingStatusLabel = tr("PAIRED");
|
|
pairingColor = QColor(Qt::gray);
|
|
} else if (pairingStatus == QBluetoothLocalDevice::AuthorizedPaired) {
|
|
pairingStatusLabel = tr("AUTHORIZED_PAIRED");
|
|
pairingColor = QColor("#89C4F4");
|
|
}
|
|
if (remoteDeviceInfo.address().isNull())
|
|
pairingColor = QColor(Qt::gray);
|
|
|
|
QString deviceLabel;
|
|
|
|
#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
|
|
if (!remoteDeviceInfo.deviceUuid().isNull()) {
|
|
// we have only a Uuid, no address, so show that and reset the pairing color
|
|
deviceLabel = QString("%1 (%2)").arg(remoteDeviceInfo.name(),remoteDeviceInfo.deviceUuid().toString());
|
|
pairingColor = QColor(Qt::white);
|
|
} else
|
|
#endif
|
|
deviceLabel = tr("%1 (%2) [State: %3]").arg(remoteDeviceInfo.name(),
|
|
remoteDeviceInfo.address().toString(),
|
|
pairingStatusLabel);
|
|
#endif
|
|
// Create the new item, set its information and add it to the list
|
|
QListWidgetItem *item = new QListWidgetItem(deviceLabel);
|
|
|
|
item->setData(Qt::UserRole, QVariant::fromValue(remoteDeviceInfo));
|
|
item->setBackgroundColor(pairingColor);
|
|
|
|
ui->discoveredDevicesList->addItem(item);
|
|
}
|
|
|
|
void BtDeviceSelectionDialog::itemClicked(QListWidgetItem *item)
|
|
{
|
|
// By default we assume that the devices are paired
|
|
QBluetoothDeviceInfo remoteDeviceInfo = item->data(Qt::UserRole).value<QBluetoothDeviceInfo>();
|
|
QString statusMessage = tr("The device %1 can be used for connection. You can press the Save button.")
|
|
.arg(remoteDeviceInfo.address().isNull() ?
|
|
remoteDeviceInfo.deviceUuid().toString() :
|
|
remoteDeviceInfo.address().toString());
|
|
bool enableSaveButton = true;
|
|
|
|
#if !defined(Q_OS_WIN)
|
|
// On other platforms than Windows we can obtain the pairing status so if the devices are not paired we disable the button
|
|
// except on MacOS for those devices that only give us a Uuid and not and address (as we have no pairing status for those, either)
|
|
if (!remoteDeviceInfo.address().isNull()) {
|
|
QBluetoothLocalDevice::Pairing pairingStatus = localDevice->pairingStatus(remoteDeviceInfo.address());
|
|
|
|
if (pairingStatus == QBluetoothLocalDevice::Unpaired) {
|
|
statusMessage = tr("The device %1 must be paired in order to be used. Please use the context menu for pairing options.")
|
|
.arg(remoteDeviceInfo.address().toString());
|
|
enableSaveButton = false;
|
|
}
|
|
}
|
|
if (remoteDeviceInfo.address().isNull() && remoteDeviceInfo.deviceUuid().isNull()) {
|
|
statusMessage = tr("A device needs a non-zero address for a connection.");
|
|
enableSaveButton = false;
|
|
}
|
|
#endif
|
|
// Update the status message and the save button
|
|
ui->dialogStatus->setText(statusMessage);
|
|
ui->save->setEnabled(enableSaveButton);
|
|
}
|
|
|
|
void BtDeviceSelectionDialog::localDeviceChanged(int index)
|
|
{
|
|
#if defined(Q_OS_WIN)
|
|
// TODO add implementation
|
|
#else
|
|
QBluetoothAddress localDeviceSelectedAddress = ui->localSelectedDevice->itemData(index, Qt::UserRole).value<QBluetoothAddress>();
|
|
|
|
// Delete the old localDevice
|
|
if (localDevice)
|
|
delete localDevice;
|
|
|
|
// Create a new local device using the selected address
|
|
localDevice = new QBluetoothLocalDevice(localDeviceSelectedAddress);
|
|
|
|
ui->dialogStatus->setText(tr("The local device was changed."));
|
|
|
|
// Clear the discovered devices list
|
|
on_clear_clicked();
|
|
|
|
// Update the UI information about the local device
|
|
updateLocalDeviceInformation();
|
|
|
|
// Initialize the device discovery agent
|
|
if (localDevice->isValid())
|
|
initializeDeviceDiscoveryAgent();
|
|
#endif
|
|
}
|
|
|
|
void BtDeviceSelectionDialog::displayPairingMenu(const QPoint &pos)
|
|
{
|
|
#if defined(Q_OS_WIN)
|
|
// TODO add implementation
|
|
#else
|
|
QMenu menu(this);
|
|
QAction *pairAction = menu.addAction(tr("Pair"));
|
|
QAction *removePairAction = menu.addAction(tr("Remove pairing"));
|
|
QAction *chosenAction = menu.exec(ui->discoveredDevicesList->viewport()->mapToGlobal(pos));
|
|
QListWidgetItem *currentItem = ui->discoveredDevicesList->currentItem();
|
|
QBluetoothDeviceInfo currentRemoteDeviceInfo = currentItem->data(Qt::UserRole).value<QBluetoothDeviceInfo>();
|
|
QBluetoothLocalDevice::Pairing pairingStatus = localDevice->pairingStatus(currentRemoteDeviceInfo.address());
|
|
|
|
//TODO: disable the actions
|
|
if (pairingStatus == QBluetoothLocalDevice::Unpaired) {
|
|
pairAction->setEnabled(true);
|
|
removePairAction->setEnabled(false);
|
|
} else {
|
|
pairAction->setEnabled(false);
|
|
removePairAction->setEnabled(true);
|
|
}
|
|
|
|
if (chosenAction == pairAction) {
|
|
ui->dialogStatus->setText(tr("Trying to pair device %1")
|
|
.arg(currentRemoteDeviceInfo.address().toString()));
|
|
localDevice->requestPairing(currentRemoteDeviceInfo.address(), QBluetoothLocalDevice::Paired);
|
|
} else if (chosenAction == removePairAction) {
|
|
ui->dialogStatus->setText(tr("Trying to unpair device %1")
|
|
.arg(currentRemoteDeviceInfo.address().toString()));
|
|
localDevice->requestPairing(currentRemoteDeviceInfo.address(), QBluetoothLocalDevice::Unpaired);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void BtDeviceSelectionDialog::pairingFinished(const QBluetoothAddress &address, QBluetoothLocalDevice::Pairing pairing)
|
|
{
|
|
// Determine the color, the new pairing status and the log message. By default we assume that the devices are UNPAIRED.
|
|
QString remoteDeviceStringAddress = address.toString();
|
|
QColor pairingColor = QColor(Qt::red);
|
|
QString pairingStatusLabel = tr("UNPAIRED");
|
|
QString dialogStatusMessage = tr("Device %1 was unpaired.").arg(remoteDeviceStringAddress);
|
|
bool enableSaveButton = false;
|
|
|
|
if (pairing == QBluetoothLocalDevice::Paired) {
|
|
pairingStatusLabel = tr("PAIRED");
|
|
pairingColor = QColor(Qt::gray);
|
|
enableSaveButton = true;
|
|
dialogStatusMessage = tr("Device %1 was paired.").arg(remoteDeviceStringAddress);
|
|
} else if (pairing == QBluetoothLocalDevice::AuthorizedPaired) {
|
|
pairingStatusLabel = tr("AUTHORIZED_PAIRED");
|
|
pairingColor = QColor(Qt::blue);
|
|
enableSaveButton = true;
|
|
dialogStatusMessage = tr("Device %1 was paired and is authorized.").arg(remoteDeviceStringAddress);
|
|
}
|
|
|
|
// Find the items which represent the BTH device and update their state
|
|
QList<QListWidgetItem *> items = ui->discoveredDevicesList->findItems(remoteDeviceStringAddress, Qt::MatchContains);
|
|
QRegularExpression pairingExpression = QRegularExpression(QString("%1|%2|%3").arg(tr("PAIRED"),
|
|
tr("AUTHORIZED_PAIRED"),
|
|
tr("UNPAIRED")));
|
|
|
|
for (int i = 0; i < items.count(); ++i) {
|
|
QListWidgetItem *item = items.at(i);
|
|
QString updatedDeviceLabel = item->text().replace(QRegularExpression(pairingExpression),
|
|
pairingStatusLabel);
|
|
|
|
item->setText(updatedDeviceLabel);
|
|
item->setBackgroundColor(pairingColor);
|
|
}
|
|
|
|
// Check if the updated device is the selected one from the list and inform the user that it can/cannot start the download mode
|
|
QListWidgetItem *currentItem = ui->discoveredDevicesList->currentItem();
|
|
|
|
if (currentItem != NULL && currentItem->text().contains(remoteDeviceStringAddress, Qt::CaseInsensitive)) {
|
|
if (pairing == QBluetoothLocalDevice::Unpaired) {
|
|
dialogStatusMessage = tr("The device %1 must be paired in order to be used. Please use the context menu for pairing options.")
|
|
.arg(remoteDeviceStringAddress);
|
|
} else {
|
|
dialogStatusMessage = tr("The device %1 can now be used for connection. You can press the Save button.")
|
|
.arg(remoteDeviceStringAddress);
|
|
}
|
|
}
|
|
|
|
// Update the save button and the dialog status message
|
|
ui->save->setEnabled(enableSaveButton);
|
|
ui->dialogStatus->setText(dialogStatusMessage);
|
|
}
|
|
|
|
void BtDeviceSelectionDialog::error(QBluetoothLocalDevice::Error error)
|
|
{
|
|
ui->dialogStatus->setText(tr("Local device error: %1.")
|
|
.arg((error == QBluetoothLocalDevice::PairingError)? tr("Pairing error. If the remote device requires a custom PIN code, "
|
|
"please try to pair the devices using your operating system. ")
|
|
: tr("Unknown error")));
|
|
}
|
|
|
|
void BtDeviceSelectionDialog::deviceDiscoveryError(QBluetoothDeviceDiscoveryAgent::Error error)
|
|
{
|
|
QString errorDescription;
|
|
|
|
switch (error) {
|
|
case QBluetoothDeviceDiscoveryAgent::PoweredOffError:
|
|
errorDescription = tr("The Bluetooth adaptor is powered off, power it on before doing discovery.");
|
|
break;
|
|
case QBluetoothDeviceDiscoveryAgent::InputOutputError:
|
|
errorDescription = tr("Writing to or reading from the device resulted in an error.");
|
|
break;
|
|
default:
|
|
#if defined(Q_OS_WIN)
|
|
errorDescription = remoteDeviceDiscoveryAgent->errorToString();
|
|
#else
|
|
errorDescription = tr("An unknown error has occurred.");
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
ui->dialogStatus->setText(tr("Device discovery error: %1.").arg(errorDescription));
|
|
}
|
|
|
|
extern QString markBLEAddress(const QBluetoothDeviceInfo *device);
|
|
|
|
QString BtDeviceSelectionDialog::getSelectedDeviceAddress()
|
|
{
|
|
if (selectedRemoteDeviceInfo)
|
|
return markBLEAddress(selectedRemoteDeviceInfo.data());
|
|
|
|
return QString();
|
|
}
|
|
|
|
QString BtDeviceSelectionDialog::getSelectedDeviceName()
|
|
{
|
|
if (selectedRemoteDeviceInfo) {
|
|
return selectedRemoteDeviceInfo.data()->name();
|
|
}
|
|
|
|
return QString();
|
|
}
|
|
|
|
void BtDeviceSelectionDialog::updateLocalDeviceInformation()
|
|
{
|
|
#if defined(Q_OS_WIN)
|
|
// TODO add implementation
|
|
#else
|
|
// Check if the selected Bluetooth device can be accessed
|
|
if (!localDevice->isValid()) {
|
|
QString na = tr("Not available");
|
|
|
|
// Update the UI information
|
|
ui->deviceAddress->setText(na);
|
|
ui->deviceName->setText(na);
|
|
|
|
// Announce the user that there is a problem with the selected local Bluetooth adapter
|
|
ui->dialogStatus->setText(tr("The local Bluetooth adapter cannot be accessed."));
|
|
|
|
// Disable the buttons
|
|
ui->save->setEnabled(false);
|
|
ui->scan->setEnabled(false);
|
|
ui->clear->setEnabled(false);
|
|
ui->changeDeviceState->setEnabled(false);
|
|
|
|
return;
|
|
}
|
|
|
|
// Set UI information about the local device
|
|
ui->deviceAddress->setText(localDevice->address().toString());
|
|
ui->deviceName->setText(localDevice->name());
|
|
|
|
connect(localDevice, SIGNAL(hostModeStateChanged(QBluetoothLocalDevice::HostMode)),
|
|
this, SLOT(hostModeStateChanged(QBluetoothLocalDevice::HostMode)));
|
|
|
|
// Initialize the state of the local device and activate/deactive the scan button
|
|
hostModeStateChanged(localDevice->hostMode());
|
|
|
|
// Add context menu for devices to be able to pair them
|
|
ui->discoveredDevicesList->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
connect(ui->discoveredDevicesList, SIGNAL(customContextMenuRequested(QPoint)),
|
|
this, SLOT(displayPairingMenu(QPoint)));
|
|
connect(localDevice, SIGNAL(pairingFinished(QBluetoothAddress, QBluetoothLocalDevice::Pairing)),
|
|
this, SLOT(pairingFinished(QBluetoothAddress, QBluetoothLocalDevice::Pairing)));
|
|
|
|
connect(localDevice, SIGNAL(error(QBluetoothLocalDevice::Error)),
|
|
this, SLOT(error(QBluetoothLocalDevice::Error)));
|
|
#endif
|
|
}
|
|
|
|
void BtDeviceSelectionDialog::initializeDeviceDiscoveryAgent()
|
|
{
|
|
#if defined(Q_OS_WIN)
|
|
// Register QBluetoothDeviceInfo metatype
|
|
qRegisterMetaType<QBluetoothDeviceInfo>();
|
|
|
|
// Register QBluetoothDeviceDiscoveryAgent metatype (Needed for QBluetoothDeviceDiscoveryAgent::Error)
|
|
qRegisterMetaType<QBluetoothDeviceDiscoveryAgent::Error>();
|
|
|
|
// Intialize the discovery agent
|
|
remoteDeviceDiscoveryAgent = new WinBluetoothDeviceDiscoveryAgent(this);
|
|
#else
|
|
// Intialize the discovery agent
|
|
remoteDeviceDiscoveryAgent = new QBluetoothDeviceDiscoveryAgent(localDevice->address());
|
|
|
|
// Test if the discovery agent was successfully created
|
|
if (remoteDeviceDiscoveryAgent->error() == QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError) {
|
|
ui->dialogStatus->setText(tr("The device discovery agent was not created because the %1 address does not "
|
|
"match the physical adapter address of any local Bluetooth device.")
|
|
.arg(localDevice->address().toString()));
|
|
ui->scan->setEnabled(false);
|
|
ui->clear->setEnabled(false);
|
|
return;
|
|
}
|
|
#endif
|
|
connect(remoteDeviceDiscoveryAgent, SIGNAL(deviceDiscovered(QBluetoothDeviceInfo)),
|
|
this, SLOT(addRemoteDevice(QBluetoothDeviceInfo)));
|
|
connect(remoteDeviceDiscoveryAgent, SIGNAL(finished()),
|
|
this, SLOT(remoteDeviceScanFinished()));
|
|
connect(remoteDeviceDiscoveryAgent, SIGNAL(error(QBluetoothDeviceDiscoveryAgent::Error)),
|
|
this, SLOT(deviceDiscoveryError(QBluetoothDeviceDiscoveryAgent::Error)));
|
|
}
|
|
|
|
#if defined(Q_OS_WIN)
|
|
WinBluetoothDeviceDiscoveryAgent::WinBluetoothDeviceDiscoveryAgent(QObject *parent) : QThread(parent)
|
|
{
|
|
// Initialize the internal flags by their default values
|
|
running = false;
|
|
stopped = false;
|
|
lastError = QBluetoothDeviceDiscoveryAgent::NoError;
|
|
lastErrorToString = tr("No error");
|
|
}
|
|
|
|
WinBluetoothDeviceDiscoveryAgent::~WinBluetoothDeviceDiscoveryAgent()
|
|
{
|
|
}
|
|
|
|
bool WinBluetoothDeviceDiscoveryAgent::isActive() const
|
|
{
|
|
return running;
|
|
}
|
|
|
|
QString WinBluetoothDeviceDiscoveryAgent::errorToString() const
|
|
{
|
|
return lastErrorToString;
|
|
}
|
|
|
|
QBluetoothDeviceDiscoveryAgent::Error WinBluetoothDeviceDiscoveryAgent::error() const
|
|
{
|
|
return lastError;
|
|
}
|
|
|
|
void WinBluetoothDeviceDiscoveryAgent::run()
|
|
{
|
|
// Initialize query for device and start the lookup service
|
|
WSAQUERYSET queryset;
|
|
HANDLE hLookup;
|
|
int result = SUCCESS;
|
|
|
|
running = true;
|
|
lastError = QBluetoothDeviceDiscoveryAgent::NoError;
|
|
lastErrorToString = tr("No error");
|
|
|
|
memset(&queryset, 0, sizeof(WSAQUERYSET));
|
|
queryset.dwSize = sizeof(WSAQUERYSET);
|
|
queryset.dwNameSpace = NS_BTH;
|
|
|
|
// The LUP_CONTAINERS flag is used to signal that we are doing a device inquiry
|
|
// while LUP_FLUSHCACHE flag is used to flush the device cache for all inquiries
|
|
// and to do a fresh lookup instead.
|
|
result = WSALookupServiceBegin(&queryset, LUP_CONTAINERS | LUP_FLUSHCACHE, &hLookup);
|
|
|
|
if (result != SUCCESS) {
|
|
// Get the last error and emit a signal
|
|
lastErrorToString = qt_error_string();
|
|
lastError = QBluetoothDeviceDiscoveryAgent::PoweredOffError;
|
|
emit error(lastError);
|
|
|
|
// Announce that the inquiry finished and restore the stopped flag
|
|
running = false;
|
|
stopped = false;
|
|
|
|
return;
|
|
}
|
|
|
|
// Declare the necessary variables to collect the information
|
|
BYTE buffer[4096];
|
|
DWORD bufferLength = sizeof(buffer);
|
|
WSAQUERYSET *pResults = (WSAQUERYSET*)&buffer;
|
|
|
|
memset(buffer, 0, sizeof(buffer));
|
|
|
|
pResults->dwSize = sizeof(WSAQUERYSET);
|
|
pResults->dwNameSpace = NS_BTH;
|
|
pResults->lpBlob = NULL;
|
|
|
|
//Start looking for devices
|
|
while (result == SUCCESS && !stopped){
|
|
// LUP_RETURN_NAME and LUP_RETURN_ADDR flags are used to return the name and the address of the discovered device
|
|
result = WSALookupServiceNext(hLookup, LUP_RETURN_NAME | LUP_RETURN_ADDR, &bufferLength, pResults);
|
|
|
|
if (result == SUCCESS) {
|
|
// Found a device
|
|
QString deviceAddress(BTH_ADDR_BUF_LEN, Qt::Uninitialized);
|
|
DWORD addressSize = BTH_ADDR_BUF_LEN;
|
|
|
|
// Collect the address of the device from the WSAQUERYSET
|
|
SOCKADDR_BTH *socketBthAddress = (SOCKADDR_BTH *) pResults->lpcsaBuffer->RemoteAddr.lpSockaddr;
|
|
|
|
// Convert the BTH_ADDR to string
|
|
if (WSAAddressToStringW((LPSOCKADDR) socketBthAddress,
|
|
sizeof (*socketBthAddress),
|
|
NULL,
|
|
reinterpret_cast<wchar_t*>(deviceAddress.data()),
|
|
&addressSize
|
|
) != 0) {
|
|
// Get the last error and emit a signal
|
|
lastErrorToString = qt_error_string();
|
|
lastError = QBluetoothDeviceDiscoveryAgent::UnknownError;
|
|
emit(lastError);
|
|
|
|
break;
|
|
}
|
|
|
|
// Remove the round parentheses
|
|
deviceAddress.remove(')');
|
|
deviceAddress.remove('(');
|
|
|
|
// Save the name of the discovered device and truncate the address
|
|
QString deviceName = QString(pResults->lpszServiceInstanceName);
|
|
deviceAddress.truncate(BTH_ADDR_PRETTY_STRING_LEN);
|
|
|
|
// Create an object with information about the discovered device
|
|
QBluetoothDeviceInfo deviceInfo = QBluetoothDeviceInfo(QBluetoothAddress(deviceAddress), deviceName, 0);
|
|
|
|
// Raise a signal with information about the found remote device
|
|
emit deviceDiscovered(deviceInfo);
|
|
} else {
|
|
// Get the last error and emit a signal
|
|
lastErrorToString = qt_error_string();
|
|
lastError = QBluetoothDeviceDiscoveryAgent::UnknownError;
|
|
emit(lastError);
|
|
}
|
|
}
|
|
|
|
// Announce that the inquiry finished and restore the stopped flag
|
|
running = false;
|
|
stopped = false;
|
|
|
|
// Restore the error status
|
|
lastError = QBluetoothDeviceDiscoveryAgent::NoError;
|
|
|
|
// End the lookup service
|
|
WSALookupServiceEnd(hLookup);
|
|
}
|
|
|
|
void WinBluetoothDeviceDiscoveryAgent::stop()
|
|
{
|
|
// Stop the inqury
|
|
stopped = true;
|
|
}
|
|
#endif
|