Commit a8ce2ce0 authored by Friedemann Kleint's avatar Friedemann Kleint
Browse files

S60: Rework SerialDeviceListener.

Change it into a singleton that listens for device changes
and emits signals when devices are added or removed.
parent 76036ca7
......@@ -114,7 +114,7 @@ S60DeviceRunConfiguration::S60DeviceRunConfiguration(Project *project, const QSt
m_serialPortName(QLatin1String("COM5")),
m_communicationType(SerialPortCommunication),
#else
m_serialPortName(QLatin1String(SerialDeviceLister::linuxBlueToothDeviceRootC) + QLatin1Char('0')),
m_serialPortName(QLatin1String(SymbianDeviceManager::linuxBlueToothDeviceRootC) + QLatin1Char('0')),
m_communicationType(BlueToothCommunication),
#endif
m_signingMode(SignSelf)
......@@ -498,7 +498,7 @@ S60DeviceRunControlBase::S60DeviceRunControlBase(RunConfiguration *runConfigurat
QTC_ASSERT(s60runConfig, return);
m_toolChain = s60runConfig->toolChainType();
m_serialPortName = s60runConfig->serialPortName();
m_serialPortFriendlyName = S60Manager::instance()->serialDeviceLister()->friendlyNameForPort(m_serialPortName);
m_serialPortFriendlyName = SymbianDeviceManager::instance()->friendlyNameForPort(m_serialPortName);
m_communicationType = s60runConfig->communicationType();
m_targetName = s60runConfig->targetName();
m_baseFileName = s60runConfig->basePackageFilePath();
......
......@@ -56,7 +56,7 @@
#include <QtGui/QMainWindow>
#include <QtGui/QMessageBox>
Q_DECLARE_METATYPE(Qt4ProjectManager::Internal::CommunicationDevice)
Q_DECLARE_METATYPE(Qt4ProjectManager::Internal::SymbianDevice)
namespace Qt4ProjectManager {
namespace Internal {
......@@ -100,7 +100,7 @@ S60DeviceRunConfigurationWidget::S60DeviceRunConfigurationWidget(
formLayout->addRow(tr("Install File:"), m_sisxFileLabel);
updateSerialDevices();
connect(S60Manager::instance()->serialDeviceLister(), SIGNAL(updated()),
connect(SymbianDeviceManager::instance(), SIGNAL(updated()),
this, SLOT(updateSerialDevices()));
// Serial devices control
connect(m_serialPortsCombo, SIGNAL(activated(int)), this, SLOT(setSerialPort(int)));
......@@ -180,12 +180,12 @@ void S60DeviceRunConfigurationWidget::updateSerialDevices()
m_serialPortsCombo->clear();
clearDeviceInfo();
const QString previousRunConfigurationPortName = m_runConfiguration->serialPortName();
const QList<CommunicationDevice> devices = S60Manager::instance()->serialDeviceLister()->communicationDevices();
const QList<SymbianDevice> devices = SymbianDeviceManager::instance()->devices();
int newIndex = -1;
for (int i = 0; i < devices.size(); ++i) {
const CommunicationDevice &device = devices.at(i);
m_serialPortsCombo->addItem(device.friendlyName, qVariantFromValue(device));
if (device.portName == previousRunConfigurationPortName)
const SymbianDevice &device = devices.at(i);
m_serialPortsCombo->addItem(device.friendlyName(), qVariantFromValue(device));
if (device.portName() == previousRunConfigurationPortName)
newIndex = i;
}
// Set new index: prefer to keep old or set to 0, if available.
......@@ -197,23 +197,23 @@ void S60DeviceRunConfigurationWidget::updateSerialDevices()
m_runConfiguration->setSerialPortName(QString());
} else {
m_deviceInfoButton->setEnabled(true);
const QString newPortName = device(newIndex).portName;
const QString newPortName = device(newIndex).portName();
if (newPortName != previousRunConfigurationPortName)
m_runConfiguration->setSerialPortName(newPortName);
}
}
CommunicationDevice S60DeviceRunConfigurationWidget::device(int i) const
SymbianDevice S60DeviceRunConfigurationWidget::device(int i) const
{
if (i >= 0) {
const QVariant data = m_serialPortsCombo->itemData(i);
if (qVariantCanConvert<Qt4ProjectManager::Internal::CommunicationDevice>(data))
return qVariantValue<Qt4ProjectManager::Internal::CommunicationDevice>(data);
if (qVariantCanConvert<Qt4ProjectManager::Internal::SymbianDevice>(data))
return qVariantValue<Qt4ProjectManager::Internal::SymbianDevice>(data);
}
return CommunicationDevice(SerialPortCommunication);
return SymbianDevice();
}
CommunicationDevice S60DeviceRunConfigurationWidget::currentDevice() const
SymbianDevice S60DeviceRunConfigurationWidget::currentDevice() const
{
return device(m_serialPortsCombo->currentIndex());
}
......@@ -242,9 +242,9 @@ void S60DeviceRunConfigurationWidget::updateTargetInformation()
void S60DeviceRunConfigurationWidget::setSerialPort(int index)
{
const CommunicationDevice d = device(index);
m_runConfiguration->setSerialPortName(d.portName);
m_runConfiguration->setCommunicationType(d.type);
const SymbianDevice d = device(index);
m_runConfiguration->setSerialPortName(d.portName());
m_runConfiguration->setCommunicationType(d.type());
m_deviceInfoButton->setEnabled(index >= 0);
clearDeviceInfo();
}
......@@ -323,15 +323,15 @@ void S60DeviceRunConfigurationWidget::updateDeviceInfo()
// go asynchronous afterwards to pop up launch trk box if a timeout occurs.
m_infoLauncher = new trk::Launcher(trk::Launcher::ActionPingOnly, QSharedPointer<trk::TrkDevice>(), this);
connect(m_infoLauncher, SIGNAL(stateChanged(int)), this, SLOT(slotLauncherStateChanged(int)));
const CommunicationDevice commDev = currentDevice();
m_infoLauncher->setSerialFrame(commDev.type == SerialPortCommunication);
m_infoLauncher->setTrkServerName(commDev.portName);
const SymbianDevice commDev = currentDevice();
m_infoLauncher->setSerialFrame(commDev.type() == SerialPortCommunication);
m_infoLauncher->setTrkServerName(commDev.portName());
// Prompt user
QString message;
const trk::PromptStartCommunicationResult src =
S60RunConfigBluetoothStarter::startCommunication(m_infoLauncher->trkDevice(),
commDev.portName,
commDev.type, this,
commDev.portName(),
commDev.type(), this,
&message);
switch (src) {
case trk::PromptStartCommunicationConnected:
......
......@@ -52,7 +52,7 @@ namespace trk {
namespace Qt4ProjectManager {
namespace Internal {
struct CommunicationDevice;
class SymbianDevice;
class S60DeviceRunConfiguration;
/* Configuration widget for S60 devices on serial ports that are
......@@ -81,8 +81,8 @@ private slots:
void slotWaitingForTrkClosed();
private:
inline CommunicationDevice device(int i) const;
inline CommunicationDevice currentDevice() const;
inline SymbianDevice device(int i) const;
inline SymbianDevice currentDevice() const;
void setDeviceInfoLabel(const QString &message, bool isError = false);
......
......@@ -98,8 +98,7 @@ S60Manager *S60Manager::instance() { return m_instance; }
S60Manager::S60Manager(QObject *parent)
: QObject(parent),
m_devices(new S60Devices(this)),
m_serialDeviceLister(new SerialDeviceLister(this))
m_devices(new S60Devices(this))
{
m_instance = this;
#ifdef QTCREATOR_WITH_S60
......@@ -127,7 +126,7 @@ S60Manager::S60Manager(QObject *parent)
connect(m_devices, SIGNAL(qtVersionsChanged()),
this, SLOT(updateQtVersions()));
connect(Core::ICore::instance()->mainWindow(), SIGNAL(deviceChange()),
m_serialDeviceLister, SLOT(update()));
SymbianDeviceManager::instance(), SLOT(update()));
}
S60Manager::~S60Manager()
......
......@@ -42,8 +42,6 @@ class ToolChain;
namespace Qt4ProjectManager {
namespace Internal {
class SerialDeviceLister;
class S60Manager : public QObject
{
Q_OBJECT
......@@ -62,8 +60,6 @@ public:
S60Devices::Device deviceForQtVersion(const Qt4ProjectManager::QtVersion *version) const;
QString deviceIdFromDetectionSource(const QString &autoDetectionSource) const;
SerialDeviceLister *serialDeviceLister() const { return m_serialDeviceLister; }
private slots:
void updateQtVersions();
......@@ -74,7 +70,6 @@ private:
S60Devices *m_devices;
QObjectList m_pluginObjects;
SerialDeviceLister *m_serialDeviceLister;
};
} // namespace Internal
......
......@@ -32,103 +32,290 @@
#include <QtCore/QSettings>
#include <QtCore/QStringList>
#include <QtCore/QFileInfo>
#include <QtGui/QApplication>
#include <QtGui/QWidget>
#include <QtDebug>
#include <QtCore/QtDebug>
#include <QtCore/QTextStream>
#include <QtCore/QSharedData>
#include <QtCore/QScopedPointer>
using namespace Qt4ProjectManager::Internal;
namespace Qt4ProjectManager {
namespace Internal {
namespace {
const char * const REGKEY_CURRENT_CONTROL_SET = "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet";
const char * const USBSER = "Services/usbser/Enum";
enum { debug = 0 };
static const char REGKEY_CURRENT_CONTROL_SET[] = "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet";
static const char USBSER[] = "Services/usbser/Enum";
const char *SymbianDeviceManager::linuxBlueToothDeviceRootC = "/dev/rfcomm";
// ------------- SymbianDevice
class SymbianDeviceData : public QSharedData {
public:
SymbianDeviceData() : type(SerialPortCommunication) {}
QString portName;
QString friendlyName;
QString deviceDesc;
QString manufacturer;
DeviceCommunicationType type;
};
SymbianDevice::SymbianDevice(SymbianDeviceData *data) :
m_data(data)
{
}
SymbianDevice::SymbianDevice() :
m_data(new SymbianDeviceData)
{
}
SymbianDevice::SymbianDevice(const SymbianDevice &rhs) :
m_data(rhs.m_data)
{
}
SymbianDevice &SymbianDevice::operator=(const SymbianDevice &rhs)
{
if (this != &rhs)
m_data = rhs.m_data;
return *this;
}
SymbianDevice::~SymbianDevice()
{
}
QString SymbianDevice::portName() const
{
return m_data->portName;
}
QString SymbianDevice::friendlyName() const
{
return m_data->friendlyName;
}
QString SymbianDevice::deviceDesc() const
{
return m_data->deviceDesc;
}
QString SymbianDevice::manufacturer() const
{
return m_data->manufacturer;
}
DeviceCommunicationType SymbianDevice::type() const
{
return m_data->type;
}
const char *SerialDeviceLister::linuxBlueToothDeviceRootC = "/dev/rfcomm";
bool SymbianDevice::isNull() const
{
return !m_data->portName.isEmpty();
}
CommunicationDevice::CommunicationDevice(DeviceCommunicationType t,
const QString &p,
const QString &f) :
portName(p),
friendlyName(f),
type(t)
QString SymbianDevice::toString() const
{
QString rc;
QTextStream str(&rc);
format(str);
return rc;
}
SerialDeviceLister::SerialDeviceLister(QObject *parent)
: QObject(parent),
m_initialized(false)
void SymbianDevice::format(QTextStream &str) const
{
str << (m_data->type == BlueToothCommunication ? "Bluetooth: " : "Serial: ")
<< m_data->portName;
if (!m_data->friendlyName.isEmpty()) {
str << " (" << m_data->friendlyName;
if (!m_data->deviceDesc.isEmpty())
str << " / " << m_data->deviceDesc;
str << ')';
}
if (!m_data->manufacturer.isEmpty())
str << " [" << m_data->manufacturer << ']';
}
// Compare by port and friendly name
int SymbianDevice::compare(const SymbianDevice &rhs) const
{
if (const int prc = m_data->portName.compare(rhs.m_data->portName))
return prc;
if (const int frc = m_data->friendlyName.compare(rhs.m_data->friendlyName))
return frc;
return 0;
}
QDebug operator<<(QDebug d, const SymbianDevice &cd)
{
d.nospace() << cd.toString();
return d;
}
// ------------- SymbianDeviceManagerPrivate
struct SymbianDeviceManagerPrivate {
SymbianDeviceManagerPrivate() : m_initialized(false) {}
bool m_initialized;
SymbianDeviceManager::SymbianDeviceList m_devices;
};
SymbianDeviceManager::SymbianDeviceManager(QObject *parent) :
QObject(parent),
d(new SymbianDeviceManagerPrivate)
{
}
SymbianDeviceManager::~SymbianDeviceManager()
{
delete d;
}
SerialDeviceLister::~SerialDeviceLister()
SymbianDeviceManager::SymbianDeviceList SymbianDeviceManager::devices() const
{
if (!d->m_initialized)
const_cast<SymbianDeviceManager*>(this)->update(false);
return d->m_devices;
}
QList<CommunicationDevice> SerialDeviceLister::communicationDevices() const
QString SymbianDeviceManager::toString() const
{
if (!m_initialized) {
updateSilently();
m_initialized = true;
QString rc;
QTextStream str(&rc);
const int count = d->m_devices.size();
for (int i = 0; i < count; i++) {
str << '#' << i << ' ';
d->m_devices.at(i).format(str);
str << '\n';
}
return m_devices2;
return rc;
}
QString SerialDeviceLister::friendlyNameForPort(const QString &port) const
QString SymbianDeviceManager::friendlyNameForPort(const QString &port) const
{
foreach (const CommunicationDevice &device, m_devices2) {
if (device.portName == port)
return device.friendlyName;
foreach (const SymbianDevice &device, d->m_devices) {
if (device.portName() == port)
return device.friendlyName();
}
return QString();
}
void SerialDeviceLister::update()
void SymbianDeviceManager::update()
{
updateSilently();
emit updated();
update(true);
}
void SerialDeviceLister::updateSilently() const
void SymbianDeviceManager::update(bool emitSignals)
{
m_devices2 = serialPorts() + blueToothDevices();
typedef SymbianDeviceList::iterator SymbianDeviceListIterator;
if (debug)
qDebug(">SerialDeviceLister::update(%d)\n%s", int(emitSignals),
qPrintable(toString()));
d->m_initialized = true;
// Get ordered new list
SymbianDeviceList newDevices = serialPorts() + blueToothDevices();
if (newDevices.size() > 1)
qStableSort(newDevices.begin(), newDevices.end());
if (d->m_devices == newDevices) // Happy, nothing changed.
return;
// Merge the lists and emit the respective added/removed signals, assuming
// no one can plug a different device on the same port at the speed of lightning
if (!d->m_devices.isEmpty()) {
// Find deleted devices
for (SymbianDeviceListIterator oldIt = d->m_devices.begin(); oldIt != d->m_devices.end(); ) {
if (newDevices.contains(*oldIt)) {
++oldIt;
} else {
const SymbianDevice toBeDeleted = *oldIt;
oldIt = d->m_devices.erase(oldIt);
if (emitSignals)
emit deviceRemoved(toBeDeleted);
}
}
}
if (!newDevices.isEmpty()) {
// Find new devices and insert in order
foreach(const SymbianDevice &newDevice, newDevices) {
if (!d->m_devices.contains(newDevice)) {
d->m_devices.append(newDevice);
if (emitSignals)
emit deviceAdded(newDevice);
}
}
if (d->m_devices.size() > 1)
qStableSort(d->m_devices.begin(), d->m_devices.end());
}
if (emitSignals)
emit updated();
if (debug)
qDebug("<SerialDeviceLister::update\n%s\n", qPrintable(toString()));
}
QList<CommunicationDevice> SerialDeviceLister::serialPorts() const
SymbianDeviceManager::SymbianDeviceList SymbianDeviceManager::serialPorts() const
{
QList<CommunicationDevice> rc;
#ifdef Q_OS_WIN32
QSettings registry(REGKEY_CURRENT_CONTROL_SET, QSettings::NativeFormat);
const int count = registry.value(QString::fromLatin1("%1/Count").arg(USBSER)).toInt();
SymbianDeviceList rc;
#ifdef Q_OS_WIN
const QSettings registry(REGKEY_CURRENT_CONTROL_SET, QSettings::NativeFormat);
const QString usbSerialRootKey = QLatin1String(USBSER) + QLatin1Char('/');
const int count = registry.value(usbSerialRootKey + QLatin1String("Count")).toInt();
for (int i = 0; i < count; ++i) {
QString driver = registry.value(QString::fromLatin1("%1/%2").arg(USBSER).arg(i)).toString();
if (driver.contains("JAVACOMM")) {
driver.replace('\\', '/');
CommunicationDevice device(SerialPortCommunication);
device.friendlyName = registry.value(QString::fromLatin1("Enum/%1/FriendlyName").arg(driver)).toString();
device.portName = registry.value(QString::fromLatin1("Enum/%1/Device Parameters/PortName").arg(driver)).toString();
rc.append(device);
QString driver = registry.value(usbSerialRootKey + QString::number(i)).toString();
if (driver.contains(QLatin1String("JAVACOMM"))) {
driver.replace(QLatin1Char('\\'), QLatin1Char('/'));
const QString driverRootKey = QLatin1String("Enum/") + driver + QLatin1Char('/');
if (debug > 1)
qDebug() << "SerialDeviceLister::serialPorts(): Checking " << i << count
<< REGKEY_CURRENT_CONTROL_SET << usbSerialRootKey << driverRootKey;
QScopedPointer<SymbianDeviceData> device(new SymbianDeviceData);
device->type = SerialPortCommunication;
device->friendlyName = registry.value(driverRootKey + QLatin1String("FriendlyName")).toString();
device->portName = registry.value(driverRootKey + QLatin1String("Device Parameters/PortName")).toString();
device->deviceDesc = registry.value(driverRootKey + QLatin1String("DeviceDesc")).toString();
device->manufacturer = registry.value(driverRootKey + QLatin1String("Mfg")).toString();
rc.append(SymbianDevice(device.take()));
}
}
#endif
return rc;
}
QList<CommunicationDevice> SerialDeviceLister::blueToothDevices() const
SymbianDeviceManager::SymbianDeviceList SymbianDeviceManager::blueToothDevices() const
{
QList<CommunicationDevice> rc;
SymbianDeviceList rc;
#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
// Bluetooth devices are created on connection. List the existing ones
// or at least the first one.
const QString prefix = QLatin1String(linuxBlueToothDeviceRootC);
const QString friendlyFormat = QLatin1String("Bluetooth device (%1)");
for (int d = 0; d < 4; d++) {
CommunicationDevice device(BlueToothCommunication, prefix + QString::number(d));
if (d == 0 || QFileInfo(device.portName).exists()) {
device.friendlyName = friendlyFormat.arg(device.portName);
rc.push_back(device);
QScopedPointer<SymbianDeviceData> device(new SymbianDeviceData);
device->type = BlueToothCommunication;
device->portName = prefix + QString::number(d);
if (d == 0 || QFileInfo(device->portName).exists()) {
device->friendlyName = friendlyFormat.arg(device->portName);
rc.push_back(SymbianDevice(device.take()));
}
}
#endif
return rc;
}
Q_GLOBAL_STATIC(SymbianDeviceManager, symbianDeviceManager)
SymbianDeviceManager *SymbianDeviceManager::instance()
{
return symbianDeviceManager();
}
QDebug operator<<(QDebug d, const SymbianDeviceManager &sdm)
{
d.nospace() << sdm.toString();
return d;
}
} // namespace Internal
} // namespace Qt4ProjectManager
......@@ -31,34 +31,81 @@
#define SERIALDEVICELISTER_H
#include <QtCore/QObject>
#include <QtCore/QExplicitlySharedDataPointer>
QT_BEGIN_NAMESPACE
class QDebug;
class QTextStream;
QT_END_NAMESPACE
namespace Qt4ProjectManager {
namespace Internal {
struct SymbianDeviceManagerPrivate;
class SymbianDeviceData;
enum DeviceCommunicationType {
SerialPortCommunication = 0,
BlueToothCommunication = 1
};
struct CommunicationDevice {
explicit CommunicationDevice(DeviceCommunicationType type = SerialPortCommunication,
const QString &portName = QString(),
const QString &friendlyName = QString());
QString portName;
QString friendlyName;
DeviceCommunicationType type;
// SymbianDevice, explicitly shared.
class SymbianDevice {
explicit SymbianDevice(SymbianDeviceData *data);
friend class SymbianDeviceManager;
public:
SymbianDevice();
SymbianDevice(const SymbianDevice &rhs);
SymbianDevice &operator=(const SymbianDevice &rhs);
~SymbianDevice();
int compare(const SymbianDevice &rhs) const;
DeviceCommunicationType type() const;
bool isNull() const;
QString portName() const;
QString friendlyName() const;
// Windows only.
QString deviceDesc() const;
QString manufacturer() const;
void format(QTextStream &str) const;
QString toString() const;
private:
QExplicitlySharedDataPointer<SymbianDeviceData> m_data;
};