2
* Copyright (C) 2014-2016 Canonical, Ltd.
4
* This program is free software: you can redistribute it and/or modify it under
5
* the terms of the GNU Lesser General Public License version 3, as published by
6
* the Free Software Foundation.
8
* This program is distributed in the hope that it will be useful, but WITHOUT
9
* ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10
* SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
* Lesser General Public License for more details.
13
* You should have received a copy of the GNU Lesser General Public License
14
* along with this program. If not, see <http://www.gnu.org/licenses/>.
18
#include "application.h"
19
#include "debughelpers.h"
21
#include "mirsurfaceinterface.h"
22
#include "mirsurfaceitem.h"
28
#include <mir/scene/session.h>
29
#include <mir/scene/prompt_session.h>
30
#include <mir/scene/prompt_session_manager.h>
36
namespace ms = mir::scene;
38
using unity::shell::application::ApplicationInfoInterface;
40
#define DEBUG_MSG qCDebug(QTMIR_SURFACES).nospace() << "Session[" << (void*)this << ",name=" << name() << "]::" << __func__
47
const char *sessionStateToString(SessionInterface::State state)
50
case SessionInterface::Starting:
52
case SessionInterface::Running:
54
case SessionInterface::Suspending:
56
case SessionInterface::Suspended:
58
case SessionInterface::Stopped:
67
Session::Session(const std::shared_ptr<ms::Session>& session,
68
const std::shared_ptr<ms::PromptSessionManager>& promptSessionManager,
70
: SessionInterface(parent)
72
, m_application(nullptr)
73
, m_children(new SessionModel(this))
75
, m_state(State::Starting)
77
, m_promptSessionManager(promptSessionManager)
81
setSuspendTimer(new Timer);
83
connect(&m_surfaceList, &MirSurfaceListModel::emptyChanged, this, &Session::deleteIfZombieAndEmpty);
91
const QList<SessionInterface*> children(m_children->list());
92
for (SessionInterface* child : children) {
96
m_application->setSession(nullptr);
99
delete m_children; m_children = nullptr;
101
delete m_suspendTimer;
103
Q_EMIT destroyed(this); // Early warning, while Session methods can still be accessed.
106
void Session::doSuspend()
108
Q_ASSERT(m_state == Session::Suspending);
110
if (m_surfaceList.count() == 0) {
111
DEBUG_MSG << " no surface to call stopFrameDropper() on!";
113
for (int i = 0; i < m_surfaceList.count(); ++i) {
114
auto surface = static_cast<MirSurfaceInterface*>(m_surfaceList.get(i));
115
surface->stopFrameDropper();
121
QString Session::name() const
123
return QString::fromStdString(m_session->name());
126
std::shared_ptr<ms::Session> Session::session() const
131
ApplicationInfoInterface* Session::application() const
133
return m_application;
136
MirSurfaceListModel* Session::surfaceList()
138
return &m_surfaceList;
141
MirSurfaceListModel* Session::promptSurfaceList()
143
return &m_promptSurfaceList;
146
Session::State Session::state() const
151
void Session::setState(State state)
153
if (m_state == state) {
157
DEBUG_MSG << "(state=" << sessionStateToString(state) << ")";
159
if (m_state == Suspending) {
160
m_suspendTimer->stop();
170
m_suspendTimer->start();
177
Q_EMIT stateChanged(m_state);
180
bool Session::fullscreen() const
185
bool Session::live() const
190
void Session::setApplication(ApplicationInfoInterface* application)
192
if (m_application == application)
195
m_application = static_cast<Application*>(application);
196
Q_EMIT applicationChanged(application);
199
void Session::registerSurface(MirSurfaceInterface *newSurface)
201
DEBUG_MSG << "(surface=" << newSurface << ")";
203
// Only notify QML of surface creation once it has drawn its first frame.
204
if (newSurface->isFirstFrameDrawn()) {
205
prependSurface(newSurface);
207
connect(newSurface, &MirSurfaceInterface::firstFrameDrawn, this, [this, newSurface]()
209
newSurface->disconnect(this);
210
this->prependSurface(newSurface);
215
void Session::prependSurface(MirSurfaceInterface *newSurface)
217
DEBUG_MSG << "(surface=" << newSurface << ")";
219
connect(newSurface, &MirSurfaceInterface::stateChanged,
220
this, &Session::updateFullscreenProperty);
222
connect(newSurface, &MirSurfaceInterface::closeRequested, this, [this, newSurface]()
224
m_closingSurfaces.append(newSurface);
225
if (m_closingSurfaces.count() == 1) {
226
Q_EMIT hasClosingSurfacesChanged();
229
m_surfaceList.removeSurface(newSurface);
231
connect(newSurface, &QObject::destroyed, this, [this, newSurface]()
233
this->removeSurface(newSurface);
235
connect(newSurface, &MirSurfaceInterface::focusRequested, this, &SessionInterface::focusRequested);
237
m_surfaceList.prependSurface(newSurface);
240
if (m_state == Starting) {
244
updateFullscreenProperty();
247
void Session::removeSurface(MirSurfaceInterface* surface)
249
DEBUG_MSG << "(surface=" << surface << ")";
251
surface->disconnect(this);
253
if (m_surfaceList.contains(surface)) {
254
m_surfaceList.removeSurface(surface);
257
if (m_closingSurfaces.contains(surface)) {
258
m_closingSurfaces.removeAll(surface);
259
if (m_closingSurfaces.isEmpty()) {
260
Q_EMIT hasClosingSurfacesChanged();
264
updateFullscreenProperty();
267
void Session::updateFullscreenProperty()
269
if (m_surfaceList.count() > 0) {
270
// TODO: Figure out something better
271
setFullscreen(m_surfaceList.get(0)->state() == Mir::FullscreenState);
273
// Keep the current value of the fullscreen property until we get a new
278
void Session::setFullscreen(bool fullscreen)
280
if (m_fullscreen != fullscreen) {
281
DEBUG_MSG << "(" << fullscreen << ")";
282
m_fullscreen = fullscreen;
283
Q_EMIT fullscreenChanged(m_fullscreen);
287
void Session::suspend()
289
DEBUG_MSG << " state=" << sessionStateToString(m_state);
290
if (m_state == Running) {
291
session()->set_lifecycle_state(mir_lifecycle_state_will_suspend);
292
m_suspendTimer->start();
294
foreachPromptSession([this](const std::shared_ptr<ms::PromptSession>& promptSession) {
295
m_promptSessionManager->suspend_prompt_session(promptSession);
298
foreachChildSession([](SessionInterface* session) {
302
setState(Suspending);
306
void Session::resume()
308
DEBUG_MSG << " state=" << sessionStateToString(m_state);
310
if (m_state == Suspending || m_state == Suspended) {
315
void Session::doResume()
317
if (m_state == Suspended) {
318
for (int i = 0; i < m_surfaceList.count(); ++i) {
319
auto surface = static_cast<MirSurfaceInterface*>(m_surfaceList.get(i));
320
surface->startFrameDropper();
324
session()->set_lifecycle_state(mir_lifecycle_state_resumed);
326
foreachPromptSession([this](const std::shared_ptr<ms::PromptSession>& promptSession) {
327
m_promptSessionManager->resume_prompt_session(promptSession);
330
foreachChildSession([](SessionInterface* session) {
337
void Session::close()
341
if (m_state == Stopped) return;
343
for (int i = 0; i < m_surfaceList.count(); ++i) {
344
auto surface = static_cast<MirSurfaceInterface*>(m_surfaceList.get(i));
353
if (m_state != Stopped) {
355
stopPromptSessions();
358
for (int i = 0; i < m_surfaceList.count(); ++i) {
359
auto surface = static_cast<MirSurfaceInterface*>(m_surfaceList.get(i));
360
surface->stopFrameDropper();
364
foreachChildSession([](SessionInterface* session) {
372
void Session::setLive(const bool live)
374
if (m_live != live) {
375
DEBUG_MSG << "(" << live << ")";
378
Q_EMIT liveChanged(m_live);
382
for (int i = 0; i < m_surfaceList.count(); ++i) {
383
auto surface = static_cast<MirSurfaceInterface*>(m_surfaceList.get(i));
384
surface->setLive(false);
387
deleteIfZombieAndEmpty();
392
void Session::addChildSession(SessionInterface* session)
394
insertChildSession(m_children->rowCount(), session);
397
void Session::insertChildSession(uint index, SessionInterface* session)
399
DEBUG_MSG << "(index=" << index << ", Session[" << (void*)session << ",name=" << session->name() << "])";
400
Q_ASSERT(!m_children->contains(session));
402
m_children->insert(index, session);
404
// Flatten the list of prompt surfaces
405
m_promptSurfaceList.addSurfaceList(session->surfaceList());
406
m_promptSurfaceList.addSurfaceList(session->promptSurfaceList());
408
connect(session, &QObject::destroyed, this, [this, session]() { this->removeChildSession(session); });
425
void Session::removeChildSession(SessionInterface* session)
427
DEBUG_MSG << "(Session[" << (void*)session << ",name=" << session->name() << "])";
429
disconnect(session, 0, this, 0);
431
if (m_children->contains(session)) {
432
m_children->remove(session);
433
m_promptSurfaceList.removeSurfaceList(session->surfaceList());
434
m_promptSurfaceList.removeSurfaceList(session->promptSurfaceList());
437
deleteIfZombieAndEmpty();
440
void Session::foreachChildSession(const std::function<void(SessionInterface* session)>& f) const
442
const QList<SessionInterface*> children(m_children->list());
443
for (SessionInterface* child : children) {
448
SessionModel* Session::childSessions() const
453
void Session::appendPromptSession(const std::shared_ptr<ms::PromptSession>& promptSession)
455
DEBUG_MSG << "(promptSession=" << (promptSession ? promptSession.get() : nullptr) << ")";
457
m_promptSessions.append(promptSession);
460
void Session::removePromptSession(const std::shared_ptr<ms::PromptSession>& promptSession)
462
DEBUG_MSG << "(promptSession=" << (promptSession ? promptSession.get() : nullptr) << ")";
464
m_promptSessions.removeAll(promptSession);
467
void Session::stopPromptSessions()
469
const QList<SessionInterface*> children(m_children->list());
470
for (SessionInterface* child : children) {
471
static_cast<Session*>(child)->stopPromptSessions();
474
QVector<std::shared_ptr<ms::PromptSession>> copy(m_promptSessions);
475
QVectorIterator<std::shared_ptr<ms::PromptSession>> it(copy);
476
for ( it.toBack(); it.hasPrevious(); ) {
477
std::shared_ptr<ms::PromptSession> promptSession = it.previous();
478
DEBUG_MSG << " - promptSession=" << promptSession.get();
480
m_promptSessionManager->stop_prompt_session(promptSession);
484
std::shared_ptr<ms::PromptSession> Session::activePromptSession() const
486
if (m_promptSessions.count() > 0)
487
return m_promptSessions.back();
491
void Session::foreachPromptSession(const std::function<void(const std::shared_ptr<ms::PromptSession>&)>& f) const
493
Q_FOREACH (std::shared_ptr<ms::PromptSession> promptSession, m_promptSessions) {
498
void Session::deleteIfZombieAndEmpty()
500
if (!m_live && m_children->rowCount() == 0 && m_surfaceList.isEmpty()) {
501
DEBUG_MSG << " - deleteLater()";
506
bool Session::hasClosingSurfaces() const
508
return m_closingSurfaces.count() > 0;
511
bool Session::hadSurface() const
516
bool Session::activeFocus() const
518
for (int i = 0; i < m_surfaceList.count(); ++i) {
519
auto surface = static_cast<const MirSurfaceInterface*>(m_surfaceList.get(i));
520
if (surface->activeFocus()) {
528
pid_t Session::pid() const
530
return m_session->process_id();
533
void Session::setSuspendTimer(AbstractTimer *timer)
535
bool timerWasRunning = false;
537
if (m_suspendTimer) {
538
timerWasRunning = m_suspendTimer->isRunning();
539
delete m_suspendTimer;
542
m_suspendTimer = timer;
543
m_suspendTimer->setInterval(1500);
544
m_suspendTimer->setSingleShot(true);
545
connect(m_suspendTimer, &AbstractTimer::timeout, this, &Session::doSuspend);
547
if (timerWasRunning) {
548
m_suspendTimer->start();