diff options
Diffstat (limited to 'src/modules/Unity')
21 files changed, 787 insertions, 553 deletions
diff --git a/src/modules/Unity/Application/CMakeLists.txt b/src/modules/Unity/Application/CMakeLists.txt index 8c437e6..eaf5478 100644 --- a/src/modules/Unity/Application/CMakeLists.txt +++ b/src/modules/Unity/Application/CMakeLists.txt @@ -46,6 +46,7 @@ set(QMLMIRPLUGIN_SRC ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h # Feed the automoc monster + mirsurfaceiteminterface.h session_interface.h applicationcontroller.h settings_interface.h diff --git a/src/modules/Unity/Application/application.cpp b/src/modules/Unity/Application/application.cpp index cf37c92..06471f7 100644 --- a/src/modules/Unity/Application/application.cpp +++ b/src/modules/Unity/Application/application.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2014 Canonical, Ltd. + * Copyright (C) 2013-2015 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License version 3, as published by @@ -37,25 +37,28 @@ namespace ms = mir::scene; namespace qtmir { -Application::Application(const QSharedPointer<TaskController>& taskController, - const QSharedPointer<SharedWakelock>& sharedWakelock, +QStringList Application::lifecycleExceptions; + +Application::Application(const QSharedPointer<SharedWakelock>& sharedWakelock, DesktopFileReader *desktopFileReader, - State state, const QStringList &arguments, ApplicationManager *parent) : ApplicationInfoInterface(desktopFileReader->appId(), parent) - , m_taskController(taskController) , m_sharedWakelock(sharedWakelock) , m_desktopData(desktopFileReader) , m_pid(0) , m_stage((m_desktopData->stageHint() == "SideStage") ? Application::SideStage : Application::MainStage) - , m_state(state) + , m_state(InternalState::Starting) , m_focused(false) - , m_canBeResumed(true) , m_arguments(arguments) , m_session(nullptr) + , m_requestedState(RequestedRunning) + , m_processState(ProcessUnknown) { - qCDebug(QTMIR_APPLICATIONS) << "Application::Application - appId=" << desktopFileReader->appId() << "state=" << state; + qCDebug(QTMIR_APPLICATIONS) << "Application::Application - appId=" << desktopFileReader->appId(); + + // Because m_state is InternalState::Starting + acquireWakelock(); // FIXME(greyback) need to save long appId internally until ubuntu-app-launch can hide it from us m_longAppId = desktopFileReader->file().remove(QRegExp(".desktop$")).split('/').last(); @@ -69,10 +72,33 @@ Application::~Application() { qCDebug(QTMIR_APPLICATIONS) << "Application::~Application"; + // (ricmm) -- To be on the safe side, better wipe the application QML compile cache if it crashes on startup + if (m_processState == Application::ProcessUnknown + || state() == Application::Starting + || state() == Application::Running) { + wipeQMLCache(); + } + delete m_session; delete m_desktopData; } + +void Application::wipeQMLCache() +{ + QString path(QDir::homePath() + QStringLiteral("/.cache/QML/Apps/")); + QDir dir(path); + QStringList apps = dir.entryList(); + for (int i = 0; i < apps.size(); i++) { + if (apps.at(i).contains(appId())) { + qCDebug(QTMIR_APPLICATIONS) << "Application appId=" << apps.at(i) << " Wiping QML Cache"; + dir.cd(apps.at(i)); + dir.removeRecursively(); + break; + } + } +} + bool Application::isValid() const { return m_desktopData->loaded(); @@ -159,6 +185,30 @@ QColor Application::colorFromString(const QString &colorString, const char *colo return color; } +const char* Application::internalStateToStr(InternalState state) +{ + switch (state) { + case InternalState::Starting: + return "Starting"; + case InternalState::Running: + return "Running"; + case InternalState::RunningInBackground: + return "RunningInBackground"; + case InternalState::SuspendingWaitSession: + return "SuspendingWaitSession"; + case InternalState::SuspendingWaitProcess: + return "SuspendingWaitProcess"; + case InternalState::Suspended: + return "Suspended"; + case InternalState::StoppedUnexpectedly: + return "StoppedUnexpectedly"; + case InternalState::Stopped: + return "Stopped"; + default: + return "???"; + } +} + bool Application::splashShowHeader() const { QString showHeader = m_desktopData->splashShowHeader(); @@ -204,7 +254,104 @@ Application::Stages Application::supportedStages() const Application::State Application::state() const { - return m_state; + // The public state is a simplified version of the internal one as our consumers + // don't have to know or care about all the nasty details. + switch (m_state) { + case InternalState::Starting: + return Starting; + case InternalState::Running: + case InternalState::RunningInBackground: + case InternalState::SuspendingWaitSession: + case InternalState::SuspendingWaitProcess: + return Running; + case InternalState::Suspended: + return Suspended; + case InternalState::Stopped: + default: + return Stopped; + } +} + +Application::RequestedState Application::requestedState() const +{ + return m_requestedState; +} + +void Application::setRequestedState(RequestedState value) +{ + if (m_requestedState == value) { + // nothing to do + return; + } + + qCDebug(QTMIR_APPLICATIONS) << "Application::setRequestedState - appId=" << appId() + << "requestedState=" << applicationStateToStr(value); + m_requestedState = value; + Q_EMIT requestedStateChanged(m_requestedState); + + applyRequestedState(); +} + +void Application::applyRequestedState() +{ + if (m_requestedState == RequestedRunning) { + applyRequestedRunning(); + } else { + applyRequestedSuspended(); + } +} + +void Application::applyRequestedRunning() +{ + switch (m_state) { + case InternalState::Starting: + // should leave the app alone until it reaches Running state + break; + case InternalState::Running: + // already where it's wanted to be + break; + case InternalState::RunningInBackground: + case InternalState::SuspendingWaitSession: + case InternalState::Suspended: + resume(); + break; + case InternalState::SuspendingWaitProcess: + // should leave the app alone until it reaches Suspended state + break; + case InternalState::StoppedUnexpectedly: + respawn(); + break; + case InternalState::Stopped: + // dead end. + break; + } +} + +void Application::applyRequestedSuspended() +{ + switch (m_state) { + case InternalState::Starting: + // should leave the app alone until it reaches Running state + break; + case InternalState::Running: + if (m_processState == ProcessRunning) { + suspend(); + } else { + // we can't suspend it since we have no information on the app process + Q_ASSERT(m_processState == ProcessUnknown); + } + break; + case InternalState::RunningInBackground: + case InternalState::SuspendingWaitSession: + case InternalState::SuspendingWaitProcess: + case InternalState::Suspended: + // it's already going where we it's wanted + break; + case InternalState::StoppedUnexpectedly: + case InternalState::Stopped: + // the app doesn't have a process in the first place, so there's nothing to suspend + break; + } } bool Application::focused() const @@ -219,12 +366,7 @@ bool Application::fullscreen() const bool Application::canBeResumed() const { - return m_canBeResumed; -} - -void Application::setCanBeResumed(const bool resume) -{ - m_canBeResumed = resume; + return m_processState != ProcessUnknown; } pid_t Application::pid() const @@ -242,7 +384,7 @@ void Application::setArguments(const QStringList arguments) m_arguments = arguments; } -void Application::setSession(Session *newSession) +void Application::setSession(SessionInterface *newSession) { qCDebug(QTMIR_APPLICATIONS) << "Application::setSession - appId=" << appId() << "session=" << newSession; @@ -261,10 +403,25 @@ void Application::setSession(Session *newSession) if (m_session) { m_session->setParent(this); m_session->setApplication(this); - m_session->setState(state()); - connect(m_session, &SessionInterface::suspended, this, &Application::onSessionSuspended); - connect(m_session, &SessionInterface::resumed, this, &Application::onSessionResumed); + switch (m_state) { + case InternalState::Starting: + case InternalState::Running: + case InternalState::RunningInBackground: + m_session->resume(); + break; + case InternalState::SuspendingWaitSession: + case InternalState::SuspendingWaitProcess: + case InternalState::Suspended: + m_session->suspend(); + break; + case InternalState::Stopped: + default: + m_session->stop(); + break; + } + + connect(m_session, &SessionInterface::stateChanged, this, &Application::onSessionStateChanged); connect(m_session, &SessionInterface::fullscreenChanged, this, &Application::fullscreenChanged); if (oldFullscreen != fullscreen()) @@ -288,37 +445,53 @@ void Application::setStage(Application::Stage stage) } } -void Application::setState(Application::State state) +void Application::setInternalState(Application::InternalState state) { - qCDebug(QTMIR_APPLICATIONS) << "Application::setState - appId=" << appId() << "state=" << applicationStateToStr(state); - if (m_state != state) { - if (session()) { - session()->setState((Session::State)state); - } else { - // If we have have no session, we may have to respawn it. - switch (state) - { - case Session::State::Running: - if (m_state == Session::State::Stopped) { - respawn(); - state = Session::State::Starting; - } - break; - default: - break; - } - } - m_state = state; - Q_EMIT stateChanged(state); + if (m_state == state) { + return; } + + qCDebug(QTMIR_APPLICATIONS) << "Application::setInternalState - appId=" << appId() + << "state=" << internalStateToStr(state); + + auto oldPublicState = this->state(); + m_state = state; + + switch (m_state) { + case InternalState::Starting: + case InternalState::Running: + acquireWakelock(); + break; + case InternalState::RunningInBackground: + releaseWakelock(); + break; + case InternalState::Suspended: + releaseWakelock(); + break; + case InternalState::StoppedUnexpectedly: + releaseWakelock(); + break; + case InternalState::Stopped: + Q_EMIT stopped(); + releaseWakelock(); + break; + case InternalState::SuspendingWaitSession: + case InternalState::SuspendingWaitProcess: + // transitory states. leave as it is + default: + break; + }; + + if (this->state() != oldPublicState) { + Q_EMIT stateChanged(this->state()); + } + + applyRequestedState(); } void Application::setFocused(bool focused) { qCDebug(QTMIR_APPLICATIONS) << "Application::setFocused - appId=" << appId() << "focused=" << focused; - if (focused) { - holdWakelock(true); - } if (m_focused != focused) { m_focused = focused; @@ -326,25 +499,84 @@ void Application::setFocused(bool focused) } } -void Application::onSessionSuspended() +void Application::setProcessState(ProcessState newProcessState) { - qCDebug(QTMIR_APPLICATIONS) << "Application::onSessionSuspended - appId=" << appId(); - m_taskController->suspend(longAppId()); - holdWakelock(false); + if (m_processState == newProcessState) { + return; + } + + m_processState = newProcessState; + + switch (m_processState) { + case ProcessUnknown: + // it would be a coding error + Q_ASSERT(false); + break; + case ProcessRunning: + if (m_state == InternalState::StoppedUnexpectedly) { + setInternalState(InternalState::Starting); + } + break; + case ProcessSuspended: + Q_ASSERT(m_state == InternalState::SuspendingWaitProcess); + setInternalState(InternalState::Suspended); + break; + case ProcessStopped: + // we assume the session always stop before the process + Q_ASSERT(!m_session || m_session->state() == Session::Stopped); + if (m_state == InternalState::Starting) { + setInternalState(InternalState::Stopped); + } else { + Q_ASSERT(m_state == InternalState::Stopped + || m_state == InternalState::StoppedUnexpectedly); + } + break; + } + + applyRequestedState(); } -void Application::onSessionResumed() +void Application::suspend() { - qCDebug(QTMIR_APPLICATIONS) << "Application::onSessionResumed - appId=" << appId(); - holdWakelock(true); - m_taskController->resume(longAppId()); + Q_ASSERT(m_state == InternalState::Running); + Q_ASSERT(m_session != nullptr); + + if (!lifecycleExceptions.filter(appId().section('_',0,0)).empty()) { + // Present in exceptions list. + // There's no need to keep the wakelock as the process is never suspended + // and thus has no cleanup to perform when (for example) the display is + // blanked. + setInternalState(InternalState::RunningInBackground); + } else { + setInternalState(InternalState::SuspendingWaitSession); + m_session->suspend(); + } +} + +void Application::resume() +{ + if (m_state == InternalState::Suspended) { + setInternalState(InternalState::Running); + Q_EMIT resumeProcessRequested(); + if (m_processState == ProcessSuspended) { + setProcessState(ProcessRunning); // should we wait for a resumed() signal? + } + m_session->resume(); + } else if (m_state == InternalState::SuspendingWaitSession) { + setInternalState(InternalState::Running); + m_session->resume(); + } else if (m_state == InternalState::RunningInBackground) { + setInternalState(InternalState::Running); + } } void Application::respawn() { qCDebug(QTMIR_APPLICATIONS) << "Application::respawn - appId=" << appId(); - holdWakelock(true); - m_taskController->start(appId(), m_arguments); + + setInternalState(InternalState::Starting); + + Q_EMIT startProcessRequested(); } QString Application::longAppId() const @@ -362,20 +594,60 @@ bool Application::rotatesWindowContents() const return m_rotatesWindowContents; } -Session* Application::session() const +SessionInterface* Application::session() const { return m_session; } -void Application::holdWakelock(bool enable) const +void Application::acquireWakelock() const { if (appId() == "unity8-dash") return; - if (enable) { - m_sharedWakelock->acquire(this); - } else { - m_sharedWakelock->release(this); + m_sharedWakelock->acquire(this); +} + +void Application::releaseWakelock() const +{ + if (appId() == "unity8-dash") + return; + + m_sharedWakelock->release(this); +} + +void Application::onSessionStateChanged(Session::State sessionState) +{ + switch (sessionState) { + case Session::Starting: + break; + case Session::Running: + if (m_state == InternalState::Starting) { + setInternalState(InternalState::Running); + } + break; + case Session::Suspending: + break; + case Session::Suspended: + Q_ASSERT(m_state == InternalState::SuspendingWaitSession); + setInternalState(InternalState::SuspendingWaitProcess); + Q_EMIT suspendProcessRequested(); + break; + case Session::Stopped: + if (!canBeResumed() + || m_state == InternalState::Starting + || m_state == InternalState::Running) { + /* 1. application is not managed by upstart + * 2. application is managed by upstart, but has stopped before it managed + * to create a surface, we can assume it crashed on startup, and thus + * cannot be resumed + * 3. application is managed by upstart and is in foreground (i.e. has + * Running state), if Mir reports the application disconnects, it + * either crashed or stopped itself. + */ + setInternalState(InternalState::Stopped); + } else { + setInternalState(InternalState::StoppedUnexpectedly); + } } } diff --git a/src/modules/Unity/Application/application.h b/src/modules/Unity/Application/application.h index dff36cd..9344621 100644 --- a/src/modules/Unity/Application/application.h +++ b/src/modules/Unity/Application/application.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2014 Canonical, Ltd. + * Copyright (C) 2013-2015 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License version 3, as published by @@ -28,6 +28,8 @@ // Unity API #include <unity/shell/application/ApplicationInfoInterface.h> +#include "session_interface.h" + namespace mir { namespace scene { class Session; @@ -39,7 +41,6 @@ namespace qtmir class ApplicationManager; class DesktopFileReader; -class TaskController; class Session; class SharedWakelock; @@ -51,15 +52,32 @@ class Application : public unity::shell::application::ApplicationInfoInterface Q_PROPERTY(QString exec READ exec CONSTANT) Q_PROPERTY(bool fullscreen READ fullscreen NOTIFY fullscreenChanged) Q_PROPERTY(Stage stage READ stage WRITE setStage NOTIFY stageChanged) - Q_PROPERTY(Session* session READ session NOTIFY sessionChanged DESIGNABLE false) + Q_PROPERTY(SessionInterface* session READ session NOTIFY sessionChanged DESIGNABLE false) public: Q_DECLARE_FLAGS(Stages, Stage) - Application(const QSharedPointer<TaskController>& taskController, - const QSharedPointer<SharedWakelock>& sharedWakelock, + enum ProcessState { + ProcessUnknown, + ProcessRunning, + ProcessSuspended, + ProcessStopped + }; + + enum class InternalState { + Starting, + Running, + RunningInBackground, + SuspendingWaitSession, + SuspendingWaitProcess, + Suspended, + StoppedUnexpectedly, + Stopped // It closed itself, crashed or it stopped and we can't respawn it + // In any case, this is a dead end. + }; + + Application(const QSharedPointer<SharedWakelock>& sharedWakelock, DesktopFileReader *desktopFileReader, - State state, const QStringList &arguments, ApplicationManager *parent); virtual ~Application(); @@ -71,6 +89,8 @@ public: QUrl icon() const override; Stage stage() const override; State state() const override; + RequestedState requestedState() const override; + void setRequestedState(RequestedState) override; bool focused() const override; QString splashTitle() const override; QUrl splashImage() const override; @@ -82,12 +102,16 @@ public: bool rotatesWindowContents() const override; void setStage(Stage stage); - void setState(State state); - Session* session() const; + + void setProcessState(ProcessState value); + + QStringList arguments() const { return m_arguments; } + + SessionInterface* session() const; + void setSession(SessionInterface *session); bool canBeResumed() const; - void setCanBeResumed(const bool); bool isValid() const; QString desktopFile() const; @@ -98,40 +122,58 @@ public: pid_t pid() const; + // for tests + InternalState internalState() const { return m_state; } + + static QStringList lifecycleExceptions; + Q_SIGNALS: void fullscreenChanged(bool fullscreen); void stageChanged(Stage stage); - void sessionChanged(Session *session); + void sessionChanged(SessionInterface *session); + + void startProcessRequested(); + void suspendProcessRequested(); + void resumeProcessRequested(); + void stopped(); private Q_SLOTS: - void onSessionSuspended(); - void onSessionResumed(); + void onSessionStateChanged(SessionInterface::State sessionState); void respawn(); private: + QString longAppId() const; - void holdWakelock(bool enable) const; + void acquireWakelock() const; + void releaseWakelock() const; void setPid(pid_t pid); void setArguments(const QStringList arguments); void setFocused(bool focus); - void setSession(Session *session); + void setInternalState(InternalState state); + void wipeQMLCache(); + void suspend(); + void resume(); QColor colorFromString(const QString &colorString, const char *colorName) const; + static const char* internalStateToStr(InternalState state); + void applyRequestedState(); + void applyRequestedRunning(); + void applyRequestedSuspended(); - QSharedPointer<TaskController> m_taskController; QSharedPointer<SharedWakelock> m_sharedWakelock; DesktopFileReader* m_desktopData; QString m_longAppId; qint64 m_pid; Stage m_stage; Stages m_supportedStages; - State m_state; + InternalState m_state; bool m_focused; - bool m_canBeResumed; QStringList m_arguments; Qt::ScreenOrientations m_supportedOrientations; bool m_rotatesWindowContents; - Session *m_session; + SessionInterface *m_session; + RequestedState m_requestedState; + ProcessState m_processState; friend class ApplicationManager; friend class SessionManager; diff --git a/src/modules/Unity/Application/application_manager.cpp b/src/modules/Unity/Application/application_manager.cpp index ea316e7..0624552 100644 --- a/src/modules/Unity/Application/application_manager.cpp +++ b/src/modules/Unity/Application/application_manager.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013,2014 Canonical, Ltd. + * Copyright (C) 2013-2015 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License version 3, as published by @@ -96,11 +96,13 @@ void connectToTaskController(ApplicationManager *manager, TaskController *contro manager, &ApplicationManager::onProcessStarting); QObject::connect(controller, &TaskController::processStopped, manager, &ApplicationManager::onProcessStopped); + QObject::connect(controller, &TaskController::processSuspended, + manager, &ApplicationManager::onProcessSuspended); QObject::connect(controller, &TaskController::processFailed, manager, &ApplicationManager::onProcessFailed); - QObject::connect(controller, &TaskController::requestFocus, + QObject::connect(controller, &TaskController::focusRequested, manager, &ApplicationManager::onFocusRequested); - QObject::connect(controller, &TaskController::requestResume, + QObject::connect(controller, &TaskController::resumeRequested, manager, &ApplicationManager::onResumeRequested); } @@ -180,16 +182,12 @@ ApplicationManager::ApplicationManager( : ApplicationManagerInterface(parent) , m_mirServer(mirServer) , m_focusedApplication(nullptr) - , m_mainStageApplication(nullptr) - , m_sideStageApplication(nullptr) , m_dbusWindowStack(new DBusWindowStack(this)) , m_taskController(taskController) , m_desktopFileReaderFactory(desktopFileReaderFactory) , m_procInfo(procInfo) , m_sharedWakelock(sharedWakelock) , m_settings(settings) - , m_suspended(false) - , m_forceDashActive(false) { qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::ApplicationManager (this=%p)" << this; setObjectName("qtmir::ApplicationManager"); @@ -198,7 +196,7 @@ ApplicationManager::ApplicationManager( m_roleNames.insert(RoleFullscreen, "fullscreen"); if (settings.data()) { - m_lifecycleExceptions = m_settings->get("lifecycleExemptAppids").toStringList(); + Application::lifecycleExceptions = m_settings->get("lifecycleExemptAppids").toStringList(); connect(m_settings.data(), &Settings::changed, this, &ApplicationManager::onSettingsChanged); } } @@ -289,98 +287,6 @@ QString ApplicationManager::focusedApplicationId() const } } -bool ApplicationManager::suspended() const -{ - return m_suspended; -} - -void ApplicationManager::setSuspended(bool suspended) -{ - if (suspended == m_suspended) { - return; - } - m_suspended = suspended; - Q_EMIT suspendedChanged(); - - if (m_suspended) { - suspendApplication(m_mainStageApplication); - suspendApplication(m_sideStageApplication); - if (m_focusedApplication) { - m_focusedApplication->setFocused(false); - m_dbusWindowStack->FocusedWindowChanged(0, QString(), 0); - } - } else { - resumeApplication(m_mainStageApplication); - resumeApplication(m_sideStageApplication); - if (m_focusedApplication) { - m_focusedApplication->setFocused(true); - m_dbusWindowStack->FocusedWindowChanged(0, m_focusedApplication->appId(), m_focusedApplication->stage()); - } - } -} - -bool ApplicationManager::forceDashActive() const -{ - return m_forceDashActive; -} - -void ApplicationManager::setForceDashActive(bool forceDashActive) -{ - if (m_forceDashActive == forceDashActive) { - return; - } - - m_forceDashActive = forceDashActive; - Q_EMIT forceDashActiveChanged(); - - Application *dashApp = findApplication("unity8-dash"); - if (!dashApp) { - qCWarning(QTMIR_APPLICATIONS) << "Dash doesn't seem to be running... Ignoring."; - return; - } - - if (m_forceDashActive && dashApp->state() != Application::Running) { - resumeApplication(dashApp); - } else if (!m_forceDashActive && dashApp->state() == Application::Running - && m_mainStageApplication != dashApp - && m_sideStageApplication != dashApp) { - suspendApplication(dashApp); - } -} - -bool ApplicationManager::suspendApplication(Application *application) -{ - if (application == nullptr) - return false; - qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::suspendApplication - appId=" << application->appId(); - - // Present in exceptions list, explicitly release wakelock and return. There's no need to keep the wakelock - // as the process is never suspended and thus has no cleanup to perform when (for example) the display is blanked - if (!m_lifecycleExceptions.filter(application->appId().section('_',0,0)).empty()) { - m_sharedWakelock->release(application); - return false; - } - - if (m_forceDashActive && application->appId() == "unity8-dash") { - return false; - } - - if (application->state() == Application::Running) - application->setState(Application::Suspended); - - return true; -} - -void ApplicationManager::resumeApplication(Application *application) -{ - if (application == nullptr) - return; - qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::resumeApplication - appId=" << application->appId(); - - if (application->state() == Application::Suspended || application->state() == Application::Stopped) - application->setState(Application::Running); -} - bool ApplicationManager::focusApplication(const QString &inputAppId) { const QString appId = toShortAppIdIfPossible(inputAppId); @@ -392,27 +298,8 @@ bool ApplicationManager::focusApplication(const QString &inputAppId) return false; } - resumeApplication(application); - - // set state of previously focused app to suspended if (m_focusedApplication) { m_focusedApplication->setFocused(false); - Application *lastApplication = applicationForStage(application->stage()); - if (lastApplication != application) { - suspendApplication(lastApplication); - } - } - - if (application->stage() == Application::MainStage) { - m_mainStageApplication = application; - } else { - m_sideStageApplication = application; - } - - if (!m_suspended) { - resumeApplication(application); // in case unfocusCurrentApplication() was last called - } else { - suspendApplication(application); // Make sure we also have this one suspended if everything is suspended } m_focusedApplication = application; @@ -422,9 +309,6 @@ bool ApplicationManager::focusApplication(const QString &inputAppId) Q_EMIT focusedApplicationIdChanged(); m_dbusWindowStack->FocusedWindowChanged(0, application->appId(), application->stage()); - // FIXME(dandrader): lying here. The operation is async. So we will only know whether - // the focusing was successful once the server replies. Maybe the API in unity-api should - // reflect that? return true; } @@ -432,9 +316,6 @@ void ApplicationManager::unfocusCurrentApplication() { qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::unfocusCurrentApplication"; - suspendApplication(m_sideStageApplication); - suspendApplication(m_mainStageApplication); - m_focusedApplication = nullptr; Q_EMIT focusedApplicationIdChanged(); } @@ -484,10 +365,8 @@ Application *ApplicationManager::startApplication(const QString &inputAppId, Exe application->setArguments(arguments); } else { application = new Application( - m_taskController, m_sharedWakelock, m_desktopFileReaderFactory->createInstance(appId, m_taskController->findDesktopFileForAppId(appId)), - Application::Starting, arguments, this); @@ -514,10 +393,8 @@ void ApplicationManager::onProcessStarting(const QString &appId) Application *application = findApplication(appId); if (!application) { // then shell did not start this application, so ubuntu-app-launch must have - add to list application = new Application( - m_taskController, m_sharedWakelock, m_desktopFileReaderFactory->createInstance(appId, m_taskController->findDesktopFileForAppId(appId)), - Application::Starting, QStringList(), this); @@ -530,17 +407,17 @@ void ApplicationManager::onProcessStarting(const QString &appId) Q_EMIT focusRequested(appId); } else { - // url-dispatcher can relaunch apps which have been OOM-killed - AppMan must accept the newly spawned - // application and focus it immediately (as user expects app to still be running). if (application->state() == Application::Stopped) { + // url-dispatcher can relaunch apps which have been OOM-killed - AppMan must accept the newly spawned + // application and focus it immediately (as user expects app to still be running). qCDebug(QTMIR_APPLICATIONS) << "Stopped application appId=" << appId << "is being resumed externally"; - application->setState(Application::Starting); Q_EMIT focusRequested(appId); } else { qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onProcessStarting application already found with appId" << appId; } } + application->setProcessState(Application::ProcessRunning); } /** @@ -559,14 +436,7 @@ bool ApplicationManager::stopApplication(const QString &inputAppId) return false; } - if (application == m_focusedApplication) { - // unfocus, and let shell decide what next to focus - m_focusedApplication = nullptr; - Q_EMIT focusedApplicationIdChanged(); - } - remove(application); - m_dbusWindowStack->WindowDestroyed(0, appId); bool result = m_taskController->stop(application->longAppId()); @@ -583,10 +453,7 @@ bool ApplicationManager::stopApplication(const QString &inputAppId) void ApplicationManager::onProcessFailed(const QString &appId, const bool duringStartup) { - /* Applications fail if they fail to launch, crash or are killed. If failed to start, must - * immediately remove from list of applications. If crash or kill, instead we set flag on the - * Application to indicate it can be resumed. - */ + // Applications fail if they fail to launch, crash or are killed. qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onProcessFailed - appId=" << appId << "duringStartup=" << duringStartup; @@ -598,20 +465,8 @@ void ApplicationManager::onProcessFailed(const QString &appId, const bool during } Q_UNUSED(duringStartup); // FIXME(greyback) upstart reports app that fully started up & crashes as failing during startup?? - if (application->state() == Application::Starting) { - if (application == m_focusedApplication) { - m_focusedApplication = nullptr; - Q_EMIT focusedApplicationIdChanged(); - } - remove(application); - m_dbusWindowStack->WindowDestroyed(0, application->appId()); - delete application; - } else { - // We need to set flags on the Application to say the app can be resumed, and thus should not be removed - // from the list by onProcessStopped. - application->setCanBeResumed(true); - application->setPid(0); - } + application->setProcessState(Application::ProcessStopped); + application->setPid(0); } void ApplicationManager::onProcessStopped(const QString &appId) @@ -626,29 +481,15 @@ void ApplicationManager::onProcessStopped(const QString &appId) return; } - // if shell did not stop the application, but ubuntu-app-launch says it died, we assume the process has been - // killed, so it can be respawned later. Only exception is if that application is focused or running - // as then it most likely crashed. Update this logic when ubuntu-app-launch gives some failure info. - bool removeApplication = true; - - if (application == m_focusedApplication) { - // Very bad case where focused application dies. Remove from list. Should give error message - m_focusedApplication = nullptr; - Q_EMIT focusedApplicationIdChanged(); - } - - // The following scenario is the only time that we do NOT remove the application from the app list: - if ((application->state() == Application::Suspended || application->state() == Application::Stopped) - && application->pid() == 0 // i.e. onProcessFailed was called, which resets the PID of this application - && application->canBeResumed()) { - removeApplication = false; - } + application->setProcessState(Application::ProcessStopped); + application->setPid(0); +} - if (removeApplication) { - qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onProcessStopped - removing appId=" << appId; - remove(application); - m_dbusWindowStack->WindowDestroyed(0, application->appId()); - delete application; +void ApplicationManager::onProcessSuspended(const QString &appId) +{ + Application *application = findApplication(appId); + if (application) { + application->setProcessState(Application::ProcessSuspended); } } @@ -670,10 +511,10 @@ void ApplicationManager::onResumeRequested(const QString& appId) return; } - // If app Stopped, trust that ubuntu-app-launch respawns it itself, and AppManager will - // be notified of that through the onProcessStartReportReceived slot. Else resume. + // We interpret this as a focus request for a suspended app. + // Shell will have this app resumed if it complies with the focus request if (application->state() == Application::Suspended) { - application->setState(Application::Running); + Q_EMIT focusRequested(appId); } } @@ -693,7 +534,7 @@ void ApplicationManager::onAppDataChanged(const int role) void ApplicationManager::onSettingsChanged(const QString &key) { if (key == "lifecycleExemptAppids") { - m_lifecycleExceptions = m_settings->get("lifecycleExemptAppids").toStringList(); + Application::lifecycleExceptions = m_settings->get("lifecycleExemptAppids").toStringList(); } } @@ -732,7 +573,6 @@ void ApplicationManager::authorizeSession(const quint64 pid, bool &authorized) if (info->startsWith("maliit-server") || info->contains("qt5/libexec/QtWebProcess")) { authorized = true; - m_hiddenPIDs << pid; return; } @@ -792,15 +632,12 @@ void ApplicationManager::authorizeSession(const quint64 pid, bool &authorized) QStringList arguments(info->asStringList()); application = new Application( - m_taskController, m_sharedWakelock, desktopData, - Application::Starting, arguments, this); application->setPid(pid); application->setStage(stage); - application->setCanBeResumed(false); add(application); authorized = true; } @@ -812,51 +649,10 @@ void ApplicationManager::onSessionStarting(std::shared_ptr<ms::Session> const& s void ApplicationManager::onSessionStopping(std::shared_ptr<ms::Session> const& session) { - qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onSessionStopping - sessionName=" << session->name().c_str(); - - // in case application closed not by hand of shell, check again here: Application* application = findApplicationWithSession(session); if (application) { - /* Can remove the application from the running apps list immediately in these curcumstances: - * 1. application is not managed by upstart (this message from Mir is only notice the app has stopped, must do - * it here) - * 2. application is managed by upstart, but has stopped before it managed to create a surface, we can assume - * it crashed on startup, and thus cannot be resumed - so remove it. - * 3. application is managed by upstart and is in foreground (i.e. has Running state), if Mir reports the - * application disconnects, it either crashed or stopped itself. Either case, remove it. - */ - if (!application->canBeResumed() - || application->state() == Application::Starting - || application->state() == Application::Running) { - m_dbusWindowStack->WindowDestroyed(0, application->appId()); - remove(application); - - // (ricmm) -- To be on the safe side, better wipe the application QML compile cache if it crashes on startup - QString path(QDir::homePath() + QStringLiteral("/.cache/QML/Apps/")); - QDir dir(path); - QStringList apps = dir.entryList(); - for (int i = 0; i < apps.size(); i++) { - if (apps.at(i).contains(application->appId())) { - qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onSessionStopping appId=" << apps.at(i) << " Wiping QML Cache"; - dir.cd(apps.at(i)); - dir.removeRecursively(); - break; - } - } - - delete application; - - if (application == m_focusedApplication) { - m_focusedApplication = nullptr; - Q_EMIT focusedApplicationIdChanged(); - } - } else { - // otherwise, we do not have enough information to make any changes to the model, so await events from - // upstart to go further, but set the app state - application->setState(Application::Stopped); - } + m_dbusWindowStack->WindowDestroyed(0, application->appId()); } - m_hiddenPIDs.removeOne(session->process_id()); } void ApplicationManager::onSessionCreatedSurface(ms::Session const* session, @@ -866,12 +662,8 @@ void ApplicationManager::onSessionCreatedSurface(ms::Session const* session, Q_UNUSED(surface); Application* application = findApplicationWithSession(session); - if (application && application->state() == Application::Starting) { + if (application) { m_dbusWindowStack->WindowCreated(0, application->appId()); - application->setState(Application::Running); - if ((application != m_mainStageApplication && application != m_sideStageApplication) || m_suspended) { - suspendApplication(application); - } } } @@ -900,16 +692,6 @@ Application* ApplicationManager::findApplicationWithPid(const qint64 pid) return nullptr; } -Application* ApplicationManager::applicationForStage(Application::Stage stage) -{ - qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::focusedApplicationForStage" << stage; - - if (stage == Application::MainStage) - return m_mainStageApplication; - else - return m_sideStageApplication; -} - void ApplicationManager::add(Application* application) { Q_ASSERT(application != nullptr); @@ -920,6 +702,28 @@ void ApplicationManager::add(Application* application) connect(application, &Application::stateChanged, this, [this](Application::State) { onAppDataChanged(RoleState); }); connect(application, &Application::stageChanged, this, [this](Application::Stage) { onAppDataChanged(RoleStage); }); + QString appId = application->appId(); + QString longAppId = application->longAppId(); + QStringList arguments = application->arguments(); + + // The connection is queued as a workaround an issue in the PhoneStage animation that + // happens when you tap on a killed app in the spread to bring it to foreground, causing + // a Application::respawn() to take place. + // In any case, it seems like in general QML works better when don't do too many things + // in the same event loop iteration. + connect(application, &Application::startProcessRequested, + this, [=]() { m_taskController->start(appId, arguments); }, + Qt::QueuedConnection); + + connect(application, &Application::suspendProcessRequested, this, [=]() { m_taskController->suspend(longAppId); } ); + connect(application, &Application::resumeProcessRequested, this, [=]() { m_taskController->resume(longAppId); } ); + + connect(application, &Application::stopped, this, [=]() { + remove(application); + application->deleteLater(); + }); + + beginInsertRows(QModelIndex(), m_applications.count(), m_applications.count()); m_applications.append(application); endInsertRows(); @@ -935,11 +739,6 @@ void ApplicationManager::remove(Application *application) Q_ASSERT(application != nullptr); qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::remove - appId=" << application->appId(); - if (application == m_sideStageApplication) - m_sideStageApplication = nullptr; - if (application == m_mainStageApplication) - m_mainStageApplication = nullptr; - application->disconnect(this); int i = m_applications.indexOf(application); @@ -953,6 +752,11 @@ void ApplicationManager::remove(Application *application) Q_EMIT emptyChanged(); } } + + if (application == m_focusedApplication) { + m_focusedApplication = nullptr; + Q_EMIT focusedApplicationIdChanged(); + } } void ApplicationManager::move(int from, int to) { diff --git a/src/modules/Unity/Application/application_manager.h b/src/modules/Unity/Application/application_manager.h index 77ee097..46cfa26 100644 --- a/src/modules/Unity/Application/application_manager.h +++ b/src/modules/Unity/Application/application_manager.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Canonical, Ltd. + * Copyright (C) 2013-2015 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License version 3, as published by @@ -93,10 +93,6 @@ public: // ApplicationManagerInterface QString focusedApplicationId() const override; - bool suspended() const override; - void setSuspended(bool suspended) override; - bool forceDashActive() const override; - void setForceDashActive(bool forceDashActive) override; Q_INVOKABLE qtmir::Application* get(int index) const override; Q_INVOKABLE qtmir::Application* findApplication(const QString &appId) const override; Q_INVOKABLE bool requestFocusApplication(const QString &appId) override; @@ -128,6 +124,7 @@ public Q_SLOTS: void onProcessStarting(const QString& appId); void onProcessStopped(const QString& appId); + void onProcessSuspended(const QString& appId); void onProcessFailed(const QString& appId, const bool duringStartup); void onFocusRequested(const QString& appId); void onResumeRequested(const QString& appId); @@ -146,9 +143,7 @@ private: void remove(Application* application); Application* findApplicationWithSession(const std::shared_ptr<mir::scene::Session> &session); Application* findApplicationWithSession(const mir::scene::Session *session); - Application* applicationForStage(Application::Stage stage); QModelIndex findIndex(Application* application); - bool suspendApplication(Application *application); void resumeApplication(Application *application); QString toString() const; @@ -158,9 +153,6 @@ private: QList<Application*> m_applications; Application* m_focusedApplication; - Application* m_mainStageApplication; - Application* m_sideStageApplication; - QStringList m_lifecycleExceptions; DBusWindowStack* m_dbusWindowStack; QSharedPointer<TaskController> m_taskController; QSharedPointer<DesktopFileReader::Factory> m_desktopFileReaderFactory; @@ -168,9 +160,6 @@ private: QSharedPointer<SharedWakelock> m_sharedWakelock; QSharedPointer<SettingsInterface> m_settings; static ApplicationManager* the_application_manager; - QList<pid_t> m_hiddenPIDs; - bool m_suspended; - bool m_forceDashActive; friend class Application; friend class DBusWindowStack; diff --git a/src/modules/Unity/Application/applicationcontroller.h b/src/modules/Unity/Application/applicationcontroller.h index 795ac3a..5046efc 100644 --- a/src/modules/Unity/Application/applicationcontroller.h +++ b/src/modules/Unity/Application/applicationcontroller.h @@ -57,8 +57,9 @@ Q_SIGNALS: void applicationAboutToBeStarted(const QString &appId); void applicationStarted(const QString &appId); void applicationStopped(const QString &appId); + void applicationPaused(const QString &appId); void applicationFocusRequest(const QString &appId); - void applicationResumeRequest(const QString &appId); + void applicationResumeRequested(const QString &appId); void applicationError(const QString &appId, ApplicationController::Error error); diff --git a/src/modules/Unity/Application/applicationscreenshotprovider.cpp b/src/modules/Unity/Application/applicationscreenshotprovider.cpp index c003f14..d782c33 100644 --- a/src/modules/Unity/Application/applicationscreenshotprovider.cpp +++ b/src/modules/Unity/Application/applicationscreenshotprovider.cpp @@ -55,7 +55,7 @@ QImage ApplicationScreenshotProvider::requestImage(const QString &imageId, QSize // TODO: if app not ready, return an app-provided splash image. If app has been stopped with saved state // return the screenshot that was saved to disk. - Session* session = app->session(); + SessionInterface* session = app->session(); if (!session || !session->session() || !session->session()->default_surface()) { qWarning() << "ApplicationScreenshotProvider - app session not found - asking for screenshot too early"; return QImage(); diff --git a/src/modules/Unity/Application/mirsurfaceitem.cpp b/src/modules/Unity/Application/mirsurfaceitem.cpp index 37337a4..53331e4 100644 --- a/src/modules/Unity/Application/mirsurfaceitem.cpp +++ b/src/modules/Unity/Application/mirsurfaceitem.cpp @@ -23,7 +23,6 @@ #include "mirbuffersgtexture.h" #include "session.h" #include "mirsurfaceitem.h" -#include "mirshell.h" #include "logging.h" #include "ubuntukeyboardinfo.h" @@ -47,6 +46,7 @@ #include <mir/geometry/rectangle.h> #include <mir/events/event_builders.h> #include <mir_toolkit/event.h> +#include <mir/shell/shell.h> namespace mg = mir::graphics; @@ -190,7 +190,7 @@ MirSurfaceItem::MirSurfaceItem(std::shared_ptr<mir::scene::Surface> surface, MirShell *shell, std::shared_ptr<SurfaceObserver> observer, QQuickItem *parent) - : QQuickItem(parent) + : MirSurfaceItemInterface(parent) , m_surface(surface) , m_session(session) , m_shell(shell) @@ -375,7 +375,7 @@ void MirSurfaceItem::surfaceDamaged() { if (!m_firstFrameDrawn) { m_firstFrameDrawn = true; - Q_EMIT firstFrameDrawn(this); + Q_EMIT firstFrameDrawn(); } scheduleTextureUpdate(); @@ -561,7 +561,7 @@ void MirSurfaceItem::endCurrentTouchSequence(ulong timestamp) touchEvent.updateTouchPointStatesAndType(); - auto ev = makeMirEvent(touchEvent.modifiers, touchEvent.touchPoints, + auto ev = makeMirEvent(touchEvent.modifiers, touchEvent.touchPoints, touchEvent.touchPointStates, touchEvent.timestamp); m_surface->consume(*ev); @@ -672,7 +672,7 @@ void MirSurfaceItem::setState(const State &state) } } -void MirSurfaceItem::setLive(const bool live) +void MirSurfaceItem::setLive(bool live) { if (m_live != live) { m_live = live; diff --git a/src/modules/Unity/Application/mirsurfaceitem.h b/src/modules/Unity/Application/mirsurfaceitem.h index fd3ab45..952d3cd 100644 --- a/src/modules/Unity/Application/mirsurfaceitem.h +++ b/src/modules/Unity/Application/mirsurfaceitem.h @@ -22,8 +22,6 @@ // Qt #include <QMutex> #include <QPointer> -#include <QSet> -#include <QQuickItem> #include <QTimer> #include <QQmlListProperty> @@ -31,34 +29,23 @@ #include <mir/scene/surface.h> #include <mir_toolkit/common.h> +#include "mirsurfaceiteminterface.h" #include "session_interface.h" +namespace mir { namespace shell { class Shell; }} + class SurfaceObserver; -class MirShell; +using MirShell = mir::shell::Shell; namespace qtmir { class MirSurfaceManager; class QSGMirSurfaceNode; class QMirSurfaceTextureProvider; -class Application; -class MirSurfaceItem : public QQuickItem +class MirSurfaceItem : public MirSurfaceItemInterface { Q_OBJECT - Q_ENUMS(Type) - Q_ENUMS(State) - Q_ENUMS(OrientationAngle) - - Q_PROPERTY(Type type READ type NOTIFY typeChanged) - Q_PROPERTY(State state READ state NOTIFY stateChanged) - Q_PROPERTY(QString name READ name NOTIFY nameChanged) - Q_PROPERTY(bool live READ live NOTIFY liveChanged) - - // How many degrees, clockwise, the UI in the surface has to rotate to match with the - // shell UI orientation - Q_PROPERTY(OrientationAngle orientationAngle READ orientationAngle WRITE setOrientationAngle - NOTIFY orientationAngleChanged DESIGNABLE false) public: explicit MirSurfaceItem(std::shared_ptr<mir::scene::Surface> surface, @@ -66,57 +53,29 @@ public: MirShell *shell, std::shared_ptr<SurfaceObserver> observer, QQuickItem *parent = 0); - ~MirSurfaceItem(); - - enum Type { - Normal = mir_surface_type_normal, - Utility = mir_surface_type_utility, - Dialog = mir_surface_type_dialog, - Overlay = mir_surface_type_overlay, - Freestyle = mir_surface_type_freestyle, - Popover = mir_surface_type_popover, - InputMethod = mir_surface_type_inputmethod, - }; - - enum State { - Unknown = mir_surface_state_unknown, - Restored = mir_surface_state_restored, - Minimized = mir_surface_state_minimized, - Maximized = mir_surface_state_maximized, - VertMaximized = mir_surface_state_vertmaximized, - /* SemiMaximized = mir_surface_state_semimaximized, // see mircommon/mir_toolbox/common.h*/ - Fullscreen = mir_surface_state_fullscreen, - }; - - enum OrientationAngle { - Angle0 = 0, - Angle90 = 90, - Angle180 = 180, - Angle270 = 270 - }; + virtual ~MirSurfaceItem(); //getters - Type type() const; - State state() const; - QString name() const; - bool live() const; - SessionInterface *session() const; + Type type() const override; + State state() const override; + QString name() const override; + bool live() const override; + SessionInterface *session() const override; + OrientationAngle orientationAngle() const override; - Q_INVOKABLE void release(); + Q_INVOKABLE void release() override; // Item surface/texture management bool isTextureProvider() const { return true; } QSGTextureProvider *textureProvider() const; - void stopFrameDropper(); - void startFrameDropper(); - - bool isFirstFrameDrawn() const { return m_firstFrameDrawn; } + void stopFrameDropper() override; + void startFrameDropper() override; - OrientationAngle orientationAngle() const; - void setOrientationAngle(OrientationAngle angle); + bool isFirstFrameDrawn() const override { return m_firstFrameDrawn; } - void setSession(SessionInterface *app); + void setOrientationAngle(OrientationAngle angle) override; + void setSession(SessionInterface *app) override; // to allow easy touch event injection from tests bool processTouchEvent(int eventType, @@ -125,14 +84,6 @@ public: const QList<QTouchEvent::TouchPoint> &touchPoints, Qt::TouchPointStates touchPointStates); -Q_SIGNALS: - void typeChanged(); - void stateChanged(); - void nameChanged(); - void orientationAngleChanged(OrientationAngle angle); - void liveChanged(bool live); - void firstFrameDrawn(MirSurfaceItem *item); - protected Q_SLOTS: void onSessionStateChanged(SessionInterface::State state); @@ -169,7 +120,7 @@ private: void setType(const Type&); void setState(const State&); - void setLive(const bool); + void setLive(bool) override; // called by MirSurfaceManager void setSurfaceValid(const bool); @@ -226,13 +177,8 @@ private: QList<QTouchEvent::TouchPoint> touchPoints; Qt::TouchPointStates touchPointStates; } *m_lastTouchEvent; - - friend class MirSurfaceManager; }; } // namespace qtmir -Q_DECLARE_METATYPE(qtmir::MirSurfaceItem*) -Q_DECLARE_METATYPE(qtmir::MirSurfaceItem::OrientationAngle) - #endif // MIRSURFACEITEM_H diff --git a/src/modules/Unity/Application/mirsurfaceiteminterface.h b/src/modules/Unity/Application/mirsurfaceiteminterface.h new file mode 100644 index 0000000..b197cf4 --- /dev/null +++ b/src/modules/Unity/Application/mirsurfaceiteminterface.h @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2015 Canonical, Ltd. + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3, as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef MIRSURFACEITEMINTERFACE_H +#define MIRSURFACEITEMINTERFACE_H + +// Qt +#include <QQuickItem> + +// mir +#include <mir_toolkit/common.h> + +#include "session_interface.h" + +namespace qtmir { + +class MirSurfaceItemInterface : public QQuickItem +{ + Q_OBJECT + Q_ENUMS(Type) + Q_ENUMS(State) + Q_ENUMS(OrientationAngle) + + Q_PROPERTY(Type type READ type NOTIFY typeChanged) + Q_PROPERTY(State state READ state NOTIFY stateChanged) + Q_PROPERTY(QString name READ name NOTIFY nameChanged) + Q_PROPERTY(bool live READ live NOTIFY liveChanged) + + // How many degrees, clockwise, the UI in the surface has to rotate to match with the + // shell UI orientation + Q_PROPERTY(OrientationAngle orientationAngle READ orientationAngle WRITE setOrientationAngle + NOTIFY orientationAngleChanged DESIGNABLE false) + +public: + MirSurfaceItemInterface(QQuickItem *parent) : QQuickItem(parent) {} + virtual ~MirSurfaceItemInterface() {} + + enum Type { + Normal = mir_surface_type_normal, + Utility = mir_surface_type_utility, + Dialog = mir_surface_type_dialog, + Overlay = mir_surface_type_overlay, + Freestyle = mir_surface_type_freestyle, + Popover = mir_surface_type_popover, + InputMethod = mir_surface_type_inputmethod, + }; + + enum State { + Unknown = mir_surface_state_unknown, + Restored = mir_surface_state_restored, + Minimized = mir_surface_state_minimized, + Maximized = mir_surface_state_maximized, + VertMaximized = mir_surface_state_vertmaximized, + /* SemiMaximized = mir_surface_state_semimaximized, // see mircommon/mir_toolbox/common.h*/ + Fullscreen = mir_surface_state_fullscreen, + }; + + enum OrientationAngle { + Angle0 = 0, + Angle90 = 90, + Angle180 = 180, + Angle270 = 270 + }; + + //getters + virtual Type type() const = 0; + virtual State state() const = 0; + virtual QString name() const = 0; + virtual bool live() const = 0; + virtual SessionInterface *session() const = 0; + virtual OrientationAngle orientationAngle() const = 0; + + virtual Q_INVOKABLE void release() = 0; + + virtual void stopFrameDropper() = 0; + virtual void startFrameDropper() = 0; + + virtual bool isFirstFrameDrawn() const = 0; + + virtual void setOrientationAngle(OrientationAngle angle) = 0; + virtual void setSession(SessionInterface *app) = 0; + +Q_SIGNALS: + void typeChanged(); + void stateChanged(); + void nameChanged(); + void orientationAngleChanged(OrientationAngle angle); + void liveChanged(bool live); + void firstFrameDrawn(); + +private: + virtual void setLive(bool) = 0; + + friend class MirSurfaceManager; +}; + +} // namespace qtmir + +Q_DECLARE_METATYPE(qtmir::MirSurfaceItemInterface*) +Q_DECLARE_METATYPE(qtmir::MirSurfaceItemInterface::OrientationAngle) + +#endif // MIRSURFACEITEMINTERFACE_H + diff --git a/src/modules/Unity/Application/mirsurfaceitemmodel.h b/src/modules/Unity/Application/mirsurfaceitemmodel.h index 2d81c11..d0d09f4 100644 --- a/src/modules/Unity/Application/mirsurfaceitemmodel.h +++ b/src/modules/Unity/Application/mirsurfaceitemmodel.h @@ -22,8 +22,8 @@ namespace qtmir { -class MirSurfaceItem; -typedef ObjectListModel<MirSurfaceItem> MirSurfaceItemModel; +class MirSurfaceItemInterface; +typedef ObjectListModel<MirSurfaceItemInterface> MirSurfaceItemModel; } // namespace qtmir diff --git a/src/modules/Unity/Application/mirsurfacemanager.cpp b/src/modules/Unity/Application/mirsurfacemanager.cpp index 183c3a5..9b32363 100644 --- a/src/modules/Unity/Application/mirsurfacemanager.cpp +++ b/src/modules/Unity/Application/mirsurfacemanager.cpp @@ -31,7 +31,6 @@ #include "nativeinterface.h" #include "mirserver.h" #include "sessionlistener.h" -#include "mirshell.h" #include "logging.h" Q_LOGGING_CATEGORY(QTMIR_SURFACES, "qtmir.surfaces") @@ -112,11 +111,11 @@ void MirSurfaceManager::onSessionCreatedSurface(const mir::scene::Session *mirSe session->setSurface(qmlSurface); // Only notify QML of surface creation once it has drawn its first frame. - connect(qmlSurface, &MirSurfaceItem::firstFrameDrawn, this, [&](MirSurfaceItem *item) { + connect(qmlSurface, &MirSurfaceItemInterface::firstFrameDrawn, this, [=]() { tracepoint(qtmir, firstFrameDrawn); - Q_EMIT surfaceCreated(item); + Q_EMIT surfaceCreated(qmlSurface); - insert(0, item); + insert(0, qmlSurface); }); // clean up after MirSurfaceItem is destroyed @@ -139,7 +138,7 @@ void MirSurfaceManager::onSessionDestroyingSurface(const mir::scene::Session *se qCDebug(QTMIR_SURFACES) << "MirSurfaceManager::onSessionDestroyingSurface - session=" << session << "surface=" << surface.get() << "surface.name=" << surface->name().c_str(); - MirSurfaceItem* item = nullptr; + MirSurfaceItemInterface* item = nullptr; { QMutexLocker lock(&m_mutex); auto it = m_mirSurfaceToItemHash.find(surface.get()); diff --git a/src/modules/Unity/Application/mirsurfacemanager.h b/src/modules/Unity/Application/mirsurfacemanager.h index b9e5533..83ede11 100644 --- a/src/modules/Unity/Application/mirsurfacemanager.h +++ b/src/modules/Unity/Application/mirsurfacemanager.h @@ -40,7 +40,6 @@ namespace mir { } class MirServer; -class MirShell; namespace qtmir { @@ -64,8 +63,8 @@ public: static MirSurfaceManager* singleton(); Q_SIGNALS: - void surfaceCreated(MirSurfaceItem* surface); - void surfaceDestroyed(MirSurfaceItem* surface); + void surfaceCreated(MirSurfaceItemInterface* surface); + void surfaceDestroyed(MirSurfaceItemInterface* surface); // void surfaceResized(MirSurface*); // void fullscreenSurfaceChanged(); @@ -74,7 +73,7 @@ public Q_SLOTS: void onSessionDestroyingSurface(const mir::scene::Session *, const std::shared_ptr<mir::scene::Surface> &); protected: - QHash<const mir::scene::Surface *, MirSurfaceItem *> m_mirSurfaceToItemHash; + QHash<const mir::scene::Surface *, MirSurfaceItemInterface *> m_mirSurfaceToItemHash; QMutex m_mutex; private: diff --git a/src/modules/Unity/Application/plugin.cpp b/src/modules/Unity/Application/plugin.cpp index 37ff7b4..5d03c43 100644 --- a/src/modules/Unity/Application/plugin.cpp +++ b/src/modules/Unity/Application/plugin.cpp @@ -73,7 +73,7 @@ class UnityApplicationPlugin : public QQmlExtensionPlugin { qRegisterMetaType<qtmir::ApplicationManager*>("ApplicationManager*"); //need for queueing signals qRegisterMetaType<qtmir::Application*>("Application*"); - qRegisterMetaType<qtmir::MirSurfaceItem*>("MirSurfaceItem*"); + qRegisterMetaType<qtmir::MirSurfaceItemInterface*>("MirSurfaceItemInterface*"); qRegisterMetaType<qtmir::MirSurfaceItemModel*>("MirSurfaceItemModel*"); qRegisterMetaType<qtmir::Session*>("Session*"); qRegisterMetaType<qtmir::SessionInterface*>("SessionInterface*"); @@ -92,7 +92,7 @@ class UnityApplicationPlugin : public QQmlExtensionPlugin { uri, 0, 1, "SurfaceManager", surfaceManagerSingleton); qmlRegisterSingletonType<qtmir::SessionManager>( uri, 0, 1, "SessionManager", sessionManagerSingleton); - qmlRegisterUncreatableType<qtmir::MirSurfaceItem>( + qmlRegisterUncreatableType<qtmir::MirSurfaceItemInterface>( uri, 0, 1, "MirSurfaceItem", "MirSurfaceItem can't be instantiated from QML"); qmlRegisterUncreatableType<qtmir::Session>( uri, 0, 1, "Session", "Session can't be instantiated from QML"); diff --git a/src/modules/Unity/Application/session.cpp b/src/modules/Unity/Application/session.cpp index 6999a37..2cd1024 100644 --- a/src/modules/Unity/Application/session.cpp +++ b/src/modules/Unity/Application/session.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 Canonical, Ltd. + * Copyright (C) 2014,2015 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -60,14 +60,7 @@ Session::Session(const std::shared_ptr<ms::Session>& session, QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); m_suspendTimer->setSingleShot(true); - connect(m_suspendTimer, &QTimer::timeout, this, [this]() { - if (m_surface) { - m_surface->stopFrameDropper(); - } else { - qDebug() << "Application::suspend - no surface to call stopFrameDropper() on!"; - } - Q_EMIT suspended(); - }); + connect(m_suspendTimer, &QTimer::timeout, this, &Session::doSuspend); } Session::~Session() @@ -89,6 +82,17 @@ Session::~Session() delete m_children; m_children = nullptr; } +void Session::doSuspend() +{ + Q_ASSERT(m_state == Session::Suspending); + if (m_surface) { + m_surface->stopFrameDropper(); + } else { + qDebug() << "Application::suspend - no surface to call stopFrameDropper() on!"; + } + setState(Suspended); +} + void Session::release() { qCDebug(QTMIR_SESSIONS) << "Session::release " << name(); @@ -120,7 +124,7 @@ ApplicationInfoInterface* Session::application() const return m_application; } -MirSurfaceItem* Session::surface() const +MirSurfaceItemInterface* Session::surface() const { // Only notify QML of surface creation once it has drawn its first frame. if (m_surface && m_surface->isFirstFrameDrawn()) { @@ -140,6 +144,13 @@ Session::State Session::state() const return m_state; } +void Session::setState(State state) { + if (state != m_state) { + m_state = state; + Q_EMIT stateChanged(m_state); + } +} + bool Session::fullscreen() const { return m_fullscreen; @@ -159,7 +170,7 @@ void Session::setApplication(ApplicationInfoInterface* application) Q_EMIT applicationChanged(application); } -void Session::setSurface(MirSurfaceItem *newSurface) +void Session::setSurface(MirSurfaceItemInterface *newSurface) { qCDebug(QTMIR_SESSIONS) << "Session::setSurface - session=" << name() << "surface=" << newSurface; @@ -173,34 +184,46 @@ void Session::setSurface(MirSurfaceItem *newSurface) m_surface->setParent(nullptr); } - MirSurfaceItem *previousSurface = surface(); + MirSurfaceItemInterface *previousSurface = surface(); m_surface = newSurface; if (newSurface) { m_surface->setParent(this); m_surface->setSession(this); + connect(newSurface, &MirSurfaceItemInterface::stateChanged, + this, &Session::updateFullscreenProperty); + // Only notify QML of surface creation once it has drawn its first frame. - if (!surface()) { - connect(newSurface, &MirSurfaceItem::firstFrameDrawn, - this, [this] { Q_EMIT surfaceChanged(m_surface); }); + if (m_surface->isFirstFrameDrawn()) { + setState(Running); + } else { + connect(newSurface, &MirSurfaceItemInterface::firstFrameDrawn, + this, &Session::onFirstSurfaceFrameDrawn); } - - connect(newSurface, &MirSurfaceItem::stateChanged, - this, &Session::updateFullscreenProperty); } if (previousSurface != surface()) { + qCDebug(QTMIR_SESSIONS).nospace() << "Session::surfaceChanged - session=" << this + << " surface=" << m_surface; Q_EMIT surfaceChanged(m_surface); } updateFullscreenProperty(); } +void Session::onFirstSurfaceFrameDrawn() +{ + qCDebug(QTMIR_SESSIONS).nospace() << "Session::surfaceChanged - session=" << this + << " surface=" << m_surface; + Q_EMIT surfaceChanged(m_surface); + setState(Running); +} + void Session::updateFullscreenProperty() { if (m_surface) { - setFullscreen(m_surface->state() == MirSurfaceItem::Fullscreen); + setFullscreen(m_surface->state() == MirSurfaceItemInterface::Fullscreen); } else { // Keep the current value of the fullscreen property until we get a new // surface @@ -216,59 +239,71 @@ void Session::setFullscreen(bool fullscreen) } } -void Session::setState(State state) +void Session::suspend() { - qCDebug(QTMIR_SESSIONS) << "Session::setState - session=" << this << "state=" << applicationStateToStr(state); - if (m_state != state) { - switch (state) - { - case Session::State::Suspended: - if (m_state == Session::State::Running) { - session()->set_lifecycle_state(mir_lifecycle_state_will_suspend); - m_suspendTimer->start(1500); - } - break; - case Session::State::Running: - if (m_suspendTimer->isActive()) - m_suspendTimer->stop(); - - if (m_state == Session::State::Suspended) { - if (m_surface) - m_surface->startFrameDropper(); - Q_EMIT resumed(); - session()->set_lifecycle_state(mir_lifecycle_state_resumed); - } - break; - case Session::State::Stopped: - stopPromptSessions(); - if (m_suspendTimer->isActive()) - m_suspendTimer->stop(); - if (m_surface) - m_surface->stopFrameDropper(); - break; - default: - break; - } + qCDebug(QTMIR_SESSIONS) << "Session::suspend - session=" << this << "state=" << applicationStateToStr(m_state); + if (m_state == Running) { + session()->set_lifecycle_state(mir_lifecycle_state_will_suspend); + m_suspendTimer->start(1500); - m_state = state; - Q_EMIT stateChanged(state); - - foreachPromptSession([this, state](const std::shared_ptr<ms::PromptSession>& promptSession) { - switch (state) { - case Session::State::Suspended: - m_promptSessionManager->suspend_prompt_session(promptSession); - break; - case Session::State::Running: - m_promptSessionManager->resume_prompt_session(promptSession); - break; - default: - break; - } + foreachPromptSession([this](const std::shared_ptr<ms::PromptSession>& promptSession) { + m_promptSessionManager->suspend_prompt_session(promptSession); }); - foreachChildSession([state](SessionInterface* session) { - session->setState(state); + foreachChildSession([](SessionInterface* session) { + session->suspend(); }); + + setState(Suspending); + } +} + +void Session::resume() +{ + qCDebug(QTMIR_SESSIONS) << "Session::resume - session=" << this << "state=" << applicationStateToStr(m_state); + + if (m_state == Suspending || m_state == Suspended) { + doResume(); + } +} + +void Session::doResume() +{ + if (m_state == Suspending) { + Q_ASSERT(m_suspendTimer->isActive()); + m_suspendTimer->stop(); + } else if (m_state == Suspended) { + Q_ASSERT(m_surface); + m_surface->startFrameDropper(); + } + + session()->set_lifecycle_state(mir_lifecycle_state_resumed); + + foreachPromptSession([this](const std::shared_ptr<ms::PromptSession>& promptSession) { + m_promptSessionManager->resume_prompt_session(promptSession); + }); + + foreachChildSession([](SessionInterface* session) { + session->resume(); + }); + + setState(Running); +} + +void Session::stop() +{ + if (m_state != Stopped) { + stopPromptSessions(); + if (m_suspendTimer->isActive()) + m_suspendTimer->stop(); + if (m_surface) + m_surface->stopFrameDropper(); + + foreachChildSession([](SessionInterface* session) { + session->stop(); + }); + + setState(Stopped); } } @@ -277,6 +312,9 @@ void Session::setLive(const bool live) if (m_live != live) { m_live = live; Q_EMIT liveChanged(m_live); + if (!live) { + setState(Stopped); + } } } @@ -302,7 +340,19 @@ void Session::insertChildSession(uint index, SessionInterface* session) static_cast<Session*>(session)->setParentSession(this); m_children->insert(index, session); - session->setState(state()); + switch (m_state) { + case Starting: + case Running: + session->resume(); + break; + case Suspending: + case Suspended: + session->suspend(); + break; + case Stopped: + session->stop(); + break; + } } void Session::removeChildSession(SessionInterface* session) diff --git a/src/modules/Unity/Application/session.h b/src/modules/Unity/Application/session.h index 36ffb45..cbc4cac 100644 --- a/src/modules/Unity/Application/session.h +++ b/src/modules/Unity/Application/session.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 Canonical, Ltd. + * Copyright (C) 2014-2015 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -52,15 +52,18 @@ public: //getters QString name() const override; unity::shell::application::ApplicationInfoInterface* application() const override; - MirSurfaceItem* surface() const override; + MirSurfaceItemInterface* surface() const override; SessionInterface* parentSession() const override; State state() const override; bool fullscreen() const override; bool live() const override; void setApplication(unity::shell::application::ApplicationInfoInterface* item) override; - void setSurface(MirSurfaceItem* surface) override; - void setState(State state) override; + void setSurface(MirSurfaceItemInterface* surface) override; + + void suspend() override; + void resume() override; + void stop() override; void addChildSession(SessionInterface* session) override; void insertChildSession(uint index, SessionInterface* session) override; @@ -74,23 +77,29 @@ public: SessionModel* childSessions() const override; -protected: void setFullscreen(bool fullscreen) override; void setLive(const bool) override; void appendPromptSession(const std::shared_ptr<mir::scene::PromptSession>& session) override; void removePromptSession(const std::shared_ptr<mir::scene::PromptSession>& session) override; +public Q_SLOTS: + // it's public to ease testing + void doSuspend(); + private Q_SLOTS: void updateFullscreenProperty(); + void onFirstSurfaceFrameDrawn(); private: void setParentSession(Session* session); + void setState(State state); + void doResume(); void stopPromptSessions(); std::shared_ptr<mir::scene::Session> m_session; Application* m_application; - MirSurfaceItem* m_surface; + MirSurfaceItemInterface* m_surface; SessionInterface* m_parentSession; SessionModel* m_children; bool m_fullscreen; @@ -103,6 +112,4 @@ private: } // namespace qtmir -Q_DECLARE_METATYPE(qtmir::Session*) - #endif // SESSION_H diff --git a/src/modules/Unity/Application/session_interface.h b/src/modules/Unity/Application/session_interface.h index 526a96a..186e372 100644 --- a/src/modules/Unity/Application/session_interface.h +++ b/src/modules/Unity/Application/session_interface.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 Canonical, Ltd. + * Copyright (C) 2014,2015 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -35,11 +35,11 @@ namespace mir { namespace qtmir { -class MirSurfaceItem; +class MirSurfaceItemInterface; class SessionInterface : public QObject { Q_OBJECT - Q_PROPERTY(MirSurfaceItem* surface READ surface NOTIFY surfaceChanged) + Q_PROPERTY(MirSurfaceItemInterface* surface READ surface NOTIFY surfaceChanged) Q_PROPERTY(unity::shell::application::ApplicationInfoInterface* application READ application NOTIFY applicationChanged DESIGNABLE false) Q_PROPERTY(SessionInterface* parentSession READ parentSession NOTIFY parentSessionChanged DESIGNABLE false) Q_PROPERTY(SessionModel* childSessions READ childSessions DESIGNABLE false CONSTANT) @@ -49,58 +49,66 @@ public: SessionInterface(QObject *parent = 0) : QObject(parent) {} virtual ~SessionInterface() {} - // Session State - typedef unity::shell::application::ApplicationInfoInterface::State State; + enum State { + Starting, + Running, + Suspending, + Suspended, + Stopped + }; Q_INVOKABLE virtual void release() = 0; //getters virtual QString name() const = 0; virtual unity::shell::application::ApplicationInfoInterface* application() const = 0; - virtual MirSurfaceItem* surface() const = 0; + virtual MirSurfaceItemInterface* surface() const = 0; virtual SessionInterface* parentSession() const = 0; + virtual SessionModel* childSessions() const = 0; virtual State state() const = 0; virtual bool fullscreen() const = 0; virtual bool live() const = 0; + virtual std::shared_ptr<mir::scene::Session> session() const = 0; + + // For MirSurfaceItem and MirSurfaceManager use + + virtual void setSurface(MirSurfaceItemInterface* surface) = 0; + + // For Application use + virtual void setApplication(unity::shell::application::ApplicationInfoInterface* item) = 0; - virtual void setSurface(MirSurfaceItem* surface) = 0; - virtual void setState(State state) = 0; + virtual void suspend() = 0; + virtual void resume() = 0; + virtual void stop() = 0; + + // For SessionManager use virtual void addChildSession(SessionInterface* session) = 0; virtual void insertChildSession(uint index, SessionInterface* session) = 0; virtual void removeChildSession(SessionInterface* session) = 0; virtual void foreachChildSession(std::function<void(SessionInterface* session)> f) const = 0; - virtual std::shared_ptr<mir::scene::Session> session() const = 0; - virtual std::shared_ptr<mir::scene::PromptSession> activePromptSession() const = 0; virtual void foreachPromptSession(std::function<void(const std::shared_ptr<mir::scene::PromptSession>&)> f) const = 0; - virtual SessionModel* childSessions() const = 0; + virtual void setFullscreen(bool fullscreen) = 0; + virtual void setLive(const bool) = 0; + virtual void appendPromptSession(const std::shared_ptr<mir::scene::PromptSession>& session) = 0; + virtual void removePromptSession(const std::shared_ptr<mir::scene::PromptSession>& session) = 0; Q_SIGNALS: - void surfaceChanged(MirSurfaceItem*); + void surfaceChanged(MirSurfaceItemInterface*); void parentSessionChanged(SessionInterface*); void applicationChanged(unity::shell::application::ApplicationInfoInterface* application); void aboutToBeDestroyed(); void stateChanged(State state); void fullscreenChanged(bool fullscreen); void liveChanged(bool live); - - void suspended(); - void resumed(); - -protected: - virtual void setFullscreen(bool fullscreen) = 0; - virtual void setLive(const bool) = 0; - virtual void appendPromptSession(const std::shared_ptr<mir::scene::PromptSession>& session) = 0; - virtual void removePromptSession(const std::shared_ptr<mir::scene::PromptSession>& session) = 0; - - friend class SessionManager; }; } // namespace qtmir +Q_DECLARE_METATYPE(qtmir::SessionInterface*) #endif // SESSION_INTERFACE_H diff --git a/src/modules/Unity/Application/sessionmanager.cpp b/src/modules/Unity/Application/sessionmanager.cpp index 75c4ba0..9b5e14f 100644 --- a/src/modules/Unity/Application/sessionmanager.cpp +++ b/src/modules/Unity/Application/sessionmanager.cpp @@ -26,7 +26,6 @@ #include "nativeinterface.h" #include "mirserver.h" #include "sessionlistener.h" -#include "mirshell.h" #include "logging.h" #include "promptsessionlistener.h" diff --git a/src/modules/Unity/Application/taskcontroller.cpp b/src/modules/Unity/Application/taskcontroller.cpp index e61a46d..1d9ef0d 100644 --- a/src/modules/Unity/Application/taskcontroller.cpp +++ b/src/modules/Unity/Application/taskcontroller.cpp @@ -50,14 +50,19 @@ TaskController::TaskController( &TaskController::processStopped); connect(m_appController.data(), + &ApplicationController::applicationPaused, + this, + &TaskController::processSuspended); + + connect(m_appController.data(), &ApplicationController::applicationFocusRequest, this, - &TaskController::onApplicationFocusRequest); + &TaskController::focusRequested); connect(m_appController.data(), - &ApplicationController::applicationResumeRequest, + &ApplicationController::applicationResumeRequested, this, - &TaskController::onApplicationResumeRequest); + &TaskController::resumeRequested); connect(m_appController.data(), &ApplicationController::applicationError, @@ -108,16 +113,6 @@ bool TaskController::resume(const QString &appId) return m_appController->resumeApplicationWithAppId(appId); } -void TaskController::onApplicationFocusRequest(const QString& id) -{ - Q_EMIT requestFocus(id); -} - -void TaskController::onApplicationResumeRequest(const QString& id) -{ - Q_EMIT requestResume(id); -} - void TaskController::onApplicationError(const QString& id, ApplicationController::Error error) { Q_EMIT processFailed(id, (error == ApplicationController::Error::APPLICATION_FAILED_TO_START) ); diff --git a/src/modules/Unity/Application/taskcontroller.h b/src/modules/Unity/Application/taskcontroller.h index 6a549a0..c9a5591 100644 --- a/src/modules/Unity/Application/taskcontroller.h +++ b/src/modules/Unity/Application/taskcontroller.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2014 Canonical, Ltd. + * Copyright (C) 2013-2015 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License version 3, as published by @@ -48,14 +48,12 @@ public: Q_SIGNALS: void processStarting(const QString &appId); void processStopped(const QString &appId); + void processSuspended(const QString &appId); void processFailed(const QString &appId, const bool duringStartup); - void requestFocus(const QString &appId); - void requestResume(const QString &appId); + void focusRequested(const QString &appId); + void resumeRequested(const QString &appId); private Q_SLOTS: - void onApplicationFocusRequest(const QString &id); - void onApplicationResumeRequest(const QString &id); - void onApplicationError(const QString &id, ApplicationController::Error error); private: diff --git a/src/modules/Unity/Application/upstart/applicationcontroller.cpp b/src/modules/Unity/Application/upstart/applicationcontroller.cpp index bc5d45c..4179285 100644 --- a/src/modules/Unity/Application/upstart/applicationcontroller.cpp +++ b/src/modules/Unity/Application/upstart/applicationcontroller.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 Canonical, Ltd. + * Copyright (C) 2014,2015 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License version 3, as published by @@ -40,6 +40,7 @@ struct ApplicationController::Private UbuntuAppLaunchAppObserver stopCallback = nullptr; UbuntuAppLaunchAppObserver focusCallback = nullptr; UbuntuAppLaunchAppObserver resumeCallback = nullptr; + UbuntuAppLaunchAppPausedResumedObserver pausedCallback = nullptr; UbuntuAppLaunchAppFailedObserver failureCallback = nullptr; }; @@ -125,7 +126,12 @@ ApplicationController::ApplicationController() impl->resumeCallback = [](const gchar * appId, gpointer userData) { auto thiz = static_cast<ApplicationController*>(userData); - Q_EMIT(thiz->applicationResumeRequest(toShortAppIdIfPossible(appId))); + Q_EMIT(thiz->applicationResumeRequested(toShortAppIdIfPossible(appId))); + }; + + impl->pausedCallback = [](const gchar * appId, GPid *, gpointer userData) { + auto thiz = static_cast<ApplicationController*>(userData); + Q_EMIT(thiz->applicationPaused(toShortAppIdIfPossible(appId))); }; impl->failureCallback = [](const gchar * appId, UbuntuAppLaunchAppFailed failureType, gpointer userData) { @@ -145,6 +151,7 @@ ApplicationController::ApplicationController() ubuntu_app_launch_observer_add_app_stop(impl->stopCallback, this); ubuntu_app_launch_observer_add_app_focus(impl->focusCallback, this); ubuntu_app_launch_observer_add_app_resume(impl->resumeCallback, this); + ubuntu_app_launch_observer_add_app_paused(impl->pausedCallback, this); ubuntu_app_launch_observer_add_app_failed(impl->failureCallback, this); } @@ -155,6 +162,7 @@ ApplicationController::~ApplicationController() ubuntu_app_launch_observer_delete_app_stop(impl->stopCallback, this); ubuntu_app_launch_observer_delete_app_focus(impl->focusCallback, this); ubuntu_app_launch_observer_delete_app_resume(impl->resumeCallback, this); + ubuntu_app_launch_observer_delete_app_paused(impl->pausedCallback, this); ubuntu_app_launch_observer_delete_app_failed(impl->failureCallback, this); } |