1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 #include "maemoqemumanager.h"
36 #include "maemoglobal.h"
37 #include "maemomanager.h"
38 #include "maemoqemuruntimeparser.h"
39 #include "maemosettingspages.h"
40 #include "maemorunconfiguration.h"
41 #include "qtversionmanager.h"
42 #include "qt4project.h"
43 #include "qt4projectmanagerconstants.h"
44 #include "qt4maemotarget.h"
46 #include <coreplugin/actionmanager/actionmanager.h>
47 #include <coreplugin/actionmanager/command.h>
48 #include <coreplugin/uniqueidmanager.h>
49 #include <coreplugin/coreconstants.h>
50 #include <coreplugin/icore.h>
51 #include <coreplugin/icontext.h>
52 #include <coreplugin/modemanager.h>
54 #include <projectexplorer/projectexplorer.h>
55 #include <projectexplorer/session.h>
57 #include <QtCore/QDebug>
58 #include <QtCore/QDir>
59 #include <QtCore/QList>
60 #include <QtCore/QSet>
61 #include <QtCore/QStringBuilder>
63 #include <QtGui/QAction>
64 #include <QtGui/QDesktopServices>
65 #include <QtGui/QMessageBox>
69 using namespace ProjectExplorer;
70 using namespace Qt4ProjectManager;
71 using namespace Qt4ProjectManager::Internal;
73 MaemoQemuManager *MaemoQemuManager::m_instance = 0;
75 const QSize iconSize = QSize(24, 20);
77 MaemoQemuManager::MaemoQemuManager(QObject *parent)
80 , m_qemuProcess(new QProcess(this))
81 , m_runningQtId(INT_MIN)
82 , m_userTerminated(false)
84 m_qemuStarterIcon.addFile(":/qt-maemo/images/qemu-run.png", iconSize);
85 m_qemuStarterIcon.addFile(":/qt-maemo/images/qemu-stop.png", iconSize,
86 QIcon::Normal, QIcon::On);
88 m_qemuAction = new QAction("Maemo Emulator", this);
89 m_qemuAction->setIcon(m_qemuStarterIcon.pixmap(iconSize));
90 m_qemuAction->setToolTip(tr("Start Maemo Emulator"));
91 connect(m_qemuAction, SIGNAL(triggered()), this, SLOT(startRuntime()));
93 Core::ICore *core = Core::ICore::instance();
94 Core::ActionManager *actionManager = core->actionManager();
95 Core::Command *qemuCommand = actionManager->registerAction(m_qemuAction,
96 "MaemoEmulator", Core::Context(Core::Constants::C_GLOBAL));
97 qemuCommand->setAttribute(Core::Command::CA_UpdateText);
98 qemuCommand->setAttribute(Core::Command::CA_UpdateIcon);
100 Core::ModeManager *modeManager = core->modeManager();
101 modeManager->addAction(qemuCommand->action(), 1);
102 m_qemuAction->setEnabled(false);
103 m_qemuAction->setVisible(false);
105 // listen to qt version changes to update the start button
106 connect(QtVersionManager::instance(), SIGNAL(qtVersionsChanged(QList<int>)),
107 this, SLOT(qtVersionsChanged(QList<int>)));
109 // listen to project add, remove and startup changes to udate start button
110 SessionManager *session = ProjectExplorerPlugin::instance()->session();
111 connect(session, SIGNAL(projectAdded(ProjectExplorer::Project*)), this,
112 SLOT(projectAdded(ProjectExplorer::Project*)));
113 connect(session, SIGNAL(projectRemoved(ProjectExplorer::Project*)), this,
114 SLOT(projectRemoved(ProjectExplorer::Project*)));
115 connect(session, SIGNAL(startupProjectChanged(ProjectExplorer::Project*)),
116 this, SLOT(projectChanged(ProjectExplorer::Project*)));
118 connect(m_qemuProcess, SIGNAL(error(QProcess::ProcessError)), this,
119 SLOT(qemuProcessError(QProcess::ProcessError)));
120 connect(m_qemuProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this,
121 SLOT(qemuProcessFinished()));
122 connect(m_qemuProcess, SIGNAL(readyReadStandardOutput()), this,
124 connect(m_qemuProcess, SIGNAL(readyReadStandardError()), this,
126 connect(this, SIGNAL(qemuProcessStatus(QemuStatus, QString)),
127 this, SLOT(qemuStatusChanged(QemuStatus, QString)));
129 m_runtimeRootWatcher = new QFileSystemWatcher(this);
130 connect(m_runtimeRootWatcher, SIGNAL(directoryChanged(QString)), this,
131 SLOT(runtimeRootChanged(QString)));
132 m_runtimeFolderWatcher = new QFileSystemWatcher(this);
133 connect(m_runtimeFolderWatcher, SIGNAL(directoryChanged(QString)), this,
134 SLOT(runtimeFolderChanged(QString)));
137 MaemoQemuManager::~MaemoQemuManager()
143 MaemoQemuManager &MaemoQemuManager::instance(QObject *parent)
146 m_instance = new MaemoQemuManager(parent);
150 bool MaemoQemuManager::runtimeForQtVersion(int uniqueId, MaemoQemuRuntime *rt) const
152 *rt = m_runtimes.value(uniqueId, MaemoQemuRuntime());
153 return rt->isValid();
156 bool MaemoQemuManager::qemuIsRunning() const
158 return m_runningQtId != INT_MIN;
161 void MaemoQemuManager::qtVersionsChanged(const QList<int> &uniqueIds)
163 QtVersionManager *manager = QtVersionManager::instance();
164 foreach (int uniqueId, uniqueIds) {
165 if (manager->isValidId(uniqueId)) {
166 QtVersion *version = manager->version(uniqueId);
167 if (version->supportsTargetId(Constants::MAEMO5_DEVICE_TARGET_ID)
168 || version->supportsTargetId(Constants::HARMATTAN_DEVICE_TARGET_ID)
169 || version->supportsTargetId(Constants::MEEGO_DEVICE_TARGET_ID)) {
170 MaemoQemuRuntime runtime
171 = MaemoQemuRuntimeParser::parseRuntime(version);
172 if (runtime.isValid()) {
173 m_runtimes.insert(uniqueId, runtime);
174 if (!m_runtimeRootWatcher->directories().contains(runtime.m_watchPath))
175 m_runtimeRootWatcher->addPath(runtime.m_watchPath);
177 m_runtimes.remove(uniqueId);
181 // this qt version has been removed from the settings
182 m_runtimes.remove(uniqueId);
183 if (uniqueId == m_runningQtId) {
185 emit qemuProcessStatus(QemuUserReason, tr("Qemu has been shut "
186 "down, because you removed the corresponding Qt version."));
191 showOrHideQemuButton();
194 void MaemoQemuManager::projectAdded(ProjectExplorer::Project *project)
196 // handle all target related changes, add, remove, etc...
197 connect(project, SIGNAL(addedTarget(ProjectExplorer::Target*)), this,
198 SLOT(targetAdded(ProjectExplorer::Target*)));
199 connect(project, SIGNAL(removedTarget(ProjectExplorer::Target*)), this,
200 SLOT(targetRemoved(ProjectExplorer::Target*)));
201 connect(project, SIGNAL(activeTargetChanged(ProjectExplorer::Target*)),
202 this, SLOT(targetChanged(ProjectExplorer::Target*)));
204 foreach (Target *target, project->targets())
208 void MaemoQemuManager::projectRemoved(ProjectExplorer::Project *project)
210 disconnect(project, SIGNAL(addedTarget(ProjectExplorer::Target*)), this,
211 SLOT(targetAdded(ProjectExplorer::Target*)));
212 disconnect(project, SIGNAL(removedTarget(ProjectExplorer::Target*)), this,
213 SLOT(targetRemoved(ProjectExplorer::Target*)));
214 disconnect(project, SIGNAL(activeTargetChanged(ProjectExplorer::Target*)),
215 this, SLOT(targetChanged(ProjectExplorer::Target*)));
217 foreach (Target *target, project->targets())
218 targetRemoved(target);
219 showOrHideQemuButton();
222 void MaemoQemuManager::projectChanged(ProjectExplorer::Project *project)
225 toggleStarterButton(project->activeTarget());
226 deviceConfigurationChanged(project->activeTarget());
230 void MaemoQemuManager::targetAdded(ProjectExplorer::Target *target)
232 if (!target || !MaemoGlobal::isMaemoTargetId(target->id()))
235 // handle all run configuration changes, add, remove, etc...
236 connect(target, SIGNAL(addedRunConfiguration(ProjectExplorer::RunConfiguration*)),
237 this, SLOT(runConfigurationAdded(ProjectExplorer::RunConfiguration*)));
238 connect(target, SIGNAL(removedRunConfiguration(ProjectExplorer::RunConfiguration*)),
239 this, SLOT(runConfigurationRemoved(ProjectExplorer::RunConfiguration*)));
240 connect(target, SIGNAL(activeRunConfigurationChanged(ProjectExplorer::RunConfiguration*)),
241 this, SLOT(runConfigurationChanged(ProjectExplorer::RunConfiguration*)));
243 // handle all build configuration changes, add, remove, etc...
244 connect(target, SIGNAL(removedBuildConfiguration(ProjectExplorer::BuildConfiguration*)),
245 this, SLOT(buildConfigurationAdded(ProjectExplorer::BuildConfiguration*)));
246 connect(target, SIGNAL(removedBuildConfiguration(ProjectExplorer::BuildConfiguration*)),
247 this, SLOT(buildConfigurationRemoved(ProjectExplorer::BuildConfiguration*)));
248 connect(target, SIGNAL(activeBuildConfigurationChanged(ProjectExplorer::BuildConfiguration*)),
249 this, SLOT(buildConfigurationChanged(ProjectExplorer::BuildConfiguration*)));
251 // handle the qt version changes the build configuration uses
252 connect(target, SIGNAL(environmentChanged()), this, SLOT(environmentChanged()));
254 foreach (RunConfiguration *rc, target->runConfigurations())
255 toggleDeviceConnections(qobject_cast<MaemoRunConfiguration*> (rc), true);
256 toggleStarterButton(target);
259 void MaemoQemuManager::targetRemoved(ProjectExplorer::Target *target)
261 if (!target || !MaemoGlobal::isMaemoTargetId(target->id()))
264 disconnect(target, SIGNAL(addedRunConfiguration(ProjectExplorer::RunConfiguration*)),
265 this, SLOT(runConfigurationAdded(ProjectExplorer::RunConfiguration*)));
266 disconnect(target, SIGNAL(removedRunConfiguration(ProjectExplorer::RunConfiguration*)),
267 this, SLOT(runConfigurationRemoved(ProjectExplorer::RunConfiguration*)));
268 disconnect(target, SIGNAL(activeRunConfigurationChanged(ProjectExplorer::RunConfiguration*)),
269 this, SLOT(runConfigurationChanged(ProjectExplorer::RunConfiguration*)));
271 disconnect(target, SIGNAL(removedBuildConfiguration(ProjectExplorer::BuildConfiguration*)),
272 this, SLOT(buildConfigurationAdded(ProjectExplorer::BuildConfiguration*)));
273 disconnect(target, SIGNAL(removedBuildConfiguration(ProjectExplorer::BuildConfiguration*)),
274 this, SLOT(buildConfigurationRemoved(ProjectExplorer::BuildConfiguration*)));
275 disconnect(target, SIGNAL(activeBuildConfigurationChanged(ProjectExplorer::BuildConfiguration*)),
276 this, SLOT(buildConfigurationChanged(ProjectExplorer::BuildConfiguration*)));
278 disconnect(target, SIGNAL(environmentChanged()), this, SLOT(environmentChanged()));
280 foreach (RunConfiguration *rc, target->runConfigurations())
281 toggleDeviceConnections(qobject_cast<MaemoRunConfiguration*> (rc), false);
282 showOrHideQemuButton();
285 void MaemoQemuManager::targetChanged(ProjectExplorer::Target *target)
288 toggleStarterButton(target);
289 deviceConfigurationChanged(target);
293 void MaemoQemuManager::runConfigurationAdded(ProjectExplorer::RunConfiguration *rc)
295 if (!rc || !MaemoGlobal::isMaemoTargetId(rc->target()->id()))
297 toggleDeviceConnections(qobject_cast<MaemoRunConfiguration*> (rc), true);
300 void MaemoQemuManager::runConfigurationRemoved(ProjectExplorer::RunConfiguration *rc)
302 if (!rc || !MaemoGlobal::isMaemoTargetId(rc->target()->id()))
304 toggleDeviceConnections(qobject_cast<MaemoRunConfiguration*> (rc), false);
307 void MaemoQemuManager::runConfigurationChanged(ProjectExplorer::RunConfiguration *rc)
310 m_qemuAction->setEnabled(targetUsesMatchingRuntimeConfig(rc->target()));
313 void MaemoQemuManager::buildConfigurationAdded(ProjectExplorer::BuildConfiguration *bc)
315 if (!bc || !MaemoGlobal::isMaemoTargetId(bc->target()->id()))
318 connect(bc, SIGNAL(environmentChanged()), this, SLOT(environmentChanged()));
321 void MaemoQemuManager::buildConfigurationRemoved(ProjectExplorer::BuildConfiguration *bc)
323 if (!bc || !MaemoGlobal::isMaemoTargetId(bc->target()->id()))
326 disconnect(bc, SIGNAL(environmentChanged()), this, SLOT(environmentChanged()));
329 void MaemoQemuManager::buildConfigurationChanged(ProjectExplorer::BuildConfiguration *bc)
332 toggleStarterButton(bc->target());
335 void MaemoQemuManager::environmentChanged()
337 // likely to happen when the qt version changes the build config is using
338 if (ProjectExplorerPlugin *explorer = ProjectExplorerPlugin::instance()) {
339 if (Project *project = explorer->session()->startupProject())
340 toggleStarterButton(project->activeTarget());
344 void MaemoQemuManager::deviceConfigurationChanged(ProjectExplorer::Target *target)
346 m_qemuAction->setEnabled(targetUsesMatchingRuntimeConfig(target));
349 void MaemoQemuManager::startRuntime()
351 m_userTerminated = false;
352 Project *p = ProjectExplorerPlugin::instance()->session()->startupProject();
356 if (!targetUsesMatchingRuntimeConfig(p->activeTarget(), &version)) {
357 qWarning("Strange: Qemu button was enabled, but target does not match.");
361 m_runningQtId = version->uniqueId();
362 const MaemoQemuRuntime rt = m_runtimes.value(version->uniqueId());
363 m_qemuProcess->setProcessEnvironment(rt.environment());
364 m_qemuProcess->setWorkingDirectory(rt.m_root);
365 m_qemuProcess->start(rt.m_bin % QLatin1Char(' ') % rt.m_args);
366 if (!m_qemuProcess->waitForStarted())
369 emit qemuProcessStatus(QemuStarting);
370 connect(m_qemuAction, SIGNAL(triggered()), this, SLOT(terminateRuntime()));
371 disconnect(m_qemuAction, SIGNAL(triggered()), this, SLOT(startRuntime()));
374 void MaemoQemuManager::terminateRuntime()
376 m_userTerminated = true;
378 if (m_qemuProcess->state() != QProcess::NotRunning) {
379 m_qemuProcess->terminate();
380 m_qemuProcess->kill();
383 connect(m_qemuAction, SIGNAL(triggered()), this, SLOT(startRuntime()));
384 disconnect(m_qemuAction, SIGNAL(triggered()), this, SLOT(terminateRuntime()));
387 void MaemoQemuManager::qemuProcessFinished()
389 m_runningQtId = INT_MIN;
390 QemuStatus status = QemuFinished;
393 if (!m_userTerminated) {
394 if (m_qemuProcess->exitStatus() == QProcess::CrashExit) {
395 status = QemuCrashed;
396 error = m_qemuProcess->errorString();
397 } else if (m_qemuProcess->exitCode() != 0) {
398 error = tr("Qemu finished with error: Exit code was %1.")
399 .arg(m_qemuProcess->exitCode());
403 m_userTerminated = false;
404 emit qemuProcessStatus(status, error);
407 void MaemoQemuManager::qemuProcessError(QProcess::ProcessError error)
409 if (error == QProcess::FailedToStart)
410 emit qemuProcessStatus(QemuFailedToStart, m_qemuProcess->errorString());
413 void MaemoQemuManager::qemuStatusChanged(QemuStatus status, const QString &error)
415 bool running = false;
420 case QemuFailedToStart:
421 QMessageBox::warning(0, tr("Qemu error"),
422 tr("Qemu failed to start: %1"));
425 MaemoManager::instance().qemuSettingsPage()->showQemuCrashDialog();
429 if (!error.isEmpty())
430 QMessageBox::warning(0, tr("Qemu error"), error);
433 Q_ASSERT(!"Missing handling of Qemu status");
436 updateStarterIcon(running);
439 void MaemoQemuManager::qemuOutput()
441 qDebug("%s", m_qemuProcess->readAllStandardOutput().data());
442 qDebug("%s", m_qemuProcess->readAllStandardError().data());
445 void MaemoQemuManager::runtimeRootChanged(const QString &directory)
447 QList<int> uniqueIds;
448 QMap<int, MaemoQemuRuntime>::const_iterator it;
449 for (it = m_runtimes.constBegin(); it != m_runtimes.constEnd(); ++it) {
450 if (QDir(it.value().m_watchPath) == QDir(directory))
451 uniqueIds.append(it.key());
454 foreach (int uniqueId, uniqueIds) {
455 MaemoQemuRuntime runtime = m_runtimes.value(uniqueId, MaemoQemuRuntime());
456 if (runtime.isValid()) {
457 if (QFile::exists(runtime.m_root)) {
458 // nothing changed, so we can remove it
459 uniqueIds.removeAll(uniqueId);
462 if (QFile::exists(runtime.m_root)) {
463 if (!QFile::exists(runtime.m_root + QLatin1String("/information"))) {
464 // install might be still in progress
465 uniqueIds.removeAll(uniqueId);
466 m_runtimeFolderWatcher->addPath(runtime.m_root);
474 void MaemoQemuManager::runtimeFolderChanged(const QString &directory)
476 if (QFile::exists(directory + QLatin1String("/information"))) {
477 QList<int> uniqueIds;
478 QMap<int, MaemoQemuRuntime>::const_iterator it;
479 for (it = m_runtimes.constBegin(); it != m_runtimes.constEnd(); ++it) {
480 if (QDir(it.value().m_root) == QDir(directory))
481 uniqueIds.append(it.key());
484 m_runtimeFolderWatcher->removePath(directory);
490 void MaemoQemuManager::updateStarterIcon(bool running)
496 toolTip = tr("Stop Maemo Emulator");
499 toolTip = tr("Start Maemo Emulator");
502 m_qemuAction->setToolTip(toolTip);
503 m_qemuAction->setIcon(m_qemuStarterIcon.pixmap(iconSize, QIcon::Normal,
507 void MaemoQemuManager::toggleStarterButton(Target *target)
511 if (AbstractQt4MaemoTarget *qt4Target = qobject_cast<AbstractQt4MaemoTarget*>(target)) {
512 if (Qt4BuildConfiguration *bc = qt4Target->activeBuildConfiguration()) {
513 if (QtVersion *version = bc->qtVersion())
514 uniqueId = version->uniqueId();
519 if (uniqueId >= 0 && (m_runtimes.isEmpty() || !m_runtimes.contains(uniqueId)))
520 qtVersionsChanged(QList<int>() << uniqueId);
522 bool isRunning = m_qemuProcess->state() != QProcess::NotRunning;
523 if (m_runningQtId == uniqueId)
526 const Project * const p
527 = ProjectExplorerPlugin::instance()->session()->startupProject();
528 const bool qemuButtonEnabled
529 = p && p->activeTarget() && MaemoGlobal::isMaemoTargetId(p->activeTarget()->id())
530 && m_runtimes.value(uniqueId, MaemoQemuRuntime()).isValid()
531 && targetUsesMatchingRuntimeConfig(target) && !isRunning;
532 m_qemuAction->setEnabled(qemuButtonEnabled);
533 showOrHideQemuButton();
536 bool MaemoQemuManager::sessionHasMaemoTarget() const
538 ProjectExplorerPlugin *explorer = ProjectExplorerPlugin::instance();
539 const QList<Project*> &projects = explorer->session()->projects();
540 foreach (const Project *p, projects) {
541 foreach (const Target * const target, p->targets()) {
542 if (MaemoGlobal::isMaemoTargetId(target->id()))
549 bool MaemoQemuManager::targetUsesMatchingRuntimeConfig(Target *target,
550 QtVersion **qtVersion)
555 MaemoRunConfiguration *mrc =
556 qobject_cast<MaemoRunConfiguration *> (target->activeRunConfiguration());
559 Qt4BuildConfiguration *bc
560 = qobject_cast<Qt4BuildConfiguration *>(target->activeBuildConfiguration());
563 QtVersion *version = bc->qtVersion();
564 if (!version || !m_runtimes.value(version->uniqueId(), MaemoQemuRuntime()).isValid())
568 *qtVersion = version;
569 const MaemoDeviceConfig::ConstPtr &config = mrc->deviceConfig();
570 return config && config->type() == MaemoDeviceConfig::Emulator;
573 void MaemoQemuManager::notify(const QList<int> uniqueIds)
575 qtVersionsChanged(uniqueIds);
576 environmentChanged(); // to toggle the start button
579 void MaemoQemuManager::toggleDeviceConnections(MaemoRunConfiguration *mrc,
585 if (_connect) { // handle device configuration changes
586 connect(mrc, SIGNAL(deviceConfigurationChanged(ProjectExplorer::Target*)),
587 this, SLOT(deviceConfigurationChanged(ProjectExplorer::Target*)));
589 disconnect(mrc, SIGNAL(deviceConfigurationChanged(ProjectExplorer::Target*)),
590 this, SLOT(deviceConfigurationChanged(ProjectExplorer::Target*)));
594 void MaemoQemuManager::showOrHideQemuButton()
596 const bool showButton = !m_runtimes.isEmpty() && sessionHasMaemoTarget();
599 m_qemuAction->setVisible(showButton);