1 /*
2  * KMix -- KDE's full featured mini mixer
3  *
4  * Copyright 1996-2000 Christian Esken <esken@kde.org>
5  * Copyright 2000-2003 Christian Esken <esken@kde.org>, Stefan Schimanski <1Stein@gmx.de>
6  * Copyright 2002-2007 Christian Esken <esken@kde.org>, Helio Chissini de Castro <helio@conectiva.com.br>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this program; if not, write to the Free
20  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21  */
22 
23 #include "kmixd.h"
24 
25 #include <kconfig.h>
26 #include <klocalizedstring.h>
27 #include <kpluginfactory.h>
28 #include <kpluginloader.h>
29 
30 // KMix
31 #include "core/mixertoolbox.h"
32 #include "core/kmixdevicemanager.h"
33 #include "core/mixer.h"
34 #include "settings.h"
35 
36 
37 K_PLUGIN_FACTORY_WITH_JSON(KMixDFactory,
38                            "kmixd.json",
39                            registerPlugin<KMixD>();)
40 
41 
42 /* KMixD
43  * Constructs a mixer window (KMix main window)
44  */
KMixD(QObject * parent,const QList<QVariant> &)45 KMixD::KMixD(QObject* parent, const QList<QVariant>&) :
46    KDEDModule(parent),
47    m_multiDriverMode (false) // -<- I never-ever want the multi-drivermode to be activated by accident
48 {
49     setObjectName( QStringLiteral("KMixD" ));
50 	qCDebug(KMIX_LOG) << "kmixd: Triggering delayed initialization";
51 	QTimer::singleShot( 3000, this, SLOT(delayedInitialization()));
52 }
53 
54 /**
55  * This is called by a singleShot timer. Reason is that KMixD seems to "collide" with other applications
56  * on DBUS, likely creating deadlocks. See Bug 317926 for that. By artificially delaying initialization,
57  * KMixD gets hopefully out of the way of the other applications, avoiding these deadlocks. A small delay
58  * should also not harm too much, as apps using the Mixer DBUS calls on KMixD must be prepared that the
59  * interface is not available right when they start.
60  */
delayedInitialization()61 void KMixD::delayedInitialization()
62 {
63     qCDebug(KMIX_LOG) << "Delayed initialization running now";
64    //initActions(); // init actions first, so we can use them in the loadConfig() already
65    loadConfig(); // Load config before initMixer(), e.g. due to "MultiDriver" keyword
66    MixerToolBox::initMixer(m_multiDriverMode, m_backendFilter, true);
67 
68    KMixDeviceManager *theKMixDeviceManager = KMixDeviceManager::instance();
69    connect(theKMixDeviceManager, &KMixDeviceManager::plugged, this, &KMixD::plugged);
70    connect(theKMixDeviceManager, &KMixDeviceManager::unplugged, this, &KMixD::unplugged);
71    theKMixDeviceManager->initHotplug();
72 
73    qCDebug(KMIX_LOG) << "Delayed initialization done";
74 }
75 
76 
~KMixD()77 KMixD::~KMixD()
78 {
79    MixerToolBox::deinitMixer();
80 }
81 
82 
83 
saveConfig()84 void KMixD::saveConfig()
85 {
86    qCDebug(KMIX_LOG) << "About to save config";
87    saveBaseConfig();
88   // saveVolumes(); // -<- removed from kmixd, as it is possibly a bad idea if both kmix and kmixd write the same config "kmixctrlrc"
89 #ifdef __GNUC_
90 #warn We must Sync here, or we will lose configuration data. The reson for that is unknown.
91 #endif
92 
93    qCDebug(KMIX_LOG) << "Saved config ... now syncing explicitly";
94    Settings::self()->save();
95    qCDebug(KMIX_LOG) << "Saved config ... sync finished";
96 }
97 
saveBaseConfig()98 void KMixD::saveBaseConfig()
99 {
100    qCDebug(KMIX_LOG) << "About to save config (Base)";
101 
102    Settings::setConfigVersion(KMIX_CONFIG_VERSION);
103    const Mixer *mixerMasterCard = Mixer::getGlobalMasterMixer();
104    if (mixerMasterCard!=nullptr) Settings::setMasterMixer(mixerMasterCard->id());
105    shared_ptr<MixDevice> mdMaster = Mixer::getGlobalMasterMD();
106    if (mdMaster) Settings::setMasterMixerDevice(mdMaster->id());
107    Settings::setMixerIgnoreExpression(MixerToolBox::mixerIgnoreExpression());
108    qCDebug(KMIX_LOG) << "Config (Base) saving done";
109 }
110 
loadConfig()111 void KMixD::loadConfig()
112 {
113    loadBaseConfig();
114 }
115 
loadBaseConfig()116 void KMixD::loadBaseConfig()
117 {
118     m_multiDriverMode = Settings::multiDriver();
119     QString mixerMasterCard = Settings::masterMixer();
120     QString masterDev = Settings::masterMixerDevice();
121     Mixer::setGlobalMaster(mixerMasterCard, masterDev, true);
122     QString mixerIgnoreExpression = Settings::mixerIgnoreExpression();
123     if (!mixerIgnoreExpression.isEmpty()) MixerToolBox::setMixerIgnoreExpression(mixerIgnoreExpression);
124 
125     m_backendFilter = Settings::backends();
126     MixerToolBox::setMixerIgnoreExpression(mixerIgnoreExpression);
127 }
128 
129 
plugged(const char * driverName,const QString & udi,int dev)130 void KMixD::plugged(const char *driverName, const QString &udi, int dev)
131 {
132     qCDebug(KMIX_LOG) << "dev" << dev << "driver" << driverName << "udi" << udi;
133 
134     Mixer *mixer = new Mixer(QString::fromLocal8Bit(driverName), dev);
135     if (mixer!=nullptr)
136     {
137         qCDebug(KMIX_LOG) << "adding mixer" << mixer->id() << mixer->readableName();
138         MixerToolBox::possiblyAddMixer(mixer);
139     }
140 }
141 
142 
unplugged(const QString & udi)143 void KMixD::unplugged(const QString &udi)
144 {
145     qCDebug(KMIX_LOG) << "udi" << udi;
146 
147 //     qCDebug(KMIX_LOG) << "Unplugged: udi=" <<udi << "\n";
148     for (int i=0; i<Mixer::mixers().count(); ++i) {
149         Mixer *mixer = (Mixer::mixers())[i];
150 //         qCDebug(KMIX_LOG) << "Try Match with:" << mixer->udi() << "\n";
151         if (mixer->udi() == udi ) {
152             qCDebug(KMIX_LOG) << "Unplugged Match: Removing udi=" <<udi << "\n";
153             //KMixToolBox::notification("MasterFallback", "aaa");
154             bool globalMasterMixerDestroyed = ( mixer == Mixer::getGlobalMasterMixer() );
155 
156             MixerToolBox::removeMixer(mixer);
157             // Check whether the Global Master disappeared, and select a new one if necessary
158             shared_ptr<MixDevice> md = Mixer::getGlobalMasterMD();
159             if ( globalMasterMixerDestroyed || md.get() == 0 ) {
160                 // We don't know what the global master should be now.
161                 // So lets play stupid, and just select the recommended master of the first device
162                 if ( Mixer::mixers().count() > 0 ) {
163                 	shared_ptr<MixDevice> master = ((Mixer::mixers())[0])->getLocalMasterMD();
164                     if ( master.get() != 0 ) {
165                         QString localMaster = master->id();
166                         Mixer::setGlobalMaster( ((Mixer::mixers())[0])->id(), localMaster, false);
167 
168                         QString text;
169                         text = i18n("The soundcard containing the master device was unplugged. Changing to control %1 on card %2.",
170                                 master->readableName(),
171                                 ((Mixer::mixers())[0])->readableName()
172                         );
173 //                        KMixToolBox::notification("MasterFallback", text);
174                     }
175                 }
176             }
177             if ( Mixer::mixers().count() == 0 ) {
178                 QString text;
179                 text = i18n("The last soundcard was unplugged.");
180 //                KMixToolBox::notification("MasterFallback", text);
181             }
182             break;
183         }
184     }
185 
186 }
187 
188 
189 #include "kmixd.moc"
190