Merge lp:~gerboland/unity-mir/use-upstart-app-launch2 into lp:unity-mir

Proposed by Gerry Boland on 2013-09-26
Status: Merged
Approved by: Daniel d'Andrada on 2013-09-30
Approved revision: 103
Merged at revision: 94
Proposed branch: lp:~gerboland/unity-mir/use-upstart-app-launch2
Merge into: lp:unity-mir
Diff against target: 866 lines (+299/-239)
10 files modified
debian/control (+2/-0)
src/modules/Unity/Application/Application.pro (+1/-1)
src/modules/Unity/Application/application.cpp (+28/-18)
src/modules/Unity/Application/application.h (+3/-5)
src/modules/Unity/Application/application_manager.cpp (+124/-176)
src/modules/Unity/Application/application_manager.h (+8/-6)
src/modules/Unity/Application/applicationscreenshotprovider.cpp (+7/-0)
src/modules/Unity/Application/desktopfilereader.cpp (+2/-1)
src/modules/Unity/Application/taskcontroller.cpp (+101/-27)
src/modules/Unity/Application/taskcontroller.h (+23/-5)
To merge this branch: bzr merge lp:~gerboland/unity-mir/use-upstart-app-launch2
Reviewer Review Type Date Requested Status
Daniel d'Andrada (community) 2013-09-26 Approve on 2013-09-30
PS Jenkins bot (community) continuous-integration Approve on 2013-09-27
Michał Sawicz code Approve on 2013-09-27
Review via email: mp+187769@code.launchpad.net

Commit message

Use upstart-app-launch for app start/stop

Description of the change

Use upstart-app-launch for app start/stop

To post a comment you must log in.
97. By Gerry Boland on 2013-09-26

If process ends not by hand of shell, clean up after it properly

PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Daniel d'Andrada (dandrader) wrote :

+ DLOG("Application::Application (this=%p, appId=%p, state=%d", this, qPrintable(appId), static_cast<int>(state));

s/appId=%p/appId=%s

98. By Gerry Boland on 2013-09-26

s/appId=%p/appId=%s - well spotted Daniel

Daniel d'Andrada (dandrader) wrote :

"""
|@@ -354,12 +271,14 @@ void ApplicationManager::authorizeSessio
| authorized = false; //to be proven wrong
|
| DLOG("ApplicationManager::authorizeSession (this=%p, pid=%lld)", this, pid);
|- Application* application = findApplicationWithPid(pid);
|- if (application) {
""""

Speaking of log messages, I know this code is not touched by your patch but for extra bonus points it would be nice if
s/pid=%lld)"/pid=%"PRIu64")" for architecture independence (it's also an unsigned type, so lld is is wrong in more than one way).

Daniel d'Andrada (dandrader) wrote :

"""
@@ -428,7 +347,10 @@ void ApplicationManager::authorizeSessio

     QString argStr(command.data());
     QStringList arguments(argStr.split(' '));
- application = new Application(desktopData, pid, stage, Application::Starting, arguments, m_taskController);
+ application = new Application(desktopData->appId(), Application::Starting, arguments, this);
+ delete desktopData;
+ application->setPid(pid);
+ application->setStage(stage);
"""

It's rather wasteful to parse a desktop file twice in this situation. Can't we easily avoid it by having a second constructor that takes a DesktopFileReader instead of a appId as an argument?

99. By Gerry Boland on 2013-09-26

Support respawning apps again

Daniel d'Andrada (dandrader) wrote :

I wonder if we can fix the mess that a pid is represented as a pid_t, a qint64 and a quint64.

Daniel d'Andrada (dandrader) wrote :

Code-wise, my only complaint is the unnecessary recreation of the DesktopFileReader I explained above.

review: Needs Fixing
100. By Gerry Boland on 2013-09-26

Some handy extra debug info

101. By Gerry Boland on 2013-09-26

Fix for quick focus->unfocus->focus caused app to still suspend

Gerry Boland (gerboland) wrote :

> I wonder if we can fix the mess that a pid is represented as a pid_t, a qint64
> and a quint64.
Yep, it's a mess, and on my TODO list to fix in another MR.

Gerry Boland (gerboland) wrote :

> It's rather wasteful to parse a desktop file twice in this situation. Can't we
> easily avoid it by having a second constructor that takes a DesktopFileReader
> instead of a appId as an argument?
You raise a fair point. I wanted to avoid having to parse desktop files in ApplicationManager at all, delegating it to Application alone, but the authorizer forced my hand.

I did the duplicate as I hope much of that authorizer code will go away soon. But I'll try to improve it now.

102. By Gerry Boland on 2013-09-27

Application constructor can take both appId or DesktopFileReader

103. By Gerry Boland on 2013-09-27

Add todo for screenshot

Michał Sawicz (saviq) wrote :

Code looks good, didn't test though.

review: Approve (code)
Daniel d'Andrada (dandrader) wrote :

Looks good now.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'debian/control'
--- debian/control 2013-09-25 21:06:22 +0000
+++ debian/control 2013-09-27 15:13:07 +0000
@@ -8,6 +8,7 @@
8 libmirserver-dev (>= 0.0.12),8 libmirserver-dev (>= 0.0.12),
9 libmirclient-dev (>= 0.0.12),9 libmirclient-dev (>= 0.0.12),
10 libunity-api-dev,10 libunity-api-dev,
11 libupstart-app-launch1-dev,
11 qt5-default,12 qt5-default,
12 qtbase5-dev,13 qtbase5-dev,
13 qtdeclarative5-dev,14 qtdeclarative5-dev,
@@ -39,6 +40,7 @@
39Depends: ${misc:Depends},40Depends: ${misc:Depends},
40 ${shlibs:Depends},41 ${shlibs:Depends},
41 libunity-mir1 (= ${binary:Version}),42 libunity-mir1 (= ${binary:Version}),
43 libupstart-app-launch1-dev,
42 libmirserver-dev (>= 0.0.11),44 libmirserver-dev (>= 0.0.11),
43 libmirclient-dev (>= 0.0.11),45 libmirclient-dev (>= 0.0.11),
44 libplatform-api1-dev,46 libplatform-api1-dev,
4547
=== modified file 'src/modules/Unity/Application/Application.pro'
--- src/modules/Unity/Application/Application.pro 2013-09-11 13:20:08 +0000
+++ src/modules/Unity/Application/Application.pro 2013-09-27 15:13:07 +0000
@@ -9,7 +9,7 @@
9QMAKE_CXXFLAGS_RELEASE += -Werror # so no stop on warning in debug builds9QMAKE_CXXFLAGS_RELEASE += -Werror # so no stop on warning in debug builds
10QMAKE_LFLAGS = -std=c++11 -Wl,-no-undefined10QMAKE_LFLAGS = -std=c++11 -Wl,-no-undefined
1111
12PKGCONFIG += mircommon mirserver ubuntu-platform-api12PKGCONFIG += mircommon mirserver ubuntu-platform-api glib-2.0 upstart-app-launch-1
1313
14INCLUDEPATH += ../../../unity-mir14INCLUDEPATH += ../../../unity-mir
15LIBS += -L../../../unity-mir -lunity-mir \15LIBS += -L../../../unity-mir -lunity-mir \
1616
=== modified file 'src/modules/Unity/Application/application.cpp'
--- src/modules/Unity/Application/application.cpp 2013-09-12 17:29:51 +0000
+++ src/modules/Unity/Application/application.cpp 2013-09-27 15:13:07 +0000
@@ -18,6 +18,7 @@
18#include "application.h"18#include "application.h"
19#include "application_manager.h"19#include "application_manager.h"
20#include "desktopfilereader.h"20#include "desktopfilereader.h"
21#include "taskcontroller.h"
2122
22// unity-mir23// unity-mir
23#include "logging.h"24#include "logging.h"
@@ -25,24 +26,26 @@
25// mir26// mir
26#include <mir/shell/application_session.h>27#include <mir/shell/application_session.h>
2728
28Application::Application(DesktopFileReader* desktopData, qint64 pid,29Application::Application(const QString &appId, Application::State state,
29 Application::Stage stage, Application::State state,30 const QStringList &arguments, QObject *parent)
30 const QStringList& arguments, TaskController* taskController,31 : Application(new DesktopFileReader(appId), state, arguments, parent)
31 QObject* parent)32{
32 : ApplicationInfoInterface(desktopData->appId(), parent)33}
33 , m_desktopData(desktopData)34
34 , m_pid(pid)35Application::Application(DesktopFileReader *desktopFileReader, State state,
35 , m_stage(stage)36 const QStringList &arguments, QObject *parent)
37 : ApplicationInfoInterface(desktopFileReader->appId(), parent)
38 , m_desktopData(desktopFileReader)
39 , m_pid(0)
40 , m_stage((m_desktopData->stageHint() == "SideStage") ? Application::SideStage : Application::MainStage)
36 , m_state(state)41 , m_state(state)
37 , m_focused(false)42 , m_focused(false)
38 , m_fullscreen(false)43 , m_fullscreen(false)
39 , m_arguments(arguments)44 , m_arguments(arguments)
40 , m_taskController(taskController)
41 , m_suspendTimer(new QTimer(this))45 , m_suspendTimer(new QTimer(this))
42{46{
43 DASSERT(desktopData != NULL);47 DLOG("Application::Application (this=%p, appId=%s, state=%d", this, qPrintable(desktopFileReader->appId()),
44 DLOG("Application::Application (this=%p, desktopData=%p, pid=%lld, stage=%d, state=%d",48 static_cast<int>(state));
45 this, desktopData, pid, static_cast<int>(stage), static_cast<int>(state));
4649
47 m_suspendTimer->setSingleShot(true);50 m_suspendTimer->setSingleShot(true);
48 connect(m_suspendTimer, SIGNAL(timeout()), this, SLOT(suspend()));51 connect(m_suspendTimer, SIGNAL(timeout()), this, SLOT(suspend()));
@@ -54,6 +57,11 @@
54 delete m_desktopData;57 delete m_desktopData;
55}58}
5659
60bool Application::isValid() const
61{
62 return m_desktopData->loaded();
63}
64
57QString Application::desktopFile() const65QString Application::desktopFile() const
58{66{
59 return m_desktopData->file();67 return m_desktopData->file();
@@ -130,6 +138,8 @@
130138
131void Application::setSession(const std::shared_ptr<mir::shell::ApplicationSession>& session)139void Application::setSession(const std::shared_ptr<mir::shell::ApplicationSession>& session)
132{140{
141 DLOG("Application::setSession (this=%p, session=%p)", this, session.get());
142
133 // TODO(greyback) what if called with new surface?143 // TODO(greyback) what if called with new surface?
134 m_session = session;144 m_session = session;
135}145}
@@ -166,6 +176,9 @@
166 }176 }
167 break;177 break;
168 case Application::Running:178 case Application::Running:
179 if (m_suspendTimer->isActive())
180 m_suspendTimer->stop();
181
169 if (m_state == Application::Suspended) {182 if (m_state == Application::Suspended) {
170 resume();183 resume();
171 session()->set_lifecycle_state(mir_lifecycle_state_resumed);184 session()->set_lifecycle_state(mir_lifecycle_state_resumed);
@@ -207,20 +220,17 @@
207void Application::suspend()220void Application::suspend()
208{221{
209 DLOG("Application::suspend (this=%p)", this);222 DLOG("Application::suspend (this=%p)", this);
210223 TaskController::singleton()->suspend(appId());
211 m_taskController->do_suspend(this);
212}224}
213225
214void Application::resume()226void Application::resume()
215{227{
216 DLOG("Application::resume (this=%p)", this);228 DLOG("Application::resume (this=%p)", this);
217229 TaskController::singleton()->resume(appId());
218 m_taskController->do_resume(this);
219}230}
220231
221void Application::respawn()232void Application::respawn()
222{233{
223 DLOG("Application::respawn (this=%p)", this);234 DLOG("Application::respawn (this=%p)", this);
224235 TaskController::singleton()->start(appId(), m_arguments);
225 m_taskController->do_respawn(this);
226}236}
227237
=== modified file 'src/modules/Unity/Application/application.h'
--- src/modules/Unity/Application/application.h 2013-09-11 13:20:08 +0000
+++ src/modules/Unity/Application/application.h 2013-09-27 15:13:07 +0000
@@ -42,9 +42,8 @@
42 Q_PROPERTY(bool fullscreen READ fullscreen NOTIFY fullscreenChanged)42 Q_PROPERTY(bool fullscreen READ fullscreen NOTIFY fullscreenChanged)
4343
44public:44public:
45 Application(DesktopFileReader* desktopData, qint64 pid, Stage stage, State state,45 Application(const QString &appId, State state, const QStringList &arguments, QObject *parent = 0);
46 const QStringList& arguments, TaskController* taskController,46 Application(DesktopFileReader *desktopFileReader, State state, const QStringList &arguments, QObject *parent = 0);
47 QObject* parent = 0);
48 virtual ~Application();47 virtual ~Application();
4948
50 // ApplicationInfoInterface49 // ApplicationInfoInterface
@@ -56,6 +55,7 @@
56 State state() const override;55 State state() const override;
57 bool focused() const override;56 bool focused() const override;
5857
58 bool isValid() const;
59 QString desktopFile() const;59 QString desktopFile() const;
60 QString exec() const;60 QString exec() const;
61 bool fullscreen() const;61 bool fullscreen() const;
@@ -88,10 +88,8 @@
88 std::shared_ptr<mir::shell::ApplicationSession> m_session;88 std::shared_ptr<mir::shell::ApplicationSession> m_session;
89 QString m_sessionName;89 QString m_sessionName;
90 QStringList m_arguments;90 QStringList m_arguments;
91 TaskController* m_taskController;
92 QTimer* m_suspendTimer;91 QTimer* m_suspendTimer;
9392
94 friend class TaskController;
95 friend class ApplicationManager;93 friend class ApplicationManager;
96 friend class ApplicationListModel;94 friend class ApplicationListModel;
97 friend class MirSurfaceManager;95 friend class MirSurfaceManager;
9896
=== modified file 'src/modules/Unity/Application/application_manager.cpp'
--- src/modules/Unity/Application/application_manager.cpp 2013-09-12 17:27:47 +0000
+++ src/modules/Unity/Application/application_manager.cpp 2013-09-27 15:13:07 +0000
@@ -25,6 +25,7 @@
25#include "shellserverconfiguration.h"25#include "shellserverconfiguration.h"
26#include "sessionlistener.h"26#include "sessionlistener.h"
27#include "sessionauthorizer.h"27#include "sessionauthorizer.h"
28#include "taskcontroller.h"
28#include "logging.h"29#include "logging.h"
2930
30// mir31// mir
@@ -33,13 +34,6 @@
3334
34// Qt35// Qt
35#include <QCoreApplication>36#include <QCoreApplication>
36#include <QProcess>
37
38#define USE_UPSTART 0
39
40#if !USE_UPSTART
41 #include <pwd.h>
42#endif
4337
44namespace msh = mir::shell;38namespace msh = mir::shell;
4539
@@ -58,6 +52,7 @@
58ApplicationManager::ApplicationManager(QObject *parent)52ApplicationManager::ApplicationManager(QObject *parent)
59: ApplicationManagerInterface(parent)53: ApplicationManagerInterface(parent)
60, m_focusedApplication(NULL)54, m_focusedApplication(NULL)
55, m_taskController(TaskController::singleton())
61{56{
62 DLOG("ApplicationManager::ApplicationManager (this=%p)", this);57 DLOG("ApplicationManager::ApplicationManager (this=%p)", this);
6358
@@ -82,11 +77,12 @@
82 QObject::connect(m_mirServer->sessionAuthorizer(), &SessionAuthorizer::requestAuthorizationForSession,77 QObject::connect(m_mirServer->sessionAuthorizer(), &SessionAuthorizer::requestAuthorizationForSession,
83 this, &ApplicationManager::authorizeSession, Qt::BlockingQueuedConnection);78 this, &ApplicationManager::authorizeSession, Qt::BlockingQueuedConnection);
8479
85 // we use random numbers, so seed the generator80 QObject::connect(m_taskController.data(), &TaskController::processStarted,
86 qsrand(QTime::currentTime().msec());81 this, &ApplicationManager::onProcessStarted);
82 QObject::connect(m_taskController.data(), &TaskController::processStopped,
83 this, &ApplicationManager::onProcessStopped);
8784
88 m_dbusWindowStack = new DBusWindowStack(this);85 m_dbusWindowStack = new DBusWindowStack(this);
89 m_taskController = new TaskController(this);
90}86}
9187
92ApplicationManager::~ApplicationManager()88ApplicationManager::~ApplicationManager()
@@ -152,10 +148,23 @@
152148
153bool ApplicationManager::focusApplication(const QString &appId)149bool ApplicationManager::focusApplication(const QString &appId)
154{150{
155 DLOG("ApplicationManager::focusApplication (this=%p, appId=%s)", this, appId.toLatin1().data());151 DLOG("ApplicationManager::focusApplication (this=%p, appId=%s)", this, qPrintable(appId));
156 Application *application = findApplication(appId);152 Application *application = findApplication(appId);
157153
158 m_mirServer->the_session_manager()->set_focus_to(application->session());154 if (!application) {
155 DLOG("No such running application '%s'", qPrintable(appId));
156 return false;
157 }
158
159 if (application->state() == Application::Stopped) {
160 // Respawning this app, move to end of application list so onSessionStarting works ok
161 // FIXME: this happens pretty late, shell could request respawn earlier
162 application->setState(Application::Running);
163 int from = m_applications.indexOf(application);
164 move(from, m_applications.length()-1);
165 } else {
166 m_mirServer->the_session_manager()->set_focus_to(application->session());
167 }
159168
160 // FIXME(dandrader): lying here. The operation is async. So we will only know whether169 // FIXME(dandrader): lying here. The operation is async. So we will only know whether
161 // the focusing was successful once the server replies. Maybe the API in unity-api should170 // the focusing was successful once the server replies. Maybe the API in unity-api should
@@ -170,40 +179,6 @@
170 m_mirServer->the_session_manager()->set_focus_to(NULL); //FIXME(greyback)179 m_mirServer->the_session_manager()->set_focus_to(NULL); //FIXME(greyback)
171}180}
172181
173Application* ApplicationManager::respawnApplication(Application* application)
174{
175 DLOG("ApplicationManager::respawnApplication(this=%p, application=%p)", this, application);
176
177 // Start process - set correct environment
178 setenv("QT_QPA_PLATFORM", "ubuntumirclient", 1);
179
180 bool result;
181 qint64 pid = 0;
182 struct passwd* passwd = getpwuid(getuid());
183 DLOG("current working directory: '%s' - args='%s'", passwd ? passwd->pw_dir : "/", application->m_arguments.join(' ').toLatin1().data());
184 QProcess builder;
185 builder.setProcessChannelMode(QProcess::ForwardedChannels);
186 QString exec(application->m_arguments[0]);
187 application->m_arguments.removeFirst();
188 result = builder.startDetached(exec, application->m_arguments, QString(passwd ? passwd->pw_dir : "/"), &pid);
189 application->m_arguments.prepend(exec);
190 DLOG_IF(result == false, "process failed to start");
191 if (result) {
192 // Set existing application's pid to new instance
193 application->setPid(pid);
194
195 // Push to front
196 if (m_applications.count() > 1)
197 move(m_applications.indexOf(application), 0);
198
199 DLOG("builder '%s' respawned with pid %lld", application->name().toLatin1().data(), pid);
200 return application;
201 } else {
202 DLOG("builder '%s' failed to respawn", application->name().toLatin1().data());
203 return NULL;
204 }
205}
206
207Application* ApplicationManager::startApplication(const QString &appId,182Application* ApplicationManager::startApplication(const QString &appId,
208 const QStringList &arguments)183 const QStringList &arguments)
209{184{
@@ -211,102 +186,41 @@
211}186}
212187
213Application *ApplicationManager::startApplication(const QString &appId, ExecFlags flags,188Application *ApplicationManager::startApplication(const QString &appId, ExecFlags flags,
214 const QStringList &constArgs)189 const QStringList &arguments)
215{190{
216 DLOG("ApplicationManager::startApplication (this=%p, appId=%s)", this, appId.toLatin1().data());191 DLOG("ApplicationManager::startApplication (this=%p, appId=%s)", this, qPrintable(appId));
217192
218 QStringList arguments = constArgs;193 if (!m_taskController->start(appId, arguments)) {
219194 LOG("Asking Upstart to start application '%s' failed", qPrintable(appId));
220 // Load desktop file.195 return nullptr;
221 DesktopFileReader* desktopData = new DesktopFileReader(appId);196 }
222 if (!desktopData->loaded()) {197
223 delete desktopData;198 Application* application = new Application(appId, Application::Starting, arguments, this);
224 return NULL;199 if (!application->isValid()) {
225 }200 DLOG("Unable to instantiate application with appId '%s'", qPrintable(appId));
226201 return nullptr;
227#if USE_UPSTART202 }
228 /* launch via upstart */203
229 arguments.prepend("APP_ID=" + desktopData->appId());204 // override stage if necessary
230 arguments.prepend("application");205 if (application->stage() == Application::SideStage && flags.testFlag(ApplicationManager::ForceMainStage)) {
231206 application->setStage(Application::MainStage);
232 // Start process.207 }
233 //FIXME(greyback) use upstart via DBUS208
234 QProcess process;209 add(application);
235 process.start("start", arguments);210 return application;
236 //FIXME(greyback) this is blocking, instead listen for finished() signal211}
237 process.waitForFinished(1000);212
238213void ApplicationManager::onProcessStarted(const QString &appId)
239 DLOG_IF(process.error(), "process 'start application' failed to start with error: %s", process.errorString().toLatin1().data());214{
240 if (!process.error()) {215 Application *application = findApplication(appId);
241 // fetch pid216
242 QString output = process.readAllStandardOutput();217 if (!application) { // if shell did not start this application, but upstart did
243 int lastSpace = output.lastIndexOf(' ');218 application = new Application(appId, Application::Starting, QStringList(), this);
244 qint64 pid = output.remove(0, lastSpace).toInt();219 if (!application->isValid()) {
245#else220 DLOG("Unable to instantiate application with appId '%s'", qPrintable(appId));
246 /* Launch manually */221 return;
247
248 // Format arguments.
249 // FIXME(loicm) Special field codes are simply ignored for now.
250 // http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s06.html
251 QStringList execArguments = desktopData->exec().split(" ", QString::SkipEmptyParts);
252 DASSERT(execArguments.size() > 0);
253 QString exec(execArguments[0]);
254 const int kSize = execArguments.size();
255 for (int i = kSize - 1; i > 0; i--) {
256 if ((execArguments[i].size() == 2) && (execArguments[i][0].toLatin1() == '%')) {
257 const char kChar = execArguments[i][1].toLatin1();
258 if (kChar == 'F' || kChar == 'u' || kChar == 'U' || kChar == 'd' || kChar == 'D'
259 || kChar == 'n' || kChar == 'N' || kChar == 'i' || kChar == 'c' || kChar == 'k'
260 || kChar == 'v' || kChar == 'm') {
261 continue;
262 }
263 }222 }
264 arguments.prepend(execArguments[i]);
265 }
266 arguments.append(QString("--desktop_file_hint=") + desktopData->file());
267 if (flags.testFlag(ApplicationManager::ForceMainStage))
268 arguments.append(QString("--stage_hint=main_stage"));
269 else if (desktopData->stageHint() == "SideStage")
270 arguments.append(QString("--stage_hint=side_stage"));
271
272 // Start process - set correct environment
273 setenv("QT_QPA_PLATFORM", "ubuntumirclient", 1);
274
275 bool result;
276 qint64 pid = 0;
277 QString path = "/";
278 // respect Path from .desktop file
279 if (desktopData->path() != "") {
280 path = desktopData->path();
281 } else {
282 struct passwd* passwd = getpwuid(getuid());
283 if (passwd)
284 path = passwd->pw_dir;
285 }
286 DLOG("current working directory: '%s'", path.toLatin1().data());
287 QProcess builder;
288 builder.setProcessChannelMode(QProcess::ForwardedChannels);
289 result = builder.startDetached(exec, arguments, path, &pid);
290
291 DLOG_IF(result == false, "process failed to start");
292 if (result) {
293#endif
294 DLOG("process started with pid %lld, adding '%s' to application lists", pid, desktopData->name().toLatin1().data());
295 arguments.prepend(exec);
296 Application* application = new Application(
297 desktopData, pid,
298 (flags.testFlag(ApplicationManager::ForceMainStage)
299 || desktopData->stageHint() != "SideStage")
300 ? Application::MainStage : Application::SideStage,
301 Application::Starting, //FIXME(greyback): assuming running immediately
302 arguments,
303 m_taskController
304 );
305
306 add(application);223 add(application);
307 return application;
308 } else {
309 return NULL;
310 }224 }
311}225}
312226
@@ -316,35 +230,62 @@
316230
317 Application *application = findApplication(appId);231 Application *application = findApplication(appId);
318232
319 if (application != NULL) {233 if (!application) {
320 if (application == m_focusedApplication) {234 DLOG("No such running application '%s'", qPrintable(appId));
321 // TODO(greyback) What to do?? Focus next app, or unfocus everything??235 return false;
322 m_focusedApplication = NULL;236 }
323 Q_EMIT focusedApplicationIdChanged();237
324 }238 if (application == m_focusedApplication) {
325 remove(application);239 // TODO(greyback) What to do?? Focus next app, or unfocus everything??
326 m_dbusWindowStack->WindowDestroyed(0, application->name());240 m_focusedApplication = NULL;
327241 Q_EMIT focusedApplicationIdChanged();
328 // Start process.242 }
329 bool result;243
330244 remove(application);
331#if USE_UPSTART245 m_dbusWindowStack->WindowDestroyed(0, application->name());
332 result = QProcess::startDetached("stop", QStringList() << "application"246
333 << ("APP_ID=" + application->appId()));247 bool result = m_taskController->stop(application->appId());
334 DLOG_IF(result == false, "process 'stop application' failed to execute");248
335#else249 LOG_IF(result == false, "FAILED to ask Upstart to stop application '%s'", qPrintable(application->appId()));
336 result = QProcess::startDetached("/bin/kill", QStringList() << "-9" << QString::number(application->m_pid));250 delete application;
337 DLOG_IF(result == false, "process '/bin/kill -9 %lld' failed to execute", application->m_pid);
338#endif
339 LOG_IF(!result, "ApplicationManager: Unable to stop process '%s'",
340 application->name().toLatin1().constData());
341 delete application;
342 }
343251
344 // FIXME(dandrader): lying here. The operation is async. So we will only know whether252 // FIXME(dandrader): lying here. The operation is async. So we will only know whether
345 // the focusing was successful once the server replies. Maybe the API in unity-api should253 // the focusing was successful once the server replies. Maybe the API in unity-api should
346 // reflect that?254 // reflect that?
347 return true;255 return result;
256}
257
258void ApplicationManager::onProcessStopped(const QString &appId)
259{
260 Application *application = findApplication(appId);
261
262 // if shell did not stop the application, but upstart says it died, we assume the process has been
263 // killed, so it can be respawned later. Only exception is if that application is focused or running
264 // as then it most likely crashed. Update this logic when upstart gives some failure info.
265 if (application) {
266 bool removeApplication = false;
267
268 if (application == m_focusedApplication) {
269 // Very bad case where focused application dies. Remove from list. Should give error message
270 m_focusedApplication = nullptr;
271 Q_EMIT focusedApplicationIdChanged();
272 removeApplication = true;
273 }
274
275 if (application->state() == Application::Running) {
276 // Application probably crashed, else OOM killer struck. Either way state wasn't saved
277 // so just remove application
278 removeApplication = true;
279 } else if (application->state() == Application::Suspended) {
280 application->setState(Application::Stopped);
281 }
282
283 if (removeApplication) {
284 remove(application);
285 m_dbusWindowStack->WindowDestroyed(0, application->name());
286 delete application;
287 }
288 }
348}289}
349290
350/************************************* Mir-side methods *************************************/291/************************************* Mir-side methods *************************************/
@@ -354,12 +295,14 @@
354 authorized = false; //to be proven wrong295 authorized = false; //to be proven wrong
355296
356 DLOG("ApplicationManager::authorizeSession (this=%p, pid=%lld)", this, pid);297 DLOG("ApplicationManager::authorizeSession (this=%p, pid=%lld)", this, pid);
357 Application* application = findApplicationWithPid(pid);298
358 if (application) {299 for (Application *app : m_applications) {
359 QString name = application->appId() + QString::number(qrand());300 if (app->state() == Application::Starting
360 application->setSessionName(name);301 && m_taskController->appIdHasProcessId(app->appId(), pid)) {
361 authorized = true;302 app->setPid(pid);
362 return;303 authorized = true;
304 return;
305 }
363 }306 }
364307
365 /*308 /*
@@ -403,13 +346,13 @@
403346
404 // some naughty applications use a script to launch the actual application. Check for the347 // some naughty applications use a script to launch the actual application. Check for the
405 // case where shell actually launched the script.348 // case where shell actually launched the script.
406 application = findApplication(desktopData->appId());349 Application *application = findApplication(desktopData->appId());
407 if (application && application->state() == Application::Starting) {350 if (application && application->state() == Application::Starting) {
408 DLOG("Process with pid %lld appeared, attached to existing entry '%s' in application lists",351 DLOG("Process with pid %lld appeared, attached to existing entry '%s' in application lists",
409 pid, application->appId().toLatin1().data());352 pid, application->appId().toLatin1().data());
410 delete desktopData;353 delete desktopData;
411 QString name = application->appId() + QString::number(qrand());354 application->setSessionName(application->appId());
412 application->setSessionName(name);355 application->setPid(pid);
413 authorized = true;356 authorized = true;
414 return;357 return;
415 }358 }
@@ -428,7 +371,9 @@
428371
429 QString argStr(command.data());372 QString argStr(command.data());
430 QStringList arguments(argStr.split(' '));373 QStringList arguments(argStr.split(' '));
431 application = new Application(desktopData, pid, stage, Application::Starting, arguments, m_taskController);374 application = new Application(desktopData, Application::Starting, arguments, this);
375 application->setPid(pid);
376 application->setStage(stage);
432 add(application);377 add(application);
433 authorized = true;378 authorized = true;
434}379}
@@ -538,8 +483,11 @@
538 return nullptr;483 return nullptr;
539}484}
540485
541Application* ApplicationManager::findApplicationWithPid(const int pid)486Application* ApplicationManager::findApplicationWithPid(const qint64 pid)
542{487{
488 if (pid <= 0)
489 return nullptr;
490
543 for (Application *app : m_applications) {491 for (Application *app : m_applications) {
544 if (app->m_pid == pid) {492 if (app->m_pid == pid) {
545 return app;493 return app;
546494
=== modified file 'src/modules/Unity/Application/application_manager.h'
--- src/modules/Unity/Application/application_manager.h 2013-09-11 13:20:08 +0000
+++ src/modules/Unity/Application/application_manager.h 2013-09-27 15:13:07 +0000
@@ -27,12 +27,13 @@
27// Unity API27// Unity API
28#include <unity/shell/application/ApplicationManagerInterface.h>28#include <unity/shell/application/ApplicationManagerInterface.h>
2929
30#include "taskcontroller.h"30// local
31#include "application.h"
3132
32class Application;
33class ShellServerConfiguration;33class ShellServerConfiguration;
34class DBusWindowStack;34class DBusWindowStack;
35class MirSurfaceManager;35class MirSurfaceManager;
36class TaskController;
3637
37namespace mir {38namespace mir {
38 namespace shell {39 namespace shell {
@@ -82,7 +83,7 @@
82 Q_INVOKABLE void move(int from, int to);83 Q_INVOKABLE void move(int from, int to);
8384
84 const QList<Application*> &list() const { return m_applications; }85 const QList<Application*> &list() const { return m_applications; }
85 Application* findApplicationWithPid(const int pid);86 Application* findApplicationWithPid(const qint64 pid);
8687
87public Q_SLOTS:88public Q_SLOTS:
88 void authorizeSession(const quint64 pid, bool &authorized);89 void authorizeSession(const quint64 pid, bool &authorized);
@@ -94,6 +95,9 @@
9495
95 void onSessionCreatedSurface(mir::shell::ApplicationSession const*, std::shared_ptr<mir::shell::Surface> const&);96 void onSessionCreatedSurface(mir::shell::ApplicationSession const*, std::shared_ptr<mir::shell::Surface> const&);
9697
98 void onProcessStarted(const QString& appId);
99 void onProcessStopped(const QString& appId);
100
97Q_SIGNALS:101Q_SIGNALS:
98 void focusRequested(FavoriteApplication favoriteApplication);102 void focusRequested(FavoriteApplication favoriteApplication);
99103
@@ -101,7 +105,6 @@
101 void setFocused(Application *application);105 void setFocused(Application *application);
102 void add(Application *application);106 void add(Application *application);
103 void remove(Application* application);107 void remove(Application* application);
104 Application* respawnApplication(Application* application);
105 Application* findApplicationWithSession(const std::shared_ptr<mir::shell::Session> &session);108 Application* findApplicationWithSession(const std::shared_ptr<mir::shell::Session> &session);
106 Application* findApplicationWithSession(const mir::shell::Session *session);109 Application* findApplicationWithSession(const mir::shell::Session *session);
107 Application* findLastExecutedApplication();110 Application* findLastExecutedApplication();
@@ -111,11 +114,10 @@
111 Application* m_focusedApplication; // remove as Mir has API for this114 Application* m_focusedApplication; // remove as Mir has API for this
112 ShellServerConfiguration* m_mirServer;115 ShellServerConfiguration* m_mirServer;
113 DBusWindowStack* m_dbusWindowStack;116 DBusWindowStack* m_dbusWindowStack;
114 TaskController* m_taskController;117 QScopedPointer<TaskController> m_taskController;
115 static ApplicationManager* the_application_manager;118 static ApplicationManager* the_application_manager;
116119
117 friend class DBusWindowStack;120 friend class DBusWindowStack;
118 friend class TaskController;
119 friend class MirSurfaceManager;121 friend class MirSurfaceManager;
120};122};
121123
122124
=== modified file 'src/modules/Unity/Application/applicationscreenshotprovider.cpp'
--- src/modules/Unity/Application/applicationscreenshotprovider.cpp 2013-09-24 18:48:11 +0000
+++ src/modules/Unity/Application/applicationscreenshotprovider.cpp 2013-09-27 15:13:07 +0000
@@ -67,11 +67,18 @@
67 return QImage();67 return QImage();
68 }68 }
6969
70 // TODO: if app not ready, return an app-provided splash image. If app has been stopped with saved state
71 // return the screenshot that was saved to disk.
70 if (!app->session() || !app->session()->default_surface()) {72 if (!app->session() || !app->session()->default_surface()) {
71 LOG("ApplicationScreenshotProvider - app session not found - taking screenshot too early");73 LOG("ApplicationScreenshotProvider - app session not found - taking screenshot too early");
72 return QImage();74 return QImage();
73 }75 }
7476
77 if (app->state() == Application::Stopped || app->state() == Application::Starting) {
78 LOG("ApplicationScreenshotProvider - unable to take screenshot of stopped/not-ready applications");
79 return QImage();
80 }
81
75 /* Workaround for bug https://bugs.launchpad.net/qtubuntu/+bug/1209216 - currently all qtubuntu82 /* Workaround for bug https://bugs.launchpad.net/qtubuntu/+bug/1209216 - currently all qtubuntu
76 * based applications are allocated a fullscreen Mir surface, but draw in a geometry excluding83 * based applications are allocated a fullscreen Mir surface, but draw in a geometry excluding
77 * the panel's rectangle. Mir snapshots thus have a white rectangle which the panel hides.84 * the panel's rectangle. Mir snapshots thus have a white rectangle which the panel hides.
7885
=== modified file 'src/modules/Unity/Application/desktopfilereader.cpp'
--- src/modules/Unity/Application/desktopfilereader.cpp 2013-09-17 17:52:54 +0000
+++ src/modules/Unity/Application/desktopfilereader.cpp 2013-09-27 15:13:07 +0000
@@ -98,7 +98,8 @@
98 const unsigned int kAllEntriesMask =98 const unsigned int kAllEntriesMask =
99 (1 << DesktopFileReader::kNameIndex) | (1 << DesktopFileReader::kCommentIndex)99 (1 << DesktopFileReader::kNameIndex) | (1 << DesktopFileReader::kCommentIndex)
100 | (1 << DesktopFileReader::kIconIndex) | (1 << DesktopFileReader::kExecIndex)100 | (1 << DesktopFileReader::kIconIndex) | (1 << DesktopFileReader::kExecIndex)
101 | (1 << DesktopFileReader::kPathIndex) | (1 << DesktopFileReader::kStageHintIndex); const unsigned int kMandatoryEntriesMask =101 | (1 << DesktopFileReader::kPathIndex) | (1 << DesktopFileReader::kStageHintIndex);
102 const unsigned int kMandatoryEntriesMask =
102 (1 << DesktopFileReader::kNameIndex) | (1 << DesktopFileReader::kIconIndex)103 (1 << DesktopFileReader::kNameIndex) | (1 << DesktopFileReader::kIconIndex)
103 | (1 << DesktopFileReader::kExecIndex);104 | (1 << DesktopFileReader::kExecIndex);
104 const int kEntriesCount = ARRAY_SIZE(kEntryNames);105 const int kEntriesCount = ARRAY_SIZE(kEntryNames);
105106
=== modified file 'src/modules/Unity/Application/taskcontroller.cpp'
--- src/modules/Unity/Application/taskcontroller.cpp 2013-08-27 13:31:20 +0000
+++ src/modules/Unity/Application/taskcontroller.cpp 2013-09-27 15:13:07 +0000
@@ -16,42 +16,116 @@
16 * Authored by: Ricardo Mendoza <ricardo.mendoza@canonical.com>16 * Authored by: Ricardo Mendoza <ricardo.mendoza@canonical.com>
17 */17 */
1818
19#include "logging.h"19// local
20#include "taskcontroller.h"20#include "taskcontroller.h"
21#include "application_manager.h"21
22// unity-mir
23#include <logging.h>
24
25// Qt
26#include <QStringList>
27
28// glib
29#include <glib.h>
30
31// std
22#include <signal.h>32#include <signal.h>
23#include <unistd.h>33#include <unistd.h>
2434
25TaskController::TaskController(ApplicationManager *parent) :35
36TaskController* TaskController::m_theTaskController = nullptr;
37
38TaskController* TaskController::singleton()
39{
40 if (!m_theTaskController) {
41 m_theTaskController = new TaskController();
42 }
43 return m_theTaskController;
44}
45
46TaskController::TaskController(QObject *parent) :
26 QObject(parent)47 QObject(parent)
27{48{
28 DLOG("TaskController::TaskController (this=%p)", this);49 startCallback = [](const gchar * appId, gpointer userData) {
50 Q_UNUSED(userData)
51 Q_EMIT TaskController::singleton()->processStarted(QString(appId));
52 };
53
54 stopCallback = [](const gchar * appId, gpointer userData) {
55 Q_UNUSED(userData)
56 Q_EMIT TaskController::singleton()->processStopped(QString(appId));
57 };
58
59 upstart_app_launch_observer_add_app_start(startCallback, nullptr);
60 upstart_app_launch_observer_add_app_stop(stopCallback, nullptr);
29}61}
3062
31TaskController::~TaskController()63TaskController::~TaskController()
32{64{
33 DLOG("TaskController::~TaskController (this=%p)", this);65 upstart_app_launch_observer_delete_app_start(startCallback, nullptr);
34}66 upstart_app_launch_observer_delete_app_stop(stopCallback, nullptr);
3567}
36void TaskController::do_suspend(Application* application)68
37{69bool TaskController::start(const QString& appId, const QStringList& arguments)
38 DLOG("TaskController::do_suspend (this=%p, application=%p)", this, application);70{
39 if (application->state() == Application::Suspended)71 DLOG("TaskController::start appId='%s'", qPrintable(appId));
40 kill(application->pid(), SIGSTOP);72 gchar ** upstartArgs = nullptr;
41}73 bool result = false;
4274
43void TaskController::do_resume(Application* application)75 // Convert arguments QStringList into format suitable for upstart-app-launch
44{76 upstartArgs = g_new0(gchar *, arguments.length());
45 DLOG("TaskController::do_resume (this=%p, application=%p)", this, application);77
46 if (application->state() == Application::Suspended)78 for (int i=0; i<arguments.length(); i++) {
47 kill(application->pid(), SIGCONT);79 upstartArgs[i] = arguments.at(i).toLatin1().data();
48}80 }
4981
50void TaskController::do_respawn(Application* application)82 result = upstart_app_launch_start_application(appId.toLatin1().constData(),
51{83 static_cast<const gchar * const *>(upstartArgs));
52 DLOG("TaskController::do_respwn (this=%p, application=%p)", this, application);84 g_free(upstartArgs);
53 if (application->state() == Application::Stopped) {85
54 ApplicationManager *appMgr = static_cast<ApplicationManager*>(parent());86 DLOG_IF(!result, "TaskController::startApplication appId='%s' FAILED", qPrintable(appId));
55 appMgr->respawnApplication(application);87 return result;
88}
89
90bool TaskController::stop(const QString& appId)
91{
92 DLOG("TaskController::stop appId='%s'", qPrintable(appId));
93 bool result = false;
94
95 result = upstart_app_launch_stop_application(appId.toLatin1().constData());
96
97 DLOG_IF(!result, "TaskController::stopApplication appId='%s' FAILED", qPrintable(appId));
98 return result;
99}
100
101bool TaskController::appIdHasProcessId(const QString& appId, const quint64 pid)
102{
103 DLOG("TaskController::isApplicationPid appId='%s', pid=%lld", qPrintable(appId), pid);
104 return upstart_app_launch_pid_in_app_id(pid, appId.toLatin1().constData());
105}
106
107bool TaskController::suspend(const QString& appId)
108{
109 DLOG("TaskController::suspend (this=%p, application=%p)", this, qPrintable(appId));
110 pid_t pid = upstart_app_launch_get_primary_pid(appId.toLatin1().constData());
111
112 if (pid) {
113 kill(pid, SIGSTOP);
114 return true;
115 } else {
116 return false;
117 }
118}
119
120bool TaskController::resume(const QString& appId)
121{
122 DLOG("TaskController::resume (this=%p, application=%p)", this, qPrintable(appId));
123 pid_t pid = upstart_app_launch_get_primary_pid(appId.toLatin1().constData());
124
125 if (pid) {
126 kill(pid, SIGCONT);
127 return true;
128 } else {
129 return false;
56 }130 }
57}131}
58132
=== modified file 'src/modules/Unity/Application/taskcontroller.h'
--- src/modules/Unity/Application/taskcontroller.h 2013-08-27 13:31:20 +0000
+++ src/modules/Unity/Application/taskcontroller.h 2013-09-27 15:13:07 +0000
@@ -23,17 +23,35 @@
2323
24#include "application.h"24#include "application.h"
2525
26class ApplicationManager;26// upstart
27extern "C" {
28 #include "upstart-app-launch.h"
29}
30
27class TaskController : public QObject31class TaskController : public QObject
28{32{
29 Q_OBJECT33 Q_OBJECT
30public:34public:
31 explicit TaskController(ApplicationManager* parent);35 static TaskController* singleton();
32 ~TaskController();36 ~TaskController();
3337
34 void do_resume(Application* application);38 bool start(const QString& appId, const QStringList& args);
35 void do_suspend(Application* application);39 bool stop(const QString& appId);
36 void do_respawn(Application* application);40
41 bool suspend(const QString& appId);
42 bool resume(const QString& appId);
43
44 bool appIdHasProcessId(const QString& appId, const quint64 pid);
45
46Q_SIGNALS:
47 void processStarted(const QString& appId);
48 void processStopped(const QString& appId);
49
50private:
51 TaskController(QObject *parent = 0);
52
53 static TaskController* m_theTaskController;
54 upstart_app_launch_app_observer_t startCallback, stopCallback;
37};55};
3856
39#endif // TASKCONTROLLER_H57#endif // TASKCONTROLLER_H

Subscribers

People subscribed via source and target branches