mirror of
https://bitbucket.org/librepilot/librepilot.git
synced 2025-01-17 02:52:12 +01:00
Stopped GCS crash on exit if USB is still connected - at least in Windows anyway.
git-svn-id: svn://svn.openpilot.org/OpenPilot/trunk@2882 ebee16cc-31ac-478f-84a7-5cbb03baadba
This commit is contained in:
parent
6e431c29ba
commit
333d61192b
@ -48,8 +48,9 @@
|
||||
namespace Core {
|
||||
|
||||
|
||||
ConnectionManager::ConnectionManager(Internal::MainWindow *mainWindow, Internal::FancyTabWidget *modeStack)
|
||||
: m_availableDevList(0),
|
||||
ConnectionManager::ConnectionManager(Internal::MainWindow *mainWindow, Internal::FancyTabWidget *modeStack) :
|
||||
QWidget(mainWindow), // Pip
|
||||
m_availableDevList(0),
|
||||
m_connectBtn(0),
|
||||
m_ioDev(NULL)
|
||||
{
|
||||
@ -83,22 +84,21 @@ ConnectionManager::ConnectionManager(Internal::MainWindow *mainWindow, Internal:
|
||||
|
||||
modeStack->insertCornerWidget(modeStack->cornerWidgetCount()-1, this);
|
||||
|
||||
QObject::connect(m_connectBtn, SIGNAL(pressed()),
|
||||
this, SLOT(onConnectPressed()));
|
||||
QObject::connect(m_connectBtn, SIGNAL(pressed()), this, SLOT(onConnectPressed()));
|
||||
}
|
||||
|
||||
ConnectionManager::~ConnectionManager()
|
||||
{
|
||||
disconnectDevice(); // Pip
|
||||
suspendPolling(); // Pip
|
||||
}
|
||||
|
||||
void ConnectionManager::init()
|
||||
{
|
||||
//register to the plugin manager so we can receive
|
||||
//new connection object from plugins
|
||||
QObject::connect(ExtensionSystem::PluginManager::instance(), SIGNAL(objectAdded(QObject*)),
|
||||
this, SLOT(objectAdded(QObject*)));
|
||||
QObject::connect(ExtensionSystem::PluginManager::instance(), SIGNAL(aboutToRemoveObject(QObject*)),
|
||||
this, SLOT(aboutToRemoveObject(QObject*)));
|
||||
QObject::connect(ExtensionSystem::PluginManager::instance(), SIGNAL(objectAdded(QObject*)), this, SLOT(objectAdded(QObject*)));
|
||||
QObject::connect(ExtensionSystem::PluginManager::instance(), SIGNAL(aboutToRemoveObject(QObject*)), this, SLOT(aboutToRemoveObject(QObject*)));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -108,8 +108,7 @@ void ConnectionManager::objectAdded(QObject *obj)
|
||||
{
|
||||
//Check if a plugin added a connection object to the pool
|
||||
IConnection *connection = Aggregation::query<IConnection>(obj);
|
||||
if (!connection)
|
||||
return;
|
||||
if (!connection) return;
|
||||
|
||||
//qDebug() << "Connection object registered:" << connection->connectionName();
|
||||
//qDebug() << connection->availableDevices();
|
||||
@ -121,16 +120,15 @@ void ConnectionManager::objectAdded(QObject *obj)
|
||||
// to do things
|
||||
m_connectionsList.append(connection);
|
||||
|
||||
QObject::connect(connection, SIGNAL(availableDevChanged(IConnection *)),
|
||||
this, SLOT(devChanged(IConnection*)));
|
||||
QObject::connect(connection, SIGNAL(availableDevChanged(IConnection *)), this, SLOT(devChanged(IConnection *)));
|
||||
}
|
||||
|
||||
void ConnectionManager::aboutToRemoveObject(QObject *obj)
|
||||
{
|
||||
//Check if a plugin added a connection object to the pool
|
||||
IConnection *connection = Aggregation::query<IConnection>(obj);
|
||||
if (!connection)
|
||||
return;
|
||||
if (!connection) return;
|
||||
|
||||
if (m_connectionsList.contains(connection))
|
||||
m_connectionsList.removeAt(m_connectionsList.indexOf(connection));
|
||||
}
|
||||
@ -142,36 +140,60 @@ void ConnectionManager::aboutToRemoveObject(QObject *obj)
|
||||
bool ConnectionManager::disconnectDevice()
|
||||
{
|
||||
// Check if we are currently connected or not
|
||||
if(!m_ioDev)
|
||||
if (!m_ioDev)
|
||||
return false; // We were not connected
|
||||
|
||||
//signal interested plugins that we are disconnecting the device
|
||||
emit deviceDisconnected();
|
||||
if(m_connectionDevice.connection)
|
||||
//signal interested plugins that we are disconnecting the device
|
||||
emit deviceDisconnected();
|
||||
|
||||
if (m_connectionDevice.connection)
|
||||
{
|
||||
m_connectionDevice.connection->closeDevice(m_connectionDevice.devName);
|
||||
m_ioDev = NULL;
|
||||
m_connectionDevice.connection = NULL;
|
||||
}
|
||||
|
||||
m_connectBtn->setText("Connect");
|
||||
m_connectBtn->setText("Connect");
|
||||
m_availableDevList->setEnabled(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ConnectionManager::onConnectionClosed(QObject *obj) // Pip
|
||||
{
|
||||
if (!m_connectionDevice.connection || m_connectionDevice.connection != obj)
|
||||
return;
|
||||
|
||||
m_connectionDevice.connection = NULL;
|
||||
m_ioDev = NULL;
|
||||
|
||||
m_connectBtn->setText("Connect");
|
||||
m_availableDevList->setEnabled(true);
|
||||
}
|
||||
|
||||
void ConnectionManager::onConnectionDestroyed(QObject *obj) // Pip
|
||||
{
|
||||
if (!m_connectionDevice.connection || m_connectionDevice.connection != obj)
|
||||
return;
|
||||
|
||||
m_connectionDevice.connection = NULL;
|
||||
m_ioDev = NULL;
|
||||
|
||||
m_connectBtn->setText("Connect");
|
||||
m_availableDevList->setEnabled(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Slot called when the user pressed the connect/disconnect button
|
||||
*/
|
||||
void ConnectionManager::onConnectPressed()
|
||||
{
|
||||
//check if we are trying to connect a new device
|
||||
//or if we are disconnecting it
|
||||
if(!m_ioDev)
|
||||
{
|
||||
if (!m_ioDev)
|
||||
{ // connecting
|
||||
|
||||
m_connectionDevice = findDevice(m_availableDevList->currentText());
|
||||
|
||||
if(m_connectionDevice.connection)
|
||||
if (m_connectionDevice.connection)
|
||||
{
|
||||
m_ioDev = m_connectionDevice.connection->openDevice(m_connectionDevice.devName);
|
||||
|
||||
@ -183,11 +205,17 @@ void ConnectionManager::onConnectPressed()
|
||||
if (!m_ioDev->isOpen())
|
||||
{
|
||||
qDebug() << "could not connect to " << m_connectionDevice.devName;
|
||||
|
||||
// TODO: inform the user that something went wrong
|
||||
m_ioDev = NULL;
|
||||
|
||||
m_connectionDevice.connection = NULL; // Pip
|
||||
m_ioDev = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
connect(m_connectionDevice.connection, SIGNAL(deviceClosed(QObject *)), this, SLOT(onConnectionClosed(QObject *))); // Pip
|
||||
connect(m_connectionDevice.connection, SIGNAL(destroyed(QObject *)), this, SLOT(onConnectionDestroyed(QObject *))); // Pip
|
||||
|
||||
//signal interested plugins that the user wants to connect to the device
|
||||
emit deviceConnected(m_ioDev);
|
||||
m_connectBtn->setText("Disconnect");
|
||||
@ -202,11 +230,12 @@ void ConnectionManager::onConnectPressed()
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//only do this if we are disconnecting
|
||||
//signal interested plugins that user is disconnecting his device
|
||||
emit deviceDisconnected();
|
||||
if(m_connectionDevice.connection)
|
||||
{ // disconnecting
|
||||
|
||||
//signal interested plugins that user is disconnecting his device
|
||||
emit deviceDisconnected();
|
||||
|
||||
if (m_connectionDevice.connection)
|
||||
{
|
||||
m_connectionDevice.connection->closeDevice(m_connectionDevice.devName);
|
||||
m_ioDev = NULL;
|
||||
@ -223,14 +252,14 @@ void ConnectionManager::onConnectPressed()
|
||||
*/
|
||||
devListItem ConnectionManager::findDevice(const QString &displayedName)
|
||||
{
|
||||
foreach(devListItem d, m_devList)
|
||||
foreach (devListItem d, m_devList)
|
||||
{
|
||||
if(d.displayedName == displayedName)
|
||||
{
|
||||
if (d.displayedName == displayedName)
|
||||
return d;
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "findDevice: cannot find " << displayedName << " in device list";
|
||||
|
||||
devListItem d;
|
||||
d.connection = NULL;
|
||||
return d;
|
||||
@ -242,17 +271,12 @@ devListItem ConnectionManager::findDevice(const QString &displayedName)
|
||||
*/
|
||||
void ConnectionManager::unregisterAll(IConnection *connection)
|
||||
{
|
||||
for(QLinkedList<devListItem>::iterator iter = m_devList.begin();
|
||||
iter != m_devList.end(); )
|
||||
for (QLinkedList<devListItem>::iterator iter = m_devList.begin(); iter != m_devList.end(); )
|
||||
{
|
||||
if(iter->connection == connection)
|
||||
{
|
||||
if (iter->connection == connection)
|
||||
iter = m_devList.erase(iter);
|
||||
}
|
||||
else
|
||||
{
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -263,7 +287,8 @@ void ConnectionManager::unregisterAll(IConnection *connection)
|
||||
*/
|
||||
void ConnectionManager::suspendPolling()
|
||||
{
|
||||
foreach (IConnection* cnx, m_connectionsList) {
|
||||
foreach (IConnection *cnx, m_connectionsList)
|
||||
{
|
||||
cnx->suspendPolling();
|
||||
}
|
||||
|
||||
@ -277,7 +302,8 @@ void ConnectionManager::suspendPolling()
|
||||
*/
|
||||
void ConnectionManager::resumePolling()
|
||||
{
|
||||
foreach (IConnection* cnx, m_connectionsList) {
|
||||
foreach (IConnection *cnx, m_connectionsList)
|
||||
{
|
||||
cnx->resumePolling();
|
||||
}
|
||||
|
||||
@ -315,24 +341,23 @@ void ConnectionManager::devChanged(IConnection *connection)
|
||||
|
||||
//and add them back in the list
|
||||
QStringList availableDev = connection->availableDevices();
|
||||
foreach(QString dev, availableDev)
|
||||
foreach (QString dev, availableDev)
|
||||
{
|
||||
QString cbName = connection->shortName() + ": " + dev;
|
||||
registerDevice(connection, dev, cbName);
|
||||
}
|
||||
|
||||
//add all the list again to the combobox
|
||||
foreach(devListItem d, m_devList)
|
||||
foreach (devListItem d, m_devList)
|
||||
{
|
||||
m_availableDevList->addItem(d.displayedName);
|
||||
}
|
||||
|
||||
//disable connection button if the list is empty
|
||||
if(m_availableDevList->count() > 0)
|
||||
if (m_availableDevList->count() > 0)
|
||||
m_connectBtn->setEnabled(true);
|
||||
else
|
||||
m_connectBtn->setEnabled(false);
|
||||
}
|
||||
|
||||
|
||||
} //namespace Core
|
||||
|
@ -1,108 +1,111 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
*
|
||||
* @file connectionmanager.h
|
||||
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
|
||||
* Parts by Nokia Corporation (qt-info@nokia.com) Copyright (C) 2009.
|
||||
* @addtogroup GCSPlugins GCS Plugins
|
||||
* @{
|
||||
* @addtogroup CorePlugin Core Plugin
|
||||
* @{
|
||||
* @brief The Core GCS plugin
|
||||
*****************************************************************************/
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef CONNECTIONMANAGER_H
|
||||
#define CONNECTIONMANAGER_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include <QtCore/QVector>
|
||||
#include <QtCore/QIODevice>
|
||||
#include <QtCore/QLinkedList>
|
||||
#include <QtGui/QPushButton>
|
||||
#include <QtGui/QComboBox>
|
||||
|
||||
#include "core_global.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
class IConnection;
|
||||
|
||||
namespace Internal {
|
||||
class FancyTabWidget;
|
||||
class FancyActionBar;
|
||||
class MainWindow;
|
||||
} // namespace Internal
|
||||
|
||||
|
||||
struct devListItem
|
||||
{
|
||||
IConnection *connection;
|
||||
QString devName;
|
||||
QString displayedName;
|
||||
};
|
||||
|
||||
|
||||
class CORE_EXPORT ConnectionManager : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ConnectionManager(Internal::MainWindow *mainWindow, Internal::FancyTabWidget *modeStack);
|
||||
virtual ~ConnectionManager();
|
||||
|
||||
void init();
|
||||
|
||||
QIODevice* getCurrentConnection() { return m_ioDev; }
|
||||
devListItem getCurrentDevice() { return m_connectionDevice;}
|
||||
bool disconnectDevice();
|
||||
void suspendPolling();
|
||||
void resumePolling();
|
||||
|
||||
protected:
|
||||
void unregisterAll(IConnection *connection);
|
||||
void registerDevice(IConnection *conn, const QString &devN, const QString &disp);
|
||||
devListItem findDevice(const QString &displayedName);
|
||||
|
||||
signals:
|
||||
void deviceConnected(QIODevice *dev);
|
||||
void deviceDisconnected();
|
||||
|
||||
private slots:
|
||||
void objectAdded(QObject *obj);
|
||||
void aboutToRemoveObject(QObject *obj);
|
||||
|
||||
void onConnectPressed();
|
||||
void devChanged(IConnection *connection);
|
||||
|
||||
protected:
|
||||
QComboBox *m_availableDevList;
|
||||
QPushButton *m_connectBtn;
|
||||
QLinkedList<devListItem> m_devList;
|
||||
QList<IConnection*> m_connectionsList;
|
||||
|
||||
//currently connected connection plugin
|
||||
devListItem m_connectionDevice;
|
||||
|
||||
//currently connected QIODevice
|
||||
QIODevice *m_ioDev;
|
||||
};
|
||||
|
||||
} //namespace Core
|
||||
|
||||
#endif // CONNECTIONMANAGER_H
|
||||
/**
|
||||
******************************************************************************
|
||||
*
|
||||
* @file connectionmanager.h
|
||||
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
|
||||
* Parts by Nokia Corporation (qt-info@nokia.com) Copyright (C) 2009.
|
||||
* @addtogroup GCSPlugins GCS Plugins
|
||||
* @{
|
||||
* @addtogroup CorePlugin Core Plugin
|
||||
* @{
|
||||
* @brief The Core GCS plugin
|
||||
*****************************************************************************/
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef CONNECTIONMANAGER_H
|
||||
#define CONNECTIONMANAGER_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include <QtCore/QVector>
|
||||
#include <QtCore/QIODevice>
|
||||
#include <QtCore/QLinkedList>
|
||||
#include <QtGui/QPushButton>
|
||||
#include <QtGui/QComboBox>
|
||||
|
||||
#include "core_global.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
class IConnection;
|
||||
|
||||
namespace Internal {
|
||||
class FancyTabWidget;
|
||||
class FancyActionBar;
|
||||
class MainWindow;
|
||||
} // namespace Internal
|
||||
|
||||
|
||||
struct devListItem
|
||||
{
|
||||
IConnection *connection;
|
||||
QString devName;
|
||||
QString displayedName;
|
||||
};
|
||||
|
||||
|
||||
class CORE_EXPORT ConnectionManager : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ConnectionManager(Internal::MainWindow *mainWindow, Internal::FancyTabWidget *modeStack);
|
||||
virtual ~ConnectionManager();
|
||||
|
||||
void init();
|
||||
|
||||
QIODevice* getCurrentConnection() { return m_ioDev; }
|
||||
devListItem getCurrentDevice() { return m_connectionDevice;}
|
||||
bool disconnectDevice();
|
||||
void suspendPolling();
|
||||
void resumePolling();
|
||||
|
||||
protected:
|
||||
void unregisterAll(IConnection *connection);
|
||||
void registerDevice(IConnection *conn, const QString &devN, const QString &disp);
|
||||
devListItem findDevice(const QString &displayedName);
|
||||
|
||||
signals:
|
||||
void deviceConnected(QIODevice *dev);
|
||||
void deviceDisconnected();
|
||||
|
||||
private slots:
|
||||
void objectAdded(QObject *obj);
|
||||
void aboutToRemoveObject(QObject *obj);
|
||||
|
||||
void onConnectPressed();
|
||||
void devChanged(IConnection *connection);
|
||||
|
||||
void onConnectionClosed(QObject *obj);
|
||||
void onConnectionDestroyed(QObject *obj);
|
||||
|
||||
protected:
|
||||
QComboBox *m_availableDevList;
|
||||
QPushButton *m_connectBtn;
|
||||
QLinkedList<devListItem> m_devList;
|
||||
QList<IConnection*> m_connectionsList;
|
||||
|
||||
//currently connected connection plugin
|
||||
devListItem m_connectionDevice;
|
||||
|
||||
//currently connected QIODevice
|
||||
QIODevice *m_ioDev;
|
||||
};
|
||||
|
||||
} //namespace Core
|
||||
|
||||
#endif // CONNECTIONMANAGER_H
|
||||
|
@ -206,8 +206,15 @@ MainWindow::MainWindow() :
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
hide();
|
||||
qxtLog->removeAsMessageHandler();
|
||||
if (m_connectionManager) // Pip
|
||||
{
|
||||
m_connectionManager->disconnectDevice();
|
||||
m_connectionManager->suspendPolling();
|
||||
}
|
||||
|
||||
hide();
|
||||
|
||||
qxtLog->removeAsMessageHandler();
|
||||
foreach (QString engine, qxtLog->allLoggerEngines())
|
||||
qxtLog->removeLoggerEngine(engine);
|
||||
ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
|
||||
|
@ -1,239 +1,261 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
*
|
||||
* @file rawhidplugin.cpp
|
||||
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
|
||||
* @addtogroup GCSPlugins GCS Plugins
|
||||
* @{
|
||||
* @addtogroup RawHIDPlugin Raw HID Plugin
|
||||
* @{
|
||||
* @brief Impliments a HID USB connection to the flight hardware as a QIODevice
|
||||
*****************************************************************************/
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "rawhidplugin.h"
|
||||
#include "rawhid.h"
|
||||
#include <extensionsystem/pluginmanager.h>
|
||||
|
||||
#include <QtCore/QtPlugin>
|
||||
#include <QtCore/QMutexLocker>
|
||||
|
||||
#include "pjrc_rawhid.h"
|
||||
|
||||
#include "rawhid_const.h"
|
||||
|
||||
|
||||
RawHIDEnumerationThread::RawHIDEnumerationThread(RawHIDConnection *rawhid)
|
||||
: m_rawhid(rawhid),
|
||||
m_running(true)
|
||||
{
|
||||
}
|
||||
|
||||
RawHIDEnumerationThread::~RawHIDEnumerationThread()
|
||||
{
|
||||
m_running = false;
|
||||
//wait for the thread to terminate
|
||||
if(wait(1000) == false)
|
||||
qDebug() << "Cannot terminate RawHIDEnumerationThread";
|
||||
}
|
||||
|
||||
void RawHIDEnumerationThread::run()
|
||||
{
|
||||
QStringList devices = m_rawhid->availableDevices();
|
||||
|
||||
while(m_running)
|
||||
{
|
||||
if(!m_rawhid->deviceOpened())
|
||||
{
|
||||
QStringList newDev = m_rawhid->availableDevices();
|
||||
if(devices != newDev)
|
||||
{
|
||||
devices = newDev;
|
||||
emit enumerationChanged();
|
||||
}
|
||||
}
|
||||
|
||||
msleep(1000); //update available devices every second (doesn't need more)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
RawHIDConnection::RawHIDConnection()
|
||||
: m_enumerateThread(this)
|
||||
{
|
||||
//added by andrew
|
||||
RawHidHandle = NULL;
|
||||
enablePolling = true;
|
||||
|
||||
|
||||
QObject::connect(&m_enumerateThread, SIGNAL(enumerationChanged()),
|
||||
this, SLOT(onEnumerationChanged()));
|
||||
m_enumerateThread.start();
|
||||
}
|
||||
|
||||
RawHIDConnection::~RawHIDConnection()
|
||||
{}
|
||||
|
||||
void RawHIDConnection::onEnumerationChanged()
|
||||
{
|
||||
if (enablePolling)
|
||||
emit availableDevChanged(this);
|
||||
}
|
||||
|
||||
QStringList RawHIDConnection::availableDevices()
|
||||
{
|
||||
QMutexLocker locker(&m_enumMutex);
|
||||
|
||||
QStringList devices;
|
||||
|
||||
if (enablePolling) {
|
||||
pjrc_rawhid dev;
|
||||
//open all device we can
|
||||
int opened = dev.open(MAX_DEVICES, VID, PID, USAGE_PAGE, USAGE);
|
||||
|
||||
//for each devices found, get serial number and close it back
|
||||
for(int i=0; i<opened; i++)
|
||||
{
|
||||
devices.append(dev.getserial(i));
|
||||
dev.close(i);
|
||||
}
|
||||
}
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
QIODevice *RawHIDConnection::openDevice(const QString &deviceName)
|
||||
{
|
||||
//added by andrew
|
||||
if (RawHidHandle){
|
||||
closeDevice(deviceName);
|
||||
}
|
||||
//end added by andrew
|
||||
m_deviceOpened = true;
|
||||
//return new RawHID(deviceName);
|
||||
RawHidHandle = new RawHID(deviceName);
|
||||
return RawHidHandle;
|
||||
}
|
||||
|
||||
void RawHIDConnection::closeDevice(const QString &deviceName)
|
||||
{
|
||||
Q_UNUSED(deviceName);
|
||||
//added by andrew...
|
||||
if (RawHidHandle){
|
||||
RawHidHandle->close();
|
||||
delete(RawHidHandle);
|
||||
RawHidHandle=NULL;
|
||||
}
|
||||
//end added by andrew
|
||||
|
||||
m_deviceOpened = false;
|
||||
}
|
||||
|
||||
|
||||
QString RawHIDConnection::connectionName()
|
||||
{
|
||||
return QString("Raw HID USB");
|
||||
}
|
||||
|
||||
QString RawHIDConnection::shortName()
|
||||
{
|
||||
return QString("USB");
|
||||
}
|
||||
|
||||
/**
|
||||
Tells the Raw HID plugin to stop polling for USB devices
|
||||
*/
|
||||
void RawHIDConnection::suspendPolling()
|
||||
{
|
||||
enablePolling = false;
|
||||
}
|
||||
|
||||
/**
|
||||
Tells the Raw HID plugin to resume polling for USB devices
|
||||
*/
|
||||
void RawHIDConnection::resumePolling()
|
||||
{
|
||||
enablePolling = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//usb hid test thread
|
||||
//temporary...
|
||||
RawHIDTestThread::RawHIDTestThread()
|
||||
{
|
||||
Core::ConnectionManager *cm = Core::ICore::instance()->connectionManager();
|
||||
QObject::connect(cm, SIGNAL(deviceConnected(QIODevice *)),
|
||||
this, SLOT(onDeviceConnect(QIODevice *)));
|
||||
QObject::connect(cm, SIGNAL(deviceDisconnected()),
|
||||
this, SLOT(onDeviceDisconnect()));
|
||||
}
|
||||
|
||||
void RawHIDTestThread::onDeviceConnect(QIODevice *dev)
|
||||
{
|
||||
this->dev = dev;
|
||||
dev->open(QIODevice::ReadWrite);
|
||||
QObject::connect(dev, SIGNAL(readyRead()),
|
||||
this, SLOT(onReadyRead()));
|
||||
|
||||
QObject::connect(dev, SIGNAL(bytesWritten(qint64)),
|
||||
this, SLOT(onBytesWritten(qint64)));
|
||||
dev->write("Hello raw hid device\n");
|
||||
}
|
||||
|
||||
void RawHIDTestThread::onDeviceDisconnect()
|
||||
{
|
||||
dev->close();
|
||||
}
|
||||
|
||||
void RawHIDTestThread::onReadyRead()
|
||||
{
|
||||
qDebug() << "Rx:" << dev->readLine(32);
|
||||
}
|
||||
|
||||
void RawHIDTestThread::onBytesWritten(qint64 sz)
|
||||
{
|
||||
qDebug() << "Sent " << sz << " bytes";
|
||||
}
|
||||
|
||||
RawHIDPlugin::RawHIDPlugin()
|
||||
{
|
||||
}
|
||||
|
||||
RawHIDPlugin::~RawHIDPlugin()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void RawHIDPlugin::extensionsInitialized()
|
||||
{
|
||||
hidConnection = new RawHIDConnection();
|
||||
addAutoReleasedObject(hidConnection);
|
||||
|
||||
//temp for test
|
||||
//addAutoReleasedObject(new RawHIDTestThread);
|
||||
}
|
||||
|
||||
bool RawHIDPlugin::initialize(const QStringList & arguments, QString * errorString)
|
||||
{
|
||||
Q_UNUSED(arguments);
|
||||
Q_UNUSED(errorString);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Q_EXPORT_PLUGIN(RawHIDPlugin)
|
||||
/**
|
||||
******************************************************************************
|
||||
*
|
||||
* @file rawhidplugin.cpp
|
||||
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
|
||||
* @addtogroup GCSPlugins GCS Plugins
|
||||
* @{
|
||||
* @addtogroup RawHIDPlugin Raw HID Plugin
|
||||
* @{
|
||||
* @brief Impliments a HID USB connection to the flight hardware as a QIODevice
|
||||
*****************************************************************************/
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "rawhidplugin.h"
|
||||
#include "rawhid.h"
|
||||
#include <extensionsystem/pluginmanager.h>
|
||||
|
||||
#include <QtCore/QtPlugin>
|
||||
#include <QtCore/QMutexLocker>
|
||||
|
||||
#include "pjrc_rawhid.h"
|
||||
|
||||
#include "rawhid_const.h"
|
||||
|
||||
|
||||
RawHIDEnumerationThread::RawHIDEnumerationThread(RawHIDConnection *rawhid) :
|
||||
QThread(rawhid), // Pip
|
||||
m_rawhid(rawhid),
|
||||
m_running(true)
|
||||
{
|
||||
}
|
||||
|
||||
RawHIDEnumerationThread::~RawHIDEnumerationThread()
|
||||
{
|
||||
m_running = false;
|
||||
|
||||
// wait for the thread to terminate
|
||||
if (!wait(100))
|
||||
qDebug() << "Cannot terminate RawHIDEnumerationThread";
|
||||
}
|
||||
|
||||
void RawHIDEnumerationThread::run()
|
||||
{
|
||||
QStringList devices = m_rawhid->availableDevices();
|
||||
|
||||
int counter = 0;
|
||||
|
||||
while (m_running)
|
||||
{
|
||||
// update available devices every second (doesn't need more)
|
||||
if (++counter >= 100 && !m_rawhid->deviceOpened())
|
||||
{
|
||||
counter = 0;
|
||||
|
||||
QStringList newDev = m_rawhid->availableDevices();
|
||||
if (devices != newDev)
|
||||
{
|
||||
devices = newDev;
|
||||
emit enumerationChanged();
|
||||
}
|
||||
}
|
||||
|
||||
msleep(10);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
RawHIDConnection::RawHIDConnection()
|
||||
: m_enumerateThread(this)
|
||||
{
|
||||
//added by andrew
|
||||
RawHidHandle = NULL;
|
||||
enablePolling = true;
|
||||
|
||||
QObject::connect(&m_enumerateThread, SIGNAL(enumerationChanged()), this, SLOT(onEnumerationChanged()));
|
||||
|
||||
m_enumerateThread.start();
|
||||
}
|
||||
|
||||
RawHIDConnection::~RawHIDConnection()
|
||||
{
|
||||
if (RawHidHandle)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
void RawHIDConnection::onEnumerationChanged()
|
||||
{
|
||||
if (enablePolling)
|
||||
emit availableDevChanged(this);
|
||||
}
|
||||
|
||||
QStringList RawHIDConnection::availableDevices()
|
||||
{
|
||||
QMutexLocker locker(&m_enumMutex);
|
||||
|
||||
QStringList devices;
|
||||
|
||||
if (enablePolling) {
|
||||
pjrc_rawhid dev;
|
||||
//open all device we can
|
||||
int opened = dev.open(MAX_DEVICES, VID, PID, USAGE_PAGE, USAGE);
|
||||
|
||||
//for each devices found, get serial number and close it back
|
||||
for(int i=0; i<opened; i++)
|
||||
{
|
||||
devices.append(dev.getserial(i));
|
||||
dev.close(i);
|
||||
}
|
||||
}
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
void RawHIDConnection::onRawHidDestroyed(QObject *obj) // Pip
|
||||
{
|
||||
if (!RawHidHandle || RawHidHandle != obj)
|
||||
return;
|
||||
|
||||
RawHidHandle = NULL;
|
||||
}
|
||||
|
||||
QIODevice *RawHIDConnection::openDevice(const QString &deviceName)
|
||||
{
|
||||
//added by andrew
|
||||
if (RawHidHandle)
|
||||
closeDevice(deviceName);
|
||||
//end added by andrew
|
||||
|
||||
//return new RawHID(deviceName);
|
||||
RawHidHandle = new RawHID(deviceName);
|
||||
|
||||
connect(RawHidHandle, SLOT(destroyed(QObject *)), this, SLOT(onRawHidDestroyed(QObject *))); // Pip
|
||||
|
||||
return RawHidHandle;
|
||||
}
|
||||
|
||||
void RawHIDConnection::closeDevice(const QString &deviceName)
|
||||
{
|
||||
Q_UNUSED(deviceName);
|
||||
//added by andrew...
|
||||
if (RawHidHandle)
|
||||
{
|
||||
RawHidHandle->close();
|
||||
|
||||
delete RawHidHandle;
|
||||
RawHidHandle = NULL;
|
||||
|
||||
emit deviceClosed(this); // Pip
|
||||
}
|
||||
//end added by andrew
|
||||
}
|
||||
|
||||
QString RawHIDConnection::connectionName()
|
||||
{
|
||||
return QString("Raw HID USB");
|
||||
}
|
||||
|
||||
QString RawHIDConnection::shortName()
|
||||
{
|
||||
return QString("USB");
|
||||
}
|
||||
|
||||
/**
|
||||
Tells the Raw HID plugin to stop polling for USB devices
|
||||
*/
|
||||
void RawHIDConnection::suspendPolling()
|
||||
{
|
||||
enablePolling = false;
|
||||
}
|
||||
|
||||
/**
|
||||
Tells the Raw HID plugin to resume polling for USB devices
|
||||
*/
|
||||
void RawHIDConnection::resumePolling()
|
||||
{
|
||||
enablePolling = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//usb hid test thread
|
||||
//temporary...
|
||||
RawHIDTestThread::RawHIDTestThread()
|
||||
{
|
||||
Core::ConnectionManager *cm = Core::ICore::instance()->connectionManager();
|
||||
QObject::connect(cm, SIGNAL(deviceConnected(QIODevice *)),
|
||||
this, SLOT(onDeviceConnect(QIODevice *)));
|
||||
QObject::connect(cm, SIGNAL(deviceDisconnected()),
|
||||
this, SLOT(onDeviceDisconnect()));
|
||||
}
|
||||
|
||||
void RawHIDTestThread::onDeviceConnect(QIODevice *dev)
|
||||
{
|
||||
this->dev = dev;
|
||||
dev->open(QIODevice::ReadWrite);
|
||||
QObject::connect(dev, SIGNAL(readyRead()),
|
||||
this, SLOT(onReadyRead()));
|
||||
|
||||
QObject::connect(dev, SIGNAL(bytesWritten(qint64)),
|
||||
this, SLOT(onBytesWritten(qint64)));
|
||||
dev->write("Hello raw hid device\n");
|
||||
}
|
||||
|
||||
void RawHIDTestThread::onDeviceDisconnect()
|
||||
{
|
||||
dev->close();
|
||||
}
|
||||
|
||||
void RawHIDTestThread::onReadyRead()
|
||||
{
|
||||
qDebug() << "Rx:" << dev->readLine(32);
|
||||
}
|
||||
|
||||
void RawHIDTestThread::onBytesWritten(qint64 sz)
|
||||
{
|
||||
qDebug() << "Sent " << sz << " bytes";
|
||||
}
|
||||
|
||||
RawHIDPlugin::RawHIDPlugin()
|
||||
{
|
||||
hidConnection = NULL; // Pip
|
||||
}
|
||||
|
||||
RawHIDPlugin::~RawHIDPlugin()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void RawHIDPlugin::extensionsInitialized()
|
||||
{
|
||||
hidConnection = new RawHIDConnection();
|
||||
addAutoReleasedObject(hidConnection);
|
||||
|
||||
//temp for test
|
||||
//addAutoReleasedObject(new RawHIDTestThread);
|
||||
}
|
||||
|
||||
bool RawHIDPlugin::initialize(const QStringList & arguments, QString * errorString)
|
||||
{
|
||||
Q_UNUSED(arguments);
|
||||
Q_UNUSED(errorString);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Q_EXPORT_PLUGIN(RawHIDPlugin)
|
||||
|
@ -1,137 +1,144 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
*
|
||||
* @file rawhid.h
|
||||
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
|
||||
* @addtogroup GCSPlugins GCS Plugins
|
||||
* @{
|
||||
* @addtogroup RawHIDPlugin Raw HID Plugin
|
||||
* @{
|
||||
* @brief Impliments a HID USB connection to the flight hardware as a QIODevice
|
||||
*****************************************************************************/
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef RAWHIDPLUGIN_H
|
||||
#define RAWHIDPLUGIN_H
|
||||
|
||||
#include "rawhid_global.h"
|
||||
#include "rawhid.h"
|
||||
|
||||
#include "coreplugin/iconnection.h"
|
||||
#include <extensionsystem/iplugin.h>
|
||||
|
||||
#include <QtCore/QMutex>
|
||||
#include <QtCore/QThread>
|
||||
|
||||
class IConnection;
|
||||
class RawHIDConnection;
|
||||
|
||||
|
||||
/**
|
||||
* Helper thread to check on device connection/disconnection
|
||||
* Underlying HID library is not really easy to use,
|
||||
* so we have to poll for device modification in a separate thread
|
||||
*/
|
||||
class RAWHID_EXPORT RawHIDEnumerationThread : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
RawHIDEnumerationThread(RawHIDConnection *rawhid);
|
||||
virtual ~RawHIDEnumerationThread();
|
||||
|
||||
virtual void run();
|
||||
|
||||
signals:
|
||||
void enumerationChanged();
|
||||
|
||||
protected:
|
||||
RawHIDConnection *m_rawhid;
|
||||
bool m_running;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Define a connection via the IConnection interface
|
||||
* Plugin will add a instance of this class to the pool,
|
||||
* so the connection manager can use it.
|
||||
*/
|
||||
class RAWHID_EXPORT RawHIDConnection
|
||||
: public Core::IConnection
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
RawHIDConnection();
|
||||
virtual ~RawHIDConnection();
|
||||
|
||||
virtual QStringList availableDevices();
|
||||
virtual QIODevice *openDevice(const QString &deviceName);
|
||||
virtual void closeDevice(const QString &deviceName);
|
||||
|
||||
virtual QString connectionName();
|
||||
virtual QString shortName();
|
||||
virtual void suspendPolling();
|
||||
virtual void resumePolling();
|
||||
|
||||
bool deviceOpened() {return m_deviceOpened;}
|
||||
|
||||
protected slots:
|
||||
void onEnumerationChanged();
|
||||
|
||||
private:
|
||||
RawHID *RawHidHandle;
|
||||
bool enablePolling;
|
||||
|
||||
protected:
|
||||
QMutex m_enumMutex;
|
||||
RawHIDEnumerationThread m_enumerateThread;
|
||||
bool m_deviceOpened;
|
||||
};
|
||||
|
||||
class RAWHID_EXPORT RawHIDPlugin
|
||||
: public ExtensionSystem::IPlugin
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
RawHIDPlugin();
|
||||
~RawHIDPlugin();
|
||||
|
||||
virtual bool initialize(const QStringList &arguments, QString *error_message);
|
||||
virtual void extensionsInitialized();
|
||||
private:
|
||||
RawHIDConnection *hidConnection;
|
||||
};
|
||||
|
||||
|
||||
//usb hid test thread
|
||||
#include "coreplugin/icore.h"
|
||||
#include "coreplugin/connectionmanager.h"
|
||||
class RawHIDTestThread: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
RawHIDTestThread();
|
||||
protected slots:
|
||||
void onDeviceConnect(QIODevice *);
|
||||
void onDeviceDisconnect();
|
||||
void onReadyRead();
|
||||
void onBytesWritten(qint64 sz);
|
||||
protected:
|
||||
QIODevice *dev;
|
||||
};
|
||||
|
||||
#endif // RAWHIDPLUGIN_H
|
||||
/**
|
||||
******************************************************************************
|
||||
*
|
||||
* @file rawhid.h
|
||||
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
|
||||
* @addtogroup GCSPlugins GCS Plugins
|
||||
* @{
|
||||
* @addtogroup RawHIDPlugin Raw HID Plugin
|
||||
* @{
|
||||
* @brief Impliments a HID USB connection to the flight hardware as a QIODevice
|
||||
*****************************************************************************/
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef RAWHIDPLUGIN_H
|
||||
#define RAWHIDPLUGIN_H
|
||||
|
||||
#include "rawhid_global.h"
|
||||
#include "rawhid.h"
|
||||
|
||||
#include "coreplugin/iconnection.h"
|
||||
#include <extensionsystem/iplugin.h>
|
||||
|
||||
#include <QtCore/QMutex>
|
||||
#include <QtCore/QThread>
|
||||
|
||||
class IConnection;
|
||||
class RawHIDConnection;
|
||||
|
||||
|
||||
/**
|
||||
* Helper thread to check on device connection/disconnection
|
||||
* Underlying HID library is not really easy to use,
|
||||
* so we have to poll for device modification in a separate thread
|
||||
*/
|
||||
class RAWHID_EXPORT RawHIDEnumerationThread : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
RawHIDEnumerationThread(RawHIDConnection *rawhid);
|
||||
virtual ~RawHIDEnumerationThread();
|
||||
|
||||
virtual void run();
|
||||
|
||||
signals:
|
||||
void enumerationChanged();
|
||||
|
||||
protected:
|
||||
RawHIDConnection *m_rawhid;
|
||||
bool m_running;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Define a connection via the IConnection interface
|
||||
* Plugin will add a instance of this class to the pool,
|
||||
* so the connection manager can use it.
|
||||
*/
|
||||
class RAWHID_EXPORT RawHIDConnection
|
||||
: public Core::IConnection
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
RawHIDConnection();
|
||||
virtual ~RawHIDConnection();
|
||||
|
||||
virtual QStringList availableDevices();
|
||||
virtual QIODevice *openDevice(const QString &deviceName);
|
||||
virtual void closeDevice(const QString &deviceName);
|
||||
|
||||
virtual QString connectionName();
|
||||
virtual QString shortName();
|
||||
virtual void suspendPolling();
|
||||
virtual void resumePolling();
|
||||
|
||||
bool deviceOpened() { return (RawHidHandle != NULL); } // Pip
|
||||
|
||||
signals:
|
||||
void availableDevChanged(QObject *obj); // Pip
|
||||
void deviceClosed(QObject *obj); // Pip
|
||||
|
||||
protected slots:
|
||||
void onEnumerationChanged();
|
||||
|
||||
private slots:
|
||||
void onRawHidDestroyed(QObject *obj);
|
||||
|
||||
private:
|
||||
RawHID *RawHidHandle;
|
||||
bool enablePolling;
|
||||
|
||||
protected:
|
||||
QMutex m_enumMutex;
|
||||
RawHIDEnumerationThread m_enumerateThread;
|
||||
bool m_deviceOpened;
|
||||
};
|
||||
|
||||
class RAWHID_EXPORT RawHIDPlugin
|
||||
: public ExtensionSystem::IPlugin
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
RawHIDPlugin();
|
||||
~RawHIDPlugin();
|
||||
|
||||
virtual bool initialize(const QStringList &arguments, QString *error_message);
|
||||
virtual void extensionsInitialized();
|
||||
private:
|
||||
RawHIDConnection *hidConnection;
|
||||
};
|
||||
|
||||
|
||||
//usb hid test thread
|
||||
#include "coreplugin/icore.h"
|
||||
#include "coreplugin/connectionmanager.h"
|
||||
class RawHIDTestThread: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
RawHIDTestThread();
|
||||
protected slots:
|
||||
void onDeviceConnect(QIODevice *);
|
||||
void onDeviceDisconnect();
|
||||
void onReadyRead();
|
||||
void onBytesWritten(qint64 sz);
|
||||
protected:
|
||||
QIODevice *dev;
|
||||
};
|
||||
|
||||
#endif // RAWHIDPLUGIN_H
|
||||
|
Loading…
x
Reference in New Issue
Block a user