diff options
52 files changed, 1994 insertions, 1735 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 665acfe..8555ffa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,13 +50,13 @@ add_custom_target(cppcheck COMMAND cppcheck --enable=all -q --error-exitcode=2 ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/tests) include(FindPkgConfig) -find_package(Qt5Core 5.3 REQUIRED) -find_package(Qt5DBus 5.3 REQUIRED) -find_package(Qt5Gui 5.3 REQUIRED) -find_package(Qt5Qml 5.3 REQUIRED) -find_package(Qt5Quick 5.3 REQUIRED) -find_package(Qt5Sensors 5.3 REQUIRED) -find_package(Qt5Test 5.3 REQUIRED) +find_package(Qt5Core 5.4 REQUIRED) +find_package(Qt5DBus 5.4 REQUIRED) +find_package(Qt5Gui 5.4 REQUIRED) +find_package(Qt5Qml 5.4 REQUIRED) +find_package(Qt5Quick 5.4 REQUIRED) +find_package(Qt5Sensors 5.4 REQUIRED) +find_package(Qt5Test 5.4 REQUIRED) find_package(Threads REQUIRED) @@ -65,8 +65,8 @@ if(PROTOBUF_PROTOC_EXECUTABLE STREQUAL "PROTOBUF_PROTOC_EXECUTABLE-NOTFOUND") message(SEND_ERROR "protoc executable not found! Missing protobuf-compiler package?") endif() -pkg_check_modules(MIRSERVER mirserver>=0.13 REQUIRED) -pkg_check_modules(MIRCLIENT mirclient>=0.13 REQUIRED) +pkg_check_modules(MIRSERVER mirserver>=0.14 REQUIRED) +pkg_check_modules(MIRCLIENT mirclient>=0.14 REQUIRED) pkg_check_modules(GLIB glib-2.0 REQUIRED) pkg_check_modules(PROCESS_CPP process-cpp REQUIRED) @@ -80,7 +80,9 @@ pkg_check_modules(LTTNG lttng-ust) pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt) pkg_check_modules(QTDBUSTEST libqtdbustest-1 REQUIRED) pkg_check_modules(QTDBUSMOCK libqtdbusmock-1 REQUIRED) -pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6) +pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=7) + +include_directories(${APPLICATION_API_INCLUDE_DIRS}) add_definitions(-DMIR_REQUIRE_DEPRECATED_EVENT_OPT_IN=1) diff --git a/debian/changelog b/debian/changelog index a1e2064..5817a72 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,16 @@ +qtmir (0.4.5+15.10.20150728-0ubuntu2~gcc5.1) wily; urgency=medium + + * No change rebuild using GCC 5. + + -- Matthias Klose <doko@ubuntu.com> Wed, 29 Jul 2015 15:04:01 +0200 + +qtmir (0.4.5+15.10.20150728-0ubuntu1) wily; urgency=medium + + [ Gerry Boland ] + * Remove explicit gcc4.9 dependency (LP: #1452338) + + -- CI Train Bot <ci-train-bot@canonical.com> Tue, 28 Jul 2015 09:57:00 +0000 + qtmir (0.4.5+15.10.20150722-0ubuntu1) wily; urgency=medium [ Andreas Pokorny ] diff --git a/debian/control b/debian/control index 93bca1e..cf63849 100644 --- a/debian/control +++ b/debian/control @@ -5,12 +5,6 @@ Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com> Build-Depends: cmake, cmake-extras (>= 0.3), debhelper (>= 9), -# We rely on C++11 features, and to prevent from ABI breaks -# in libstdc++ causing us issues, we explicitly select a G++ -# version. To allow cross-compiling to work, we also must -# append :native to g++-4.9 so we don't try to run armhf g++ -# on an x86 CPU for example, when cross-compiling. - g++-4.9:native, google-mock (>= 1.6.0+svn437), libfontconfig1-dev, libgles2-mesa-dev, @@ -28,7 +22,7 @@ Build-Depends: cmake, libubuntu-app-launch2-dev, libubuntu-application-api-dev (>= 2.1.0), libudev-dev, - libunity-api-dev (>= 7.97), + libunity-api-dev (>= 7.98), liburl-dispatcher1-dev, libxkbcommon-dev, libxrender-dev, diff --git a/debian/rules b/debian/rules index 027327b..f355f45 100755 --- a/debian/rules +++ b/debian/rules @@ -5,13 +5,6 @@ export DPKG_GENSYMBOLS_CHECK_LEVEL=4 include /usr/share/dpkg/default.mk -# Explicitly selecting a G{CC,++}-version here to avoid accidental -# ABI breaks introduced by toolchain updates. -export CC=$(DEB_HOST_GNU_TYPE)-gcc-4.9 -export CXX=$(DEB_HOST_GNU_TYPE)-g++-4.9 - -FLAGS = "QMAKE_CXX=$(CXX)" "QMAKE_LINK=$(CXX)" "QMAKE_LINK_SHLIB=$(CXX)" - ANDROID_DIR = build-android DESKTOP_DIR = build-desktop TMP1_DIR = $(CURDIR)/debian/tmp1 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); } diff --git a/src/platforms/mirserver/CMakeLists.txt b/src/platforms/mirserver/CMakeLists.txt index 60abe20..36d9321 100644 --- a/src/platforms/mirserver/CMakeLists.txt +++ b/src/platforms/mirserver/CMakeLists.txt @@ -31,6 +31,8 @@ include_directories( ${QT5PLATFORM_SUPPORT_INCLUDE_DIRS} ${Qt5Gui_PRIVATE_INCLUDE_DIRS} ${QT5_PLATFORMSUPPORT_INCLUDE_DIRS} + + ${APPLICATION_API_INCLUDE_DIRS} ) # We have to remove -pedantic for tracepoints.c @@ -40,7 +42,7 @@ add_definitions(-DBYTE_ORDER=__BYTE_ORDER) set(MIRSERVER_QPA_PLUGIN_SRC ${CMAKE_SOURCE_DIR}/src/common/debughelpers.cpp - mirshell.cpp + mirwindowmanager.cpp qteventfeeder.cpp plugin.cpp qmirserver.cpp diff --git a/src/platforms/mirserver/miropenglcontext.cpp b/src/platforms/mirserver/miropenglcontext.cpp index cb84666..6c9360a 100644 --- a/src/platforms/mirserver/miropenglcontext.cpp +++ b/src/platforms/mirserver/miropenglcontext.cpp @@ -36,7 +36,7 @@ // (i.e. individual display output buffers) to use as a common base context. MirOpenGLContext::MirOpenGLContext(const QSharedPointer<MirServer> &server, const QSurfaceFormat &format) -#if GL_DEBUG +#ifndef QT_NO_DEBUG : m_logger(new QOpenGLDebugLogger(this)) #endif { @@ -96,10 +96,8 @@ MirOpenGLContext::MirOpenGLContext(const QSharedPointer<MirServer> &server, cons qDebug() << "OpenGL ES extensions:" << qPrintable(string); q_printEglConfig(eglDisplay, eglConfig); -#if GL_DEBUG QObject::connect(m_logger, &QOpenGLDebugLogger::messageLogged, this, &MirOpenGLContext::onGlDebugMessageLogged, Qt::DirectConnection); -#endif // Qt>=5.2 #endif // debug } @@ -122,7 +120,7 @@ bool MirOpenGLContext::makeCurrent(QPlatformSurface *surface) if (displayBuffer) { displayBuffer->makeCurrent(); -#if GL_DEBUG +#ifndef QT_NO_DEBUG if (!m_logger->isLogging() && m_logger->initialize()) { m_logger->startLogging(QOpenGLDebugLogger::SynchronousLogging); m_logger->enableMessages(); diff --git a/src/platforms/mirserver/miropenglcontext.h b/src/platforms/mirserver/miropenglcontext.h index de3ec9c..c1d6274 100644 --- a/src/platforms/mirserver/miropenglcontext.h +++ b/src/platforms/mirserver/miropenglcontext.h @@ -21,9 +21,7 @@ #include <qpa/qplatformopenglcontext.h> -#define GL_DEBUG (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) && !defined(QT_NO_DEBUG)) - -#if GL_DEBUG +#ifndef QT_NO_DEBUG #include <QOpenGLDebugLogger> #endif @@ -46,14 +44,13 @@ public: QFunctionPointer getProcAddress(const QByteArray &procName) override; -// "#if GL_DEBUG" does not work as MOC does not understand #define -#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) && !defined(QT_NO_DEBUG)) +#ifndef QT_NO_DEBUG Q_SLOT void onGlDebugMessageLogged(QOpenGLDebugMessage m) { qDebug() << m; } #endif private: QSurfaceFormat m_format; -#if GL_DEBUG +#ifndef QT_NO_DEBUG QOpenGLDebugLogger *m_logger; #endif }; diff --git a/src/platforms/mirserver/mirserver.cpp b/src/platforms/mirserver/mirserver.cpp index a60fb0d..f7bb74a 100644 --- a/src/platforms/mirserver/mirserver.cpp +++ b/src/platforms/mirserver/mirserver.cpp @@ -19,7 +19,7 @@ #include "mirserver.h" // local -#include "mirshell.h" +#include "mirwindowmanager.h" #include "mirglconfig.h" #include "mirserverstatuslistener.h" #include "promptsessionlistener.h" @@ -86,17 +86,10 @@ MirServer::MirServer(int argc, char const* argv[], QObject* parent) return std::make_shared<MirServerStatusListener>(); }); - override_the_shell([this] + override_the_window_manager_builder([this](mir::shell::FocusController* /*focus_controller*/) + -> std::shared_ptr<mir::shell::WindowManager> { - auto const shell = std::make_shared<MirShell>( - the_input_targeter(), - the_surface_coordinator(), - the_session_coordinator(), - the_prompt_session_manager(), - the_shell_display_layout()); - - m_shell = shell; - return shell; + return std::make_shared<MirWindowManager>(the_shell_display_layout()); }); set_terminator([&](int) @@ -151,5 +144,6 @@ PromptSessionListener *MirServer::promptSessionListener() MirShell *MirServer::shell() { + std::weak_ptr<MirShell> m_shell = the_shell(); return m_shell.lock().get(); } diff --git a/src/platforms/mirserver/mirserver.h b/src/platforms/mirserver/mirserver.h index b4b8dc2..3956f9c 100644 --- a/src/platforms/mirserver/mirserver.h +++ b/src/platforms/mirserver/mirserver.h @@ -23,7 +23,7 @@ class QtEventFeeder; class SessionListener; class SessionAuthorizer; -class MirShell; +using MirShell = mir::shell::Shell; class PromptSessionListener; // We use virtual inheritance of mir::Server to facilitate derived classes (e.g. testing) @@ -61,7 +61,6 @@ public: private: std::shared_ptr<QtEventFeeder> m_qtEventFeeder; - std::weak_ptr<MirShell> m_shell; }; #endif // MIRSERVER_H diff --git a/src/platforms/mirserver/mirserverintegration.cpp b/src/platforms/mirserver/mirserverintegration.cpp index ad33caa..28c8c9d 100644 --- a/src/platforms/mirserver/mirserverintegration.cpp +++ b/src/platforms/mirserver/mirserverintegration.cpp @@ -31,11 +31,6 @@ #include <QCoreApplication> #include <QOpenGLContext> - -#if QT_VERSION < QT_VERSION_CHECK(5, 2, 0) -#include <private/qguiapplication_p.h> -#endif - #include <QDebug> // Mir @@ -59,9 +54,6 @@ MirServerIntegration::MirServerIntegration() : m_accessibility(new QPlatformAccessibility()) , m_fontDb(new QGenericUnixFontDatabase()) , m_services(new Services) -#if QT_VERSION < QT_VERSION_CHECK(5, 2, 0) - , m_eventDispatcher(createUnixEventDispatcher()) -#endif , m_mirServer(new QMirServer(QCoreApplication::arguments())) , m_display(nullptr) , m_nativeInterface(nullptr) @@ -82,11 +74,6 @@ MirServerIntegration::MirServerIntegration() QObject::connect(m_mirServer.data(), &QMirServer::stopped, QCoreApplication::instance(), &QCoreApplication::quit); -#if QT_VERSION < QT_VERSION_CHECK(5, 2, 0) - QGuiApplicationPrivate::instance()->setEventDispatcher(eventDispatcher_); - initialize(); -#endif - m_inputContext = QPlatformInputContextFactory::create(); } @@ -105,10 +92,8 @@ bool MirServerIntegration::hasCapability(QPlatformIntegration::Capability cap) c case SharedGraphicsCache: return true; case BufferQueueingOpenGL: return true; case MultipleWindows: return false; // multi-monitor support -#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) case WindowManagement: return false; // platform has no WM, as this implements the WM! case NonFullScreenWindows: return false; -#endif default: return QPlatformIntegration::hasCapability(cap); } } @@ -158,12 +143,10 @@ QPlatformOpenGLContext *MirServerIntegration::createPlatformOpenGLContext(QOpenG return new MirOpenGLContext(m_mirServer->mirServer(), context->format()); } -#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) QAbstractEventDispatcher *MirServerIntegration::createEventDispatcher() const { return createUnixEventDispatcher(); } -#endif void MirServerIntegration::initialize() { diff --git a/src/platforms/mirserver/mirserverintegration.h b/src/platforms/mirserver/mirserverintegration.h index a02405e..cc719e8 100644 --- a/src/platforms/mirserver/mirserverintegration.h +++ b/src/platforms/mirserver/mirserverintegration.h @@ -47,13 +47,8 @@ public: QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const override; QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const override; -#if QT_VERSION < QT_VERSION_CHECK(5, 2, 0) - QAbstractEventDispatcher* guiThreadEventDispatcher() const override { return eventDispatcher_; } - void initialize(); -#else QAbstractEventDispatcher *createEventDispatcher() const override; void initialize() override; -#endif QPlatformClipboard *clipboard() const override; @@ -72,9 +67,6 @@ private: QScopedPointer<QPlatformAccessibility> m_accessibility; QScopedPointer<QPlatformFontDatabase> m_fontDb; QScopedPointer<QPlatformServices> m_services; -#if QT_VERSION < QT_VERSION_CHECK(5, 2, 0) - QScopedPointer<QAbstractEventDispatcher> m_eventDispatcher; -#endif QScopedPointer<QMirServer> m_mirServer; diff --git a/src/platforms/mirserver/mirshell.cpp b/src/platforms/mirserver/mirshell.cpp deleted file mode 100644 index 3b1e9c4..0000000 --- a/src/platforms/mirserver/mirshell.cpp +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright © 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/>. - */ - -#include "mirshell.h" -#include "logging.h" -#include "tracepoints.h" // generated from tracepoints.tp - -#include <mir/geometry/rectangle.h> -#include <mir/scene/session.h> -#include <mir/scene/surface_creation_parameters.h> -#include <mir/scene/surface.h> -#include <mir/shell/display_layout.h> -#include <mir/shell/window_manager.h> - -namespace ms = mir::scene; -using mir::shell::AbstractShell; - -namespace -{ -class NullWindowManager : public mir::shell::WindowManager -{ -public: - void add_session(std::shared_ptr<ms::Session> const& session) override; - - void remove_session(std::shared_ptr<ms::Session> const& session) override; - - mir::frontend::SurfaceId add_surface( - std::shared_ptr<ms::Session> const& session, - ms::SurfaceCreationParameters const& params, - std::function<mir::frontend::SurfaceId(std::shared_ptr<ms::Session> const& session, ms::SurfaceCreationParameters const& params)> const& build) override; - - void remove_surface( - std::shared_ptr<ms::Session> const& session, - std::weak_ptr<ms::Surface> const& surface) override; - - void add_display(mir::geometry::Rectangle const& area) override; - - void remove_display(mir::geometry::Rectangle const& area) override; - - bool handle_keyboard_event(MirKeyboardEvent const* event) override; - - bool handle_touch_event(MirTouchEvent const* event) override; - - bool handle_pointer_event(MirPointerEvent const* event) override; - - int set_surface_attribute( - std::shared_ptr<ms::Session> const& session, - std::shared_ptr<ms::Surface> const& surface, - MirSurfaceAttrib attrib, - int value) override; - - void modify_surface(const std::shared_ptr<mir::scene::Session>&, const std::shared_ptr<mir::scene::Surface>&, const mir::shell::SurfaceSpecification&); -}; -} - - -MirShell::MirShell( - const std::shared_ptr<mir::shell::InputTargeter> &inputTargeter, - const std::shared_ptr<mir::scene::SurfaceCoordinator> &surfaceCoordinator, - const std::shared_ptr<mir::scene::SessionCoordinator> &sessionCoordinator, - const std::shared_ptr<mir::scene::PromptSessionManager> &promptSessionManager, - const std::shared_ptr<mir::shell::DisplayLayout> &displayLayout) : - AbstractShell(inputTargeter, surfaceCoordinator, sessionCoordinator, promptSessionManager, - [](mir::shell::FocusController*) { return std::make_shared<NullWindowManager>(); }), - m_displayLayout{displayLayout} -{ - qCDebug(QTMIR_MIR_MESSAGES) << "MirShell::MirShell"; -} - -mir::frontend::SurfaceId MirShell::create_surface(const std::shared_ptr<ms::Session> &session, const ms::SurfaceCreationParameters &requestParameters) -{ - tracepoint(qtmirserver, surfacePlacementStart); - - // TODO: Callback unity8 so that it can make a decision on that. - // unity8 must bear in mind that the called function will be on a Mir thread though. - // The QPA shouldn't be deciding for itself on such things. - - ms::SurfaceCreationParameters placedParameters = requestParameters; - - // Just make it fullscreen for now - mir::geometry::Rectangle rect{requestParameters.top_left, requestParameters.size}; - m_displayLayout->size_to_output(rect); - placedParameters.size = rect.size; - - qCDebug(QTMIR_MIR_MESSAGES) << "MirShell::create_surface(): size requested (" - << requestParameters.size.width.as_int() << "," << requestParameters.size.height.as_int() << ") and placed (" - << placedParameters.size.width.as_int() << "," << placedParameters.size.height.as_int() << ")"; - - tracepoint(qtmirserver, surfacePlacementEnd); - - return AbstractShell::create_surface(session, placedParameters); -} - -void NullWindowManager::add_session(std::shared_ptr<ms::Session> const& /*session*/) -{ -} - -void NullWindowManager::remove_session(std::shared_ptr<ms::Session> const& /*session*/) -{ -} - -auto NullWindowManager::add_surface( - std::shared_ptr<ms::Session> const& session, - ms::SurfaceCreationParameters const& params, - std::function<mir::frontend::SurfaceId(std::shared_ptr<ms::Session> const& session, ms::SurfaceCreationParameters const& params)> const& build) --> mir::frontend::SurfaceId -{ - return build(session, params); -} - -void NullWindowManager::remove_surface( - std::shared_ptr<ms::Session> const& /*session*/, - std::weak_ptr<ms::Surface> const& /*surface*/) -{ -} - -void NullWindowManager::add_display(mir::geometry::Rectangle const& /*area*/) -{ -} - -void NullWindowManager::remove_display(mir::geometry::Rectangle const& /*area*/) -{ -} - -bool NullWindowManager::handle_keyboard_event(MirKeyboardEvent const* /*event*/) -{ - return false; -} - -bool NullWindowManager::handle_touch_event(MirTouchEvent const* /*event*/) -{ - return false; -} - -bool NullWindowManager::handle_pointer_event(MirPointerEvent const* /*event*/) -{ - return false; -} - -int NullWindowManager::set_surface_attribute( - std::shared_ptr<ms::Session> const& /*session*/, - std::shared_ptr<ms::Surface> const& surface, - MirSurfaceAttrib attrib, - int value) -{ - return surface->configure(attrib, value); -} - -void NullWindowManager::modify_surface(const std::shared_ptr<mir::scene::Session>&, const std::shared_ptr<mir::scene::Surface>&, const mir::shell::SurfaceSpecification&) -{ -} diff --git a/src/platforms/mirserver/mirshell.h b/src/platforms/mirserver/mirshell.h deleted file mode 100644 index 85c243e..0000000 --- a/src/platforms/mirserver/mirshell.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright © 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 QPAMIRSERVER_SHELL_H -#define QPAMIRSERVER_SHELL_H - -#include <mir/shell/abstract_shell.h> -#include <QObject> - -namespace mir { - namespace shell { - class DisplayLayout; - } -} - -class MirShell : public QObject, public mir::shell::AbstractShell -{ - Q_OBJECT - -public: - MirShell( - const std::shared_ptr<mir::shell::InputTargeter> &inputTargeter, - const std::shared_ptr<mir::scene::SurfaceCoordinator> &surfaceCoordinator, - const std::shared_ptr<mir::scene::SessionCoordinator> &sessionCoordinator, - const std::shared_ptr<mir::scene::PromptSessionManager> &promptSessionManager, - const std::shared_ptr<mir::shell::DisplayLayout> &displayLayout); - - virtual mir::frontend::SurfaceId create_surface(const std::shared_ptr<mir::scene::Session>& session, const mir::scene::SurfaceCreationParameters ¶ms); - -private: - std::shared_ptr<mir::shell::DisplayLayout> const m_displayLayout; -}; - -#endif /* QPAMIRSERVER_SHELL_H */ diff --git a/src/platforms/mirserver/mirwindowmanager.cpp b/src/platforms/mirserver/mirwindowmanager.cpp new file mode 100644 index 0000000..cae66d4 --- /dev/null +++ b/src/platforms/mirserver/mirwindowmanager.cpp @@ -0,0 +1,112 @@ +/* + * Copyright © 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/>. + */ + +#include "mirwindowmanager.h" +#include "logging.h" +#include "tracepoints.h" // generated from tracepoints.tp + +#include <mir/geometry/rectangle.h> +#include <mir/scene/session.h> +#include <mir/scene/surface_creation_parameters.h> +#include <mir/scene/surface.h> +#include <mir/shell/display_layout.h> + +namespace ms = mir::scene; + +MirWindowManager::MirWindowManager(const std::shared_ptr<mir::shell::DisplayLayout> &displayLayout) : + m_displayLayout{displayLayout} +{ + qCDebug(QTMIR_MIR_MESSAGES) << "MirWindowManager::MirWindowManager"; +} + +void MirWindowManager::add_session(std::shared_ptr<ms::Session> const& /*session*/) +{ +} + +void MirWindowManager::remove_session(std::shared_ptr<ms::Session> const& /*session*/) +{ +} + +auto MirWindowManager::add_surface( + std::shared_ptr<ms::Session> const& session, + ms::SurfaceCreationParameters const& requestParameters, + std::function<mir::frontend::SurfaceId(std::shared_ptr<ms::Session> const& session, ms::SurfaceCreationParameters const& params)> const& build) +-> mir::frontend::SurfaceId +{ + tracepoint(qtmirserver, surfacePlacementStart); + + // TODO: Callback unity8 so that it can make a decision on that. + // unity8 must bear in mind that the called function will be on a Mir thread though. + // The QPA shouldn't be deciding for itself on such things. + + ms::SurfaceCreationParameters placedParameters = requestParameters; + + // Just make it fullscreen for now + mir::geometry::Rectangle rect{requestParameters.top_left, requestParameters.size}; + m_displayLayout->size_to_output(rect); + placedParameters.size = rect.size; + + qCDebug(QTMIR_MIR_MESSAGES) << "MirWindowManager::add_surface(): size requested (" + << requestParameters.size.width.as_int() << "," << requestParameters.size.height.as_int() << ") and placed (" + << placedParameters.size.width.as_int() << "," << placedParameters.size.height.as_int() << ")"; + + tracepoint(qtmirserver, surfacePlacementEnd); + + return build(session, placedParameters); +} + +void MirWindowManager::remove_surface( + std::shared_ptr<ms::Session> const& /*session*/, + std::weak_ptr<ms::Surface> const& /*surface*/) +{ +} + +void MirWindowManager::add_display(mir::geometry::Rectangle const& /*area*/) +{ +} + +void MirWindowManager::remove_display(mir::geometry::Rectangle const& /*area*/) +{ +} + +bool MirWindowManager::handle_keyboard_event(MirKeyboardEvent const* /*event*/) +{ + return false; +} + +bool MirWindowManager::handle_touch_event(MirTouchEvent const* /*event*/) +{ + return false; +} + +bool MirWindowManager::handle_pointer_event(MirPointerEvent const* /*event*/) +{ + return false; +} + +int MirWindowManager::set_surface_attribute( + std::shared_ptr<ms::Session> const& /*session*/, + std::shared_ptr<ms::Surface> const& surface, + MirSurfaceAttrib attrib, + int value) +{ + return surface->configure(attrib, value); +} + +void MirWindowManager::modify_surface(const std::shared_ptr<mir::scene::Session>&, const std::shared_ptr<mir::scene::Surface>&, const mir::shell::SurfaceSpecification&) +{ + // TODO support surface modifications +} diff --git a/src/platforms/mirserver/mirwindowmanager.h b/src/platforms/mirserver/mirwindowmanager.h new file mode 100644 index 0000000..0ec1839 --- /dev/null +++ b/src/platforms/mirserver/mirwindowmanager.h @@ -0,0 +1,73 @@ +/* + * Copyright © 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 QPAMIRSERVER_WINDOW_MANAGER_H +#define QPAMIRSERVER_WINDOW_MANAGER_H + +#include <mir/shell/window_manager.h> + +#include <QObject> + +namespace mir { + namespace shell { + class DisplayLayout; + } +} + +class MirWindowManager : public QObject, public mir::shell::WindowManager +{ + Q_OBJECT + +public: + + MirWindowManager(const std::shared_ptr<mir::shell::DisplayLayout> &displayLayout); + + void add_session(std::shared_ptr<mir::scene::Session> const& session) override; + + void remove_session(std::shared_ptr<mir::scene::Session> const& session) override; + + mir::frontend::SurfaceId add_surface( + std::shared_ptr<mir::scene::Session> const& session, + mir::scene::SurfaceCreationParameters const& params, + std::function<mir::frontend::SurfaceId(std::shared_ptr<mir::scene::Session> const& session, mir::scene::SurfaceCreationParameters const& params)> const& build) override; + + void remove_surface( + std::shared_ptr<mir::scene::Session> const& session, + std::weak_ptr<mir::scene::Surface> const& surface) override; + + void add_display(mir::geometry::Rectangle const& area) override; + + void remove_display(mir::geometry::Rectangle const& area) override; + + bool handle_keyboard_event(MirKeyboardEvent const* event) override; + + bool handle_touch_event(MirTouchEvent const* event) override; + + bool handle_pointer_event(MirPointerEvent const* event) override; + + int set_surface_attribute( + std::shared_ptr<mir::scene::Session> const& session, + std::shared_ptr<mir::scene::Surface> const& surface, + MirSurfaceAttrib attrib, + int value) override; + + void modify_surface(const std::shared_ptr<mir::scene::Session>&, const std::shared_ptr<mir::scene::Surface>&, const mir::shell::SurfaceSpecification&); + +private: + std::shared_ptr<mir::shell::DisplayLayout> const m_displayLayout; +}; + +#endif /* QPAMIRSERVER_WINDOW_MANAGER_H */ diff --git a/src/platforms/mirserver/qteventfeeder.cpp b/src/platforms/mirserver/qteventfeeder.cpp index 5490096..e51c8d1 100644 --- a/src/platforms/mirserver/qteventfeeder.cpp +++ b/src/platforms/mirserver/qteventfeeder.cpp @@ -37,87 +37,315 @@ Q_LOGGING_CATEGORY(QTMIR_MIR_INPUT, "qtmir.mir.input", QtWarningMsg) // XKB Keysyms which do not map directly to Qt types (i.e. Unicode points) static const uint32_t KeyTable[] = { - XKB_KEY_Escape, Qt::Key_Escape, - XKB_KEY_Tab, Qt::Key_Tab, - XKB_KEY_ISO_Left_Tab, Qt::Key_Backtab, - XKB_KEY_BackSpace, Qt::Key_Backspace, - XKB_KEY_Return, Qt::Key_Return, - XKB_KEY_Insert, Qt::Key_Insert, - XKB_KEY_Delete, Qt::Key_Delete, - XKB_KEY_Clear, Qt::Key_Delete, - XKB_KEY_Pause, Qt::Key_Pause, - XKB_KEY_Print, Qt::Key_Print, - - XKB_KEY_Home, Qt::Key_Home, - XKB_KEY_End, Qt::Key_End, - XKB_KEY_Left, Qt::Key_Left, - XKB_KEY_Up, Qt::Key_Up, - XKB_KEY_Right, Qt::Key_Right, - XKB_KEY_Down, Qt::Key_Down, - XKB_KEY_Prior, Qt::Key_PageUp, - XKB_KEY_Next, Qt::Key_PageDown, - - XKB_KEY_Shift_L, Qt::Key_Shift, - XKB_KEY_Shift_R, Qt::Key_Shift, - XKB_KEY_Shift_Lock, Qt::Key_Shift, - XKB_KEY_Control_L, Qt::Key_Control, - XKB_KEY_Control_R, Qt::Key_Control, - XKB_KEY_Meta_L, Qt::Key_Meta, - XKB_KEY_Meta_R, Qt::Key_Meta, - XKB_KEY_Alt_L, Qt::Key_Alt, - XKB_KEY_Alt_R, Qt::Key_Alt, - XKB_KEY_Caps_Lock, Qt::Key_CapsLock, - XKB_KEY_Num_Lock, Qt::Key_NumLock, - XKB_KEY_Scroll_Lock, Qt::Key_ScrollLock, - XKB_KEY_Super_L, Qt::Key_Super_L, - XKB_KEY_Super_R, Qt::Key_Super_R, - XKB_KEY_Menu, Qt::Key_Menu, - XKB_KEY_Hyper_L, Qt::Key_Hyper_L, - XKB_KEY_Hyper_R, Qt::Key_Hyper_R, - XKB_KEY_Help, Qt::Key_Help, - - XKB_KEY_KP_Space, Qt::Key_Space, - XKB_KEY_KP_Tab, Qt::Key_Tab, - XKB_KEY_KP_Enter, Qt::Key_Enter, - XKB_KEY_KP_Home, Qt::Key_Home, - XKB_KEY_KP_Left, Qt::Key_Left, - XKB_KEY_KP_Up, Qt::Key_Up, - XKB_KEY_KP_Right, Qt::Key_Right, - XKB_KEY_KP_Down, Qt::Key_Down, - XKB_KEY_KP_Prior, Qt::Key_PageUp, - XKB_KEY_KP_Next, Qt::Key_PageDown, - XKB_KEY_KP_End, Qt::Key_End, - XKB_KEY_KP_Begin, Qt::Key_Clear, - XKB_KEY_KP_Insert, Qt::Key_Insert, - XKB_KEY_KP_Delete, Qt::Key_Delete, - XKB_KEY_KP_Equal, Qt::Key_Equal, - XKB_KEY_KP_Multiply, Qt::Key_Asterisk, - XKB_KEY_KP_Add, Qt::Key_Plus, - XKB_KEY_KP_Separator, Qt::Key_Comma, - XKB_KEY_KP_Subtract, Qt::Key_Minus, - XKB_KEY_KP_Decimal, Qt::Key_Period, - XKB_KEY_KP_Divide, Qt::Key_Slash, - - XKB_KEY_ISO_Level3_Shift, Qt::Key_AltGr, - XKB_KEY_Multi_key, Qt::Key_Multi_key, - XKB_KEY_Codeinput, Qt::Key_Codeinput, - XKB_KEY_SingleCandidate, Qt::Key_SingleCandidate, - XKB_KEY_MultipleCandidate, Qt::Key_MultipleCandidate, - XKB_KEY_PreviousCandidate, Qt::Key_PreviousCandidate, - - XKB_KEY_Mode_switch, Qt::Key_Mode_switch, - XKB_KEY_script_switch, Qt::Key_Mode_switch, - XKB_KEY_XF86AudioRaiseVolume, Qt::Key_VolumeUp, + // misc keys + XKB_KEY_Escape, Qt::Key_Escape, + XKB_KEY_Tab, Qt::Key_Tab, + XKB_KEY_ISO_Left_Tab, Qt::Key_Backtab, + XKB_KEY_BackSpace, Qt::Key_Backspace, + XKB_KEY_Return, Qt::Key_Return, + XKB_KEY_Insert, Qt::Key_Insert, + XKB_KEY_Delete, Qt::Key_Delete, + XKB_KEY_Clear, Qt::Key_Delete, + XKB_KEY_Pause, Qt::Key_Pause, + XKB_KEY_Print, Qt::Key_Print, + 0x1005FF60, Qt::Key_SysReq, // hardcoded Sun SysReq + 0x1007ff00, Qt::Key_SysReq, // hardcoded X386 SysReq + + // cursor movement + + XKB_KEY_Home, Qt::Key_Home, + XKB_KEY_End, Qt::Key_End, + XKB_KEY_Left, Qt::Key_Left, + XKB_KEY_Up, Qt::Key_Up, + XKB_KEY_Right, Qt::Key_Right, + XKB_KEY_Down, Qt::Key_Down, + XKB_KEY_Prior, Qt::Key_PageUp, + XKB_KEY_Next, Qt::Key_PageDown, + + // modifiers + + XKB_KEY_Shift_L, Qt::Key_Shift, + XKB_KEY_Shift_R, Qt::Key_Shift, + XKB_KEY_Shift_Lock, Qt::Key_Shift, + XKB_KEY_Control_L, Qt::Key_Control, + XKB_KEY_Control_R, Qt::Key_Control, + XKB_KEY_Meta_L, Qt::Key_Meta, + XKB_KEY_Meta_R, Qt::Key_Meta, + XKB_KEY_Alt_L, Qt::Key_Alt, + XKB_KEY_Alt_R, Qt::Key_Alt, + XKB_KEY_Caps_Lock, Qt::Key_CapsLock, + XKB_KEY_Num_Lock, Qt::Key_NumLock, + XKB_KEY_Scroll_Lock, Qt::Key_ScrollLock, + XKB_KEY_Super_L, Qt::Key_Super_L, + XKB_KEY_Super_R, Qt::Key_Super_R, + XKB_KEY_Menu, Qt::Key_Menu, + XKB_KEY_Hyper_L, Qt::Key_Hyper_L, + XKB_KEY_Hyper_R, Qt::Key_Hyper_R, + XKB_KEY_Help, Qt::Key_Help, + 0x1000FF74, Qt::Key_Backtab, // hardcoded HP backtab + 0x1005FF10, Qt::Key_F11, // hardcoded Sun F36 (labeled F11) + 0x1005FF11, Qt::Key_F12, // hardcoded Sun F37 (labeled F12) + + // numeric and function keypad keys + + XKB_KEY_KP_Space, Qt::Key_Space, + XKB_KEY_KP_Tab, Qt::Key_Tab, + XKB_KEY_KP_Enter, Qt::Key_Enter, + //XKB_KEY_KP_F1, Qt::Key_F1, + //XKB_KEY_KP_F2, Qt::Key_F2, + //XKB_KEY_KP_F3, Qt::Key_F3, + //XKB_KEY_KP_F4, Qt::Key_F4, + XKB_KEY_KP_Home, Qt::Key_Home, + XKB_KEY_KP_Left, Qt::Key_Left, + XKB_KEY_KP_Up, Qt::Key_Up, + XKB_KEY_KP_Right, Qt::Key_Right, + XKB_KEY_KP_Down, Qt::Key_Down, + XKB_KEY_KP_Prior, Qt::Key_PageUp, + XKB_KEY_KP_Next, Qt::Key_PageDown, + XKB_KEY_KP_End, Qt::Key_End, + XKB_KEY_KP_Begin, Qt::Key_Clear, + XKB_KEY_KP_Insert, Qt::Key_Insert, + XKB_KEY_KP_Delete, Qt::Key_Delete, + XKB_KEY_KP_Equal, Qt::Key_Equal, + XKB_KEY_KP_Multiply, Qt::Key_Asterisk, + XKB_KEY_KP_Add, Qt::Key_Plus, + XKB_KEY_KP_Separator, Qt::Key_Comma, + XKB_KEY_KP_Subtract, Qt::Key_Minus, + XKB_KEY_KP_Decimal, Qt::Key_Period, + XKB_KEY_KP_Divide, Qt::Key_Slash, + + // International input method support keys + + // International & multi-key character composition + XKB_KEY_ISO_Level3_Shift, Qt::Key_AltGr, + XKB_KEY_Multi_key, Qt::Key_Multi_key, + XKB_KEY_Codeinput, Qt::Key_Codeinput, + XKB_KEY_SingleCandidate, Qt::Key_SingleCandidate, + XKB_KEY_MultipleCandidate, Qt::Key_MultipleCandidate, + XKB_KEY_PreviousCandidate, Qt::Key_PreviousCandidate, + + // Misc Functions + XKB_KEY_Mode_switch, Qt::Key_Mode_switch, + XKB_KEY_script_switch, Qt::Key_Mode_switch, + + // Japanese keyboard support + XKB_KEY_Kanji, Qt::Key_Kanji, + XKB_KEY_Muhenkan, Qt::Key_Muhenkan, + //XKB_KEY_Henkan_Mode, Qt::Key_Henkan_Mode, + XKB_KEY_Henkan_Mode, Qt::Key_Henkan, + XKB_KEY_Henkan, Qt::Key_Henkan, + XKB_KEY_Romaji, Qt::Key_Romaji, + XKB_KEY_Hiragana, Qt::Key_Hiragana, + XKB_KEY_Katakana, Qt::Key_Katakana, + XKB_KEY_Hiragana_Katakana, Qt::Key_Hiragana_Katakana, + XKB_KEY_Zenkaku, Qt::Key_Zenkaku, + XKB_KEY_Hankaku, Qt::Key_Hankaku, + XKB_KEY_Zenkaku_Hankaku, Qt::Key_Zenkaku_Hankaku, + XKB_KEY_Touroku, Qt::Key_Touroku, + XKB_KEY_Massyo, Qt::Key_Massyo, + XKB_KEY_Kana_Lock, Qt::Key_Kana_Lock, + XKB_KEY_Kana_Shift, Qt::Key_Kana_Shift, + XKB_KEY_Eisu_Shift, Qt::Key_Eisu_Shift, + XKB_KEY_Eisu_toggle, Qt::Key_Eisu_toggle, + //XKB_KEY_Kanji_Bangou, Qt::Key_Kanji_Bangou, + //XKB_KEY_Zen_Koho, Qt::Key_Zen_Koho, + //XKB_KEY_Mae_Koho, Qt::Key_Mae_Koho, + XKB_KEY_Kanji_Bangou, Qt::Key_Codeinput, + XKB_KEY_Zen_Koho, Qt::Key_MultipleCandidate, + XKB_KEY_Mae_Koho, Qt::Key_PreviousCandidate, + +#ifdef XKB_KEY_KOREAN + // Korean keyboard support + XKB_KEY_Hangul, Qt::Key_Hangul, + XKB_KEY_Hangul_Start, Qt::Key_Hangul_Start, + XKB_KEY_Hangul_End, Qt::Key_Hangul_End, + XKB_KEY_Hangul_Hanja, Qt::Key_Hangul_Hanja, + XKB_KEY_Hangul_Jamo, Qt::Key_Hangul_Jamo, + XKB_KEY_Hangul_Romaja, Qt::Key_Hangul_Romaja, + //XKB_KEY_Hangul_Codeinput, Qt::Key_Hangul_Codeinput, + XKB_KEY_Hangul_Codeinput, Qt::Key_Codeinput, + XKB_KEY_Hangul_Jeonja, Qt::Key_Hangul_Jeonja, + XKB_KEY_Hangul_Banja, Qt::Key_Hangul_Banja, + XKB_KEY_Hangul_PreHanja, Qt::Key_Hangul_PreHanja, + XKB_KEY_Hangul_PostHanja, Qt::Key_Hangul_PostHanja, + //XKB_KEY_Hangul_SingleCandidate,Qt::Key_Hangul_SingleCandidate, + //XKB_KEY_Hangul_MultipleCandidate,Qt::Key_Hangul_MultipleCandidate, + //XKB_KEY_Hangul_PreviousCandidate,Qt::Key_Hangul_PreviousCandidate, + XKB_KEY_Hangul_SingleCandidate, Qt::Key_SingleCandidate, + XKB_KEY_Hangul_MultipleCandidate,Qt::Key_MultipleCandidate, + XKB_KEY_Hangul_PreviousCandidate,Qt::Key_PreviousCandidate, + XKB_KEY_Hangul_Special, Qt::Key_Hangul_Special, + //XKB_KEY_Hangul_switch, Qt::Key_Hangul_switch, + XKB_KEY_Hangul_switch, Qt::Key_Mode_switch, +#endif // XKB_KEY_KOREAN + + // dead keys + XKB_KEY_dead_grave, Qt::Key_Dead_Grave, + XKB_KEY_dead_acute, Qt::Key_Dead_Acute, + XKB_KEY_dead_circumflex, Qt::Key_Dead_Circumflex, + XKB_KEY_dead_tilde, Qt::Key_Dead_Tilde, + XKB_KEY_dead_macron, Qt::Key_Dead_Macron, + XKB_KEY_dead_breve, Qt::Key_Dead_Breve, + XKB_KEY_dead_abovedot, Qt::Key_Dead_Abovedot, + XKB_KEY_dead_diaeresis, Qt::Key_Dead_Diaeresis, + XKB_KEY_dead_abovering, Qt::Key_Dead_Abovering, + XKB_KEY_dead_doubleacute, Qt::Key_Dead_Doubleacute, + XKB_KEY_dead_caron, Qt::Key_Dead_Caron, + XKB_KEY_dead_cedilla, Qt::Key_Dead_Cedilla, + XKB_KEY_dead_ogonek, Qt::Key_Dead_Ogonek, + XKB_KEY_dead_iota, Qt::Key_Dead_Iota, + XKB_KEY_dead_voiced_sound, Qt::Key_Dead_Voiced_Sound, + XKB_KEY_dead_semivoiced_sound, Qt::Key_Dead_Semivoiced_Sound, + XKB_KEY_dead_belowdot, Qt::Key_Dead_Belowdot, + XKB_KEY_dead_hook, Qt::Key_Dead_Hook, + XKB_KEY_dead_horn, Qt::Key_Dead_Horn, + + // Special keys from X.org - This include multimedia keys, + // wireless/bluetooth/uwb keys, special launcher keys, etc. + XKB_KEY_XF86Back, Qt::Key_Back, + XKB_KEY_XF86Forward, Qt::Key_Forward, + XKB_KEY_XF86Stop, Qt::Key_Stop, + XKB_KEY_XF86Refresh, Qt::Key_Refresh, + XKB_KEY_XF86Favorites, Qt::Key_Favorites, + XKB_KEY_XF86AudioMedia, Qt::Key_LaunchMedia, + XKB_KEY_XF86OpenURL, Qt::Key_OpenUrl, + XKB_KEY_XF86HomePage, Qt::Key_HomePage, + XKB_KEY_XF86Search, Qt::Key_Search, XKB_KEY_XF86AudioLowerVolume, Qt::Key_VolumeDown, - XKB_KEY_XF86PowerOff, Qt::Key_PowerOff, - XKB_KEY_XF86PowerDown, Qt::Key_PowerDown, - - /* Bluetooth / Wired headset multimedia keys */ + XKB_KEY_XF86AudioMute, Qt::Key_VolumeMute, + XKB_KEY_XF86AudioRaiseVolume, Qt::Key_VolumeUp, XKB_KEY_XF86AudioPlay, Qt::Key_MediaPlay, + XKB_KEY_XF86AudioStop, Qt::Key_MediaStop, XKB_KEY_XF86AudioPrev, Qt::Key_MediaPrevious, XKB_KEY_XF86AudioNext, Qt::Key_MediaNext, + XKB_KEY_XF86AudioRecord, Qt::Key_MediaRecord, XKB_KEY_XF86AudioPause, Qt::Key_MediaPause, - XKB_KEY_XF86AudioMedia, Qt::Key_MediaTogglePlayPause, + XKB_KEY_XF86Mail, Qt::Key_LaunchMail, + XKB_KEY_XF86MyComputer, Qt::Key_Launch0, // ### Qt 6: remap properly + XKB_KEY_XF86Calculator, Qt::Key_Launch1, + XKB_KEY_XF86Memo, Qt::Key_Memo, + XKB_KEY_XF86ToDoList, Qt::Key_ToDoList, + XKB_KEY_XF86Calendar, Qt::Key_Calendar, + XKB_KEY_XF86PowerDown, Qt::Key_PowerDown, + XKB_KEY_XF86ContrastAdjust, Qt::Key_ContrastAdjust, + XKB_KEY_XF86Standby, Qt::Key_Standby, + XKB_KEY_XF86MonBrightnessUp, Qt::Key_MonBrightnessUp, + XKB_KEY_XF86MonBrightnessDown, Qt::Key_MonBrightnessDown, + XKB_KEY_XF86KbdLightOnOff, Qt::Key_KeyboardLightOnOff, + XKB_KEY_XF86KbdBrightnessUp, Qt::Key_KeyboardBrightnessUp, + XKB_KEY_XF86KbdBrightnessDown, Qt::Key_KeyboardBrightnessDown, + XKB_KEY_XF86PowerOff, Qt::Key_PowerOff, + XKB_KEY_XF86WakeUp, Qt::Key_WakeUp, + XKB_KEY_XF86Eject, Qt::Key_Eject, + XKB_KEY_XF86ScreenSaver, Qt::Key_ScreenSaver, + XKB_KEY_XF86WWW, Qt::Key_WWW, + XKB_KEY_XF86Sleep, Qt::Key_Sleep, + XKB_KEY_XF86LightBulb, Qt::Key_LightBulb, + XKB_KEY_XF86Shop, Qt::Key_Shop, + XKB_KEY_XF86History, Qt::Key_History, + XKB_KEY_XF86AddFavorite, Qt::Key_AddFavorite, + XKB_KEY_XF86HotLinks, Qt::Key_HotLinks, + XKB_KEY_XF86BrightnessAdjust, Qt::Key_BrightnessAdjust, + XKB_KEY_XF86Finance, Qt::Key_Finance, + XKB_KEY_XF86Community, Qt::Key_Community, + XKB_KEY_XF86AudioRewind, Qt::Key_AudioRewind, + XKB_KEY_XF86BackForward, Qt::Key_BackForward, + XKB_KEY_XF86ApplicationLeft, Qt::Key_ApplicationLeft, + XKB_KEY_XF86ApplicationRight, Qt::Key_ApplicationRight, + XKB_KEY_XF86Book, Qt::Key_Book, + XKB_KEY_XF86CD, Qt::Key_CD, + XKB_KEY_XF86Calculater, Qt::Key_Calculator, + XKB_KEY_XF86Clear, Qt::Key_Clear, + XKB_KEY_XF86ClearGrab, Qt::Key_ClearGrab, + XKB_KEY_XF86Close, Qt::Key_Close, + XKB_KEY_XF86Copy, Qt::Key_Copy, + XKB_KEY_XF86Cut, Qt::Key_Cut, + XKB_KEY_XF86Display, Qt::Key_Display, + XKB_KEY_XF86DOS, Qt::Key_DOS, + XKB_KEY_XF86Documents, Qt::Key_Documents, + XKB_KEY_XF86Excel, Qt::Key_Excel, + XKB_KEY_XF86Explorer, Qt::Key_Explorer, + XKB_KEY_XF86Game, Qt::Key_Game, + XKB_KEY_XF86Go, Qt::Key_Go, + XKB_KEY_XF86iTouch, Qt::Key_iTouch, + XKB_KEY_XF86LogOff, Qt::Key_LogOff, + XKB_KEY_XF86Market, Qt::Key_Market, + XKB_KEY_XF86Meeting, Qt::Key_Meeting, + XKB_KEY_XF86MenuKB, Qt::Key_MenuKB, + XKB_KEY_XF86MenuPB, Qt::Key_MenuPB, + XKB_KEY_XF86MySites, Qt::Key_MySites, + XKB_KEY_XF86New, Qt::Key_New, + XKB_KEY_XF86News, Qt::Key_News, + XKB_KEY_XF86OfficeHome, Qt::Key_OfficeHome, + XKB_KEY_XF86Open, Qt::Key_Open, + XKB_KEY_XF86Option, Qt::Key_Option, + XKB_KEY_XF86Paste, Qt::Key_Paste, + XKB_KEY_XF86Phone, Qt::Key_Phone, + XKB_KEY_XF86Reply, Qt::Key_Reply, + XKB_KEY_XF86Reload, Qt::Key_Reload, + XKB_KEY_XF86RotateWindows, Qt::Key_RotateWindows, + XKB_KEY_XF86RotationPB, Qt::Key_RotationPB, + XKB_KEY_XF86RotationKB, Qt::Key_RotationKB, + XKB_KEY_XF86Save, Qt::Key_Save, + XKB_KEY_XF86Send, Qt::Key_Send, + XKB_KEY_XF86Spell, Qt::Key_Spell, + XKB_KEY_XF86SplitScreen, Qt::Key_SplitScreen, + XKB_KEY_XF86Support, Qt::Key_Support, + XKB_KEY_XF86TaskPane, Qt::Key_TaskPane, + XKB_KEY_XF86Terminal, Qt::Key_Terminal, + XKB_KEY_XF86Tools, Qt::Key_Tools, + XKB_KEY_XF86Travel, Qt::Key_Travel, + XKB_KEY_XF86Video, Qt::Key_Video, + XKB_KEY_XF86Word, Qt::Key_Word, + XKB_KEY_XF86Xfer, Qt::Key_Xfer, + XKB_KEY_XF86ZoomIn, Qt::Key_ZoomIn, + XKB_KEY_XF86ZoomOut, Qt::Key_ZoomOut, + XKB_KEY_XF86Away, Qt::Key_Away, + XKB_KEY_XF86Messenger, Qt::Key_Messenger, + XKB_KEY_XF86WebCam, Qt::Key_WebCam, + XKB_KEY_XF86MailForward, Qt::Key_MailForward, + XKB_KEY_XF86Pictures, Qt::Key_Pictures, + XKB_KEY_XF86Music, Qt::Key_Music, + XKB_KEY_XF86Battery, Qt::Key_Battery, + XKB_KEY_XF86Bluetooth, Qt::Key_Bluetooth, + XKB_KEY_XF86WLAN, Qt::Key_WLAN, + XKB_KEY_XF86UWB, Qt::Key_UWB, + XKB_KEY_XF86AudioForward, Qt::Key_AudioForward, + XKB_KEY_XF86AudioRepeat, Qt::Key_AudioRepeat, + XKB_KEY_XF86AudioRandomPlay, Qt::Key_AudioRandomPlay, + XKB_KEY_XF86Subtitle, Qt::Key_Subtitle, + XKB_KEY_XF86AudioCycleTrack, Qt::Key_AudioCycleTrack, + XKB_KEY_XF86Time, Qt::Key_Time, + XKB_KEY_XF86Select, Qt::Key_Select, + XKB_KEY_XF86View, Qt::Key_View, + XKB_KEY_XF86TopMenu, Qt::Key_TopMenu, + XKB_KEY_XF86Red, Qt::Key_Red, + XKB_KEY_XF86Green, Qt::Key_Green, + XKB_KEY_XF86Yellow, Qt::Key_Yellow, + XKB_KEY_XF86Blue, Qt::Key_Blue, + XKB_KEY_XF86Bluetooth, Qt::Key_Bluetooth, + XKB_KEY_XF86Suspend, Qt::Key_Suspend, + XKB_KEY_XF86Hibernate, Qt::Key_Hibernate, + XKB_KEY_XF86TouchpadToggle, Qt::Key_TouchpadToggle, + XKB_KEY_XF86TouchpadOn, Qt::Key_TouchpadOn, + XKB_KEY_XF86TouchpadOff, Qt::Key_TouchpadOff, + XKB_KEY_XF86AudioMicMute, Qt::Key_MicMute, + XKB_KEY_XF86Launch0, Qt::Key_Launch2, // ### Qt 6: remap properly + XKB_KEY_XF86Launch1, Qt::Key_Launch3, + XKB_KEY_XF86Launch2, Qt::Key_Launch4, + XKB_KEY_XF86Launch3, Qt::Key_Launch5, + XKB_KEY_XF86Launch4, Qt::Key_Launch6, + XKB_KEY_XF86Launch5, Qt::Key_Launch7, + XKB_KEY_XF86Launch6, Qt::Key_Launch8, + XKB_KEY_XF86Launch7, Qt::Key_Launch9, + XKB_KEY_XF86Launch8, Qt::Key_LaunchA, + XKB_KEY_XF86Launch9, Qt::Key_LaunchB, + XKB_KEY_XF86LaunchA, Qt::Key_LaunchC, + XKB_KEY_XF86LaunchB, Qt::Key_LaunchD, + XKB_KEY_XF86LaunchC, Qt::Key_LaunchE, + XKB_KEY_XF86LaunchD, Qt::Key_LaunchF, + XKB_KEY_XF86LaunchE, Qt::Key_LaunchG, + XKB_KEY_XF86LaunchF, Qt::Key_LaunchH, 0, 0 }; @@ -271,10 +499,12 @@ Qt::MouseButton getQtMouseButtonsfromMirPointerEvent(MirPointerEvent const* pev) if (mir_pointer_event_button_state(pev, mir_pointer_button_secondary)) buttons |= Qt::RightButton; if (mir_pointer_event_button_state(pev, mir_pointer_button_tertiary)) - buttons |= Qt::MidButton; + buttons |= Qt::MiddleButton; + if (mir_pointer_event_button_state(pev, mir_pointer_button_back)) + buttons |= Qt::BackButton; + if (mir_pointer_event_button_state(pev, mir_pointer_button_forward)) + buttons |= Qt::ForwardButton; - // TODO: Should mir back and forward buttons exist? - // should they be Qt::X button 1 and 2? return static_cast<Qt::MouseButton>(buttons); } } diff --git a/tests/modules/Application/CMakeLists.txt b/tests/modules/Application/CMakeLists.txt index efef3c3..7787d26 100644 --- a/tests/modules/Application/CMakeLists.txt +++ b/tests/modules/Application/CMakeLists.txt @@ -1,9 +1,11 @@ set( APPLICATION_TEST_SOURCES application_test.cpp + ${CMAKE_SOURCE_DIR}/tests/modules/common/qtmir_test.cpp ) include_directories( + ${APPLICATION_API_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/src/platforms/mirserver ${CMAKE_SOURCE_DIR}/src/modules ${CMAKE_SOURCE_DIR}/tests/modules/common @@ -18,6 +20,8 @@ target_link_libraries( unityapplicationplugin qpa-mirserver + Qt5::Test + ${GTEST_BOTH_LIBRARIES} ${GMOCK_LIBRARIES} ) diff --git a/tests/modules/Application/application_test.cpp b/tests/modules/Application/application_test.cpp index b201917..e2b0e19 100644 --- a/tests/modules/Application/application_test.cpp +++ b/tests/modules/Application/application_test.cpp @@ -20,6 +20,10 @@ #include "qtmir_test.h" +#include <mock_session.h> + +#include <QScopedPointer> +#include <QSignalSpy> using namespace qtmir; @@ -30,93 +34,163 @@ public: {} }; -TEST_F(ApplicationTests, checkFocusAcquiresWakeLock) +TEST_F(ApplicationTests, acquiresWakelockWhenRunningAndReleasesWhenSuspended) { using namespace ::testing; + QString appId("foo-app"); - EXPECT_CALL(sharedWakelock, acquire(_)).Times(1); + auto desktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo()); + ON_CALL(*desktopFileReader, loaded()).WillByDefault(Return(true)); - startApplication(123, "app"); - applicationManager.focusApplication("app"); -} + QScopedPointer<Application> application(new Application( + QSharedPointer<MockSharedWakelock>(&sharedWakelock, [](MockSharedWakelock *){}), + desktopFileReader, QStringList(), nullptr)); -TEST_F(ApplicationTests, checkSuspendReleasesWakeLock) -{ - using namespace ::testing; + application->setProcessState(Application::ProcessRunning); + + NiceMock<MockSession> *session = new NiceMock<MockSession>; + + EXPECT_CALL(*session, setApplication(_)); + EXPECT_CALL(*session, fullscreen()).WillRepeatedly(Return(false)); + + application->setSession(session); + + ASSERT_EQ(Application::InternalState::Starting, application->internalState()); + + session->setState(SessionInterface::Running); + + EXPECT_TRUE(sharedWakelock.enabled()); - auto app = startApplication(123, "app"); - auto session = app->session(); + ASSERT_EQ(Application::InternalState::Running, application->internalState()); - applicationManager.focusApplication("app"); + application->setRequestedState(Application::RequestedSuspended); + + ASSERT_EQ(SessionInterface::Suspending, session->state()); + ASSERT_EQ(Application::InternalState::SuspendingWaitSession, application->internalState()); + + session->setState(SessionInterface::Suspended); + + ASSERT_EQ(Application::InternalState::SuspendingWaitProcess, application->internalState()); + + application->setProcessState(Application::ProcessSuspended); + + ASSERT_EQ(Application::InternalState::Suspended, application->internalState()); - Q_EMIT session->suspended(); EXPECT_FALSE(sharedWakelock.enabled()); } TEST_F(ApplicationTests, checkResumeAcquiresWakeLock) { using namespace ::testing; + QString appId("foo-app"); + + auto desktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo()); + ON_CALL(*desktopFileReader, loaded()).WillByDefault(Return(true)); + + QScopedPointer<Application> application(new Application( + QSharedPointer<MockSharedWakelock>(&sharedWakelock, [](MockSharedWakelock *){}), + desktopFileReader, QStringList(), nullptr)); + NiceMock<MockSession> *session = new NiceMock<MockSession>; + + // Get it running and then suspend it + application->setProcessState(Application::ProcessRunning); + application->setSession(session); + session->setState(SessionInterface::Running); + application->setRequestedState(Application::RequestedSuspended); + session->setState(SessionInterface::Suspended); + application->setProcessState(Application::ProcessSuspended); + ASSERT_EQ(Application::InternalState::Suspended, application->internalState()); + + EXPECT_FALSE(sharedWakelock.enabled()); - EXPECT_CALL(sharedWakelock, acquire(_)).Times(1); + application->setRequestedState(Application::RequestedRunning); - auto app = startApplication(123, "app"); - auto session = app->session(); + ASSERT_EQ(Application::InternalState::Running, application->internalState()); - Q_EMIT session->resumed(); + EXPECT_TRUE(sharedWakelock.enabled()); } TEST_F(ApplicationTests, checkRespawnAcquiresWakeLock) { using namespace ::testing; + QString appId("foo-app"); + + auto desktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo()); + ON_CALL(*desktopFileReader, loaded()).WillByDefault(Return(true)); + + QScopedPointer<Application> application(new Application( + QSharedPointer<MockSharedWakelock>(&sharedWakelock, [](MockSharedWakelock *){}), + desktopFileReader, QStringList(), nullptr)); + NiceMock<MockSession> *session = new NiceMock<MockSession>; + + // Get it running, suspend it, and finally stop it + application->setProcessState(Application::ProcessRunning); + application->setSession(session); + session->setState(SessionInterface::Running); + application->setRequestedState(Application::RequestedSuspended); + session->setState(SessionInterface::Suspended); + application->setProcessState(Application::ProcessSuspended); + ASSERT_EQ(Application::InternalState::Suspended, application->internalState()); + session->setState(SessionInterface::Stopped); + application->setProcessState(Application::ProcessStopped); + ASSERT_EQ(Application::InternalState::StoppedUnexpectedly, application->internalState()); - EXPECT_CALL(sharedWakelock, acquire(_)).Times(1); - const QString appId = "app"; + EXPECT_FALSE(sharedWakelock.enabled()); - auto app = startApplication(123, "app"); + QSignalSpy spyStartProcess(application.data(), SIGNAL(startProcessRequested())); + application->setRequestedState(Application::RequestedRunning); + ASSERT_EQ(1, spyStartProcess.count()); + application->setProcessState(Application::ProcessRunning); - // as respawn fires startApplicationWithAppIdAndArgs again, keep gmock quiet about another call - EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _)) - .Times(1) - .WillRepeatedly(Return(true)); + ASSERT_EQ(Application::InternalState::Starting, application->internalState()); - // respawn by setting app state as Stopped, delete the Session associated, then set to Running state - app->setState(Session::State::Stopped); - delete app->session(); - app->setState(Session::State::Running); + EXPECT_TRUE(sharedWakelock.enabled()); } -TEST_F(ApplicationTests, checkDashFocusDoesNotAcquireWakeLock) +TEST_F(ApplicationTests, checkDashDoesNotImpactWakeLock) { using namespace ::testing; + QString appId("unity8-dash"); EXPECT_CALL(sharedWakelock, acquire(_)).Times(0); + EXPECT_CALL(sharedWakelock, release(_)).Times(0); - startApplication(123, "unity8-dash"); - applicationManager.focusApplication("unity8-dash"); -} + auto desktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo()); + ON_CALL(*desktopFileReader, loaded()).WillByDefault(Return(true)); -TEST_F(ApplicationTests, checkDashSuspendDoesNotImpactWakeLock) -{ - using namespace ::testing; + QScopedPointer<Application> application(new Application( + QSharedPointer<MockSharedWakelock>(&sharedWakelock, [](MockSharedWakelock *){}), + desktopFileReader, QStringList(), nullptr)); - auto app = startApplication(123, "unity8-dash"); - auto session = app->session(); + application->setProcessState(Application::ProcessRunning); - applicationManager.focusApplication("unity8-dash"); + NiceMock<MockSession> *session = new NiceMock<MockSession>; - Q_EMIT session->suspended(); - EXPECT_FALSE(sharedWakelock.enabled()); -} + EXPECT_CALL(*session, setApplication(_)); + EXPECT_CALL(*session, fullscreen()).WillRepeatedly(Return(false)); -TEST_F(ApplicationTests, checkDashResumeDoesNotAcquireWakeLock) -{ - using namespace ::testing; + application->setSession(session); - EXPECT_CALL(sharedWakelock, acquire(_)).Times(0); + ASSERT_EQ(Application::InternalState::Starting, application->internalState()); - auto app = startApplication(123, "unity8-dash"); - auto session = app->session(); + session->setState(SessionInterface::Running); - Q_EMIT session->resumed(); -} + ASSERT_EQ(Application::InternalState::Running, application->internalState()); + + application->setRequestedState(Application::RequestedSuspended); + + ASSERT_EQ(SessionInterface::Suspending, session->state()); + ASSERT_EQ(Application::InternalState::SuspendingWaitSession, application->internalState()); + + session->setState(SessionInterface::Suspended); + + ASSERT_EQ(Application::InternalState::SuspendingWaitProcess, application->internalState()); + application->setProcessState(Application::ProcessSuspended); + + ASSERT_EQ(Application::InternalState::Suspended, application->internalState()); + + application->setRequestedState(Application::RequestedRunning); + + ASSERT_EQ(Application::InternalState::Running, application->internalState()); +} diff --git a/tests/modules/ApplicationManager/CMakeLists.txt b/tests/modules/ApplicationManager/CMakeLists.txt index 6590576..d90491c 100644 --- a/tests/modules/ApplicationManager/CMakeLists.txt +++ b/tests/modules/ApplicationManager/CMakeLists.txt @@ -2,9 +2,12 @@ set( APPLICATION_MANAGER_TEST_SOURCES application_manager_test.cpp ${CMAKE_SOURCE_DIR}/src/common/debughelpers.cpp + ${CMAKE_SOURCE_DIR}/tests/modules/common/qtmir_test.cpp + ../common/fake_mirsurfaceitem.h ) include_directories( + ${APPLICATION_API_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/src/platforms/mirserver ${CMAKE_SOURCE_DIR}/src/modules ${CMAKE_SOURCE_DIR}/tests/modules/common @@ -15,7 +18,7 @@ add_executable(applicationmanager_test ${APPLICATION_MANAGER_TEST_SOURCES}) target_link_libraries( applicationmanager_test - + qpa-mirserver unityapplicationplugin diff --git a/tests/modules/ApplicationManager/application_manager_test.cpp b/tests/modules/ApplicationManager/application_manager_test.cpp index 751000d..a21a5c8 100644 --- a/tests/modules/ApplicationManager/application_manager_test.cpp +++ b/tests/modules/ApplicationManager/application_manager_test.cpp @@ -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 @@ -23,8 +23,9 @@ #include <Unity/Application/applicationscreenshotprovider.h> - #include "mock_surface.h" - #include "qtmir_test.h" + #include <fake_mirsurfaceitem.h> + #include <mock_surface.h> + #include <qtmir_test.h> using namespace qtmir; using mir::scene::MockSession; @@ -45,100 +46,41 @@ public: applicationManager.onSessionStopping(session); sessionManager.onSessionStopping(session); } -}; - -TEST_F(ApplicationManagerTests, SuspendingAndResumingARunningApplicationResultsInOomScoreAdjustment) -{ - using namespace ::testing; - - const QString appId("com.canonical.does.not.exist"); - - EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(_, _)).Times(1); - EXPECT_CALL(appController, findDesktopFileForAppId(appId)).Times(1); - - EXPECT_CALL(desktopFileReaderFactory, createInstance(_, _)).Times(1); - - auto application = applicationManager.startApplication( - appId, - ApplicationManager::NoFlag, - QStringList()); - - // FIXME - this is doesn't really excerise the actualt behaviour since suspend/resume should be - // controlled by state changes. Requires using suspend timer. - QMetaObject::invokeMethod(application, "onSessionSuspended"); - QMetaObject::invokeMethod(application, "onSessionResumed"); -} - -TEST_F(ApplicationManagerTests, SuspendingAndResumingDashResultsInOomScoreAdjustment) -{ - using namespace ::testing; - - quint64 procId = 5921; - std::shared_ptr<mir::scene::Surface> aSurface(nullptr); - QByteArray cmdLine( "/usr/bin/app1 --desktop_file_hint=unity8-dash"); - - EXPECT_CALL(procInfo,command_line(procId)) - .Times(1) - .WillOnce(Return(cmdLine)); - - ON_CALL(appController,appIdHasProcessId(_,_)).WillByDefault(Return(false)); - - bool authed = true; - - std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("Oo", procId); - applicationManager.authorizeSession(procId, authed); - onSessionStarting(session); - - auto application = applicationManager.findApplication("unity8-dash"); - applicationManager.onSessionCreatedSurface(session.get(), aSurface); - - // FIXME - this is doesn't really excerise the actualt behaviour since suspend/resume should be - // controlled by state changes. Requires using suspend timer. - QMetaObject::invokeMethod(application, "onSessionSuspended"); - QMetaObject::invokeMethod(application, "onSessionResumed"); -} - -// Currently disabled as we need to make sure that we have a corresponding mir session, too. -TEST_F(ApplicationManagerTests, DISABLED_FocusingRunningApplicationResultsInOomScoreAdjustment) -{ - using namespace ::testing; - - const QString appId("com.canonical.does.not.exist"); - - QSet<QString> appIds; - - for (unsigned int i = 0; i < 50; i++) - { - QString appIdFormat("%1.does.not.exist"); - auto appId = appIdFormat.arg(i); - - auto application = applicationManager.startApplication( - appId, - ApplicationManager::NoFlag, - QStringList()); - - std::shared_ptr<mir::scene::Session> mirSession = std::make_shared<MockSession>(appIdFormat.toStdString(), i); - onSessionStarting( mirSession ); - - EXPECT_NE(nullptr, application); + inline void onSessionCreatedSurface(const mir::scene::Session *mirSession, + MirSurfaceItemInterface *qmlSurface) { + + SessionInterface* qmlSession = sessionManager.findSession(mirSession); + if (qmlSession) { + qmlSession->setSurface(qmlSurface); + } + + // I assume that applicationManager ignores the mirSurface parameter, so sending + // a null shared pointer must suffice + std::shared_ptr<mir::scene::Surface> mirSurface(nullptr); + applicationManager.onSessionCreatedSurface(mirSession, mirSurface); } - for (auto appId : appIds) - { - applicationManager.focusApplication(appId); + inline void suspend(Application *application) { + application->setRequestedState(Application::RequestedSuspended); + ASSERT_EQ(Application::InternalState::SuspendingWaitSession, application->internalState()); + static_cast<qtmir::Session*>(application->session())->doSuspend(); + ASSERT_EQ(Application::InternalState::SuspendingWaitProcess, application->internalState()); + applicationManager.onProcessSuspended(application->appId()); + ASSERT_EQ(Application::InternalState::Suspended, application->internalState()); } -} +}; TEST_F(ApplicationManagerTests,bug_case_1240400_second_dialer_app_fails_to_authorize_and_gets_mixed_up_with_first_one) { using namespace ::testing; - std::shared_ptr<mir::scene::Surface> aSurface(nullptr); quint64 firstProcId = 5921; quint64 secondProcId = 5922; const char dialer_app_id[] = "dialer-app"; QByteArray cmdLine( "/usr/bin/dialer-app --desktop_file_hint=dialer-app"); QByteArray secondcmdLine( "/usr/bin/dialer-app"); + FakeMirSurfaceItem *surface = new FakeMirSurfaceItem; + EXPECT_CALL(procInfo,command_line(firstProcId)) .Times(1) .WillOnce(Return(cmdLine)); @@ -150,18 +92,20 @@ TEST_F(ApplicationManagerTests,bug_case_1240400_second_dialer_app_fails_to_autho std::shared_ptr<mir::scene::Session> mirSession = std::make_shared<MockSession>(dialer_app_id, firstProcId); applicationManager.authorizeSession(firstProcId, authed); - EXPECT_EQ(true, authed); + ASSERT_EQ(true, authed); onSessionStarting(mirSession); - applicationManager.onSessionCreatedSurface(mirSession.get(),aSurface); - Application * app = applicationManager.findApplication(dialer_app_id); - EXPECT_NE(nullptr,app); + onSessionCreatedSurface(mirSession.get(), surface); + surface->drawFirstFrame(); + Application * application = applicationManager.findApplication(dialer_app_id); + ASSERT_NE(nullptr,application); + ASSERT_EQ(Application::InternalState::Running, application->internalState()); // now a second session without desktop file is launched: applicationManager.authorizeSession(secondProcId, authed); applicationManager.onProcessStarting(dialer_app_id); EXPECT_FALSE(authed); - EXPECT_EQ(app,applicationManager.findApplication(dialer_app_id)); + EXPECT_EQ(application, applicationManager.findApplication(dialer_app_id)); } TEST_F(ApplicationManagerTests,application_dies_while_starting) @@ -182,6 +126,7 @@ TEST_F(ApplicationManagerTests,application_dies_while_starting) onSessionStarting(mirSession); Application * beforeFailure = applicationManager.findApplication(app_id); applicationManager.onProcessStarting(app_id); + onSessionStopping(mirSession); applicationManager.onProcessFailed(app_id, true); Application * afterFailure = applicationManager.findApplication(app_id); @@ -190,34 +135,6 @@ TEST_F(ApplicationManagerTests,application_dies_while_starting) EXPECT_EQ(nullptr, afterFailure); } -TEST_F(ApplicationManagerTests,application_start_failure_after_starting) -{ - using namespace ::testing; - quint64 procId = 5921; - std::shared_ptr<mir::scene::Surface> aSurface(nullptr); - const char app_id[] = "my-app"; - QByteArray cmdLine( "/usr/bin/my-app --desktop_file_hint=my-app"); - - EXPECT_CALL(procInfo,command_line(procId)) - .Times(1) - .WillOnce(Return(cmdLine)); - - bool authed = true; - - std::shared_ptr<mir::scene::Session> mirSession = std::make_shared<MockSession>(app_id, procId); - applicationManager.authorizeSession(procId, authed); - onSessionStarting(mirSession); - Application * beforeFailure = applicationManager.findApplication(app_id); - applicationManager.onSessionCreatedSurface(mirSession.get(), aSurface); - applicationManager.onProcessStarting(app_id); - applicationManager.onProcessFailed(app_id, false); - Application * afterFailure = applicationManager.findApplication(app_id); - - EXPECT_EQ(true, authed); - EXPECT_NE(nullptr, beforeFailure); - EXPECT_EQ(beforeFailure, afterFailure); -} - TEST_F(ApplicationManagerTests,startApplicationSupportsShortAppId) { using namespace ::testing; @@ -400,7 +317,7 @@ TEST_F(ApplicationManagerTests,two_session_on_one_application_after_starting) quint64 a_procId = 5921; const char an_app_id[] = "some_app"; QByteArray a_cmd( "/usr/bin/app1 --desktop_file_hint=some_app"); - std::shared_ptr<mir::scene::Surface> aSurface(nullptr); + FakeMirSurfaceItem *aSurface = new FakeMirSurfaceItem; ON_CALL(procInfo,command_line(_)).WillByDefault(Return(a_cmd)); @@ -413,8 +330,8 @@ TEST_F(ApplicationManagerTests,two_session_on_one_application_after_starting) applicationManager.authorizeSession(a_procId, authed); onSessionStarting(first_session); - applicationManager.focusApplication(an_app_id); - applicationManager.onSessionCreatedSurface(first_session.get(), aSurface); + onSessionCreatedSurface(first_session.get(), aSurface); + aSurface->drawFirstFrame(); onSessionStarting(second_session); Application * the_app = applicationManager.findApplication(an_app_id); @@ -430,18 +347,19 @@ TEST_F(ApplicationManagerTests, focused_app_can_rerequest_focus) quint64 a_procId = 5921; const char an_app_id[] = "some_app"; QByteArray a_cmd("/usr/bin/app1 --desktop_file_hint=some_app"); - std::shared_ptr<mir::scene::Surface> aSurface(nullptr); + FakeMirSurfaceItem *aSurface = new FakeMirSurfaceItem; ON_CALL(procInfo, command_line(_)).WillByDefault(Return(a_cmd)); ON_CALL(appController, appIdHasProcessId(_,_)).WillByDefault(Return(false)); - + bool authed = true; std::shared_ptr<mir::scene::Session> a_session = std::make_shared<MockSession>("Oo", a_procId); - + applicationManager.authorizeSession(a_procId, authed); onSessionStarting(a_session); - applicationManager.onSessionCreatedSurface(a_session.get(), aSurface); + onSessionCreatedSurface(a_session.get(), aSurface); + aSurface->drawFirstFrame(); Application * the_app = applicationManager.findApplication(an_app_id); applicationManager.focusApplication(an_app_id); @@ -453,188 +371,38 @@ TEST_F(ApplicationManagerTests, focused_app_can_rerequest_focus) EXPECT_EQ(true, the_app->focused()); } -TEST_F(ApplicationManagerTests,suspended_suspends_focused_app_and_marks_it_unfocused_in_the_model) +TEST_F(ApplicationManagerTests,starting_app_is_suspended_when_it_gets_ready_if_requested) { using namespace ::testing; - quint64 a_procId = 5921; - const char an_app_id[] = "some_app"; - QByteArray a_cmd( "/usr/bin/app1 --desktop_file_hint=some_app"); - std::shared_ptr<mir::scene::Surface> aSurface(nullptr); - - ON_CALL(procInfo,command_line(_)).WillByDefault(Return(a_cmd)); - - ON_CALL(appController,appIdHasProcessId(_,_)).WillByDefault(Return(false)); - - bool authed = true; - - std::shared_ptr<mir::scene::Session> first_session = std::make_shared<MockSession>("Oo", a_procId); - std::shared_ptr<mir::scene::Session> second_session = std::make_shared<MockSession>("oO", a_procId); - applicationManager.authorizeSession(a_procId, authed); - - onSessionStarting(first_session); - applicationManager.onSessionCreatedSurface(first_session.get(), aSurface); - onSessionStarting(second_session); - - Application * the_app = applicationManager.findApplication(an_app_id); - applicationManager.focusApplication(an_app_id); - - EXPECT_EQ(Application::Running, the_app->state()); - - applicationManager.setSuspended(true); - - EXPECT_EQ(Application::Suspended, the_app->state()); - EXPECT_FALSE(the_app->focused()); - - applicationManager.setSuspended(false); - - EXPECT_EQ(Application::Running, the_app->state()); - EXPECT_EQ(true, the_app->focused()); -} - -TEST_F(ApplicationManagerTests,suspended_suspends_starting_app_when_it_gets_ready) -{ - using namespace ::testing; - quint64 a_procId = 5921; - const char an_app_id[] = "some_app"; - QByteArray a_cmd( "/usr/bin/app1 --desktop_file_hint=some_app"); - std::shared_ptr<mir::scene::Surface> aSurface(nullptr); - - ON_CALL(procInfo,command_line(_)).WillByDefault(Return(a_cmd)); - - ON_CALL(appController,appIdHasProcessId(_,_)).WillByDefault(Return(false)); - - bool authed = true; - - std::shared_ptr<mir::scene::Session> first_session = std::make_shared<MockSession>("Oo", a_procId); - applicationManager.authorizeSession(a_procId, authed); - - onSessionStarting(first_session); - - Application * the_app = applicationManager.findApplication(an_app_id); - applicationManager.focusApplication(an_app_id); - EXPECT_EQ(Application::Starting, the_app->state()); - - applicationManager.setSuspended(true); - - // Not suspending yet, as it's still starting - EXPECT_EQ(Application::Starting, the_app->state()); - - // This signals the app is ready now - applicationManager.onSessionCreatedSurface(first_session.get(), aSurface); - - // And given that the AppManager is suspended now, this should go to suspended too - EXPECT_EQ(Application::Suspended, the_app->state()); - EXPECT_FALSE(the_app->focused()); - - applicationManager.setSuspended(false); - - EXPECT_EQ(Application::Running, the_app->state()); - EXPECT_EQ(true, the_app->focused()); -} - -TEST_F(ApplicationManagerTests,focus_change_suspends_starting_app_when_it_gets_ready) -{ - using namespace ::testing; - quint64 first_procId = 5921; - quint64 second_procId = 5922; - std::shared_ptr<mir::scene::Surface> aSurface(nullptr); - QByteArray first_cmdLine( "/usr/bin/app1 --desktop_file_hint=app1"); - QByteArray second_cmdLine( "/usr/bin/app2--desktop_file_hint=app2"); + quint64 procId = 5921; + FakeMirSurfaceItem *aSurface = new FakeMirSurfaceItem; + QByteArray cmdLine( "/usr/bin/app --desktop_file_hint=app"); - EXPECT_CALL(procInfo,command_line(first_procId)) + EXPECT_CALL(procInfo,command_line(procId)) .Times(1) - .WillOnce(Return(first_cmdLine)); + .WillOnce(Return(cmdLine)); ON_CALL(appController,appIdHasProcessId(_,_)).WillByDefault(Return(false)); - EXPECT_CALL(procInfo,command_line(second_procId)) - .Times(1) - .WillOnce(Return(second_cmdLine)); - bool authed = true; - std::shared_ptr<mir::scene::Session> first_session = std::make_shared<MockSession>("Oo", first_procId); - std::shared_ptr<mir::scene::Session> second_session = std::make_shared<MockSession>("oO", second_procId); - applicationManager.authorizeSession(first_procId, authed); - applicationManager.authorizeSession(second_procId, authed); - onSessionStarting(first_session); + std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("Oo", procId); + applicationManager.authorizeSession(procId, authed); + onSessionStarting(session); - Application * app1 = applicationManager.findApplication("app1"); - applicationManager.focusApplication("app1"); + Application * app = applicationManager.findApplication("app"); + app->setRequestedState(Application::RequestedSuspended); // First app starting... - EXPECT_EQ(Application::Starting, app1->state()); + EXPECT_EQ(Application::Starting, app->state()); - onSessionStarting(second_session); - Application * app2 = applicationManager.findApplication("app2"); - applicationManager.focusApplication("app2"); - - // Second app starting... - EXPECT_EQ(Application::Starting, app2->state()); - - // Make sure first one is still in starting state - EXPECT_EQ(Application::Starting, app1->state()); - - // Signal app1 is ready now - applicationManager.onSessionCreatedSurface(first_session.get(), aSurface); + // Signal app is ready now + applicationManager.onProcessStarting("app"); + onSessionCreatedSurface(session.get(), aSurface); + aSurface->drawFirstFrame(); - // Make sure AppMan suspended it now that its ready - EXPECT_EQ(Application::Suspended, app1->state()); -} - -TEST_F(ApplicationManagerTests,forceDashActive_activates_dash_while_not_focused) -{ - using namespace ::testing; - quint64 first_procId = 5921; - quint64 second_procId = 5922; - std::shared_ptr<mir::scene::Surface> aSurface(nullptr); - QByteArray first_cmdLine( "/usr/bin/app1 --desktop_file_hint=unity8-dash"); - QByteArray second_cmdLine( "/usr/bin/app2--desktop_file_hint=app2"); - - EXPECT_CALL(procInfo,command_line(first_procId)) - .Times(1) - .WillOnce(Return(first_cmdLine)); - - ON_CALL(appController,appIdHasProcessId(_,_)).WillByDefault(Return(false)); - - EXPECT_CALL(procInfo,command_line(second_procId)) - .Times(1) - .WillOnce(Return(second_cmdLine)); - - bool authed = true; - - std::shared_ptr<mir::scene::Session> first_session = std::make_shared<MockSession>("Oo", first_procId); - std::shared_ptr<mir::scene::Session> second_session = std::make_shared<MockSession>("oO", second_procId); - applicationManager.authorizeSession(first_procId, authed); - applicationManager.authorizeSession(second_procId, authed); - onSessionStarting(first_session); - - Application * dashApp = applicationManager.findApplication("unity8-dash"); - applicationManager.onSessionCreatedSurface(first_session.get(), aSurface); - applicationManager.focusApplication("unity8-dash"); - - // Dash app should be ready now... - EXPECT_EQ(Application::Running, dashApp->state()); - - // Launch second app - onSessionStarting(second_session); - applicationManager.onSessionCreatedSurface(second_session.get(), aSurface); - applicationManager.focusApplication("app2"); - EXPECT_EQ(applicationManager.focusedApplicationId(), "app2"); - - // Make sure the dash is suspended - EXPECT_EQ(dashApp->state(), Application::Suspended); - - // Now set the dashactive flag - applicationManager.setForceDashActive(true); - - // And make sure the dash is woken up but not focused - EXPECT_EQ(applicationManager.focusedApplicationId(), "app2"); - EXPECT_EQ(dashApp->state(), Application::Running); - - // Unset the dashactive flag - applicationManager.setForceDashActive(false); - EXPECT_EQ(dashApp->state(), Application::Suspended); + // now that its ready, suspend process should have begun + EXPECT_EQ(Application::InternalState::SuspendingWaitSession, app->internalState()); } TEST_F(ApplicationManagerTests,requestFocusApplication) @@ -713,20 +481,20 @@ TEST_F(ApplicationManagerTests,appStartedByShell) Application *theApp = applicationManager.startApplication(appId, ApplicationManager::NoFlag); // check application data - EXPECT_EQ(theApp->state(), Application::Starting); - EXPECT_EQ(theApp->appId(), appId); - EXPECT_EQ(theApp->name(), name); - EXPECT_EQ(theApp->canBeResumed(), true); + EXPECT_EQ(Application::Starting, theApp->state()); + EXPECT_EQ(appId, theApp->appId()); + EXPECT_EQ(name, theApp->name()); + EXPECT_FALSE(theApp->canBeResumed()); // check signals were emitted - EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback) - EXPECT_EQ(applicationManager.count(), 1); - EXPECT_EQ(addedSpy.count(), 1); - EXPECT_EQ(addedSpy.takeFirst().at(0).toString(), appId); + EXPECT_EQ(2, countSpy.count()); //FIXME(greyback) + EXPECT_EQ(1, applicationManager.count()); + EXPECT_EQ(1, addedSpy.count()); + EXPECT_EQ(appId, addedSpy.takeFirst().at(0).toString()); // check application in list of apps Application *theAppAgain = applicationManager.findApplication(appId); - EXPECT_EQ(theAppAgain, theApp); + EXPECT_EQ(theApp, theAppAgain); } /* @@ -757,18 +525,18 @@ TEST_F(ApplicationManagerTests,appStartedByUpstart) Application *theApp = applicationManager.findApplication(appId); // check application data - EXPECT_EQ(theApp->state(), Application::Starting); - EXPECT_EQ(theApp->appId(), appId); - EXPECT_EQ(theApp->name(), name); - EXPECT_EQ(theApp->canBeResumed(), true); + EXPECT_EQ(Application::Starting, theApp->state()); + EXPECT_EQ(appId, theApp->appId()); + EXPECT_EQ(name, theApp->name()); + EXPECT_EQ(true, theApp->canBeResumed()); // check signals were emitted - EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback) - EXPECT_EQ(applicationManager.count(), 1); - EXPECT_EQ(addedSpy.count(), 1); - EXPECT_EQ(addedSpy.takeFirst().at(0).toString(), appId); - EXPECT_EQ(focusSpy.count(), 1); - EXPECT_EQ(focusSpy.takeFirst().at(0).toString(), appId); + EXPECT_EQ(2, countSpy.count()); //FIXME(greyback) + EXPECT_EQ(1, applicationManager.count()); + EXPECT_EQ(1, addedSpy.count()); + EXPECT_EQ(appId, addedSpy.takeFirst().at(0).toString()); + EXPECT_EQ(1, focusSpy.count()); + EXPECT_EQ(appId, focusSpy.takeFirst().at(0).toString()); } /* @@ -1067,7 +835,6 @@ TEST_F(ApplicationManagerTests,onceAppAddedToApplicationLists_mirSurfaceCreatedE applicationManager.startApplication(appId, ApplicationManager::NoFlag); applicationManager.onProcessStarting(appId); - applicationManager.focusApplication(appId); std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId); @@ -1075,9 +842,10 @@ TEST_F(ApplicationManagerTests,onceAppAddedToApplicationLists_mirSurfaceCreatedE applicationManager.authorizeSession(procId, authed); onSessionStarting(session); - std::shared_ptr<mir::scene::Surface> surface(nullptr); + FakeMirSurfaceItem *surface = new FakeMirSurfaceItem; - applicationManager.onSessionCreatedSurface(session.get(), surface); + onSessionCreatedSurface(session.get(), surface); + surface->drawFirstFrame(); // Check application state is correctly set Application *theApp = applicationManager.findApplication(appId); @@ -1124,7 +892,7 @@ TEST_F(ApplicationManagerTests,shellStopsAppCorrectlyBeforeSurfaceCreated) } /* - * Test that the foreground application is stopped correctly (is in Running state, has surface) + * Test that a running application is stopped correctly (is in Running state, has surface) */ TEST_F(ApplicationManagerTests,shellStopsForegroundAppCorrectly) { @@ -1150,10 +918,9 @@ TEST_F(ApplicationManagerTests,shellStopsForegroundAppCorrectly) applicationManager.authorizeSession(procId, authed); onSessionStarting(session); - std::shared_ptr<mir::scene::Surface> surface(nullptr); - applicationManager.onSessionCreatedSurface(session.get(), surface); - applicationManager.focusApplication(appId); - EXPECT_EQ(applicationManager.focusedApplicationId(), appId); + FakeMirSurfaceItem *surface = new FakeMirSurfaceItem; + onSessionCreatedSurface(session.get(), surface); + surface->drawFirstFrame(); QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &))); @@ -1168,9 +935,9 @@ TEST_F(ApplicationManagerTests,shellStopsForegroundAppCorrectly) } /* - * Test that the background application is stopped correctly + * Test that a suspended application is stopped correctly */ -TEST_F(ApplicationManagerTests,shellStopsBackgroundAppCorrectly) +TEST_F(ApplicationManagerTests,shellStopsSuspendedAppCorrectly) { using namespace ::testing; const QString appId("testAppId"); @@ -1187,18 +954,19 @@ TEST_F(ApplicationManagerTests,shellStopsBackgroundAppCorrectly) .Times(1) .WillOnce(Return(true)); - applicationManager.startApplication(appId, ApplicationManager::NoFlag); + Application *application = applicationManager.startApplication(appId, ApplicationManager::NoFlag); applicationManager.onProcessStarting(appId); std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId); bool authed = true; applicationManager.authorizeSession(procId, authed); onSessionStarting(session); + applicationManager.onProcessStarting(appId); - std::shared_ptr<mir::scene::Surface> surface(nullptr); - applicationManager.onSessionCreatedSurface(session.get(), surface); - applicationManager.unfocusCurrentApplication(); + FakeMirSurfaceItem *surface = new FakeMirSurfaceItem; + onSessionCreatedSurface(session.get(), surface); + surface->drawFirstFrame(); - EXPECT_EQ(applicationManager.focusedApplicationId(), QString()); + suspend(application); QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &))); @@ -1213,45 +981,6 @@ TEST_F(ApplicationManagerTests,shellStopsBackgroundAppCorrectly) } /* - * Test that if an application is stopped by upstart, before it has created a surface, AppMan cleans up after it ok - */ -TEST_F(ApplicationManagerTests,upstartNotificationOfStartingAppBeingStopped) -{ - using namespace ::testing; - const QString appId("testAppId"); - quint64 procId = 5551; - - // Set up Mocks & signal watcher - auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo()); - ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true)); - ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId)); - - ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader)); - - EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _)) - .Times(1) - .WillOnce(Return(true)); - - applicationManager.startApplication(appId, ApplicationManager::NoFlag); - applicationManager.onProcessStarting(appId); - std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId); - bool authed = true; - applicationManager.authorizeSession(procId, authed); - onSessionStarting(session); - - QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); - QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &))); - - // Upstart notifies of stopping app - applicationManager.onProcessStopped(appId); - - EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback) - EXPECT_EQ(applicationManager.count(), 0); - EXPECT_EQ(removedSpy.count(), 1); - EXPECT_EQ(removedSpy.takeFirst().at(0).toString(), appId); -} - -/* * Test that if the foreground Running application is stopped by upstart, AppMan cleans up after it ok */ TEST_F(ApplicationManagerTests,upstartNotifiesOfStoppingForegroundApp) @@ -1278,28 +1007,28 @@ TEST_F(ApplicationManagerTests,upstartNotifiesOfStoppingForegroundApp) applicationManager.authorizeSession(procId, authed); onSessionStarting(session); - std::shared_ptr<mir::scene::Surface> surface(nullptr); - applicationManager.onSessionCreatedSurface(session.get(), surface); - applicationManager.focusApplication(appId); - EXPECT_EQ(applicationManager.focusedApplicationId(), appId); + FakeMirSurfaceItem *surface = new FakeMirSurfaceItem; + onSessionCreatedSurface(session.get(), surface); + surface->drawFirstFrame(); QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &))); + onSessionStopping(session); // Upstart notifies of stopping app applicationManager.onProcessStopped(appId); - EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback) - EXPECT_EQ(applicationManager.count(), 0); - EXPECT_EQ(removedSpy.count(), 1); - EXPECT_EQ(removedSpy.takeFirst().at(0).toString(), appId); + EXPECT_EQ(2, countSpy.count()); //FIXME(greyback) + EXPECT_EQ(0, applicationManager.count()); + EXPECT_EQ(1, removedSpy.count()); + EXPECT_EQ(appId, removedSpy.takeFirst().at(0).toString()); } /* - * Test that if the foreground Running application is reported to unexpectedly stop by upstart, AppMan - * cleans up after it ok (as was not in background, had not lifecycle saved its state, so cannot be resumed) + * Test that if a running application is reported to have stopped unexpectedly by upstart, AppMan + * cleans up after it ok (as was not suspended, had not lifecycle saved its state, so cannot be resumed) */ -TEST_F(ApplicationManagerTests,upstartNotifiesOfUnexpectedStopOfForegroundApp) +TEST_F(ApplicationManagerTests,upstartNotifiesOfUnexpectedStopOfRunningApp) { using namespace ::testing; const QString appId("testAppId"); @@ -1323,14 +1052,15 @@ TEST_F(ApplicationManagerTests,upstartNotifiesOfUnexpectedStopOfForegroundApp) applicationManager.authorizeSession(procId, authed); onSessionStarting(session); - std::shared_ptr<mir::scene::Surface> surface(nullptr); - applicationManager.onSessionCreatedSurface(session.get(), surface); - applicationManager.focusApplication(appId); - EXPECT_EQ(applicationManager.focusedApplicationId(), appId); + FakeMirSurfaceItem *surface = new FakeMirSurfaceItem; + onSessionCreatedSurface(session.get(), surface); + surface->drawFirstFrame(); QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &))); + onSessionStopping(session); + // Upstart notifies of crashing / OOM killed app applicationManager.onProcessFailed(appId, false); @@ -1344,53 +1074,6 @@ TEST_F(ApplicationManagerTests,upstartNotifiesOfUnexpectedStopOfForegroundApp) } /* - * Test that if a background application is stopped by upstart, AppMan removes it from the app list - * as the event is a result of direct user interaction - */ -TEST_F(ApplicationManagerTests,upstartNotifiesOfStoppingBackgroundApp) -{ - using namespace ::testing; - const QString appId("testAppId"); - quint64 procId = 5551; - - // Set up Mocks & signal watcher - auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo()); - ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true)); - ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId)); - - ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader)); - - EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _)) - .Times(1) - .WillOnce(Return(true)); - - applicationManager.startApplication(appId, ApplicationManager::NoFlag); - applicationManager.onProcessStarting(appId); - std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId); - bool authed = true; - applicationManager.authorizeSession(procId, authed); - onSessionStarting(session); - - std::shared_ptr<mir::scene::Surface> surface(nullptr); - applicationManager.onSessionCreatedSurface(session.get(), surface); - applicationManager.unfocusCurrentApplication(); - EXPECT_EQ(applicationManager.focusedApplicationId(), QString()); - - QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); - QSignalSpy focusSpy(&applicationManager, SIGNAL(focusedApplicationIdChanged())); - QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &))); - - // Upstart notifies of stopping app - applicationManager.onProcessStopped(appId); - - EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback) - EXPECT_EQ(applicationManager.count(), 0); - EXPECT_EQ(focusSpy.count(), 0); - EXPECT_EQ(removedSpy.count(), 1); - EXPECT_EQ(removedSpy.takeFirst().at(0).toString(), appId); -} - -/* * Test that if a background application is reported to unexpectedly stop by upstart, AppMan does not remove * it from the app lists but instead considers it Stopped, ready to be resumed. This is due to the fact the * app should have saved its state, so can be resumed. This situation can occur due to the OOM killer, or @@ -1413,18 +1096,18 @@ TEST_F(ApplicationManagerTests,unexpectedStopOfBackgroundApp) .Times(1) .WillOnce(Return(true)); - applicationManager.startApplication(appId, ApplicationManager::NoFlag); + Application *app = applicationManager.startApplication(appId, ApplicationManager::NoFlag); applicationManager.onProcessStarting(appId); std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId); bool authed = true; applicationManager.authorizeSession(procId, authed); onSessionStarting(session); - std::shared_ptr<mir::scene::Surface> surface(nullptr); - applicationManager.onSessionCreatedSurface(session.get(), surface); - applicationManager.focusApplication(appId); - applicationManager.unfocusCurrentApplication(); - EXPECT_EQ(applicationManager.focusedApplicationId(), QString()); + FakeMirSurfaceItem *surface = new FakeMirSurfaceItem; + onSessionCreatedSurface(session.get(), surface); + surface->drawFirstFrame(); + + suspend(app); QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); QSignalSpy focusSpy(&applicationManager, SIGNAL(focusedApplicationIdChanged())); @@ -1436,14 +1119,14 @@ TEST_F(ApplicationManagerTests,unexpectedStopOfBackgroundApp) // Upstart notifies of crashing / OOM-killed app applicationManager.onProcessFailed(appId, false); - EXPECT_EQ(focusSpy.count(), 0); + EXPECT_EQ(0, focusSpy.count()); // Upstart finally notifies the app stopped applicationManager.onProcessStopped(appId); - EXPECT_EQ(countSpy.count(), 0); - EXPECT_EQ(applicationManager.count(), 1); - EXPECT_EQ(removedSpy.count(), 0); + EXPECT_EQ(0, countSpy.count()); + EXPECT_EQ(1, applicationManager.count()); + EXPECT_EQ(0, removedSpy.count()); } /* @@ -1471,18 +1154,18 @@ TEST_F(ApplicationManagerTests,unexpectedStopOfBackgroundAppCheckingUpstartBug) .Times(1) .WillOnce(Return(true)); - applicationManager.startApplication(appId, ApplicationManager::NoFlag); + Application *app = applicationManager.startApplication(appId, ApplicationManager::NoFlag); applicationManager.onProcessStarting(appId); std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId); bool authed = true; applicationManager.authorizeSession(procId, authed); onSessionStarting(session); - std::shared_ptr<mir::scene::Surface> surface(nullptr); - applicationManager.onSessionCreatedSurface(session.get(), surface); - applicationManager.focusApplication(appId); - applicationManager.unfocusCurrentApplication(); - EXPECT_EQ(applicationManager.focusedApplicationId(), QString()); + FakeMirSurfaceItem *surface = new FakeMirSurfaceItem; + onSessionCreatedSurface(session.get(), surface); + surface->drawFirstFrame(); + + suspend(app); QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); QSignalSpy focusSpy(&applicationManager, SIGNAL(focusedApplicationIdChanged())); @@ -1571,10 +1254,9 @@ TEST_F(ApplicationManagerTests,mirNotifiesOfStoppingForegroundApp) onSessionStarting(session); // Associate a surface so AppMan considers app Running, check focused - std::shared_ptr<mir::scene::Surface> surface(nullptr); - applicationManager.onSessionCreatedSurface(session.get(), surface); - applicationManager.focusApplication(appId); - EXPECT_EQ(applicationManager.focusedApplicationId(), appId); + FakeMirSurfaceItem *surface = new FakeMirSurfaceItem; + onSessionCreatedSurface(session.get(), surface); + surface->drawFirstFrame(); QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &))); @@ -1589,10 +1271,10 @@ TEST_F(ApplicationManagerTests,mirNotifiesOfStoppingForegroundApp) } /* - * Test that if a foreground application (one launched via desktop_file_hint) is reported to be stopping by + * Test that if an application (one launched via desktop_file_hint) is reported to be stopping by * Mir, AppMan removes it from the model immediately */ -TEST_F(ApplicationManagerTests,mirNotifiesOfStoppingForegroundAppLaunchedWithDesktopFileHint) +TEST_F(ApplicationManagerTests,mirNotifiesOfStoppingAppLaunchedWithDesktopFileHint) { using namespace ::testing; const QString appId("testAppId"); @@ -1621,10 +1303,9 @@ TEST_F(ApplicationManagerTests,mirNotifiesOfStoppingForegroundAppLaunchedWithDes onSessionStarting(session); // Associate a surface so AppMan considers app Running, check focused - std::shared_ptr<mir::scene::Surface> surface(nullptr); - applicationManager.onSessionCreatedSurface(session.get(), surface); - applicationManager.focusApplication(appId); - EXPECT_EQ(applicationManager.focusedApplicationId(), appId); + FakeMirSurfaceItem *surface = new FakeMirSurfaceItem; + onSessionCreatedSurface(session.get(), surface); + surface->drawFirstFrame(); QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &))); @@ -1661,73 +1342,31 @@ TEST_F(ApplicationManagerTests,mirNotifiesOfStoppingBackgroundApp) .Times(1) .WillOnce(Return(true)); - applicationManager.startApplication(appId, ApplicationManager::NoFlag); + Application *app = applicationManager.startApplication(appId, ApplicationManager::NoFlag); applicationManager.onProcessStarting(appId); std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId); bool authed = true; applicationManager.authorizeSession(procId, authed); onSessionStarting(session); + EXPECT_EQ(Application::Starting, app->state()); - // Associate a surface so AppMan considers app Running, check in background - std::shared_ptr<mir::scene::Surface> surface(nullptr); - applicationManager.onSessionCreatedSurface(session.get(), surface); - applicationManager.focusApplication(appId); - applicationManager.unfocusCurrentApplication(); - EXPECT_EQ(applicationManager.focusedApplicationId(), QString()); - - QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); - QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &))); - - // Mir notifies of stopping app - onSessionStopping(session); - - EXPECT_EQ(countSpy.count(), 0); - EXPECT_EQ(applicationManager.count(), 1); - EXPECT_EQ(removedSpy.count(), 0); - - Application * app = applicationManager.findApplication(appId); - EXPECT_NE(nullptr,app); - EXPECT_EQ(app->state(), Application::Stopped); -} + app->setRequestedState(Application::RequestedSuspended); -/* - * Test that if a background application (one launched via desktop_file_hint) is reported to be stopping by - * Mir, AppMan removes it from the model immediately - */ -TEST_F(ApplicationManagerTests,mirNotifiesOfStoppingBackgroundAppLaunchedWithDesktopFileHint) -{ - using namespace ::testing; - const QString appId("testAppId"); - const QString name("Test App"); - quint64 procId = 5551; - QByteArray cmdLine("/usr/bin/testApp --desktop_file_hint="); - cmdLine = cmdLine.append(appId); - - // Set up Mocks & signal watcher - EXPECT_CALL(procInfo,command_line(procId)) - .Times(1) - .WillOnce(Return(cmdLine)); + // should not suspend an app that`s still starting up + ASSERT_EQ(Application::InternalState::Starting, app->internalState()); - auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo()); - ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true)); - ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId)); - ON_CALL(*mockDesktopFileReader, name()).WillByDefault(Return(name)); + // Associate a surface so AppMan considers app Running + FakeMirSurfaceItem *surface = new FakeMirSurfaceItem; + onSessionCreatedSurface(session.get(), surface); + surface->drawFirstFrame(); - ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader)); + ASSERT_EQ(Application::InternalState::SuspendingWaitSession, app->internalState()); - // Mir requests authentication for an application that was started - bool authed = true; - applicationManager.authorizeSession(procId, authed); - EXPECT_EQ(authed, true); - std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId); - onSessionStarting(session); + static_cast<qtmir::Session*>(app->session())->doSuspend(); + ASSERT_EQ(Application::InternalState::SuspendingWaitProcess, app->internalState()); - // Associate a surface so AppMan considers app Running, check in background - std::shared_ptr<mir::scene::Surface> surface(nullptr); - applicationManager.onSessionCreatedSurface(session.get(), surface); - applicationManager.focusApplication(appId); - applicationManager.unfocusCurrentApplication(); - EXPECT_EQ(applicationManager.focusedApplicationId(), QString()); + applicationManager.onProcessSuspended(app->appId()); + ASSERT_EQ(Application::InternalState::Suspended, app->internalState()); QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &))); @@ -1735,12 +1374,11 @@ TEST_F(ApplicationManagerTests,mirNotifiesOfStoppingBackgroundAppLaunchedWithDes // Mir notifies of stopping app onSessionStopping(session); - EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback) - EXPECT_EQ(applicationManager.count(), 0); - EXPECT_EQ(removedSpy.count(), 1); + EXPECT_EQ(0, countSpy.count()); + EXPECT_EQ(1, applicationManager.count()); + EXPECT_EQ(0, removedSpy.count()); - Application * app = applicationManager.findApplication(appId); - EXPECT_EQ(nullptr,app); + EXPECT_EQ(Application::Stopped, app->state()); } /* @@ -1822,45 +1460,6 @@ TEST_F(ApplicationManagerTests,shellStoppedApp_mirSessionStoppingEventIgnored) } /* - * Test that if an application is stopped by upstart, the Mir stopping event is ignored - */ -TEST_F(ApplicationManagerTests,appStoppedByUpstart_mirSessionStoppingEventIgnored) -{ - using namespace ::testing; - const QString appId("testAppId"); - quint64 procId = 5551; - - // Set up Mocks & signal watcher - auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo()); - ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true)); - ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId)); - - ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader)); - - EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _)) - .Times(1) - .WillOnce(Return(true)); - - applicationManager.startApplication(appId, ApplicationManager::NoFlag); - applicationManager.onProcessStarting(appId); - std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId); - bool authed = true; - applicationManager.authorizeSession(procId, authed); - onSessionStarting(session); - - applicationManager.onProcessStopped(appId); - - QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); - QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &))); - - // Mir notifies of stopping app - onSessionStopping(session); - - EXPECT_EQ(countSpy.count(), 0); - EXPECT_EQ(removedSpy.count(), 0); -} - -/* * Webapps have multiple sessions, but only one is linked to the application (other is considered a hidden session). * If webapp in foreground stops unexpectedly, remove it and it alone from app list */ @@ -1903,8 +1502,9 @@ TEST_F(ApplicationManagerTests,unexpectedStopOfForegroundWebapp) applicationManager.authorizeSession(procId2, authed); onSessionStarting(session2); EXPECT_EQ(authed, true); - std::shared_ptr<mir::scene::Surface> surface(nullptr); - applicationManager.onSessionCreatedSurface(session2.get(), surface); + FakeMirSurfaceItem *surface = new FakeMirSurfaceItem; + onSessionCreatedSurface(session2.get(), surface); + surface->drawFirstFrame(); QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &))); @@ -1950,7 +1550,7 @@ TEST_F(ApplicationManagerTests,unexpectedStopOfBackgroundWebapp) .Times(1) .WillOnce(Return(true)); - applicationManager.startApplication(appId, ApplicationManager::NoFlag); + Application *app = applicationManager.startApplication(appId, ApplicationManager::NoFlag); applicationManager.onProcessStarting(appId); std::shared_ptr<mir::scene::Session> session1 = std::make_shared<MockSession>("", procId1); std::shared_ptr<mir::scene::Session> session2 = std::make_shared<MockSession>("", procId2); @@ -1958,19 +1558,20 @@ TEST_F(ApplicationManagerTests,unexpectedStopOfBackgroundWebapp) bool authed = false; applicationManager.authorizeSession(procId1, authed); onSessionStarting(session1); - EXPECT_EQ(authed, true); + EXPECT_EQ(true, authed); applicationManager.authorizeSession(procId2, authed); onSessionStarting(session2); - EXPECT_EQ(authed, true); + EXPECT_EQ(true, authed); - // both sessions create surfaces, then unfocus everything. - std::shared_ptr<mir::scene::Surface> surface1(nullptr); - applicationManager.onSessionCreatedSurface(session1.get(), surface1); - std::shared_ptr<mir::scene::Surface> surface2(nullptr); - applicationManager.onSessionCreatedSurface(session2.get(), surface2); - applicationManager.focusApplication(appId); - applicationManager.unfocusCurrentApplication(); - EXPECT_EQ(applicationManager.focusedApplicationId(), QString()); + // both sessions create surfaces, then get them all suspended + FakeMirSurfaceItem *surface1 = new FakeMirSurfaceItem; + onSessionCreatedSurface(session1.get(), surface1); + surface1->drawFirstFrame(); + FakeMirSurfaceItem *surface2 = new FakeMirSurfaceItem; + onSessionCreatedSurface(session2.get(), surface2); + surface2->drawFirstFrame(); + suspend(app); + EXPECT_EQ(Application::Suspended, app->state()); QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &))); @@ -1979,8 +1580,8 @@ TEST_F(ApplicationManagerTests,unexpectedStopOfBackgroundWebapp) onSessionStopping(session2); onSessionStopping(session1); - EXPECT_EQ(countSpy.count(), 0); - EXPECT_EQ(removedSpy.count(), 0); + EXPECT_EQ(0, countSpy.count()); + EXPECT_EQ(0, removedSpy.count()); } /* @@ -2007,34 +1608,33 @@ TEST_F(ApplicationManagerTests,stoppedBackgroundAppRelaunchedByUpstart) .Times(1) .WillOnce(Return(true)); - applicationManager.startApplication(appId, ApplicationManager::NoFlag); + Application *app = applicationManager.startApplication(appId, ApplicationManager::NoFlag); applicationManager.onProcessStarting(appId); std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId); bool authed = true; applicationManager.authorizeSession(procId, authed); onSessionStarting(session); - // App creates surface, focuses it, puts it in background, then is OOM killed. - std::shared_ptr<mir::scene::Surface> surface(nullptr); - applicationManager.onSessionCreatedSurface(session.get(), surface); - applicationManager.focusApplication(appId); - applicationManager.unfocusCurrentApplication(); + // App creates surface, puts it in background, then is OOM killed. + FakeMirSurfaceItem *surface = new FakeMirSurfaceItem; + onSessionCreatedSurface(session.get(), surface); + surface->drawFirstFrame(); + suspend(app); onSessionStopping(session); applicationManager.onProcessFailed(appId, false); applicationManager.onProcessStopped(appId); - Application *app = applicationManager.findApplication(appId); - EXPECT_EQ(app->state(), Application::Stopped); + EXPECT_EQ(Application::Stopped, app->state()); QSignalSpy focusRequestSpy(&applicationManager, SIGNAL(focusRequested(const QString &))); // Upstart re-launches app applicationManager.onProcessStarting(appId); - EXPECT_EQ(app->state(), Application::Starting); - EXPECT_EQ(focusRequestSpy.count(), 1); - EXPECT_EQ(applicationManager.count(), 1); + EXPECT_EQ(Application::Starting, app->state()); + EXPECT_EQ(1, focusRequestSpy.count()); + EXPECT_EQ(1, applicationManager.count()); } /* @@ -2138,142 +1738,61 @@ TEST_F(ApplicationManagerTests, threadedScreenshotAfterAppDelete) } } -/* - 1 - launch and focus a main stage app - * main stage app is running and focused - 2 - launch and focus a side stage app - * main stage app is running but is not focused - * side stage app is running and has focus - 3 - focus the main stage app - * main stage app is running and has focus - * side stage app is running but is not focused - - This is a regression test for the bug where on step 3, the main stage app was momentarily - suspended and then resumed again. - */ -TEST_F(ApplicationManagerTests, focusMainStageAfterSideStage) -{ - using namespace testing; - - QString webbrowserAppId("webbrowser-app"); - quint64 webbrowserPID = 123; - std::shared_ptr<mir::scene::Surface> webbrowserSurface(nullptr); - - QString dialerAppId("dialer-app"); - quint64 dialerPID = 456; - std::shared_ptr<mir::scene::Surface> dialerSurface(nullptr); - - /*** Start webbrowser-app (main stage) ***/ - - ON_CALL(appController,appIdHasProcessId(webbrowserPID, webbrowserAppId)).WillByDefault(Return(true)); - - { - auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(webbrowserAppId, QFileInfo()); - ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true)); - ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(webbrowserAppId)); - ON_CALL(*mockDesktopFileReader, stageHint()).WillByDefault(Return("MainStage")); - - ON_CALL(desktopFileReaderFactory, createInstance(webbrowserAppId, _)) - .WillByDefault(Return(mockDesktopFileReader)); - } - - EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(webbrowserAppId, _)) - .Times(1) - .WillOnce(Return(true)); - - /*auto application =*/ applicationManager.startApplication(webbrowserAppId, ApplicationManager::NoFlag); - applicationManager.onProcessStarting(webbrowserAppId); - - { - bool authed = false; - applicationManager.authorizeSession(webbrowserPID, authed); - EXPECT_EQ(authed, true); - } - - auto webbrowserSession = std::make_shared<mir::scene::MockSession>(webbrowserAppId.toStdString(), webbrowserPID); - sessionManager.onSessionStarting(webbrowserSession); - applicationManager.focusApplication(webbrowserAppId); - applicationManager.onSessionCreatedSurface(webbrowserSession.get(), webbrowserSurface); - - /*** Start dialer-app (side stage) ***/ - - ON_CALL(appController, appIdHasProcessId(dialerPID, dialerAppId)).WillByDefault(Return(true)); - - { - auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(dialerAppId, QFileInfo()); - ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true)); - ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(dialerAppId)); - ON_CALL(*mockDesktopFileReader, stageHint()).WillByDefault(Return("SideStage")); - - ON_CALL(desktopFileReaderFactory, createInstance(dialerAppId, _)) - .WillByDefault(Return(mockDesktopFileReader)); - } - - EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(dialerAppId, _)) - .Times(1) - .WillOnce(Return(true)); - - /*auto application =*/ applicationManager.startApplication(dialerAppId, ApplicationManager::NoFlag); - applicationManager.onProcessStarting(dialerAppId); - - { - bool authed = false; - applicationManager.authorizeSession(dialerPID, authed); - EXPECT_EQ(authed, true); - } - - auto dialerSession = std::make_shared<mir::scene::MockSession>(dialerAppId.toStdString(), dialerPID); - sessionManager.onSessionStarting(dialerSession); - applicationManager.focusApplication(dialerAppId); - applicationManager.onSessionCreatedSurface(dialerSession.get(), dialerSurface); - - /*** Focus webbrowser ***/ - - // Nothing should happen as it's already the running main stage app - EXPECT_CALL(*webbrowserSession.get(), set_lifecycle_state(_)) - .Times(0); - applicationManager.focusApplication(webbrowserAppId); -} - TEST_F(ApplicationManagerTests,lifecycle_exempt_appId_is_not_suspended) { using namespace ::testing; quint64 a_procId = 5921; - const char an_app_id[] = "some_app"; - QByteArray a_cmd( "/usr/bin/app1 --desktop_file_hint=some_app"); - std::shared_ptr<mir::scene::Surface> aSurface(nullptr); + const QString appId("some_app"); + QByteArray a_cmd("/usr/bin/app1"); ON_CALL(procInfo,command_line(_)).WillByDefault(Return(a_cmd)); - ON_CALL(appController,appIdHasProcessId(_,_)).WillByDefault(Return(false)); + ON_CALL(appController,appIdHasProcessId(a_procId, appId)).WillByDefault(Return(true)); - bool authed = true; + // Set up Mocks & signal watcher + auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo()); + ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true)); + ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId)); + + ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader)); + + + Application *the_app = applicationManager.startApplication(appId, ApplicationManager::NoFlag); + applicationManager.onProcessStarting(appId); std::shared_ptr<mir::scene::Session> first_session = std::make_shared<MockSession>("Oo", a_procId); std::shared_ptr<mir::scene::Session> second_session = std::make_shared<MockSession>("oO", a_procId); - applicationManager.authorizeSession(a_procId, authed); + { + bool authed = false; + applicationManager.authorizeSession(a_procId, authed); + ASSERT_EQ(authed, true); + } onSessionStarting(first_session); - applicationManager.onSessionCreatedSurface(first_session.get(), aSurface); + FakeMirSurfaceItem *aSurface = new FakeMirSurfaceItem; + onSessionCreatedSurface(first_session.get(), aSurface); + aSurface->drawFirstFrame(); onSessionStarting(second_session); - Application * the_app = applicationManager.findApplication(an_app_id); - applicationManager.focusApplication(an_app_id); - // Add to other apps to the list (Not "some_app") QVariantList lifecycleExemptAppIds; lifecycleExemptAppIds << "one_app" << "another_app"; ON_CALL(settings,get(_)).WillByDefault(Return(lifecycleExemptAppIds)); settings.changed("lifecycleExemptAppids"); - EXPECT_EQ(Application::Running, the_app->state()); + ASSERT_EQ(Application::InternalState::Running, the_app->internalState()); - applicationManager.setSuspended(true); + EXPECT_CALL(*(mir::scene::MockSession*)first_session.get(), set_lifecycle_state(mir_lifecycle_state_will_suspend)); + the_app->setRequestedState(Application::RequestedSuspended); + ASSERT_EQ(Application::InternalState::SuspendingWaitSession, the_app->internalState()); - // And expect "some_app" to get suspended - EXPECT_EQ(Application::Suspended, the_app->state()); + static_cast<qtmir::Session*>(the_app->session())->doSuspend(); + ASSERT_EQ(Application::InternalState::SuspendingWaitProcess, the_app->internalState()); + applicationManager.onProcessSuspended(the_app->appId()); + ASSERT_EQ(Application::InternalState::Suspended, the_app->internalState()); - applicationManager.setSuspended(false); + EXPECT_CALL(*(mir::scene::MockSession*)first_session.get(), set_lifecycle_state(mir_lifecycle_state_resumed)); + the_app->setRequestedState(Application::RequestedRunning); EXPECT_EQ(Application::Running, the_app->state()); @@ -2284,14 +1803,16 @@ TEST_F(ApplicationManagerTests,lifecycle_exempt_appId_is_not_suspended) EXPECT_EQ(Application::Running, the_app->state()); - applicationManager.setSuspended(true); + EXPECT_CALL(*(mir::scene::MockSession*)first_session.get(), set_lifecycle_state(_)).Times(0); + the_app->setRequestedState(Application::RequestedSuspended); // And expect it to be running still - EXPECT_EQ(Application::Running, the_app->state()); + ASSERT_EQ(Application::InternalState::RunningInBackground, the_app->internalState()); - applicationManager.setSuspended(false); + the_app->setRequestedState(Application::RequestedRunning); EXPECT_EQ(Application::Running, the_app->state()); + ASSERT_EQ(Application::InternalState::Running, the_app->internalState()); } /* @@ -2323,51 +1844,13 @@ TEST_F(ApplicationManagerTests,lifecycleExemptAppsHaveWakelockReleasedOnAttempte onSessionStarting(session); // App creates surface, focuses it so state is running - std::shared_ptr<mir::scene::Surface> surface(nullptr); - applicationManager.onSessionCreatedSurface(session.get(), surface); - applicationManager.focusApplication(appId); - - applicationManager.unfocusCurrentApplication(); - - EXPECT_FALSE(sharedWakelock.enabled()); - EXPECT_EQ(application->state(), Application::Running); -} - -/* - * Test lifecycle exempt applications have their wakelocks released on suspend - */ -TEST_F(ApplicationManagerTests,lifecycleExemptAppsHaveWakelockReleasedOnUnSuspend) -{ - using namespace ::testing; - - const QString appId("com.ubuntu.music"); // member of lifecycle exemption list - const quint64 procId = 12345; - - // Set up Mocks & signal watcher - auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo()); - ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true)); - ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId)); - - ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader)); - - EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _)) - .Times(1) - .WillOnce(Return(true)); - - auto application = applicationManager.startApplication(appId, ApplicationManager::NoFlag); - applicationManager.onProcessStarting(appId); - std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId); - bool authed = true; - applicationManager.authorizeSession(procId, authed); - onSessionStarting(session); - - // App creates surface, focuses it so state is running - std::shared_ptr<mir::scene::Surface> surface(nullptr); - applicationManager.onSessionCreatedSurface(session.get(), surface); - applicationManager.focusApplication(appId); + FakeMirSurfaceItem *surface = new FakeMirSurfaceItem; + onSessionCreatedSurface(session.get(), surface); + surface->drawFirstFrame(); - applicationManager.setSuspended(true); + application->setRequestedState(Application::RequestedSuspended); EXPECT_FALSE(sharedWakelock.enabled()); - EXPECT_EQ(application->state(), Application::Running); + ASSERT_EQ(Application::InternalState::RunningInBackground, application->internalState()); + EXPECT_EQ(Application::Running, application->state()); } diff --git a/tests/modules/MirSurfaceItem/CMakeLists.txt b/tests/modules/MirSurfaceItem/CMakeLists.txt index 36d16a8..bac797f 100644 --- a/tests/modules/MirSurfaceItem/CMakeLists.txt +++ b/tests/modules/MirSurfaceItem/CMakeLists.txt @@ -5,7 +5,7 @@ set( ) include_directories( - ${CMAKE_SOURCE_DIR}/src/modules/Unity/Application + ${CMAKE_SOURCE_DIR}/src/modules ${CMAKE_SOURCE_DIR}/tests/modules/common ${MIRSERVER_INCLUDE_DIRS} ) diff --git a/tests/modules/MirSurfaceItem/mirsurfaceitem_test.cpp b/tests/modules/MirSurfaceItem/mirsurfaceitem_test.cpp index 3275b60..bc6a0de 100644 --- a/tests/modules/MirSurfaceItem/mirsurfaceitem_test.cpp +++ b/tests/modules/MirSurfaceItem/mirsurfaceitem_test.cpp @@ -23,7 +23,7 @@ #include <QTest> // the test subject -#include <mirsurfaceitem.h> +#include <Unity/Application/mirsurfaceitem.h> // mocks #include <mock_surface.h> diff --git a/tests/modules/SessionManager/CMakeLists.txt b/tests/modules/SessionManager/CMakeLists.txt index a4e25d2..055449b 100644 --- a/tests/modules/SessionManager/CMakeLists.txt +++ b/tests/modules/SessionManager/CMakeLists.txt @@ -3,9 +3,12 @@ set( session_manager_test.cpp session_test.cpp ${CMAKE_SOURCE_DIR}/src/common/debughelpers.cpp + ${CMAKE_SOURCE_DIR}/tests/modules/common/qtmir_test.cpp + ${CMAKE_SOURCE_DIR}/tests/modules/common/fake_mirsurfaceitem.h ) include_directories( + ${APPLICATION_API_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/src/platforms/mirserver ${CMAKE_SOURCE_DIR}/src/modules ${CMAKE_SOURCE_DIR}/tests/modules/common diff --git a/tests/modules/SessionManager/session_manager_test.cpp b/tests/modules/SessionManager/session_manager_test.cpp index f53fe12..0a1a806 100644 --- a/tests/modules/SessionManager/session_manager_test.cpp +++ b/tests/modules/SessionManager/session_manager_test.cpp @@ -21,7 +21,7 @@ #include <Unity/Application/session.h> - #include "qtmir_test.h" +#include "qtmir_test.h" using namespace qtmir; using mir::scene::MockSession; diff --git a/tests/modules/SessionManager/session_test.cpp b/tests/modules/SessionManager/session_test.cpp index a108cfe..3f0e0e8 100644 --- a/tests/modules/SessionManager/session_test.cpp +++ b/tests/modules/SessionManager/session_test.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 @@ -15,16 +15,17 @@ * */ +#include <qtmir_test.h> +#include <fake_mirsurfaceitem.h> + #include <Unity/Application/application.h> #include <Unity/Application/mirsurfaceitem.h> -#include "qtmir_test.h" #include "stub_scene_surface.h" using namespace qtmir; using mir::scene::MockSession; - namespace ms = mir::scene; namespace mtd = mir::test::doubles; @@ -43,6 +44,30 @@ public: } }; +TEST_F(SessionTests, FromStartingToRunningOnceSurfaceDrawsFirstFrame) +{ + using namespace testing; + + const QString appId("test-app"); + quint64 procId = 5551; + + auto mirSession = std::make_shared<MockSession>(appId.toStdString(), procId); + + auto session = std::make_shared<qtmir::Session>(mirSession, mirServer->the_prompt_session_manager()); + + // On Starting as it has no surface. + EXPECT_EQ(Session::Starting, session->state()); + + FakeMirSurfaceItem *surface = new FakeMirSurfaceItem; + session->setSurface(surface); + + // Still on Starting as the surface hasn't drawn its first frame yet + EXPECT_EQ(Session::Starting, session->state()); + + surface->drawFirstFrame(); + EXPECT_EQ(Session::Running, session->state()); +} + TEST_F(SessionTests, AddChildSession) { using namespace testing; @@ -192,17 +217,6 @@ TEST_F(SessionTests, DeleteSessionDeletesChildSessions) EXPECT_THAT(destroyed, UnorderedElementsAre(session1, session2, session3)); } -class MockQtMirSession : public qtmir::Session -{ -public: - MockQtMirSession(const std::shared_ptr<ms::Session>& session, - const std::shared_ptr<ms::PromptSessionManager>& promptSessionManager) - : Session(session, promptSessionManager) - {} - - using SessionInterface::appendPromptSession; -}; - TEST_F(SessionTests, SuspendPromptSessionWhenSessionSuspends) { using namespace testing; @@ -211,17 +225,25 @@ TEST_F(SessionTests, SuspendPromptSessionWhenSessionSuspends) quint64 procId = 5551; auto mirSession = std::make_shared<MockSession>(appId.toStdString(), procId); - EXPECT_CALL(*mirSession, set_lifecycle_state(_)); - auto session = std::make_shared<MockQtMirSession>(mirSession, mirServer->the_prompt_session_manager()); - session->setState(Session::State::Running); + auto session = std::make_shared<qtmir::Session>(mirSession, mirServer->the_prompt_session_manager()); + { + FakeMirSurfaceItem *surface = new FakeMirSurfaceItem; + session->setSurface(surface); + surface->drawFirstFrame(); + } + EXPECT_EQ(Session::Running, session->state()); auto mirPromptSession = std::make_shared<ms::MockPromptSession>(); session->appendPromptSession(mirPromptSession); EXPECT_CALL(*mirServer->the_mock_prompt_session_manager(), suspend_prompt_session(_)).Times(1); - session->setState(Session::State::Suspended); + EXPECT_CALL(*mirSession, set_lifecycle_state(mir_lifecycle_state_will_suspend)); + session->suspend(); + EXPECT_EQ(Session::Suspending, session->state()); + session->doSuspend(); + EXPECT_EQ(Session::Suspended, session->state()); Mock::VerifyAndClear(mirServer->the_mock_prompt_session_manager().get()); } @@ -234,18 +256,29 @@ TEST_F(SessionTests, ResumePromptSessionWhenSessionResumes) quint64 procId = 5551; auto mirSession = std::make_shared<MockSession>(appId.toStdString(), procId); - EXPECT_CALL(*mirSession, set_lifecycle_state(_)); - auto session = std::make_shared<MockQtMirSession>(mirSession, mirServer->the_prompt_session_manager()); - session->setState(Session::State::Suspended); + auto session = std::make_shared<qtmir::Session>(mirSession, mirServer->the_prompt_session_manager()); + { + FakeMirSurfaceItem *surface = new FakeMirSurfaceItem; + session->setSurface(surface); + surface->drawFirstFrame(); + } + EXPECT_EQ(Session::Running, session->state()); + + EXPECT_CALL(*mirSession, set_lifecycle_state(mir_lifecycle_state_will_suspend)); + session->suspend(); + EXPECT_EQ(Session::Suspending, session->state()); + session->doSuspend(); + EXPECT_EQ(Session::Suspended, session->state()); auto mirPromptSession = std::make_shared<ms::MockPromptSession>(); session->appendPromptSession(mirPromptSession); EXPECT_CALL(*mirServer->the_mock_prompt_session_manager(), resume_prompt_session(_)).Times(1); - session->setState(Session::State::Running); + EXPECT_CALL(*mirSession, set_lifecycle_state(mir_lifecycle_state_resumed)); + session->resume(); + EXPECT_EQ(Session::Running, session->state()); Mock::VerifyAndClear(mirServer->the_mock_prompt_session_manager().get()); } - diff --git a/tests/modules/TaskController/CMakeLists.txt b/tests/modules/TaskController/CMakeLists.txt index e6785ab..2e7c227 100644 --- a/tests/modules/TaskController/CMakeLists.txt +++ b/tests/modules/TaskController/CMakeLists.txt @@ -5,6 +5,7 @@ set( ) include_directories( + ${APPLICATION_API_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/src/modules ${CMAKE_SOURCE_DIR}/tests/modules/common ${MIRSERVER_INCLUDE_DIRS} diff --git a/tests/modules/common/fake_mirsurfaceitem.h b/tests/modules/common/fake_mirsurfaceitem.h new file mode 100644 index 0000000..ea55d14 --- /dev/null +++ b/tests/modules/common/fake_mirsurfaceitem.h @@ -0,0 +1,95 @@ +/* + * 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 FAKE_MIRSURFACEITEMINTERFACE_H +#define FAKE_MIRSURFACEITEMINTERFACE_H + +#include <Unity/Application/mirsurfaceiteminterface.h> + +namespace qtmir { + +class FakeMirSurfaceItem : public MirSurfaceItemInterface +{ + Q_OBJECT + +public: + FakeMirSurfaceItem(QQuickItem *parent = nullptr) + : MirSurfaceItemInterface(parent) + , m_state(Restored) + , m_live(true) + , m_session(nullptr) + , m_isFirstFrameDrawn(false) + , m_isFrameDropperRunning(true) + {} + + Type type() const override { return Normal; } + State state() const override { return m_state; } + QString name() const override { return QString("fake app surface"); } + bool live() const override { return m_live; } + SessionInterface *session() const override { return m_session; } + OrientationAngle orientationAngle() const override { return Angle0; } + + Q_INVOKABLE void release() override {} + + void stopFrameDropper() override { + m_isFrameDropperRunning = false; + } + void startFrameDropper() override { + m_isFrameDropperRunning = true; + } + + bool isFirstFrameDrawn() const override { + return m_isFirstFrameDrawn; + } + + void setOrientationAngle(OrientationAngle) override { + } + + void setSession(SessionInterface *session) override { + m_session = session; + } + + void setState(State state) { + if (m_state != state) { + m_state = state; + Q_EMIT stateChanged(); + } + } + + void drawFirstFrame() { + if (!m_isFirstFrameDrawn) { + m_isFirstFrameDrawn = true; + Q_EMIT firstFrameDrawn(); + } + } + + bool isFrameDropperRunning() const { + return m_isFrameDropperRunning; + } + +private: + void setLive(bool value) override { m_live = value; } + + State m_state; + bool m_live; + SessionInterface *m_session; + bool m_isFirstFrameDrawn; + bool m_isFrameDropperRunning; +}; + +} // namespace qtmir + +#endif // FAKE_MIRSURFACEITEMINTERFACE_H diff --git a/tests/modules/common/mock_mirsurfaceitem.h b/tests/modules/common/mock_mirsurfaceitem.h new file mode 100644 index 0000000..84409bf --- /dev/null +++ b/tests/modules/common/mock_mirsurfaceitem.h @@ -0,0 +1,50 @@ +/* + * 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 MOCK_QTMIR_MIRSURFACEITEM_H +#define MOCK_QTMIR_MIRSURFACEITEM_H + +#include <Unity/Application/mirsurfaceiteminterface.h> +#include <gmock/gmock.h> + +namespace qtmir { + +class MockMirSurfaceItem : public MirSurfaceItemInterface { +public: + MockMirSurfaceItem(QQuickItem *parent = nullptr) : MirSurfaceItemInterface(parent) {} + + MOCK_CONST_METHOD0(type, Type()); + MOCK_CONST_METHOD0(state, State()); + MOCK_CONST_METHOD0(name, QString()); + MOCK_CONST_METHOD0(live, bool()); + MOCK_CONST_METHOD0(orientationAngle, OrientationAngle()); + MOCK_CONST_METHOD0(session, SessionInterface*()); + + MOCK_METHOD0(release, void()); + + MOCK_METHOD0(stopFrameDropper, void()); + MOCK_METHOD0(startFrameDropper, void()); + + MOCK_CONST_METHOD0(isFirstFrameDrawn, bool()); + + MOCK_METHOD1(setOrientationAngle, void(OrientationAngle angle)); + MOCK_METHOD1(setSession, void(SessionInterface *app)); +}; + +} // namespace qtmir + +#endif // MOCK_QTMIR_MIRSURFACEITEM_H diff --git a/tests/modules/common/mock_session.h b/tests/modules/common/mock_session.h index a6446c6..08941cb 100644 --- a/tests/modules/common/mock_session.h +++ b/tests/modules/common/mock_session.h @@ -18,28 +18,39 @@ #ifndef MOCK_QTMIR_SESSION_H #define MOCK_QTMIR_SESSION_H -#include <session_interface.h> +#include <Unity/Application/session_interface.h> #include <gmock/gmock.h> namespace qtmir { class MockSession : public SessionInterface { public: - MockSession() : SessionInterface(0) {} + MockSession() : SessionInterface(0) { + m_state = Starting; + ON_CALL(*this, suspend()).WillByDefault(::testing::Invoke(this, &MockSession::doSuspend)); + ON_CALL(*this, resume()).WillByDefault(::testing::Invoke(this, &MockSession::doResume)); + ON_CALL(*this, stop()).WillByDefault(::testing::Invoke(this, &MockSession::doStop)); + ON_CALL(*this, state()).WillByDefault(::testing::Invoke(this, &MockSession::doState)); + } MOCK_METHOD0(release, void()); MOCK_CONST_METHOD0(name, QString()); MOCK_CONST_METHOD0(application, unity::shell::application::ApplicationInfoInterface*()); - MOCK_CONST_METHOD0(surface, MirSurfaceItem*()); + MOCK_CONST_METHOD0(surface, MirSurfaceItemInterface*()); MOCK_CONST_METHOD0(parentSession, SessionInterface*()); + MOCK_CONST_METHOD0(state, State()); + MOCK_CONST_METHOD0(fullscreen, bool()); MOCK_CONST_METHOD0(live, bool()); MOCK_METHOD1(setApplication, void(unity::shell::application::ApplicationInfoInterface* item)); - MOCK_METHOD1(setSurface, void(MirSurfaceItem* surface)); - MOCK_METHOD1(setState, void(State state)); + MOCK_METHOD1(setSurface, void(MirSurfaceItemInterface* surface)); + + MOCK_METHOD0(suspend, void()); + MOCK_METHOD0(resume, void()); + MOCK_METHOD0(stop, void()); MOCK_METHOD1(addChildSession, void(SessionInterface* session)); MOCK_METHOD2(insertChildSession, void(uint index, SessionInterface* session)); @@ -53,11 +64,39 @@ public: MOCK_CONST_METHOD0(childSessions, SessionModel*()); + void setState(State state) { + if (m_state != state) { + m_state = state; + Q_EMIT stateChanged(m_state); + } + } + + void doSuspend() { + if (m_state == Running) { + setState(Suspending); + } + } + void doResume() { + if (m_state == Suspending || m_state == Suspended) { + setState(Running); + } + } + void doStop() { + setState(Stopped); + } + State doState() const { + return m_state; + } + protected: MOCK_METHOD1(setFullscreen, void(bool fullscreen)); MOCK_METHOD1(setLive, void(const bool)); MOCK_METHOD1(appendPromptSession, void(const std::shared_ptr<mir::scene::PromptSession>& session)); MOCK_METHOD1(removePromptSession, void(const std::shared_ptr<mir::scene::PromptSession>& session)); + +private: + + State m_state; }; } // namespace qtmir diff --git a/tests/modules/common/qtmir_test.cpp b/tests/modules/common/qtmir_test.cpp new file mode 100644 index 0000000..33a6c27 --- /dev/null +++ b/tests/modules/common/qtmir_test.cpp @@ -0,0 +1,61 @@ +#include "qtmir_test.h" + +namespace qtmir { + +void PrintTo(const Application::InternalState& state, ::std::ostream* os) { + switch (state) { + case Application::InternalState::Starting: + *os << "Starting"; + break; + case Application::InternalState::Running: + *os << "Running"; + break; + case Application::InternalState::RunningInBackground: + *os << "RunningInBackground"; + break; + case Application::InternalState::SuspendingWaitSession: + *os << "SuspendingWaitSession"; + break; + case Application::InternalState::SuspendingWaitProcess: + *os << "SuspendingWaitProcess"; + break; + case Application::InternalState::Suspended: + *os << "Suspended"; + break; + case Application::InternalState::StoppedUnexpectedly: + *os << "StoppedUnexpectedly"; + break; + case Application::InternalState::Stopped: + *os << "Stopped"; + break; + default: + *os << "???"; + break; + } +} + +void PrintTo(const Session::State& state, ::std::ostream* os) +{ + switch (state) { + case SessionInterface::Starting: + *os << "Starting"; + break; + case SessionInterface::Running: + *os << "Running"; + break; + case SessionInterface::Suspending: + *os << "Suspending"; + break; + case SessionInterface::Suspended: + *os << "Suspended"; + break; + case SessionInterface::Stopped: + *os << "Stopped"; + break; + default: + *os << "???"; + break; + } +} + +} // namespace qtmir diff --git a/tests/modules/common/qtmir_test.h b/tests/modules/common/qtmir_test.h index 9ee81af..7832a73 100644 --- a/tests/modules/common/qtmir_test.h +++ b/tests/modules/common/qtmir_test.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 Lesser General Public License version 3, as published by @@ -18,12 +18,16 @@ #ifndef QT_MIR_TEST_FRAMEWORK_H #define QT_MIR_TEST_FRAMEWORK_H +#include <memory> + #include <gtest/gtest.h> +#include <Unity/Application/application.h> #include <Unity/Application/application_manager.h> #include <Unity/Application/applicationcontroller.h> #include <Unity/Application/mirsurfacemanager.h> #include <Unity/Application/sessionmanager.h> +#include <Unity/Application/session_interface.h> #include <Unity/Application/sharedwakelock.h> #include <Unity/Application/taskcontroller.h> #include <Unity/Application/proc_info.h> @@ -43,6 +47,10 @@ using namespace qtmir; namespace qtmir { +// For better output in ASSERT_* and EXPECT_* error messages +void PrintTo(const Application::InternalState& state, ::std::ostream* os); +void PrintTo(const SessionInterface::State& state, ::std::ostream* os); + // Initialization of mir::Server needed for by tests class TestMirServerInit : virtual mir::Server { |