/*
* 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
* 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 .
*/
// local
#include "application.h"
#include "debughelpers.h"
#include "session.h"
#include "mirsurfacemanager.h"
#include "mirsurfaceitem.h"
// mirserver
#include "logging.h"
// mir
#include
#include
#include
// Qt
#include
#include
namespace ms = mir::scene;
using unity::shell::application::ApplicationInfoInterface;
namespace qtmir
{
Session::Session(const std::shared_ptr& session,
const std::shared_ptr& promptSessionManager,
QObject *parent)
: SessionInterface(parent)
, m_session(session)
, m_application(nullptr)
, m_surface(nullptr)
, m_parentSession(nullptr)
, m_children(new SessionModel(this))
, m_fullscreen(false)
, m_state(State::Starting)
, m_live(true)
, m_released(false)
, m_suspendTimer(new QTimer(this))
, m_promptSessionManager(promptSessionManager)
{
qCDebug(QTMIR_SESSIONS) << "Session::Session() " << this->name();
QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
m_suspendTimer->setSingleShot(true);
connect(m_suspendTimer, &QTimer::timeout, this, &Session::doSuspend);
}
Session::~Session()
{
qCDebug(QTMIR_SESSIONS) << "Session::~Session() " << name();
stopPromptSessions();
QList children(m_children->list());
for (SessionInterface* child : children) {
delete child;
}
if (m_parentSession) {
m_parentSession->removeChildSession(this);
}
if (m_application) {
m_application->setSession(nullptr);
}
delete m_surface; m_surface = nullptr;
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();
m_released = true;
if (m_state == Stopped) {
deleteLater();
}
}
QString Session::name() const
{
return QString::fromStdString(m_session->name());
}
std::shared_ptr Session::session() const
{
return m_session;
}
ApplicationInfoInterface* Session::application() const
{
return m_application;
}
MirSurfaceItemInterface* Session::surface() const
{
// Only notify QML of surface creation once it has drawn its first frame.
if (m_surface && m_surface->isFirstFrameDrawn()) {
return m_surface;
} else {
return nullptr;
}
}
SessionInterface* Session::parentSession() const
{
return m_parentSession;
}
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;
}
bool Session::live() const
{
return m_live;
}
void Session::setApplication(ApplicationInfoInterface* application)
{
if (m_application == application)
return;
m_application = static_cast(application);
Q_EMIT applicationChanged(application);
}
void Session::setSurface(MirSurfaceItemInterface *newSurface)
{
qCDebug(QTMIR_SESSIONS) << "Session::setSurface - session=" << name() << "surface=" << newSurface;
if (newSurface == m_surface) {
return;
}
if (m_surface) {
m_surface->disconnect(this);
m_surface->setSession(nullptr);
m_surface->setParent(nullptr);
}
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 (m_surface->isFirstFrameDrawn()) {
setState(Running);
} else {
connect(newSurface, &MirSurfaceItemInterface::firstFrameDrawn,
this, &Session::onFirstSurfaceFrameDrawn);
}
}
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() == MirSurfaceItemInterface::Fullscreen);
} else {
// Keep the current value of the fullscreen property until we get a new
// surface
}
}
void Session::setFullscreen(bool fullscreen)
{
qCDebug(QTMIR_SESSIONS) << "Session::setFullscreen - session=" << this << "fullscreen=" << fullscreen;
if (m_fullscreen != fullscreen) {
m_fullscreen = fullscreen;
Q_EMIT fullscreenChanged(m_fullscreen);
}
}
void Session::suspend()
{
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);
foreachPromptSession([this](const std::shared_ptr& promptSession) {
m_promptSessionManager->suspend_prompt_session(promptSession);
});
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& 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);
if (m_released) {
deleteLater();
}
}
}
void Session::setLive(const bool live)
{
if (m_live != live) {
m_live = live;
Q_EMIT liveChanged(m_live);
if (!live) {
setState(Stopped);
if (m_released) {
deleteLater();
}
}
}
}
void Session::setParentSession(Session* session)
{
if (m_parentSession == session || session == this)
return;
m_parentSession = session;
Q_EMIT parentSessionChanged(session);
}
void Session::addChildSession(SessionInterface* session)
{
insertChildSession(m_children->rowCount(), session);
}
void Session::insertChildSession(uint index, SessionInterface* session)
{
qCDebug(QTMIR_SESSIONS) << "Session::insertChildSession - " << session->name() << " to " << name() << " @ " << index;
static_cast(session)->setParentSession(this);
m_children->insert(index, session);
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)
{
qCDebug(QTMIR_SESSIONS) << "Session::removeChildSession - " << session->name() << " from " << name();
if (m_children->contains(session)) {
m_children->remove(session);
static_cast(session)->setParentSession(nullptr);
}
}
void Session::foreachChildSession(std::function f) const
{
QList children(m_children->list());
for (SessionInterface* child : children) {
f(child);
}
}
SessionModel* Session::childSessions() const
{
return m_children;
}
void Session::appendPromptSession(const std::shared_ptr& promptSession)
{
qCDebug(QTMIR_SESSIONS) << "Session::appendPromptSession session=" << name()
<< "promptSession=" << (promptSession ? promptSession.get() : nullptr);
m_promptSessions.append(promptSession);
}
void Session::removePromptSession(const std::shared_ptr& promptSession)
{
qCDebug(QTMIR_SESSIONS) << "Session::removePromptSession session=" << name()
<< "promptSession=" << (promptSession ? promptSession.get() : nullptr);
m_promptSessions.removeAll(promptSession);
}
void Session::stopPromptSessions()
{
QList children(m_children->list());
for (SessionInterface* child : children) {
static_cast(child)->stopPromptSessions();
}
QList> copy(m_promptSessions);
QListIterator> it(copy);
for ( it.toBack(); it.hasPrevious(); ) {
std::shared_ptr promptSession = it.previous();
qCDebug(QTMIR_SESSIONS) << "Session::stopPromptSessions - promptSession=" << promptSession.get();
m_promptSessionManager->stop_prompt_session(promptSession);
}
}
std::shared_ptr Session::activePromptSession() const
{
if (m_promptSessions.count() > 0)
return m_promptSessions.back();
return nullptr;
}
void Session::foreachPromptSession(std::function&)> f) const
{
for (std::shared_ptr promptSession : m_promptSessions) {
f(promptSession);
}
}
} // namespace qtmir