File: kmixapp.cpp

package info (click to toggle)
kmix 4%3A25.12.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 5,408 kB
  • sloc: cpp: 14,231; xml: 464; sh: 97; ansic: 34; makefile: 3
file content (272 lines) | stat: -rw-r--r-- 9,927 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
/*
 * KMix -- KDE's full featured mini mixer
 *
 * Copyright (C) 2000 Stefan Schimanski <schimmi@kde.org>
 * Copyright (C) 2001 Preston Brown <pbrown@kde.org>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this program; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include "kmixapp.h"

#include <qapplication.h>
#include <qcommandlineparser.h>

#include "kmix_debug.h"
#include "core/ControlManager.h"
#include "core/mixertoolbox.h"
#include "core/volume.h"
#include "apps/kmixwindow.h"
#include "settings.h"


static bool firstCaller = true;


// Originally this class was a subclass of KUniqueApplication.
// Now that a unique application is enforced earlier by KDBusService,
// the only purpose of this class is to receive the activateRequested()
// signal.  It can therefore be a simple QObject.

KMixApp::KMixApp()
{
	// We must disable QuitOnLastWindowClosed. Rationale:
	// 1) The normal state of KMix is to only have the dock icon shown.
	// 2a) The dock icon gets reconstructed, whenever a soundcard is hotplugged or unplugged.
	// 2b) The dock icon gets reconstructed, when the user selects a new master.
	// 3) During the reconstruction, it can easily happen that no window is present => KMix would quit
	// => disable QuitOnLastWindowClosed
	qApp->setQuitOnLastWindowClosed(false);

	m_startupOptions = KMixApp::StartupOptions();	// command line not parsed
}

KMixApp::~KMixApp()
{
	qCDebug(KMIX_LOG) << "Deleting KMixApp";
	ControlManager::instance()->shutdownNow();
	delete m_kmix;
	Settings::self()->save();
}

void KMixApp::createWindowOnce()
{
	// Create window, if it was not yet created (e.g. via autostart or manually)
	if (m_kmix.isNull())
	{
		qCDebug(KMIX_LOG) << "Creating new KMixWindow";
		m_kmix = new KMixWindow(m_startupOptions);
	}
}

bool KMixApp::restoreSessionIfApplicable()
{
	/**
	 * We should lock session creation. Rationale:
	 * KMix can be started multiple times during session start. By "autostart" and "session restore". The order is
	 * undetermined, as KMix will initialize in the background of KDE session startup (Hint: As a
	 * KUniqueApplication it decouples from the startkde process!).
	 *
	 * Now we must make sure that window creation is definitely done, before the "other" process comes, as it might
	 * want to restore the session. Working on a half-created window would not be smart! Why can this happen? It
	 * depends on implementation details inside Qt, which COULD potentially lead to the following scenarios:
	 * 1) "Autostart" and "session restore" run concurrently in 2 different Threads.
	 * 2) The current "main/gui" thread "pops up" a "session restore" message from the Qt event dispatcher.
	 *    This means that  "Autostart" and "session restore" run interleaved in a single Thread.
	 */
	creationLock.lock();

	// TODO: is it really worth supporting session restore in this application?
	// There can only be one instance of KMix, with one main window and
	// optionally a system tray icon.  It is started on desktop login via
	// autostart and persists.  Volumes are also restored via autostart.
	// So it is not clear what session restore actually achieves - maybe
	// just the open/hidden state of the window, even if that?  Eliminating
	// session restore would simplify a lot.

	const bool restore = qApp->isSessionRestored(); // && KMainWindow::canBeRestored(0);
	qCDebug(KMIX_LOG) << "Startup options" << m_startupOptions << "restore?" << restore;
	int createCount = 0;
	if (restore)
	{
		if (m_startupOptions & KMixApp::FailsafeReset)
		{
			qCWarning(KMIX_LOG) << "Reset cannot be performed while KMix is running. Please quit KMix and retry then.";
		}
		int n = 1;
		while (KMainWindow::canBeRestored(n))
		{
			qCDebug(KMIX_LOG) << "Restoring window" << n;
			if (n > 1)
			{
				// This code path is "impossible". It is here only for analyzing possible issues with session restoring.
				// KMix is a single-instance app. If more than one instance is created we have a bug.
				qCWarning(KMIX_LOG) << "KDE session management wants to restore multiple instances of KMix. Please report this as a bug.";
				break;
			}
			else
			{
				// Create the window, if it has not yet been created
				// (e.g. via autostart or manually).
				createWindowOnce();

				// Having done the above, we now know that m_kmix
				// is valid.  Its restore() is called with the 'show'
				// parameter as false, as KMixWindow makes its own
				// decision as to whether to do that.
				m_kmix->restore(n, false);
				createCount++;
				n++;
			}
		}
	}

	if (createCount == 0)
	{
		// Normal start, or if nothing could be restored
		createWindowOnce();
	}

	creationLock.unlock();
	return restore;
}


void KMixApp::newInstance(const QStringList &arguments, const QString &workingDirectory)
{
	qCDebug(KMIX_LOG);

	/**
	 * There are 3 cases when starting KMix:
	 * Autostart            : Cases 1) or 3) below
	 * Session restore      : Cases 1) or 2a) below
	 * Manual start by user : Cases 1) or 2b) below
	 *
	 * Each may be the creator a new instance, but only if the instance did not exist yet.
	 */


	//qCDebug(KMIX_LOG) <<  "KMixApp::newInstance() isRestored()=" << isRestored() << "_keepVisibility=" << _keepVisibility;
	/**
	 * 		NB See https://qa.mandriva.com/show_bug.cgi?id=56893#c3
	 *
	 * 		It is important to track this via a separate variable and not
	 * 		based on m_kmix to handle this race condition.
	 * 		Specific protection for the activation-prior-to-full-construction
	 * 		case exists above in the 'already running case'
	 */
	creationLock.lock(); // Guard a complete construction
	const bool first = firstCaller;
	firstCaller = false;

	if (first)
	{
		/** CASE 1 *******************************************************
		 *
		 * Typical case: Normal start. KMix was not running yet => create a new KMixWindow
		 */
		restoreSessionIfApplicable();
	}
	else
	{
		if (!(m_startupOptions & KMixApp::KeepVisibility))
		{
			/** CASE 2 ******************************************************
			 *
			 * KMix is running, AND the *USER* starts it again (w/o --keepvisibilty)
			 * 2a) Restored the KMix main window will be shown.
			 * 2b) Not restored
			 */

			/*
			 * Restore Session. This may look strange to you, as the instance already exists. But the following
			 * sequence might happen:
			 * 1) Autostart (no restore) => create m_kmix instance (via CASE 1)
			 * 2) Session restore => we are here at this line of code (CASE 2). m_kmix exists, but still must be restored
			 *
			 */
			bool wasRestored = restoreSessionIfApplicable();
			if (!wasRestored)
			{
				//
				// Use standard newInstances(), which shows and activates the main window. But skip it for the
				// special "restored" case, as we should not override the session rules.
				// TODO: what should be done for KF5?
				//KUniqueApplication::newInstance();
			}
			// else: Do nothing, as session restore has done it.
		}
		else
		{
			/** CASE 3 ******************************************************
			 *
			 * KMix is running, AND launched again with --keepvisibilty
			 *
			 * Typical use case: Autostart
			 *
			 * =>  We don't want to change the visibility, thus we don't call show() here.
			 *
			 * Hint: --keepVisibility is used in kmix_autostart.desktop. It was used in history by KMilo
			 *       (see BKO 58901), but nowadays Mixer Applets might want to use it, though they should
			 *       use KMixD instead.
			 */
			qCDebug(KMIX_LOG) << "Normal startup with options" << m_startupOptions;
		}
	}

	creationLock.unlock();
}


void KMixApp::parseOptions(const QCommandLineParser &parser)
{
	if (parser.isSet("keepvisibility")) m_startupOptions |= KMixApp::KeepVisibility;
	if (parser.isSet("failsafe")) m_startupOptions |= KMixApp::FailsafeReset;
	if (parser.isSet("nosystemtray")) m_startupOptions |= KMixApp::NoSystemTray;

	// Hidden configuration settings which affect the entire application
	// are also parsed and set here.  Therefore they do not need to be passed
	// all the way down to KMixWindow only to eventually end up in the same
	// place anyway.

	// The match expression for ignored mixer names.
	QString mixerIgnoreExpression = Settings::mixerIgnoreExpression();
	if (!mixerIgnoreExpression.isEmpty()) MixerToolBox::setMixerIgnoreExpression(mixerIgnoreExpression);

	// The global volume step setting.
	const int volumePercentageStep = Settings::volumePercentageStep();
	if (volumePercentageStep>0) Volume::setVolumeStep(volumePercentageStep);

	// The following log is very helpful in bug reports. Please keep it.
	QStringList backendFilter = Settings::backends();
	qCDebug(KMIX_LOG) << "Backends from settings =" << backendFilter;
	if (parser.isSet("backends"))
	{
		// The command line option overrides the configured setting.
		backendFilter = parser.value("backends").split(',');
		qCDebug(KMIX_LOG) << "Backends from command line =" << backendFilter;
	}
	if (!backendFilter.isEmpty()) MixerToolBox::setAllowedBackends(backendFilter);

	bool multiDriverMode = Settings::multiDriver();
	qCDebug(KMIX_LOG) << "Multi driver mode from settings =" << multiDriverMode;
	if (parser.isSet("multidriver"))
	{
		multiDriverMode = true;
		qCDebug(KMIX_LOG) << "Multi driver mode from command line =" << multiDriverMode;
	}

	if (multiDriverMode) MixerToolBox::setMultiDriverMode(true);
}