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

Proposed by Gerry Boland
Status: Merged
Approved by: Daniel d'Andrada
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) Approve
PS Jenkins bot (community) continuous-integration Approve
MichaƂ Sawicz code Approve
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.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
97. By Gerry Boland

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

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
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

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

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

Revision history for this message
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).

Revision history for this message
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?

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

Support respawning apps again

Revision history for this message
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.

Revision history for this message
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

Some handy extra debug info

101. By Gerry Boland

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

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 :

> 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.

Revision history for this message
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

Application constructor can take both appId or DesktopFileReader

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

Add todo for screenshot

Revision history for this message
MichaƂ Sawicz (saviq) wrote :

Code looks good, didn't test though.

review: Approve (code)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
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