Merge lp:~gerboland/unity-mir/add-fail-and-resume-focus-support into lp:unity-mir

Proposed by Gerry Boland
Status: Merged
Approved by: Michał Sawicz
Approved revision: 107
Merged at revision: 100
Proposed branch: lp:~gerboland/unity-mir/add-fail-and-resume-focus-support
Merge into: lp:unity-mir
Prerequisite: lp:~gerboland/unity-mir/use-upstart-app-launch2
Diff against target: 303 lines (+112/-15)
5 files modified
src/modules/Unity/Application/ApplicationImage.qml (+0/-1)
src/modules/Unity/Application/application_manager.cpp (+70/-7)
src/modules/Unity/Application/application_manager.h (+6/-2)
src/modules/Unity/Application/taskcontroller.cpp (+30/-2)
src/modules/Unity/Application/taskcontroller.h (+6/-3)
To merge this branch: bzr merge lp:~gerboland/unity-mir/add-fail-and-resume-focus-support
Reviewer Review Type Date Requested Status
Michał Sawicz Approve
Ricardo Mendoza (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+188040@code.launchpad.net

Commit message

Add support for upstart-app-launch to request application resume & focus. This revealed extra work: prevent unexpected focus events propagating to shell, fix lifecycle bug and add workaround for handling of non-application sessions (like QtWebProcess and maliit).

Description of the change

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michał Sawicz (saviq) wrote :

How does upstart know if an app stop was expected or not? Exit code?

77 + // be notified of that through the onProcessStarted slot. Else resume.

No such slot anymore.

review: Needs Information
Revision history for this message
Gerry Boland (gerboland) wrote :

> How does upstart know if an app stop was expected or not? Exit code?
An excellent question that I cannot answer.

> 77 + // be notified of that through the onProcessStarted slot. Else
> resume.
>
> No such slot anymore.
Fixed

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Gerry Boland (gerboland) wrote :

Oops, pushed bad stuff here. Will fix

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Gerry Boland (gerboland) wrote :

Ok, ready for final review

107. By Gerry Boland

Message a bit noisy, make it debug only

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Ricardo Mendoza (ricmm) wrote :

Tried and tested on Nexus4, perfecto.

review: Approve
Revision history for this message
Michał Sawicz (saviq) wrote :

Code-wise looks good, will need someone else to have a look functionally.

review: Approve (code)
Revision history for this message
Michał Sawicz (saviq) wrote :

OK, worky!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/modules/Unity/Application/ApplicationImage.qml'
2--- src/modules/Unity/Application/ApplicationImage.qml 2013-08-30 16:00:46 +0000
3+++ src/modules/Unity/Application/ApplicationImage.qml 2013-10-02 21:19:30 +0000
4@@ -22,7 +22,6 @@
5 readonly property bool ready: source && (image.status == Image.Ready)
6
7 function scheduleUpdate() {
8- print("ApplicationImage::scheduleUpdate", (source) ? source.appId : "");
9 image.source = "";
10 if (source) {
11 image.source = Qt.binding( function() {
12
13=== modified file 'src/modules/Unity/Application/application_manager.cpp'
14--- src/modules/Unity/Application/application_manager.cpp 2013-10-02 11:48:37 +0000
15+++ src/modules/Unity/Application/application_manager.cpp 2013-10-02 21:19:30 +0000
16@@ -51,9 +51,11 @@
17
18 ApplicationManager::ApplicationManager(QObject *parent)
19 : ApplicationManagerInterface(parent)
20-, m_focusedApplication(NULL)
21+, m_focusedApplication(nullptr)
22+, m_applicationToBeFocused(nullptr)
23 , m_lifecycleExceptions(QStringList() << "music-app")
24 , m_taskController(TaskController::singleton())
25+, m_fenceNext(false)
26 {
27 DLOG("ApplicationManager::ApplicationManager (this=%p)", this);
28
29@@ -78,10 +80,14 @@
30 QObject::connect(m_mirServer->sessionAuthorizer(), &SessionAuthorizer::requestAuthorizationForSession,
31 this, &ApplicationManager::authorizeSession, Qt::BlockingQueuedConnection);
32
33- QObject::connect(m_taskController.data(), &TaskController::processStarted,
34- this, &ApplicationManager::onProcessStarted);
35+ QObject::connect(m_taskController.data(), &TaskController::processStartReport,
36+ this, &ApplicationManager::onProcessStartReportReceived);
37 QObject::connect(m_taskController.data(), &TaskController::processStopped,
38 this, &ApplicationManager::onProcessStopped);
39+ QObject::connect(m_taskController.data(), &TaskController::requestFocus,
40+ this, &ApplicationManager::onFocusRequested);
41+ QObject::connect(m_taskController.data(), &TaskController::requestResume,
42+ this, &ApplicationManager::onResumeRequested);
43
44 m_dbusWindowStack = new DBusWindowStack(this);
45 }
46@@ -157,6 +163,8 @@
47 return false;
48 }
49
50+ m_applicationToBeFocused = application;
51+
52 if (application->state() == Application::Stopped) {
53 // Respawning this app, move to end of application list so onSessionStarting works ok
54 // FIXME: this happens pretty late, shell could request respawn earlier
55@@ -177,6 +185,7 @@
56 {
57 DLOG("ApplicationManager::unfocusCurrentApplication (this=%p)", this);
58
59+ m_applicationToBeFocused = nullptr;
60 m_mirServer->the_session_manager()->set_focus_to(NULL); //FIXME(greyback)
61 }
62
63@@ -211,8 +220,15 @@
64 return application;
65 }
66
67-void ApplicationManager::onProcessStarted(const QString &appId)
68+void ApplicationManager::onProcessStartReportReceived(const QString &appId, const bool failure)
69 {
70+ DLOG("ApplicationManager::onProcessStartReportReceived (this=%p, appId=%s, failure=%c)",
71+ this, qPrintable(appId), (failure) ? 'Y' : 'N');
72+
73+ if (failure) {
74+ onProcessStopped(appId, true);
75+ }
76+
77 Application *application = findApplication(appId);
78
79 if (!application) { // if shell did not start this application, but upstart did
80@@ -256,7 +272,7 @@
81 return result;
82 }
83
84-void ApplicationManager::onProcessStopped(const QString &appId)
85+void ApplicationManager::onProcessStopped(const QString &appId, const bool unexpected)
86 {
87 Application *application = findApplication(appId);
88
89@@ -287,6 +303,36 @@
90 delete application;
91 }
92 }
93+
94+ if (unexpected) {
95+ // TODO: pop up a message box/notification?
96+ LOG("ApplicationManager: application '%s' died unexpectedly!", qPrintable(appId));
97+ }
98+}
99+
100+void ApplicationManager::onFocusRequested(const QString& appId)
101+{
102+ DLOG("ApplicationManager::onFocusRequested (this=%p, appId=%s)", this, qPrintable(appId));
103+
104+ focusApplication(appId);
105+}
106+
107+void ApplicationManager::onResumeRequested(const QString& appId)
108+{
109+ DLOG("ApplicationManager::onResumeRequested (this=%p, appId=%s)", this, qPrintable(appId));
110+
111+ Application *application = findApplication(appId);
112+
113+ if (!application) {
114+ DLOG("ApplicationManager::onResumeRequested: No such running application '%s'", qPrintable(appId));
115+ return;
116+ }
117+
118+ // If app Stopped, trust that upstart-app-launch respawns it itself, and AppManager will
119+ // be notified of that through the onProcessStartReportReceived slot. Else resume.
120+ if (application->state() == Application::Suspended) {
121+ application->setState(Application::Running);
122+ }
123 }
124
125 /************************************* Mir-side methods *************************************/
126@@ -323,6 +369,7 @@
127 // FIXME: special exception for the OSK - maliit-server - not very secure
128 if (command.startsWith("maliit-server") || command.startsWith("/usr/lib/arm-linux-gnueabihf/qt5/libexec/QtWebProcess")) {
129 authorized = true;
130+ m_fenceNext = true;
131 return;
132 }
133
134@@ -383,11 +430,17 @@
135 {
136 DLOG("ApplicationManager::onSessionStarting (this=%p, application=%s)", this, session->name().c_str());
137
138+ if (m_fenceNext) {
139+ m_fenceNext = false;
140+ return;
141+ }
142+
143 //FIXME(greyback) Mir not supplying any identifier that we can use to link the PID to the session
144 // so am assuming that the *most recently* launched application session is the one that connects
145 Application* application = findLastExecutedApplication();
146 if (application && application->state() != Application::Running) {
147 application->setSession(session);
148+ m_applicationToBeFocused = application;
149 } else {
150 DLOG("ApplicationManager::onSessionStarting - unauthorized application!!");
151 }
152@@ -417,11 +470,15 @@
153 Application* application = findApplicationWithSession(session);
154
155 // Don't give application focus until it has created it's surface, when it is set as state "Running"
156- if (application && application->state() != Application::Starting
157+ // and only notify shell of focus changes that it actually expects
158+ if (application && application->state() != Application::Starting && application == m_applicationToBeFocused
159 && application != m_focusedApplication) {
160 setFocused(application);
161 QModelIndex appIndex = findIndex(application);
162 Q_EMIT dataChanged(appIndex, appIndex, QVector<int>() << RoleFocused);
163+ } else {
164+ if (application == nullptr)
165+ DLOG("Invalid application focused, discarding the event");
166 }
167 }
168
169@@ -432,7 +489,8 @@
170 Q_ASSERT(m_focusedApplication->focused());
171 m_focusedApplication->setFocused(false);
172
173- if (!m_lifecycleExceptions.contains(m_focusedApplication->appId())) {
174+ if (!m_lifecycleExceptions.contains(m_focusedApplication->appId())
175+ && m_focusedApplication->state() != Application::Suspended) {
176 m_focusedApplication->setState(Application::Suspended);
177 }
178
179@@ -466,6 +524,11 @@
180 if (application == m_focusedApplication)
181 return;
182
183+ // set state of previously focused app to suspended
184+ if (m_focusedApplication && !m_lifecycleExceptions.contains(m_focusedApplication->appId())) {
185+ m_focusedApplication->setState(Application::Suspended);
186+ }
187+
188 m_focusedApplication = application;
189 m_focusedApplication->setFocused(true);
190 m_focusedApplication->setState(Application::Running);
191
192=== modified file 'src/modules/Unity/Application/application_manager.h'
193--- src/modules/Unity/Application/application_manager.h 2013-09-30 16:22:31 +0000
194+++ src/modules/Unity/Application/application_manager.h 2013-10-02 21:19:30 +0000
195@@ -95,8 +95,10 @@
196
197 void onSessionCreatedSurface(mir::shell::ApplicationSession const*, std::shared_ptr<mir::shell::Surface> const&);
198
199- void onProcessStarted(const QString& appId);
200- void onProcessStopped(const QString& appId);
201+ void onProcessStartReportReceived(const QString& appId, const bool failure);
202+ void onProcessStopped(const QString& appId, const bool unexpected);
203+ void onFocusRequested(const QString& appId);
204+ void onResumeRequested(const QString& appId);
205
206 Q_SIGNALS:
207 void focusRequested(FavoriteApplication favoriteApplication);
208@@ -112,11 +114,13 @@
209
210 QList<Application*> m_applications;
211 Application* m_focusedApplication; // remove as Mir has API for this
212+ Application* m_applicationToBeFocused; // a basic form of focus stealing prevention
213 QStringList m_lifecycleExceptions;
214 ShellServerConfiguration* m_mirServer;
215 DBusWindowStack* m_dbusWindowStack;
216 QScopedPointer<TaskController> m_taskController;
217 static ApplicationManager* the_application_manager;
218+ bool m_fenceNext;
219
220 friend class DBusWindowStack;
221 friend class MirSurfaceManager;
222
223=== modified file 'src/modules/Unity/Application/taskcontroller.cpp'
224--- src/modules/Unity/Application/taskcontroller.cpp 2013-09-26 10:02:15 +0000
225+++ src/modules/Unity/Application/taskcontroller.cpp 2013-10-02 21:19:30 +0000
226@@ -48,22 +48,50 @@
227 {
228 startCallback = [](const gchar * appId, gpointer userData) {
229 Q_UNUSED(userData)
230- Q_EMIT TaskController::singleton()->processStarted(QString(appId));
231+ Q_EMIT TaskController::singleton()->processStartReport(QString(appId), false);
232 };
233
234 stopCallback = [](const gchar * appId, gpointer userData) {
235 Q_UNUSED(userData)
236- Q_EMIT TaskController::singleton()->processStopped(QString(appId));
237+ Q_EMIT TaskController::singleton()->processStopped(QString(appId), false);
238+ };
239+
240+ focusCallback = [](const gchar * appId, gpointer userData) {
241+ Q_UNUSED(userData)
242+ Q_EMIT TaskController::singleton()->requestFocus(QString(appId));
243+ };
244+
245+ resumeCallback = [](const gchar * appId, gpointer userData) {
246+ Q_UNUSED(userData)
247+ Q_EMIT TaskController::singleton()->requestResume(QString(appId));
248+ };
249+
250+ failureCallback = [](const gchar * appId, upstart_app_launch_app_failed_t failureType, gpointer userData) {
251+ Q_UNUSED(userData)
252+ if (failureType == UPSTART_APP_LAUNCH_APP_FAILED_CRASH) {
253+ Q_EMIT TaskController::singleton()->processStopped(QString(appId), true);
254+ } else if (failureType == UPSTART_APP_LAUNCH_APP_FAILED_START_FAILURE) {
255+ Q_EMIT TaskController::singleton()->processStartReport(QString(appId), true);
256+ } else {
257+ LOG("TaskController: unknown failure type returned from upstart-app-launch");
258+ }
259+ Q_EMIT TaskController::singleton()->requestResume(QString(appId));
260 };
261
262 upstart_app_launch_observer_add_app_start(startCallback, nullptr);
263 upstart_app_launch_observer_add_app_stop(stopCallback, nullptr);
264+ upstart_app_launch_observer_add_app_focus(focusCallback, nullptr);
265+ upstart_app_launch_observer_add_app_resume(resumeCallback, nullptr);
266+ upstart_app_launch_observer_add_app_failed(failureCallback, nullptr);
267 }
268
269 TaskController::~TaskController()
270 {
271 upstart_app_launch_observer_delete_app_start(startCallback, nullptr);
272 upstart_app_launch_observer_delete_app_stop(stopCallback, nullptr);
273+ upstart_app_launch_observer_delete_app_focus(focusCallback, nullptr);
274+ upstart_app_launch_observer_delete_app_resume(resumeCallback, nullptr);
275+ upstart_app_launch_observer_delete_app_failed(failureCallback, nullptr);
276 }
277
278 bool TaskController::start(const QString& appId, const QStringList& arguments)
279
280=== modified file 'src/modules/Unity/Application/taskcontroller.h'
281--- src/modules/Unity/Application/taskcontroller.h 2013-09-26 10:02:15 +0000
282+++ src/modules/Unity/Application/taskcontroller.h 2013-10-02 21:19:30 +0000
283@@ -44,14 +44,17 @@
284 bool appIdHasProcessId(const QString& appId, const quint64 pid);
285
286 Q_SIGNALS:
287- void processStarted(const QString& appId);
288- void processStopped(const QString& appId);
289+ void processStartReport(const QString& appId, const bool failure);
290+ void processStopped(const QString& appId, const bool unexpectedly);
291+ void requestFocus(const QString& appId);
292+ void requestResume(const QString& appId);
293
294 private:
295 TaskController(QObject *parent = 0);
296
297 static TaskController* m_theTaskController;
298- upstart_app_launch_app_observer_t startCallback, stopCallback;
299+ upstart_app_launch_app_observer_t startCallback, stopCallback, focusCallback, resumeCallback;
300+ upstart_app_launch_app_failed_observer_t failureCallback;
301 };
302
303 #endif // TASKCONTROLLER_H

Subscribers

People subscribed via source and target branches