Commit bf1462c4 authored by Jonathan Marten's avatar Jonathan Marten

KDE Frameworks 5 support for hotplugging

As can be seen by the amount of old code commented out in KMixDeviceManager,
a rather simplistic approach is taken:  the Solid UDIs are only used to
identify a sound card and extract a device ID, using regexp matching.
The UDIs used elsewhere in the application are generated from the Solid
UDI.  As noted in the KF5 porting notes, OSS was never supported and
ALSA is assumed here.
parent b2f9810f
......@@ -194,6 +194,7 @@ target_link_libraries(kmixcore
PRIVATE
Qt5::DBus
KF5::I18n
KF5::Solid
PUBLIC
KF5::ConfigCore
KF5::ConfigGui
......
......@@ -104,10 +104,10 @@ KMixWindow::KMixWindow(bool invisible, bool reset) :
setInitialSize();
fixConfigAfterRead();
connect(theKMixDeviceManager, &KMixDeviceManager::plugged, this, &KMixWindow::plugged);
connect(theKMixDeviceManager, &KMixDeviceManager::unplugged, this, &KMixWindow::unplugged);
theKMixDeviceManager->initHotplug();
connect(theKMixDeviceManager, SIGNAL(plugged(const char*,QString,QString&)),
SLOT(plugged(const char*,QString,QString&)));
connect(theKMixDeviceManager, SIGNAL(unplugged(QString)), SLOT(unplugged(QString)));
if (m_startVisible && !invisible)
show(); // Started visible
......@@ -879,38 +879,36 @@ void KMixWindow::fixConfigAfterRead()
} // if config version < 3
}
void KMixWindow::plugged(const char* driverName, const QString& udi, QString& dev)
void KMixWindow::plugged(const char *driverName, const QString &udi, int dev)
{
qCDebug(KMIX_LOG)
<< "Plugged: dev=" << dev << "(" << driverName << ") udi=" << udi << "\n";
QString driverNameString;
driverNameString = driverName;
int devNum = dev.toInt();
Mixer *mixer = new Mixer(driverNameString, devNum);
if (mixer != 0)
qCDebug(KMIX_LOG) << "dev" << dev << "driver" << driverName << "udi" << udi;
Mixer *mixer = new Mixer(QString::fromLocal8Bit(driverName), dev);
if (mixer!=nullptr)
{
qCDebug(KMIX_LOG)
<< "Plugged: dev=" << dev << "\n";
if (MixerToolBox::instance()->possiblyAddMixer(mixer))
{
qCDebug(KMIX_LOG) << "adding mixer id" << mixer->id() << "name" << mixer->readableName();
recreateGUI(true, mixer->id(), true, false);
}
else qCWarning(KMIX_LOG) << "Cannot add mixer to GUI";
}
}
void KMixWindow::unplugged(const QString& udi)
void KMixWindow::unplugged(const QString &udi)
{
qCDebug(KMIX_LOG)
<< "Unplugged: udi=" << udi << "\n";
qCDebug(KMIX_LOG) << "udi" << udi;
for (int i = 0; i < Mixer::mixers().count(); ++i)
{
Mixer *mixer = (Mixer::mixers())[i];
// qCDebug(KMIX_LOG) << "Try Match with:" << mixer->udi() << "\n";
// qCDebug(KMIX_LOG) << "Try Match with:" << mixer->udi();
if (mixer->udi() == udi)
{
qCDebug(KMIX_LOG)
<< "Unplugged Match: Removing udi=" << udi << "\n";
//KMixToolBox::notification("MasterFallback", "aaa");
qCDebug(KMIX_LOG) << "Removing mixer";
bool globalMasterMixerDestroyed = (mixer == Mixer::getGlobalMasterMixer());
// Part 1) Remove Tab
// Part 1: Remove tab from GUI
for (int i = 0; i < m_wsMixers->count(); ++i)
{
QWidget *w = m_wsMixers->widget(i);
......@@ -921,8 +919,12 @@ void KMixWindow::unplugged(const QString& udi)
i = -1; // Restart loop from scratch (indices are most likely invalidated at removeTab() )
}
}
// Part 2: Remove mixer from known list
MixerToolBox::instance()->removeMixer(mixer);
// Check whether the Global Master disappeared, and select a new one if necessary
// Part 3: Check whether the Global Master disappeared,
// and select a new one if necessary
shared_ptr<MixDevice> md = Mixer::getGlobalMasterMD();
if (globalMasterMixerDestroyed || md.get() == 0)
{
......
......@@ -141,8 +141,10 @@ private:
void slotHWInfo();
void slotKdeAudioSetupExec();
void slotConfigureCurrentView();
void plugged( const char* driverName, const QString& udi, QString& dev);
void unplugged( const QString& udi);
void plugged(const char *driverName, const QString &udi, int dev);
void unplugged(const QString &udi);
void hideOrClose();
void slotIncreaseVolume();
void slotDecreaseVolume();
......
......@@ -81,16 +81,17 @@ KMixD::KMixD(QObject* parent, const QList<QVariant>&) :
*/
void KMixD::delayedInitialization()
{
qCDebug(KMIX_LOG) << "kmixd: Delayed initialization running now";
qCDebug(KMIX_LOG) << "Delayed initialization running now";
//initActions(); // init actions first, so we can use them in the loadConfig() already
loadConfig(); // Load config before initMixer(), e.g. due to "MultiDriver" keyword
MixerToolBox::instance()->initMixer(m_multiDriverMode, m_backendFilter, m_hwInfoString, true);
KMixDeviceManager *theKMixDeviceManager = KMixDeviceManager::instance();
connect(theKMixDeviceManager, &KMixDeviceManager::plugged, this, &KMixD::plugged);
connect(theKMixDeviceManager, &KMixDeviceManager::unplugged, this, &KMixD::unplugged);
theKMixDeviceManager->initHotplug();
connect(theKMixDeviceManager, SIGNAL(plugged(const char*,QString,QString&)), SLOT (plugged(const char*,QString,QString&)) );
connect(theKMixDeviceManager, SIGNAL(unplugged(QString)), SLOT (unplugged(QString)) );
qCDebug(KMIX_LOG) << "kmixd: Delayed initialization done";
qCDebug(KMIX_LOG) << "Delayed initialization done";
}
......@@ -156,22 +157,23 @@ void KMixD::loadBaseConfig()
}
void KMixD::plugged( const char* driverName, const QString& /*udi*/, QString& dev)
void KMixD::plugged(const char *driverName, const QString &udi, int dev)
{
// qCDebug(KMIX_LOG) << "Plugged: dev=" << dev << "(" << driverName << ") udi=" << udi << "\n";
QString driverNameString;
driverNameString = driverName;
int devNum = dev.toInt();
Mixer *mixer = new Mixer( driverNameString, devNum );
if ( mixer != 0 ) {
qCDebug(KMIX_LOG) << "Plugged: dev=" << dev << "\n";
qCDebug(KMIX_LOG) << "dev" << dev << "driver" << driverName << "udi" << udi;
Mixer *mixer = new Mixer(QString::fromLocal8Bit(driverName), dev);
if (mixer!=nullptr)
{
qCDebug(KMIX_LOG) << "adding mixer" << mixer->id() << mixer->readableName();
MixerToolBox::instance()->possiblyAddMixer(mixer);
}
}
void KMixD::unplugged( const QString& udi)
void KMixD::unplugged(const QString &udi)
{
qCDebug(KMIX_LOG) << "udi" << udi;
// qCDebug(KMIX_LOG) << "Unplugged: udi=" <<udi << "\n";
for (int i=0; i<Mixer::mixers().count(); ++i) {
Mixer *mixer = (Mixer::mixers())[i];
......
......@@ -60,8 +60,9 @@ KMixD : public KDEDModule, protected QDBusContext
private slots:
void delayedInitialization();
void saveConfig();
void plugged( const char* driverName, const QString& udi, QString& dev);
void unplugged( const QString& udi);
void plugged(const char *driverName, const QString &udi, int dev);
void unplugged(const QString &udi);
};
#endif // KMIXD_H
......@@ -82,10 +82,19 @@ void Mixer_Backend::freeMixDevices()
m_mixDevices.clear();
}
bool Mixer_Backend::openIfValid()
{
int ret = open();
if (ret == 0 && (m_mixDevices.count() > 0 || _mixer->isDynamic()))
const int ret = open();
if (ret!=0)
{
//qCWarning(KMIX_LOG) << "open" << getName() << "failed" << ret;
return false; // could not open
}
qCDebug(KMIX_LOG) << "opened" << getName() << "count" << m_mixDevices.count()
<< "dynamic?" << _mixer->isDynamic() << "needsPolling?" << needsPolling();
if (m_mixDevices.count() > 0 || _mixer->isDynamic())
{
if (needsPolling())
{
......@@ -96,15 +105,16 @@ bool Mixer_Backend::openIfValid()
// The initial state must be read manually
QTimer::singleShot( POLL_RATE_FAST, this, SLOT(readSetFromHW()));
}
return true; // could be opened
return true; // could be opened
}
else
{
//shutdown();
return false; // could not open
qCWarning(KMIX_LOG) << "no mix devices and not dynamic";
return false; // could not open
}
}
bool Mixer_Backend::isOpen() {
return m_isOpen;
}
......
This diff is collapsed.
......@@ -32,17 +32,17 @@ class KMIXCORE_EXPORT KMixDeviceManager : public QObject
public:
static KMixDeviceManager* instance();
void initHotplug();
void setHotpluggingBackends(const QString& backendName) { _hotpluggingBackend = backendName; } ;
void setHotpluggingBackends(const QString& backendName);
QString getUDI_ALSA(int num);
QString getUDI_OSS(const QString& devname);
signals:
void plugged( const char* driverName, const QString& udi, QString& dev);
void unplugged( const QString& udi);
void plugged(const char *driverName, const QString &udi, int dev);
void unplugged(const QString &udi);
private:
KMixDeviceManager();
~KMixDeviceManager();
KMixDeviceManager() = default;
virtual ~KMixDeviceManager() = default;
QString _hotpluggingBackend;
private slots:
......
......@@ -86,8 +86,10 @@ bool Mixer::pulseaudioPresent()
}
Mixer::Mixer( QString& ref_driverName, int device )
: m_balance(0), _mixerBackend(0L), m_dynamic(false)
Mixer::Mixer(const QString &ref_driverName, int device)
: m_balance(0),
_mixerBackend(nullptr),
m_dynamic(false)
{
_mixerBackend = 0;
int driverCount = numDrivers();
......@@ -243,48 +245,41 @@ void Mixer::volumeLoad( KConfig *config )
* Opens the mixer.
* Also, starts the polling timer, for polling the Volumes from the Mixer.
*
* @param cardId The cardId Usually this will be 1, but if there is
* more than one card with the same name install, then you need
* to use 2, 3, ...
*
* @return true, if Mixer could be opened.
*/
bool Mixer::openIfValid()
{
if (_mixerBackend == 0 )
{
// if we did not instantiate a suitable Backend, then Mixer is invalid
return false;
}
if (_mixerBackend==nullptr)
{
// If we did not instantiate a suitable backend, then the mixer is invalid.
qCWarning(KMIX_LOG) << "no mixer backend";
return false;
}
bool ok = _mixerBackend->openIfValid();
if ( ok )
if (!ok) return (false);
recreateId();
shared_ptr<MixDevice> recommendedMaster = _mixerBackend->recommendedMaster();
if (recommendedMaster.get()!=nullptr)
{
recreateId();
shared_ptr<MixDevice> recommendedMaster = _mixerBackend->recommendedMaster();
if ( recommendedMaster.get() != 0 )
{
QString recommendedMasterStr = recommendedMaster->id();
setLocalMasterMD( recommendedMasterStr );
qCDebug(KMIX_LOG) << "Mixer::open() detected master: " << recommendedMaster->id();
}
else
{
if ( !m_dynamic )
qCCritical(KMIX_LOG) << "Mixer::open() no master detected.";
else
qCDebug(KMIX_LOG) << "Mixer::open() no master detected.";
QString noMaster = "---no-master-detected---";
setLocalMasterMD(noMaster); // no master
}
// cesken: The following connect() looks mighty strange. I removed it on 2013-12-18
//connect( _mixerBackend, SIGNAL(controlChanged()), SIGNAL(controlChanged()) );
new DBusMixerWrapper(this, dbusPath());
QString recommendedMasterStr = recommendedMaster->id();
setLocalMasterMD( recommendedMasterStr );
qCDebug(KMIX_LOG) << "Detected master" << recommendedMaster->id();
}
else
{
if (!m_dynamic) qCCritical(KMIX_LOG) << "No master detected and not dynamic";
else qCDebug(KMIX_LOG) << "No master detected but dynamic";
QString noMaster = "---no-master-detected---";
setLocalMasterMD(noMaster); // no master
}
return ok;
new DBusMixerWrapper(this, dbusPath());
return (true);
}
/**
* Closes the mixer.
*/
......
......@@ -61,7 +61,7 @@ public:
ERR_OPEN, OK_UNCHANGED };
Mixer( QString& ref_driverName, int device );
Mixer(const QString &ref_driverName, int device);
virtual ~Mixer();
static int numDrivers();
......@@ -112,6 +112,7 @@ public:
QString translateKernelToWhatsthis(const QString &kernelName);
/// Return the name of the card/chip/hardware, which is suitable for humans
// TODO: combine with default paramaeter
QString readableName();
QString readableName(bool ampersandQuoted);
......
......@@ -300,6 +300,7 @@ void MixerToolBox::initMixerInternal(MultiDriverMode multiDriverMode, QList<QStr
}
/**
* Opens and adds a mixer to the KMix wide Mixer array, if the given Mixer is valid.
* Otherwise the Mixer is deleted.
......@@ -310,33 +311,26 @@ void MixerToolBox::initMixerInternal(MultiDriverMode multiDriverMode, QList<QStr
*/
bool MixerToolBox::possiblyAddMixer(Mixer *mixer)
{
if ( mixer->openIfValid() )
if (mixer->openIfValid())
{
if ( (!s_ignoreMixerExpression.isEmpty()) && mixer->id().contains(s_ignoreMixerExpression) )
if (s_ignoreMixerExpression.isEmpty() || !mixer->id().contains(s_ignoreMixerExpression))
{
// This Mixer should be ignored (default expression is "Modem").
// next 3 lines are duplicated code
delete mixer;
mixer = 0;
return false;
Mixer::mixers().append(mixer);
qCDebug(KMIX_LOG) << "Added mixer " << mixer->id();
return (true);
}
else
{
Mixer::mixers().append( mixer );
qCDebug(KMIX_LOG) << "Added card " << mixer->id();
emit mixerAdded(mixer->id()); // TODO should we still use this, as we now have our publish/subscribe notification system?
return true;
// This mixer should be ignored (the default ignore expression is "Modem").
qCDebug(KMIX_LOG) << "mixer" << mixer->id() << "ignored";
}
} // valid
else
{
delete mixer;
mixer = 0;
return false;
} // invalid
}
delete mixer;
return (false);
}
/* This allows to set an expression form Mixers that should be ignored.
The default is "Modem", because most people don't want to control the modem volume. */
void MixerToolBox::setMixerIgnoreExpression(const QString& ignoreExpr)
......
......@@ -36,6 +36,9 @@ class Mixer;
* It only contains no-GUI code. The shared with-GUI code is in KMixToolBox
* The reason, why it is not put in a common base class is, that the classes are
* very different and cannot be changed (e.g. KPanelApplet) without major headache.
// TODO: it can therefore be a namespace with only static methods.
*/
class KMIXCORE_EXPORT MixerToolBox : public QObject
{
......@@ -56,9 +59,6 @@ class KMIXCORE_EXPORT MixerToolBox : public QObject
//static KLocale* whatsthisControlLocale();
signals:
void mixerAdded(QString mixerID);
private:
static MixerToolBox* s_instance;
static QRegExp s_ignoreMixerExpression;
......
......@@ -373,6 +373,7 @@ void ViewDockAreaPopup::constructionFinished()
optionsLayout = new QHBoxLayout();
optionsLayout->addWidget(mainWindowButton);
optionsLayout->addStretch(1);
optionsLayout->addWidget(configureViewButton);
#ifdef RESTORE_VOLUME_BUTTON
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment