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