~alan-griffiths/qtmir/migrate-to-mir-Server-API

« back to all changes in this revision

Viewing changes to src/modules/Unity/Application/application_manager.cpp

  • Committer: Gerry Boland
  • Date: 2014-07-01 13:38:06 UTC
  • mto: This revision was merged to the branch mainline in revision 158.
  • Revision ID: gerry.boland@canonical.com-20140701133806-lwfnplkijthydzj4
Update QML plugin: rename to Unity.Application, merge latest unity-mir changes, add tests (not passing yet) and use category logging

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
#include "application.h"
20
20
#include "desktopfilereader.h"
21
21
#include "dbuswindowstack.h"
22
 
 
23
 
// unity-mir
 
22
#include "proc_info.h"
 
23
#include "taskcontroller.h"
 
24
#include "upstart/applicationcontroller.h"
 
25
 
 
26
 
 
27
// mirserver
24
28
#include "mirserverconfiguration.h"
25
29
#include "nativeinterface.h"
26
30
#include "sessionlistener.h"
39
43
 
40
44
namespace ms = mir::scene;
41
45
 
 
46
Q_LOGGING_CATEGORY(QTMIR_APPLICATIONS, "qtmir.applications")
 
47
 
42
48
using namespace unity::shell::application;
43
49
 
44
 
ApplicationManager *ApplicationManager::the_application_manager = nullptr;
45
 
 
46
 
ApplicationManager* ApplicationManager::singleton()
 
50
namespace qtmir
47
51
{
48
 
    if (!the_application_manager) {
49
 
        the_application_manager = new ApplicationManager();
 
52
 
 
53
namespace {
 
54
 
 
55
// FIXME: To be removed once shell has fully adopted short appIds!!
 
56
QString toShortAppIdIfPossible(const QString &appId) {
 
57
    QRegExp longAppIdMask("[a-z0-9][a-z0-9+.-]+_[a-zA-Z0-9+.-]+_[0-9][a-zA-Z0-9.+:~-]*");
 
58
    if (longAppIdMask.exactMatch(appId)) {
 
59
        qWarning() << "WARNING: long App ID encountered:" << appId;
 
60
        // input string a long AppId, chop the version string off the end
 
61
        QStringList parts = appId.split("_");
 
62
        return QString("%1_%2").arg(parts.first()).arg(parts.at(1));
50
63
    }
51
 
    return the_application_manager;
52
 
}
53
 
 
54
 
ApplicationManager::ApplicationManager(QObject *parent)
55
 
:   ApplicationManagerInterface(parent)
56
 
,   m_focusedApplication(nullptr)
57
 
,   m_applicationToBeFocused(nullptr)
58
 
,   m_lifecycleExceptions(QStringList() << "com.ubuntu.music")
59
 
,   m_taskController(TaskController::singleton())
60
 
,   m_fenceNext(false)
61
 
{
62
 
    DLOG("ApplicationManager::ApplicationManager (this=%p)", this);
63
 
 
64
 
    m_roleNames.insert(RoleSurface, "surface");
65
 
    m_roleNames.insert(RoleFullscreen, "fullscreen");
66
 
 
 
64
    return appId;
 
65
}
 
66
 
 
67
void connectToSessionListener(ApplicationManager *manager, SessionListener *listener)
 
68
{
 
69
    QObject::connect(listener, &SessionListener::sessionStarting,
 
70
                     manager, &ApplicationManager::onSessionStarting);
 
71
    QObject::connect(listener, &SessionListener::sessionStopping,
 
72
                     manager, &ApplicationManager::onSessionStopping);
 
73
    QObject::connect(listener, &SessionListener::sessionCreatedSurface,
 
74
                     manager, &ApplicationManager::onSessionCreatedSurface);
 
75
}
 
76
 
 
77
void connectToSessionAuthorizer(ApplicationManager *manager, SessionAuthorizer *authorizer)
 
78
{
 
79
    QObject::connect(authorizer, &SessionAuthorizer::requestAuthorizationForSession,
 
80
                     manager, &ApplicationManager::authorizeSession, Qt::BlockingQueuedConnection);
 
81
}
 
82
 
 
83
void connectToTaskController(ApplicationManager *manager, TaskController *controller)
 
84
{
 
85
    QObject::connect(controller, &TaskController::processStarting,
 
86
                     manager, &ApplicationManager::onProcessStarting);
 
87
    QObject::connect(controller, &TaskController::processStopped,
 
88
                     manager, &ApplicationManager::onProcessStopped);
 
89
    QObject::connect(controller, &TaskController::processFailed,
 
90
                     manager, &ApplicationManager::onProcessFailed);
 
91
    QObject::connect(controller, &TaskController::requestFocus,
 
92
                     manager, &ApplicationManager::onFocusRequested);
 
93
    QObject::connect(controller, &TaskController::requestResume,
 
94
                     manager, &ApplicationManager::onResumeRequested);
 
95
}
 
96
 
 
97
} // namespace
 
98
 
 
99
ApplicationManager* ApplicationManager::Factory::Factory::create()
 
100
{
67
101
    NativeInterface *nativeInterface = dynamic_cast<NativeInterface*>(QGuiApplication::platformNativeInterface());
68
102
 
69
 
    m_mirConfig = nativeInterface->m_mirConfig;
70
 
 
71
103
    if (!nativeInterface) {
72
 
        LOG("ERROR: Unity.Application QML plugin requires use of the 'mirserver' QPA plugin");
 
104
        qCritical() << "ERROR: Unity.Application QML plugin requires use of the 'mirserver' QPA plugin";
73
105
        QGuiApplication::quit();
74
 
        return;
 
106
        return nullptr;
75
107
    }
76
108
 
 
109
    auto mirConfig = nativeInterface->m_mirConfig;
 
110
 
77
111
    SessionListener *sessionListener = static_cast<SessionListener*>(nativeInterface->nativeResourceForIntegration("SessionListener"));
78
112
    SessionAuthorizer *sessionAuthorizer = static_cast<SessionAuthorizer*>(nativeInterface->nativeResourceForIntegration("SessionAuthorizer"));
79
 
    qDebug() << sessionListener << sessionAuthorizer;
80
 
    QObject::connect(sessionListener, &SessionListener::sessionStarting,
81
 
                     this, &ApplicationManager::onSessionStarting);
82
 
    QObject::connect(sessionListener, &SessionListener::sessionStopping,
83
 
                     this, &ApplicationManager::onSessionStopping);
84
 
    QObject::connect(sessionListener, &SessionListener::sessionFocused,
85
 
                     this, &ApplicationManager::onSessionFocused);
86
 
    QObject::connect(sessionListener, &SessionListener::sessionUnfocused,
87
 
                     this, &ApplicationManager::onSessionUnfocused);
88
 
    QObject::connect(sessionListener, &SessionListener::sessionCreatedSurface,
89
 
                     this, &ApplicationManager::onSessionCreatedSurface);
90
 
    QObject::connect(sessionAuthorizer, &SessionAuthorizer::requestAuthorizationForSession,
91
 
                     this, &ApplicationManager::authorizeSession, Qt::BlockingQueuedConnection);
92
 
 
93
 
    QObject::connect(m_taskController.data(), &TaskController::processStartReport,
94
 
                     this, &ApplicationManager::onProcessStartReportReceived);
95
 
    QObject::connect(m_taskController.data(), &TaskController::processStopped,
96
 
                     this, &ApplicationManager::onProcessStopped);
97
 
    QObject::connect(m_taskController.data(), &TaskController::requestFocus,
98
 
                     this, &ApplicationManager::onFocusRequested);
99
 
    QObject::connect(m_taskController.data(), &TaskController::requestResume,
100
 
                     this, &ApplicationManager::onResumeRequested);
101
 
 
102
 
    m_dbusWindowStack = new DBusWindowStack(this);
 
113
 
 
114
    QSharedPointer<upstart::ApplicationController> appController(new upstart::ApplicationController());
 
115
    QSharedPointer<TaskController> taskController(new TaskController(nullptr, appController));
 
116
    QSharedPointer<DesktopFileReader::Factory> fileReaderFactory(new DesktopFileReader::Factory());
 
117
    QSharedPointer<ProcInfo> procInfo(new ProcInfo());
 
118
 
 
119
    // FIXME: We should use a QSharedPointer to wrap this ApplicationManager object, which requires us
 
120
    // to use the data() method to pass the raw pointer to the QML engine. However the QML engine appears
 
121
    // to take ownership of the object, and deletes it when it wants to. This conflicts with the purpose
 
122
    // of the QSharedPointer, and a double-delete results. Trying QQmlEngine::setObjectOwnership on the
 
123
    // object no effect, which it should. Need to investigate why.
 
124
    ApplicationManager* appManager = new ApplicationManager(
 
125
                                             mirConfig,
 
126
                                             taskController,
 
127
                                             fileReaderFactory,
 
128
                                             procInfo
 
129
                                         );
 
130
 
 
131
    connectToSessionListener(appManager, sessionListener);
 
132
    connectToSessionAuthorizer(appManager, sessionAuthorizer);
 
133
    connectToTaskController(appManager, taskController.data());
 
134
 
 
135
    return appManager;
 
136
}
 
137
 
 
138
 
 
139
ApplicationManager* ApplicationManager::singleton()
 
140
{
 
141
    static ApplicationManager* instance;
 
142
    if (!instance) {
 
143
        Factory appFactory;
 
144
        instance = appFactory.create();
 
145
    }
 
146
    return instance;
 
147
}
 
148
 
 
149
ApplicationManager::ApplicationManager(
 
150
        const QSharedPointer<MirServerConfiguration>& mirConfig,
 
151
        const QSharedPointer<TaskController>& taskController,
 
152
        const QSharedPointer<DesktopFileReader::Factory>& desktopFileReaderFactory,
 
153
        const QSharedPointer<ProcInfo>& procInfo,
 
154
        QObject *parent)
 
155
    : ApplicationManagerInterface(parent)
 
156
    , m_mirConfig(mirConfig)
 
157
    , m_focusedApplication(nullptr)
 
158
    , m_mainStageApplication(nullptr)
 
159
    , m_sideStageApplication(nullptr)
 
160
    , m_msApplicationToBeFocused(nullptr)
 
161
    , m_ssApplicationToBeFocused(nullptr)
 
162
    , m_lifecycleExceptions(QStringList() << "com.ubuntu.music")
 
163
    , m_dbusWindowStack(new DBusWindowStack(this))
 
164
    , m_taskController(taskController)
 
165
    , m_desktopFileReaderFactory(desktopFileReaderFactory)
 
166
    , m_procInfo(procInfo)
 
167
    , m_fenceNext(false)
 
168
    , m_suspended(false)
 
169
{
 
170
    qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::ApplicationManager (this=%p)" << this;
 
171
 
 
172
    m_roleNames.insert(RoleSurface, "surface");
 
173
    m_roleNames.insert(RoleFullscreen, "fullscreen");
103
174
}
104
175
 
105
176
ApplicationManager::~ApplicationManager()
106
177
{
107
 
    DLOG("ApplicationManager::~ApplicationManager");
 
178
    qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::~ApplicationManager";
108
179
}
109
180
 
110
181
int ApplicationManager::rowCount(const QModelIndex &parent) const
147
218
 
148
219
Application* ApplicationManager::get(int index) const
149
220
{
150
 
    DLOG("ApplicationManager::get (this=%p, index=%i, count=%i)", this, index, m_applications.count());
 
221
    qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::get - index=" << index  << "count=" << m_applications.count();
151
222
    if (index < 0 || index >= m_applications.count()) {
152
223
        return nullptr;
153
224
    }
154
225
    return m_applications.at(index);
155
226
}
156
227
 
157
 
Application* ApplicationManager::findApplication(const QString &appId) const
 
228
Application* ApplicationManager::findApplication(const QString &inputAppId) const
158
229
{
 
230
    const QString appId = toShortAppIdIfPossible(inputAppId);
 
231
 
159
232
    for (Application *app : m_applications) {
160
233
        if (app->appId() == appId) {
161
234
            return app;
164
237
    return nullptr;
165
238
}
166
239
 
167
 
bool ApplicationManager::requestFocusApplication(const QString &appId)
 
240
bool ApplicationManager::requestFocusApplication(const QString &inputAppId)
168
241
{
169
 
    DLOG("ApplicationManager::requestFocusApplication (this=%p, appId=%s)", this, qPrintable(appId));
 
242
    const QString appId = toShortAppIdIfPossible(inputAppId);
 
243
 
 
244
    qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::requestFocusApplication - appId=" << appId;
170
245
    Application *application = findApplication(appId);
171
246
 
172
247
    if (!application) {
173
 
        DLOG("No such running application '%s'", qPrintable(appId));
 
248
        qDebug() << "No such running application with appId=" << appId;
174
249
        return false;
175
250
    }
176
251
 
177
252
    if (application == m_focusedApplication) {
178
 
        DLOG("Application %s is already focused", qPrintable(appId));
179
253
        return true;
180
254
    }
181
255
 
213
287
    Q_EMIT suspendedChanged();
214
288
 
215
289
    if (m_suspended) {
216
 
        suspendApplication(m_focusedApplication);
 
290
        suspendApplication(m_mainStageApplication);
 
291
        suspendApplication(m_sideStageApplication);
217
292
    } else {
218
 
        resumeApplication(m_focusedApplication);
 
293
        resumeApplication(m_mainStageApplication);
 
294
        resumeApplication(m_sideStageApplication);
219
295
    }
220
296
}
221
297
 
222
 
void ApplicationManager::suspendApplication(Application *application)
 
298
bool ApplicationManager::suspendApplication(Application *application)
223
299
{
224
300
    if (application == nullptr)
225
 
        return;
226
 
    DLOG("ApplicationManager::suspendApplication (appId=%s)", qPrintable(application->appId()));
 
301
        return false;
 
302
    qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::suspendApplication - appId=" << application->appId();
227
303
 
228
304
    updateScreenshot(application->appId());
229
305
 
230
306
    // Present in exceptions list, return.
231
307
    if (!m_lifecycleExceptions.filter(application->appId().section('_',0,0)).empty())
232
 
        return;
 
308
        return false;
233
309
 
234
310
    if (application->state() == Application::Running)
235
311
        application->setState(Application::Suspended);
 
312
 
 
313
    return true;
236
314
}
237
315
 
238
316
void ApplicationManager::resumeApplication(Application *application)
239
317
{
240
318
    if (application == nullptr)
241
319
        return;
 
320
    qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::resumeApplication - appId=" << application->appId();
242
321
 
243
322
    if (application->state() != Application::Running)
244
323
        application->setState(Application::Running);
245
324
}
246
325
 
247
 
bool ApplicationManager::focusApplication(const QString &appId)
 
326
bool ApplicationManager::focusApplication(const QString &inputAppId)
248
327
{
249
 
    DLOG("ApplicationManager::focusApplication (this=%p, appId=%s)", this, qPrintable(appId));
 
328
    const QString appId = toShortAppIdIfPossible(inputAppId);
 
329
    qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::focusApplication - appId=" << appId;
250
330
    Application *application = findApplication(appId);
251
331
 
252
332
    if (!application) {
253
 
        DLOG("No such running application '%s'", qPrintable(appId));
 
333
        qDebug() << "No such running application with appId=" << appId;
254
334
        return false;
255
335
    }
256
336
 
257
 
    m_applicationToBeFocused = application;
 
337
    if (application->stage() == Application::MainStage && m_sideStageApplication)
 
338
        suspendApplication(m_sideStageApplication);
 
339
 
 
340
    if (application->stage() == Application::MainStage)
 
341
        m_msApplicationToBeFocused = application;
 
342
    else
 
343
        m_ssApplicationToBeFocused = application;
258
344
 
259
345
    if (application->state() == Application::Stopped) {
260
346
        // Respawning this app, move to end of application list so onSessionStarting works ok
264
350
        move(from, m_applications.length()-1);
265
351
    } else {
266
352
        if (application->session()) {
267
 
            m_mirConfig->the_focus_controller()->set_focus_to(application->session());
268
353
            int from = m_applications.indexOf(application);
269
354
            move(from, 0);
270
355
        }
271
356
    }
272
357
 
273
358
    setFocused(application);
274
 
    QModelIndex appIndex = findIndex(application);
275
 
    Q_EMIT dataChanged(appIndex, appIndex, QVector<int>() << RoleFocused);
276
359
 
277
360
    // FIXME(dandrader): lying here. The operation is async. So we will only know whether
278
361
    // the focusing was successful once the server replies. Maybe the API in unity-api should
282
365
 
283
366
void ApplicationManager::unfocusCurrentApplication()
284
367
{
285
 
    DLOG("ApplicationManager::unfocusCurrentApplication (this=%p)", this);
286
 
 
287
 
    m_applicationToBeFocused = nullptr;
288
 
 
289
 
    m_mirConfig->the_focus_controller()->set_focus_to(NULL); //FIXME(greyback)
 
368
    qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::unfocusCurrentApplication";
 
369
 
 
370
    suspendApplication(m_sideStageApplication);
 
371
    suspendApplication(m_mainStageApplication);
 
372
 
 
373
    // Clear both stages
 
374
    m_msApplicationToBeFocused = nullptr;
 
375
    m_ssApplicationToBeFocused = nullptr;
290
376
}
291
377
 
 
378
/**
 
379
 * @brief ApplicationManager::startApplication launches an application identified by an "application id" or appId.
 
380
 *
 
381
 * Note: due to an implementation detail, appIds come in two forms:
 
382
 * * long appId: $(click_package)_$(application)_$(version)
 
383
 * * short appId: $(click_package)_$(application)
 
384
 * It is expected that the shell uses _only_ short appIds (but long appIds are accepted by this method for legacy
 
385
 * reasons - but be warned, this ability will be removed)
 
386
 *
 
387
 * Unless stated otherwise, we always use short appIds in this API.
 
388
 *
 
389
 * @param inputAppId AppId of application to launch (long appId supported)
 
390
 * @param arguments Command line arguments to pass to the application to be launched
 
391
 * @return Pointer to Application object representing the launched process. If process already running, return nullptr
 
392
 */
292
393
Application* ApplicationManager::startApplication(const QString &appId,
293
394
                                                  const QStringList &arguments)
294
395
{
295
396
    return startApplication(appId, NoFlag, arguments);
296
397
}
297
398
 
298
 
Application *ApplicationManager::startApplication(const QString &appId, ExecFlags flags,
 
399
Application *ApplicationManager::startApplication(const QString &inputAppId, ExecFlags flags,
299
400
                                                  const QStringList &arguments)
300
401
{
301
 
    DLOG("ApplicationManager::startApplication (this=%p, appId=%s)", this, qPrintable(appId));
 
402
    QString appId = toShortAppIdIfPossible(inputAppId);
 
403
    qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::startApplication - this=" << this << "appId" << qPrintable(appId);
 
404
 
 
405
    Application *application = findApplication(appId);
 
406
    if (application) {
 
407
        qWarning() << "ApplicationManager::startApplication - application appId=" << appId << " already exists";
 
408
        return nullptr;
 
409
    }
302
410
 
303
411
    if (!m_taskController->start(appId, arguments)) {
304
 
        LOG("Asking Upstart to start application '%s' failed", qPrintable(appId));
 
412
        qWarning() << "Upstart failed to start application with appId" << appId;
305
413
        return nullptr;
306
414
    }
307
415
 
308
 
    Application* application = new Application(appId, Application::Starting, arguments, this);
 
416
    application = new Application(
 
417
                m_taskController,
 
418
                m_desktopFileReaderFactory->createInstance(appId, m_taskController->findDesktopFileForAppId(appId)),
 
419
                Application::Starting,
 
420
                arguments,
 
421
                this);
 
422
 
309
423
    if (!application->isValid()) {
310
 
        DLOG("Unable to instantiate application with appId '%s'", qPrintable(appId));
 
424
        qWarning() << "Unable to instantiate application with appId" << appId;
311
425
        return nullptr;
312
426
    }
313
427
 
320
434
    return application;
321
435
}
322
436
 
323
 
void ApplicationManager::onProcessStartReportReceived(const QString &appId, const bool failure)
 
437
void ApplicationManager::onProcessStarting(const QString &appId)
324
438
{
325
 
    DLOG("ApplicationManager::onProcessStartReportReceived (this=%p, appId=%s, failure=%c)",
326
 
         this, qPrintable(appId), (failure) ? 'Y' : 'N');
327
 
 
328
 
    if (failure) {
329
 
        onProcessStopped(appId, true);
330
 
    }
 
439
    qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onProcessStarting - appId=" << appId;
331
440
 
332
441
    Application *application = findApplication(appId);
 
442
    if (!application) { // then shell did not start this application, so ubuntu-app-launch must have - add to list
 
443
        application = new Application(
 
444
                    m_taskController,
 
445
                    m_desktopFileReaderFactory->createInstance(appId, m_taskController->findDesktopFileForAppId(appId)),
 
446
                    Application::Starting,
 
447
                    QStringList(), this);
333
448
 
334
 
    if (!application) { // if shell did not start this application, but ubuntu-app-launch did
335
 
        application = new Application(appId, Application::Starting, QStringList(), this);
336
449
        if (!application->isValid()) {
337
 
            DLOG("Unable to instantiate application with appId '%s'", qPrintable(appId));
 
450
            qWarning() << "Unable to instantiate application with appId" << appId;
338
451
            return;
339
452
        }
 
453
 
340
454
        add(application);
341
455
        Q_EMIT focusRequested(appId);
342
456
    }
 
457
    else {
 
458
        qWarning() << "ApplicationManager::onProcessStarting application already found with appId" << appId;
 
459
    }
343
460
}
344
461
 
345
 
bool ApplicationManager::stopApplication(const QString &appId)
 
462
/**
 
463
 * @brief ApplicationManager::stopApplication - stop a running application and remove from list
 
464
 * @param inputAppId
 
465
 * @return True if running application was stopped, false if application did not exist or could not be stopped
 
466
 */
 
467
bool ApplicationManager::stopApplication(const QString &inputAppId)
346
468
{
347
 
    DLOG("ApplicationManager::stopApplication (this=%p, appId=%s)", this, qPrintable(appId));
 
469
    const QString appId = toShortAppIdIfPossible(inputAppId);
 
470
    qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::stopApplication - appId=" << appId;
348
471
 
349
472
    Application *application = findApplication(appId);
350
 
 
351
473
    if (!application) {
352
 
        DLOG("No such running application '%s'", qPrintable(appId));
 
474
        qCritical() << "No such running application with appId" << appId;
353
475
        return false;
354
476
    }
355
477
 
356
478
    if (application == m_focusedApplication) {
357
479
        // TODO(greyback) What to do?? Focus next app, or unfocus everything??
358
 
        m_focusedApplication = NULL;
 
480
        m_focusedApplication = nullptr;
359
481
        Q_EMIT focusedApplicationIdChanged();
360
482
    }
361
483
 
362
484
    remove(application);
363
 
    m_dbusWindowStack->WindowDestroyed(0, application->appId());
364
 
 
365
 
    bool result = m_taskController->stop(application->appId());
366
 
 
367
 
    LOG_IF(result == false, "FAILED to ask Upstart to stop application '%s'", qPrintable(application->appId()));
 
485
    m_dbusWindowStack->WindowDestroyed(0, appId);
 
486
 
 
487
    bool result = m_taskController->stop(application->longAppId());
 
488
 
 
489
    if (!result && application->pid() > 0) {
 
490
        qWarning() << "FAILED to ask Upstart to stop application with appId" << appId
 
491
                   << "Sending SIGTERM to process:" << application->pid();
 
492
        kill(application->pid(), SIGTERM);
 
493
        result = true;
 
494
    }
 
495
 
368
496
    delete application;
369
 
 
370
 
    // FIXME(dandrader): lying here. The operation is async. So we will only know whether
371
 
    // the focusing was successful once the server replies. Maybe the API in unity-api should
372
 
    // reflect that?
373
497
    return result;
374
498
}
375
499
 
377
501
{
378
502
    Application *application = findApplication(appId);
379
503
    if (!application) {
380
 
        DLOG("No such running application '%s'", qPrintable(appId));
 
504
        qWarning() << "ApplicationManager::updateScreenshot - No such running application with appId=" << appId;
381
505
        return false;
382
506
    }
383
507
 
387
511
    return true;
388
512
}
389
513
 
390
 
void ApplicationManager::onProcessStopped(const QString &appId, const bool unexpected)
391
 
{
392
 
    Application *application = findApplication(appId);
 
514
void ApplicationManager::onProcessFailed(const QString &appId, const bool duringStartup)
 
515
{
 
516
    /* Applications fail if they fail to launch, crash or are killed. If failed to start, must
 
517
     * immediately remove from list of applications. If crash or kill, instead we set flag on the
 
518
     * Application to indicate it can be resumed.
 
519
     */
 
520
 
 
521
    qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onProcessStopped - appId=" << appId << "duringStartup=" << duringStartup;
 
522
 
 
523
    Application *application = findApplication(appId);
 
524
    if (!application) {
 
525
        qWarning() << "ApplicationManager::onProcessFailed - upstart reports failure of application" << appId
 
526
                   << "that AppManager is not managing";
 
527
        return;
 
528
    }
 
529
 
 
530
    Q_UNUSED(duringStartup); // FIXME(greyback) upstart reports app that fully started up & crashes as failing during startup??
 
531
    if (application->state() == Application::Starting) {
 
532
        if (application == m_focusedApplication) {
 
533
            m_focusedApplication = nullptr;
 
534
            Q_EMIT focusedApplicationIdChanged();
 
535
        }
 
536
        remove(application);
 
537
        m_dbusWindowStack->WindowDestroyed(0, application->appId());
 
538
        delete application;
 
539
    } else {
 
540
        // We need to set flags on the Application to say the app can be resumed, and thus should not be removed
 
541
        // from the list by onProcessStopped.
 
542
        application->setCanBeResumed(true);
 
543
        application->setPid(0);
 
544
    }
 
545
}
 
546
 
 
547
void ApplicationManager::onProcessStopped(const QString &appId)
 
548
{
 
549
    qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onProcessStopped - appId=" << appId;
 
550
    Application *application = findApplication(appId);
 
551
 
 
552
    if (!application) {
 
553
        qDebug() << "ApplicationManager::onProcessStopped reports stop of appId=" << appId
 
554
                 << "which AppMan is not managing, ignoring the event";
 
555
        return;
 
556
    }
393
557
 
394
558
    // if shell did not stop the application, but ubuntu-app-launch says it died, we assume the process has been
395
559
    // killed, so it can be respawned later. Only exception is if that application is focused or running
396
560
    // as then it most likely crashed. Update this logic when ubuntu-app-launch gives some failure info.
397
 
    if (application) {
398
 
        bool removeApplication = false;
399
 
 
400
 
        if (application == m_focusedApplication) {
401
 
            // Very bad case where focused application dies. Remove from list. Should give error message
402
 
            m_focusedApplication = nullptr;
403
 
            Q_EMIT focusedApplicationIdChanged();
404
 
            removeApplication = true;
405
 
        }
406
 
 
407
 
        if (application->state() == Application::Running || application->state() == Application::Starting) {
408
 
            // Application probably crashed, else OOM killer struck. Either way state wasn't saved
409
 
            // so just remove application
410
 
            removeApplication = true;
411
 
        } else if (application->state() == Application::Suspended) {
412
 
            application->setState(Application::Stopped);
413
 
            application->setSession(nullptr);
414
 
        }
415
 
 
416
 
        if (removeApplication) {
417
 
            remove(application);
418
 
            m_dbusWindowStack->WindowDestroyed(0, application->appId());
419
 
            delete application;
420
 
        }
421
 
    }
422
 
 
423
 
    if (unexpected) {
424
 
        // TODO: pop up a message box/notification?
425
 
        LOG("ApplicationManager: application '%s' died unexpectedly!", qPrintable(appId));
 
561
    bool removeApplication = true;
 
562
 
 
563
    if (application == m_focusedApplication) {
 
564
        // Very bad case where focused application dies. Remove from list. Should give error message
 
565
        m_focusedApplication = nullptr;
 
566
        Q_EMIT focusedApplicationIdChanged();
 
567
    }
 
568
 
 
569
    // The following scenario is the only time that we do NOT remove the application from the app list:
 
570
    if ((application->state() == Application::Suspended || application->state() == Application::Stopped)
 
571
            && application->pid() == 0 // i.e. onProcessFailed was called, which resets the PID of this application
 
572
            && application->canBeResumed()) {
 
573
        removeApplication = false;
 
574
    }
 
575
 
 
576
    if (removeApplication) {
 
577
        qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onProcessStopped - removing appId=" << appId;
 
578
        remove(application);
 
579
        m_dbusWindowStack->WindowDestroyed(0, application->appId());
 
580
        delete application;
426
581
    }
427
582
}
428
583
 
429
584
void ApplicationManager::onFocusRequested(const QString& appId)
430
585
{
431
 
    DLOG("ApplicationManager::onFocusRequested (this=%p, appId=%s)", this, qPrintable(appId));
 
586
    qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onFocusRequested - appId=" << appId;
432
587
 
433
588
    Q_EMIT focusRequested(appId);
434
589
}
435
590
 
436
591
void ApplicationManager::onResumeRequested(const QString& appId)
437
592
{
438
 
    DLOG("ApplicationManager::onResumeRequested (this=%p, appId=%s)", this, qPrintable(appId));
 
593
    qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onResumeRequested - appId=" << appId;
439
594
 
440
595
    Application *application = findApplication(appId);
441
596
 
442
597
    if (!application) {
443
 
        DLOG("ApplicationManager::onResumeRequested: No such running application '%s'", qPrintable(appId));
 
598
        qCritical() << "ApplicationManager::onResumeRequested: No such running application" << appId;
444
599
        return;
445
600
    }
446
601
 
453
608
 
454
609
void ApplicationManager::screenshotUpdated()
455
610
{
456
 
    Application *application = static_cast<Application*>(sender());
457
 
    QModelIndex appIndex = findIndex(application);
458
 
    Q_EMIT dataChanged(appIndex, appIndex, QVector<int>() << RoleScreenshot);
459
 
 
460
 
    DLOG("updated screenshot for '%s'", qPrintable(application->appId()));
461
 
 
462
 
    if (!m_nextFocusedAppId.isEmpty()) {
463
 
        Q_EMIT focusRequested(m_nextFocusedAppId);
464
 
        m_nextFocusedAppId.clear();
 
611
    if (sender()) {
 
612
        Application *application = static_cast<Application*>(sender());
 
613
        QModelIndex appIndex = findIndex(application);
 
614
        Q_EMIT dataChanged(appIndex, appIndex, QVector<int>() << RoleScreenshot);
 
615
 
 
616
        qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::screenshotUpdated: Received new screenshot for", application->appId();
 
617
 
 
618
        if (!m_nextFocusedAppId.isEmpty()) {
 
619
            Q_EMIT focusRequested(m_nextFocusedAppId);
 
620
            m_nextFocusedAppId.clear();
 
621
        }
 
622
    } else {
 
623
        qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::screenshotUpdated: Received screenshotUpdated signal but application has disappeard.";
465
624
    }
466
625
}
467
626
 
468
 
/************************************* Mir-side methods *************************************/
469
 
 
470
627
void ApplicationManager::authorizeSession(const quint64 pid, bool &authorized)
471
628
{
472
629
    authorized = false; //to be proven wrong
473
630
 
474
 
    DLOG("ApplicationManager::authorizeSession (this=%p, pid=%lld)", this, pid);
 
631
    qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::authorizeSession - pid=" << pid;
475
632
 
476
633
    for (Application *app : m_applications) {
477
634
        if (app->state() == Application::Starting
483
640
    }
484
641
 
485
642
    /*
486
 
     * Hack: Allow applications to be launched externally, but must be executed with the
487
 
     * "desktop_file_hint" parameter attached. This exists until ubuntu-app-launch can
488
 
     * notify shell it is starting an application and so shell should allow it. Also reads
489
 
     * the --stage parameter to determine the desired stage
 
643
     * Hack: Allow applications to be launched without being managed by upstart, where AppManager
 
644
     * itself manages processes executed with a "--desktop_file_hint=/path/to/desktopFile.desktop"
 
645
     * parameter attached. This exists until ubuntu-app-launch can notify shell any application is
 
646
     * and so shell should allow it. Also reads the --stage parameter to determine the desired stage
490
647
     */
491
 
    QFile cmdline(QString("/proc/%1/cmdline").arg(pid));
492
 
    if (!cmdline.open(QIODevice::ReadOnly | QIODevice::Text)) {
493
 
        DLOG("ApplicationManager REJECTED connection from app with pid %lld as unable to read process command", pid);
 
648
    std::unique_ptr<ProcInfo::CommandLine> info = m_procInfo->commandLine(pid);
 
649
    if (!info) {
 
650
        qWarning() << "ApplicationManager REJECTED connection from app with pid" << pid
 
651
                   << "as unable to read the process command line";
494
652
        return;
495
653
    }
496
654
 
497
 
    QByteArray command = cmdline.readLine().replace('\0', ' ');
498
 
 
499
 
    // FIXME: special exception for the OSK - maliit-server - not very secure
500
 
    if (command.startsWith("maliit-server") || command.startsWith("/usr/lib/arm-linux-gnueabihf/qt5/libexec/QtWebProcess")
501
 
        || command.startsWith("/usr/bin/signon-ui")) {
 
655
    if (info->startsWith("maliit-server") || info->contains("qt5/libexec/QtWebProcess")) {
502
656
        authorized = true;
503
 
        m_fenceNext = true;
504
 
        return;
505
 
    }
506
 
 
507
 
    QString pattern = QRegularExpression::escape("--desktop_file_hint=") + "(\\S+)";
508
 
    QRegularExpression regExp(pattern);
509
 
    QRegularExpressionMatch regExpMatch = regExp.match(command);
510
 
 
511
 
    if (!regExpMatch.hasMatch()) {
512
 
        LOG("ApplicationManager REJECTED connection from app with pid %lld as no desktop_file_hint specified", pid);
513
 
        return;
514
 
    }
515
 
 
516
 
    QString desktopFileName = regExpMatch.captured(1);
517
 
    DLOG("Process supplied desktop_file_hint, loading '%s'", desktopFileName.toLatin1().data());
 
657
        return;
 
658
    }
 
659
 
 
660
    boost::optional<QString> desktopFileName{ info->getParameter("--desktop_file_hint=") };
 
661
 
 
662
    if (!desktopFileName) {
 
663
        qCritical() << "ApplicationManager REJECTED connection from app with pid" << pid
 
664
                    << "as no desktop_file_hint specified";
 
665
        return;
 
666
    }
 
667
 
 
668
    qCDebug(QTMIR_APPLICATIONS) << "Process supplied desktop_file_hint, loading" << desktopFileName;
 
669
 
 
670
    // Guess appId from the desktop file hint
 
671
    QString appId = toShortAppIdIfPossible(desktopFileName.get().remove(QRegExp(".desktop$")).split('/').last());
518
672
 
519
673
    // FIXME: right now we support --desktop_file_hint=appId for historical reasons. So let's try that in
520
674
    // case we didn't get an existing .desktop file path
521
675
    DesktopFileReader* desktopData;
522
 
    if (QFileInfo(desktopFileName).exists()) {
523
 
        desktopData = new DesktopFileReader(QFileInfo(desktopFileName));
 
676
    if (QFileInfo(desktopFileName.get()).exists()) {
 
677
        desktopData = m_desktopFileReaderFactory->createInstance(appId, QFileInfo(desktopFileName.get()));
524
678
    } else {
525
 
        desktopData = new DesktopFileReader(desktopFileName);
 
679
        desktopData = m_desktopFileReaderFactory->createInstance(appId, m_taskController->findDesktopFileForAppId(appId));
526
680
    }
527
681
 
528
682
    if (!desktopData->loaded()) {
529
683
        delete desktopData;
530
 
        LOG("ApplicationManager REJECTED connection from app with pid %lld as desktop_file_hint file not found", pid);
 
684
        qCritical() << "ApplicationManager REJECTED connection from app with pid" << pid
 
685
                    << "as the file specified by the desktop_file_hint argument could not be opened";
531
686
        return;
532
687
    }
533
688
 
535
690
    // case where shell actually launched the script.
536
691
    Application *application = findApplication(desktopData->appId());
537
692
    if (application && application->state() == Application::Starting) {
538
 
        DLOG("Process with pid %lld appeared, attached to existing entry '%s' in application lists",
539
 
             pid, application->appId().toLatin1().data());
 
693
        qCDebug(QTMIR_APPLICATIONS) << "Process with pid" << pid << "appeared, attaching to existing entry"
 
694
                                    << "in application list with appId:" << application->appId();
540
695
        delete desktopData;
541
696
        application->setSessionName(application->appId());
542
697
        application->setPid(pid);
546
701
 
547
702
    // if stage supplied in CLI, fetch that
548
703
    Application::Stage stage = Application::MainStage;
549
 
    pattern = QRegularExpression::escape("--stage=") + "(\\S+)";
550
 
    regExp.setPattern(pattern);
551
 
    regExpMatch = regExp.match(command);
 
704
    boost::optional<QString> stageParam = info->getParameter("--stage_hint=");
552
705
 
553
 
    if (regExpMatch.hasMatch() && regExpMatch.captured(1) == "side_stage") {
 
706
    if (stageParam && stageParam.get() == "side_stage") {
554
707
        stage = Application::SideStage;
555
708
    }
556
709
 
557
 
    DLOG("Existing process with pid %lld appeared, adding '%s' to application lists", pid, desktopData->name().toLatin1().data());
 
710
    qCDebug(QTMIR_APPLICATIONS) << "New process with pid" << pid << "appeared, adding new application to the"
 
711
                                << "application list with appId:" << desktopData->appId();
558
712
 
559
 
    QString argStr(command.data());
560
 
    QStringList arguments(argStr.split(' '));
561
 
    application = new Application(desktopData, Application::Starting, arguments, this);
 
713
    QStringList arguments(info->asStringList());
 
714
    application = new Application(m_taskController, desktopData, Application::Starting, arguments, this);
562
715
    application->setPid(pid);
563
716
    application->setStage(stage);
 
717
    application->setCanBeResumed(false);
564
718
    add(application);
565
719
    authorized = true;
566
720
}
567
721
 
568
722
void ApplicationManager::onSessionStarting(std::shared_ptr<ms::Session> const& session)
569
723
{
570
 
    DLOG("ApplicationManager::onSessionStarting (this=%p, application=%s)", this, session->name().c_str());
 
724
    qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onSessionStarting - sessionName=" <<  session->name().c_str();
571
725
 
572
 
    if (m_fenceNext) {
 
726
    if (m_fenceNext) { // needed to ignore sessions created by non-user processes (e.g. maliit)
573
727
        m_fenceNext = false;
574
728
        return;
575
729
    }
577
731
    Application* application = findApplicationWithPid(session->process_id());
578
732
    if (application && application->state() != Application::Running) {
579
733
        application->setSession(session);
580
 
        m_applicationToBeFocused = application;
 
734
        if (application->stage() == Application::MainStage)
 
735
            m_msApplicationToBeFocused = application;
 
736
        else
 
737
            m_ssApplicationToBeFocused = application;
581
738
    } else {
582
 
        DLOG("ApplicationManager::onSessionStarting - unauthorized application!!");
 
739
        qCritical() << "ApplicationManager::onSessionStarting - unauthorized application!!";
583
740
    }
584
741
}
585
742
 
586
743
void ApplicationManager::onSessionStopping(std::shared_ptr<ms::Session> const& session)
587
744
{
588
 
    DLOG("ApplicationManager::onSessionStopping (this=%p, application=%s)", this, session->name().c_str());
 
745
    qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onSessionStopping - sessionName=" << session->name().c_str();
589
746
 
590
747
    // in case application closed not by hand of shell, check again here:
591
748
    Application* application = findApplicationWithSession(session);
592
749
    if (application) {
593
 
        bool removeApplication = true;
 
750
        /* Can remove the application from the running apps list immediately in these curcumstances:
 
751
         *  1. application is not managed by upstart (this message from Mir is only notice the app has stopped, must do
 
752
         *     it here)
 
753
         *  2. application is managed by upstart, but has stopped before it managed to create a surface, we can assume
 
754
         *     it crashed on startup, and thus cannot be resumed - so remove it.
 
755
         *  3. application is managed by upstart and is in foreground (i.e. has Running state), if Mir reports the
 
756
         *     application disconnects, it either crashed or stopped itself. Either case, remove it.
 
757
         */
 
758
        if (!application->canBeResumed()
 
759
                || application->state() == Application::Starting
 
760
                || application->state() == Application::Running) { qDebug() << "A" << application->canBeResumed() << application->state();
 
761
            m_dbusWindowStack->WindowDestroyed(0, application->appId());
 
762
            remove(application);
 
763
            delete application;
594
764
 
595
 
        if (application->state() != Application::Starting) {
596
 
            application->setState(Application::Stopped);
597
 
            application->setSession(nullptr);
598
 
            m_dbusWindowStack->WindowDestroyed(0, application->appId());
599
 
            if (application != m_focusedApplication) {
600
 
                   removeApplication = false;
 
765
            if (application == m_focusedApplication) {
 
766
                m_focusedApplication = nullptr;
 
767
                Q_EMIT focusedApplicationIdChanged();
601
768
            }
602
 
        }
603
 
 
604
 
        if (removeApplication) {
605
 
            // TODO(greyback) What to do?? Focus next app, or unfocus everything??
606
 
            m_focusedApplication = NULL;
607
 
            remove(application);
608
 
            delete application;
609
 
            Q_EMIT focusedApplicationIdChanged();
610
 
        }
611
 
    }
612
 
}
613
 
 
614
 
void ApplicationManager::onSessionFocused(std::shared_ptr<ms::Session> const& session)
615
 
{
616
 
    DLOG("ApplicationManager::onSessionFocused (this=%p, application=%s)", this, session->name().c_str());
617
 
    Application* application = findApplicationWithSession(session);
618
 
 
619
 
    // Don't give application focus until it has created it's surface, when it is set as state "Running"
620
 
    // and only notify shell of focus changes that it actually expects
621
 
    if (application && application->state() != Application::Starting && application == m_applicationToBeFocused
622
 
            && application != m_focusedApplication) {
623
 
        setFocused(application);
624
 
        QModelIndex appIndex = findIndex(application);
625
 
        Q_EMIT dataChanged(appIndex, appIndex, QVector<int>() << RoleFocused);
626
 
    } else {
627
 
        if (application == nullptr) {
628
 
            DLOG("Invalid application focused, discarding the event");
629
 
            if (NULL != m_focusedApplication)
630
 
                focusApplication(m_focusedApplication->appId());
631
 
        }
632
 
    }
633
 
}
634
 
 
635
 
void ApplicationManager::onSessionUnfocused()
636
 
{
637
 
    DLOG("ApplicationManager::onSessionUnfocused (this=%p)", this);
638
 
    if (NULL != m_focusedApplication) {
639
 
        Q_ASSERT(m_focusedApplication->focused());
640
 
        m_focusedApplication->setFocused(false);
641
 
 
642
 
        //suspendApplication(m_focusedApplication);
643
 
 
644
 
        m_focusedApplication = NULL;
645
 
        Q_EMIT focusedApplicationIdChanged();
646
 
        m_dbusWindowStack->FocusedWindowChanged(0, QString(), 0);
647
 
 
648
 
        QModelIndex appIndex = findIndex(m_focusedApplication);
649
 
        Q_EMIT dataChanged(appIndex, appIndex, QVector<int>() << RoleFocused << RoleState);
 
769
        } else {
 
770
            // otherwise, we do not have enough information to make any changes to the model, so await events from
 
771
            // upstart to go further, but set the app state
 
772
            application->setState(Application::Stopped);
 
773
        }
650
774
    }
651
775
}
652
776
 
653
777
void ApplicationManager::onSessionCreatedSurface(ms::Session const* session,
654
778
                                               std::shared_ptr<ms::Surface> const& surface)
655
779
{
656
 
    DLOG("ApplicationManager::onSessionCreatedSurface (this=%p)", this);
 
780
    qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onSessionCreatedSurface - sessionName=" << session->name().c_str();
657
781
    Q_UNUSED(surface);
658
782
 
659
783
    Application* application = findApplicationWithSession(session);
665
789
 
666
790
void ApplicationManager::setFocused(Application *application)
667
791
{
668
 
    DLOG("ApplicationManager::setFocused (appId=%s)", qPrintable(application->appId()));
 
792
    qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::setFocused - appId=" << application->appId();
669
793
 
670
794
    if (application == m_focusedApplication)
671
795
        return;
672
796
 
673
797
    // set state of previously focused app to suspended
674
 
    //suspendApplication(m_focusedApplication);
 
798
    if (m_focusedApplication && m_lifecycleExceptions.filter(m_focusedApplication->appId().section('_',0,0)).empty()) {
 
799
        Application *lastApplication = applicationForStage(application->stage());
 
800
        suspendApplication(lastApplication);
 
801
    }
675
802
 
 
803
    if (application->stage() == Application::MainStage)
 
804
        m_mainStageApplication = application;
 
805
    else
 
806
        m_sideStageApplication = application;
676
807
 
677
808
    m_focusedApplication = application;
678
809
    m_focusedApplication->setFocused(true);
 
810
    m_focusedApplication->setState(Application::Running);
679
811
    move(m_applications.indexOf(application), 0);
680
812
    Q_EMIT focusedApplicationIdChanged();
681
813
    m_dbusWindowStack->FocusedWindowChanged(0, application->appId(), application->stage());
709
841
    return nullptr;
710
842
}
711
843
 
 
844
Application* ApplicationManager::applicationForStage(Application::Stage stage)
 
845
{
 
846
    qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::focusedApplicationForStage" << stage;
 
847
 
 
848
    if (stage == Application::MainStage)
 
849
        return m_mainStageApplication;
 
850
    else
 
851
        return m_sideStageApplication;
 
852
}
 
853
 
712
854
void ApplicationManager::add(Application* application)
713
855
{
714
 
    DASSERT(application != NULL);
715
 
    DLOG("ApplicationManager::add (this=%p, application='%s')", this, qPrintable(application->name()));
 
856
    Q_ASSERT(application != nullptr);
 
857
    qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::add - appId=" << application->appId();
716
858
 
717
859
    connect(application, &Application::screenshotChanged, this, &ApplicationManager::screenshotUpdated);
718
860
 
719
861
    beginInsertRows(QModelIndex(), m_applications.count(), m_applications.count());
720
862
    m_applications.append(application);
721
863
    endInsertRows();
722
 
    emit countChanged();
723
 
    emit applicationAdded(application->appId());
 
864
    Q_EMIT countChanged();
 
865
    Q_EMIT applicationAdded(application->appId());
724
866
    if (m_applications.size() == 1) {
725
 
        emit topmostApplicationChanged(application);
726
 
        emit emptyChanged();
 
867
        Q_EMIT topmostApplicationChanged(application);
 
868
        Q_EMIT emptyChanged();
727
869
    }
728
870
}
729
871
 
730
872
void ApplicationManager::remove(Application *application)
731
873
{
732
 
    DASSERT(application != NULL);
733
 
    DLOG("ApplicationManager::remove (this=%p, application='%s')", this, qPrintable(application->name()));
 
874
    Q_ASSERT(application != nullptr);
 
875
    qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::remove - appId=" << application->appId();
 
876
 
 
877
    if (application == m_sideStageApplication)
 
878
        m_sideStageApplication = nullptr;
 
879
    if (application == m_mainStageApplication)
 
880
        m_mainStageApplication = nullptr;
734
881
 
735
882
    int i = m_applications.indexOf(application);
736
883
    if (i != -1) {
737
884
        beginRemoveRows(QModelIndex(), i, i);
738
885
        m_applications.removeAt(i);
739
886
        endRemoveRows();
740
 
        emit applicationRemoved(application->appId());
741
 
        emit countChanged();
 
887
        Q_EMIT applicationRemoved(application->appId());
 
888
        Q_EMIT countChanged();
742
889
        if (i == 0) {
743
 
            emit topmostApplicationChanged(topmostApplication());
744
 
            emit emptyChanged();
 
890
            Q_EMIT topmostApplicationChanged(topmostApplication());
 
891
            Q_EMIT emptyChanged();
745
892
        }
746
893
    }
747
894
}
748
895
 
749
896
void ApplicationManager::move(int from, int to) {
750
 
    DLOG("ApplicationManager::move (this=%p, from=%d, to=%d)", this, from, to);
 
897
    qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::move - from=" << from << "to=" << to;
751
898
    if (from == to) return;
752
899
 
753
900
    if (from >= 0 && from < m_applications.size() && to >= 0 && to < m_applications.size()) {
761
908
        m_applications.move(from, to);
762
909
        endMoveRows();
763
910
        if (topmostApplication() != oldTopmost) {
764
 
            emit topmostApplicationChanged(topmostApplication());
 
911
            Q_EMIT topmostApplicationChanged(topmostApplication());
765
912
        }
766
913
    }
767
 
    DLOG("ApplicationManager::move after (%s)", qPrintable(toString()));
 
914
    qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::move after " << toString();
768
915
}
769
916
 
770
917
QModelIndex ApplicationManager::findIndex(Application* application)
798
945
        return m_applications[0];
799
946
    }
800
947
}
 
948
 
 
949
} // namespace qtmir