Merge lp:~gerboland/unity-mir/use-upstart-app-launch2 into lp:unity-mir
- use-upstart-app-launch2
- Merge into trunk
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 | ||||||||||||||||
Related bugs: |
|
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
PS Jenkins bot (ps-jenkins) wrote : | # |
- 97. By Gerry Boland
-
If process ends not by hand of shell, clean up after it properly
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:97
http://
Executed test runs:
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Daniel d'Andrada (dandrader) wrote : | # |
+ DLOG("Applicati
s/appId=%p/appId=%s
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:97
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 98. By Gerry Boland
-
s/appId=%p/appId=%s - well spotted Daniel
Daniel d'Andrada (dandrader) wrote : | # |
"""
|@@ -354,12 +271,14 @@ void ApplicationMana
| authorized = false; //to be proven wrong
|
| DLOG("Applicati
|- Application* application = findApplication
|- 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=%
Daniel d'Andrada (dandrader) wrote : | # |
"""
@@ -428,7 +347,10 @@ void ApplicationMana
QString argStr(
QStringList arguments(
- application = new Application(
+ application = new Application(
+ delete desktopData;
+ application-
+ application-
"""
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?
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:98
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 99. By Gerry Boland
-
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.
- 100. By Gerry Boland
-
Some handy extra debug info
- 101. By Gerry Boland
-
Fix for quick focus->
unfocus- >focus caused app to still suspend
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:99
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:101
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
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
-
Application constructor can take both appId or DesktopFileReader
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:102
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 103. By Gerry Boland
-
Add todo for screenshot
MichaĆ Sawicz (saviq) wrote : | # |
Code looks good, didn't test though.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:103
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Daniel d'Andrada (dandrader) wrote : | # |
Looks good now.
Preview Diff
1 | === modified file 'debian/control' | |||
2 | --- debian/control 2013-09-25 21:06:22 +0000 | |||
3 | +++ debian/control 2013-09-27 15:13:07 +0000 | |||
4 | @@ -8,6 +8,7 @@ | |||
5 | 8 | libmirserver-dev (>= 0.0.12), | 8 | libmirserver-dev (>= 0.0.12), |
6 | 9 | libmirclient-dev (>= 0.0.12), | 9 | libmirclient-dev (>= 0.0.12), |
7 | 10 | libunity-api-dev, | 10 | libunity-api-dev, |
8 | 11 | libupstart-app-launch1-dev, | ||
9 | 11 | qt5-default, | 12 | qt5-default, |
10 | 12 | qtbase5-dev, | 13 | qtbase5-dev, |
11 | 13 | qtdeclarative5-dev, | 14 | qtdeclarative5-dev, |
12 | @@ -39,6 +40,7 @@ | |||
13 | 39 | Depends: ${misc:Depends}, | 40 | Depends: ${misc:Depends}, |
14 | 40 | ${shlibs:Depends}, | 41 | ${shlibs:Depends}, |
15 | 41 | libunity-mir1 (= ${binary:Version}), | 42 | libunity-mir1 (= ${binary:Version}), |
16 | 43 | libupstart-app-launch1-dev, | ||
17 | 42 | libmirserver-dev (>= 0.0.11), | 44 | libmirserver-dev (>= 0.0.11), |
18 | 43 | libmirclient-dev (>= 0.0.11), | 45 | libmirclient-dev (>= 0.0.11), |
19 | 44 | libplatform-api1-dev, | 46 | libplatform-api1-dev, |
20 | 45 | 47 | ||
21 | === modified file 'src/modules/Unity/Application/Application.pro' | |||
22 | --- src/modules/Unity/Application/Application.pro 2013-09-11 13:20:08 +0000 | |||
23 | +++ src/modules/Unity/Application/Application.pro 2013-09-27 15:13:07 +0000 | |||
24 | @@ -9,7 +9,7 @@ | |||
25 | 9 | QMAKE_CXXFLAGS_RELEASE += -Werror # so no stop on warning in debug builds | 9 | QMAKE_CXXFLAGS_RELEASE += -Werror # so no stop on warning in debug builds |
26 | 10 | QMAKE_LFLAGS = -std=c++11 -Wl,-no-undefined | 10 | QMAKE_LFLAGS = -std=c++11 -Wl,-no-undefined |
27 | 11 | 11 | ||
29 | 12 | PKGCONFIG += mircommon mirserver ubuntu-platform-api | 12 | PKGCONFIG += mircommon mirserver ubuntu-platform-api glib-2.0 upstart-app-launch-1 |
30 | 13 | 13 | ||
31 | 14 | INCLUDEPATH += ../../../unity-mir | 14 | INCLUDEPATH += ../../../unity-mir |
32 | 15 | LIBS += -L../../../unity-mir -lunity-mir \ | 15 | LIBS += -L../../../unity-mir -lunity-mir \ |
33 | 16 | 16 | ||
34 | === modified file 'src/modules/Unity/Application/application.cpp' | |||
35 | --- src/modules/Unity/Application/application.cpp 2013-09-12 17:29:51 +0000 | |||
36 | +++ src/modules/Unity/Application/application.cpp 2013-09-27 15:13:07 +0000 | |||
37 | @@ -18,6 +18,7 @@ | |||
38 | 18 | #include "application.h" | 18 | #include "application.h" |
39 | 19 | #include "application_manager.h" | 19 | #include "application_manager.h" |
40 | 20 | #include "desktopfilereader.h" | 20 | #include "desktopfilereader.h" |
41 | 21 | #include "taskcontroller.h" | ||
42 | 21 | 22 | ||
43 | 22 | // unity-mir | 23 | // unity-mir |
44 | 23 | #include "logging.h" | 24 | #include "logging.h" |
45 | @@ -25,24 +26,26 @@ | |||
46 | 25 | // mir | 26 | // mir |
47 | 26 | #include <mir/shell/application_session.h> | 27 | #include <mir/shell/application_session.h> |
48 | 27 | 28 | ||
57 | 28 | Application::Application(DesktopFileReader* desktopData, qint64 pid, | 29 | Application::Application(const QString &appId, Application::State state, |
58 | 29 | Application::Stage stage, Application::State state, | 30 | const QStringList &arguments, QObject *parent) |
59 | 30 | const QStringList& arguments, TaskController* taskController, | 31 | : Application(new DesktopFileReader(appId), state, arguments, parent) |
60 | 31 | QObject* parent) | 32 | { |
61 | 32 | : ApplicationInfoInterface(desktopData->appId(), parent) | 33 | } |
62 | 33 | , m_desktopData(desktopData) | 34 | |
63 | 34 | , m_pid(pid) | 35 | Application::Application(DesktopFileReader *desktopFileReader, State state, |
64 | 35 | , m_stage(stage) | 36 | const QStringList &arguments, QObject *parent) |
65 | 37 | : ApplicationInfoInterface(desktopFileReader->appId(), parent) | ||
66 | 38 | , m_desktopData(desktopFileReader) | ||
67 | 39 | , m_pid(0) | ||
68 | 40 | , m_stage((m_desktopData->stageHint() == "SideStage") ? Application::SideStage : Application::MainStage) | ||
69 | 36 | , m_state(state) | 41 | , m_state(state) |
70 | 37 | , m_focused(false) | 42 | , m_focused(false) |
71 | 38 | , m_fullscreen(false) | 43 | , m_fullscreen(false) |
72 | 39 | , m_arguments(arguments) | 44 | , m_arguments(arguments) |
73 | 40 | , m_taskController(taskController) | ||
74 | 41 | , m_suspendTimer(new QTimer(this)) | 45 | , m_suspendTimer(new QTimer(this)) |
75 | 42 | { | 46 | { |
79 | 43 | DASSERT(desktopData != NULL); | 47 | DLOG("Application::Application (this=%p, appId=%s, state=%d", this, qPrintable(desktopFileReader->appId()), |
80 | 44 | DLOG("Application::Application (this=%p, desktopData=%p, pid=%lld, stage=%d, state=%d", | 48 | static_cast<int>(state)); |
78 | 45 | this, desktopData, pid, static_cast<int>(stage), static_cast<int>(state)); | ||
81 | 46 | 49 | ||
82 | 47 | m_suspendTimer->setSingleShot(true); | 50 | m_suspendTimer->setSingleShot(true); |
83 | 48 | connect(m_suspendTimer, SIGNAL(timeout()), this, SLOT(suspend())); | 51 | connect(m_suspendTimer, SIGNAL(timeout()), this, SLOT(suspend())); |
84 | @@ -54,6 +57,11 @@ | |||
85 | 54 | delete m_desktopData; | 57 | delete m_desktopData; |
86 | 55 | } | 58 | } |
87 | 56 | 59 | ||
88 | 60 | bool Application::isValid() const | ||
89 | 61 | { | ||
90 | 62 | return m_desktopData->loaded(); | ||
91 | 63 | } | ||
92 | 64 | |||
93 | 57 | QString Application::desktopFile() const | 65 | QString Application::desktopFile() const |
94 | 58 | { | 66 | { |
95 | 59 | return m_desktopData->file(); | 67 | return m_desktopData->file(); |
96 | @@ -130,6 +138,8 @@ | |||
97 | 130 | 138 | ||
98 | 131 | void Application::setSession(const std::shared_ptr<mir::shell::ApplicationSession>& session) | 139 | void Application::setSession(const std::shared_ptr<mir::shell::ApplicationSession>& session) |
99 | 132 | { | 140 | { |
100 | 141 | DLOG("Application::setSession (this=%p, session=%p)", this, session.get()); | ||
101 | 142 | |||
102 | 133 | // TODO(greyback) what if called with new surface? | 143 | // TODO(greyback) what if called with new surface? |
103 | 134 | m_session = session; | 144 | m_session = session; |
104 | 135 | } | 145 | } |
105 | @@ -166,6 +176,9 @@ | |||
106 | 166 | } | 176 | } |
107 | 167 | break; | 177 | break; |
108 | 168 | case Application::Running: | 178 | case Application::Running: |
109 | 179 | if (m_suspendTimer->isActive()) | ||
110 | 180 | m_suspendTimer->stop(); | ||
111 | 181 | |||
112 | 169 | if (m_state == Application::Suspended) { | 182 | if (m_state == Application::Suspended) { |
113 | 170 | resume(); | 183 | resume(); |
114 | 171 | session()->set_lifecycle_state(mir_lifecycle_state_resumed); | 184 | session()->set_lifecycle_state(mir_lifecycle_state_resumed); |
115 | @@ -207,20 +220,17 @@ | |||
116 | 207 | void Application::suspend() | 220 | void Application::suspend() |
117 | 208 | { | 221 | { |
118 | 209 | DLOG("Application::suspend (this=%p)", this); | 222 | DLOG("Application::suspend (this=%p)", this); |
121 | 210 | 223 | TaskController::singleton()->suspend(appId()); | |
120 | 211 | m_taskController->do_suspend(this); | ||
122 | 212 | } | 224 | } |
123 | 213 | 225 | ||
124 | 214 | void Application::resume() | 226 | void Application::resume() |
125 | 215 | { | 227 | { |
126 | 216 | DLOG("Application::resume (this=%p)", this); | 228 | DLOG("Application::resume (this=%p)", this); |
129 | 217 | 229 | TaskController::singleton()->resume(appId()); | |
128 | 218 | m_taskController->do_resume(this); | ||
130 | 219 | } | 230 | } |
131 | 220 | 231 | ||
132 | 221 | void Application::respawn() | 232 | void Application::respawn() |
133 | 222 | { | 233 | { |
134 | 223 | DLOG("Application::respawn (this=%p)", this); | 234 | DLOG("Application::respawn (this=%p)", this); |
137 | 224 | 235 | TaskController::singleton()->start(appId(), m_arguments); | |
136 | 225 | m_taskController->do_respawn(this); | ||
138 | 226 | } | 236 | } |
139 | 227 | 237 | ||
140 | === modified file 'src/modules/Unity/Application/application.h' | |||
141 | --- src/modules/Unity/Application/application.h 2013-09-11 13:20:08 +0000 | |||
142 | +++ src/modules/Unity/Application/application.h 2013-09-27 15:13:07 +0000 | |||
143 | @@ -42,9 +42,8 @@ | |||
144 | 42 | Q_PROPERTY(bool fullscreen READ fullscreen NOTIFY fullscreenChanged) | 42 | Q_PROPERTY(bool fullscreen READ fullscreen NOTIFY fullscreenChanged) |
145 | 43 | 43 | ||
146 | 44 | public: | 44 | public: |
150 | 45 | Application(DesktopFileReader* desktopData, qint64 pid, Stage stage, State state, | 45 | Application(const QString &appId, State state, const QStringList &arguments, QObject *parent = 0); |
151 | 46 | const QStringList& arguments, TaskController* taskController, | 46 | Application(DesktopFileReader *desktopFileReader, State state, const QStringList &arguments, QObject *parent = 0); |
149 | 47 | QObject* parent = 0); | ||
152 | 48 | virtual ~Application(); | 47 | virtual ~Application(); |
153 | 49 | 48 | ||
154 | 50 | // ApplicationInfoInterface | 49 | // ApplicationInfoInterface |
155 | @@ -56,6 +55,7 @@ | |||
156 | 56 | State state() const override; | 55 | State state() const override; |
157 | 57 | bool focused() const override; | 56 | bool focused() const override; |
158 | 58 | 57 | ||
159 | 58 | bool isValid() const; | ||
160 | 59 | QString desktopFile() const; | 59 | QString desktopFile() const; |
161 | 60 | QString exec() const; | 60 | QString exec() const; |
162 | 61 | bool fullscreen() const; | 61 | bool fullscreen() const; |
163 | @@ -88,10 +88,8 @@ | |||
164 | 88 | std::shared_ptr<mir::shell::ApplicationSession> m_session; | 88 | std::shared_ptr<mir::shell::ApplicationSession> m_session; |
165 | 89 | QString m_sessionName; | 89 | QString m_sessionName; |
166 | 90 | QStringList m_arguments; | 90 | QStringList m_arguments; |
167 | 91 | TaskController* m_taskController; | ||
168 | 92 | QTimer* m_suspendTimer; | 91 | QTimer* m_suspendTimer; |
169 | 93 | 92 | ||
170 | 94 | friend class TaskController; | ||
171 | 95 | friend class ApplicationManager; | 93 | friend class ApplicationManager; |
172 | 96 | friend class ApplicationListModel; | 94 | friend class ApplicationListModel; |
173 | 97 | friend class MirSurfaceManager; | 95 | friend class MirSurfaceManager; |
174 | 98 | 96 | ||
175 | === modified file 'src/modules/Unity/Application/application_manager.cpp' | |||
176 | --- src/modules/Unity/Application/application_manager.cpp 2013-09-12 17:27:47 +0000 | |||
177 | +++ src/modules/Unity/Application/application_manager.cpp 2013-09-27 15:13:07 +0000 | |||
178 | @@ -25,6 +25,7 @@ | |||
179 | 25 | #include "shellserverconfiguration.h" | 25 | #include "shellserverconfiguration.h" |
180 | 26 | #include "sessionlistener.h" | 26 | #include "sessionlistener.h" |
181 | 27 | #include "sessionauthorizer.h" | 27 | #include "sessionauthorizer.h" |
182 | 28 | #include "taskcontroller.h" | ||
183 | 28 | #include "logging.h" | 29 | #include "logging.h" |
184 | 29 | 30 | ||
185 | 30 | // mir | 31 | // mir |
186 | @@ -33,13 +34,6 @@ | |||
187 | 33 | 34 | ||
188 | 34 | // Qt | 35 | // Qt |
189 | 35 | #include <QCoreApplication> | 36 | #include <QCoreApplication> |
190 | 36 | #include <QProcess> | ||
191 | 37 | |||
192 | 38 | #define USE_UPSTART 0 | ||
193 | 39 | |||
194 | 40 | #if !USE_UPSTART | ||
195 | 41 | #include <pwd.h> | ||
196 | 42 | #endif | ||
197 | 43 | 37 | ||
198 | 44 | namespace msh = mir::shell; | 38 | namespace msh = mir::shell; |
199 | 45 | 39 | ||
200 | @@ -58,6 +52,7 @@ | |||
201 | 58 | ApplicationManager::ApplicationManager(QObject *parent) | 52 | ApplicationManager::ApplicationManager(QObject *parent) |
202 | 59 | : ApplicationManagerInterface(parent) | 53 | : ApplicationManagerInterface(parent) |
203 | 60 | , m_focusedApplication(NULL) | 54 | , m_focusedApplication(NULL) |
204 | 55 | , m_taskController(TaskController::singleton()) | ||
205 | 61 | { | 56 | { |
206 | 62 | DLOG("ApplicationManager::ApplicationManager (this=%p)", this); | 57 | DLOG("ApplicationManager::ApplicationManager (this=%p)", this); |
207 | 63 | 58 | ||
208 | @@ -82,11 +77,12 @@ | |||
209 | 82 | QObject::connect(m_mirServer->sessionAuthorizer(), &SessionAuthorizer::requestAuthorizationForSession, | 77 | QObject::connect(m_mirServer->sessionAuthorizer(), &SessionAuthorizer::requestAuthorizationForSession, |
210 | 83 | this, &ApplicationManager::authorizeSession, Qt::BlockingQueuedConnection); | 78 | this, &ApplicationManager::authorizeSession, Qt::BlockingQueuedConnection); |
211 | 84 | 79 | ||
214 | 85 | // we use random numbers, so seed the generator | 80 | QObject::connect(m_taskController.data(), &TaskController::processStarted, |
215 | 86 | qsrand(QTime::currentTime().msec()); | 81 | this, &ApplicationManager::onProcessStarted); |
216 | 82 | QObject::connect(m_taskController.data(), &TaskController::processStopped, | ||
217 | 83 | this, &ApplicationManager::onProcessStopped); | ||
218 | 87 | 84 | ||
219 | 88 | m_dbusWindowStack = new DBusWindowStack(this); | 85 | m_dbusWindowStack = new DBusWindowStack(this); |
220 | 89 | m_taskController = new TaskController(this); | ||
221 | 90 | } | 86 | } |
222 | 91 | 87 | ||
223 | 92 | ApplicationManager::~ApplicationManager() | 88 | ApplicationManager::~ApplicationManager() |
224 | @@ -152,10 +148,23 @@ | |||
225 | 152 | 148 | ||
226 | 153 | bool ApplicationManager::focusApplication(const QString &appId) | 149 | bool ApplicationManager::focusApplication(const QString &appId) |
227 | 154 | { | 150 | { |
229 | 155 | DLOG("ApplicationManager::focusApplication (this=%p, appId=%s)", this, appId.toLatin1().data()); | 151 | DLOG("ApplicationManager::focusApplication (this=%p, appId=%s)", this, qPrintable(appId)); |
230 | 156 | Application *application = findApplication(appId); | 152 | Application *application = findApplication(appId); |
231 | 157 | 153 | ||
233 | 158 | m_mirServer->the_session_manager()->set_focus_to(application->session()); | 154 | if (!application) { |
234 | 155 | DLOG("No such running application '%s'", qPrintable(appId)); | ||
235 | 156 | return false; | ||
236 | 157 | } | ||
237 | 158 | |||
238 | 159 | if (application->state() == Application::Stopped) { | ||
239 | 160 | // Respawning this app, move to end of application list so onSessionStarting works ok | ||
240 | 161 | // FIXME: this happens pretty late, shell could request respawn earlier | ||
241 | 162 | application->setState(Application::Running); | ||
242 | 163 | int from = m_applications.indexOf(application); | ||
243 | 164 | move(from, m_applications.length()-1); | ||
244 | 165 | } else { | ||
245 | 166 | m_mirServer->the_session_manager()->set_focus_to(application->session()); | ||
246 | 167 | } | ||
247 | 159 | 168 | ||
248 | 160 | // FIXME(dandrader): lying here. The operation is async. So we will only know whether | 169 | // FIXME(dandrader): lying here. The operation is async. So we will only know whether |
249 | 161 | // the focusing was successful once the server replies. Maybe the API in unity-api should | 170 | // the focusing was successful once the server replies. Maybe the API in unity-api should |
250 | @@ -170,40 +179,6 @@ | |||
251 | 170 | m_mirServer->the_session_manager()->set_focus_to(NULL); //FIXME(greyback) | 179 | m_mirServer->the_session_manager()->set_focus_to(NULL); //FIXME(greyback) |
252 | 171 | } | 180 | } |
253 | 172 | 181 | ||
254 | 173 | Application* ApplicationManager::respawnApplication(Application* application) | ||
255 | 174 | { | ||
256 | 175 | DLOG("ApplicationManager::respawnApplication(this=%p, application=%p)", this, application); | ||
257 | 176 | |||
258 | 177 | // Start process - set correct environment | ||
259 | 178 | setenv("QT_QPA_PLATFORM", "ubuntumirclient", 1); | ||
260 | 179 | |||
261 | 180 | bool result; | ||
262 | 181 | qint64 pid = 0; | ||
263 | 182 | struct passwd* passwd = getpwuid(getuid()); | ||
264 | 183 | DLOG("current working directory: '%s' - args='%s'", passwd ? passwd->pw_dir : "/", application->m_arguments.join(' ').toLatin1().data()); | ||
265 | 184 | QProcess builder; | ||
266 | 185 | builder.setProcessChannelMode(QProcess::ForwardedChannels); | ||
267 | 186 | QString exec(application->m_arguments[0]); | ||
268 | 187 | application->m_arguments.removeFirst(); | ||
269 | 188 | result = builder.startDetached(exec, application->m_arguments, QString(passwd ? passwd->pw_dir : "/"), &pid); | ||
270 | 189 | application->m_arguments.prepend(exec); | ||
271 | 190 | DLOG_IF(result == false, "process failed to start"); | ||
272 | 191 | if (result) { | ||
273 | 192 | // Set existing application's pid to new instance | ||
274 | 193 | application->setPid(pid); | ||
275 | 194 | |||
276 | 195 | // Push to front | ||
277 | 196 | if (m_applications.count() > 1) | ||
278 | 197 | move(m_applications.indexOf(application), 0); | ||
279 | 198 | |||
280 | 199 | DLOG("builder '%s' respawned with pid %lld", application->name().toLatin1().data(), pid); | ||
281 | 200 | return application; | ||
282 | 201 | } else { | ||
283 | 202 | DLOG("builder '%s' failed to respawn", application->name().toLatin1().data()); | ||
284 | 203 | return NULL; | ||
285 | 204 | } | ||
286 | 205 | } | ||
287 | 206 | |||
288 | 207 | Application* ApplicationManager::startApplication(const QString &appId, | 182 | Application* ApplicationManager::startApplication(const QString &appId, |
289 | 208 | const QStringList &arguments) | 183 | const QStringList &arguments) |
290 | 209 | { | 184 | { |
291 | @@ -211,102 +186,41 @@ | |||
292 | 211 | } | 186 | } |
293 | 212 | 187 | ||
294 | 213 | Application *ApplicationManager::startApplication(const QString &appId, ExecFlags flags, | 188 | Application *ApplicationManager::startApplication(const QString &appId, ExecFlags flags, |
344 | 214 | const QStringList &constArgs) | 189 | const QStringList &arguments) |
345 | 215 | { | 190 | { |
346 | 216 | DLOG("ApplicationManager::startApplication (this=%p, appId=%s)", this, appId.toLatin1().data()); | 191 | DLOG("ApplicationManager::startApplication (this=%p, appId=%s)", this, qPrintable(appId)); |
347 | 217 | 192 | ||
348 | 218 | QStringList arguments = constArgs; | 193 | if (!m_taskController->start(appId, arguments)) { |
349 | 219 | 194 | LOG("Asking Upstart to start application '%s' failed", qPrintable(appId)); | |
350 | 220 | // Load desktop file. | 195 | return nullptr; |
351 | 221 | DesktopFileReader* desktopData = new DesktopFileReader(appId); | 196 | } |
352 | 222 | if (!desktopData->loaded()) { | 197 | |
353 | 223 | delete desktopData; | 198 | Application* application = new Application(appId, Application::Starting, arguments, this); |
354 | 224 | return NULL; | 199 | if (!application->isValid()) { |
355 | 225 | } | 200 | DLOG("Unable to instantiate application with appId '%s'", qPrintable(appId)); |
356 | 226 | 201 | return nullptr; | |
357 | 227 | #if USE_UPSTART | 202 | } |
358 | 228 | /* launch via upstart */ | 203 | |
359 | 229 | arguments.prepend("APP_ID=" + desktopData->appId()); | 204 | // override stage if necessary |
360 | 230 | arguments.prepend("application"); | 205 | if (application->stage() == Application::SideStage && flags.testFlag(ApplicationManager::ForceMainStage)) { |
361 | 231 | 206 | application->setStage(Application::MainStage); | |
362 | 232 | // Start process. | 207 | } |
363 | 233 | //FIXME(greyback) use upstart via DBUS | 208 | |
364 | 234 | QProcess process; | 209 | add(application); |
365 | 235 | process.start("start", arguments); | 210 | return application; |
366 | 236 | //FIXME(greyback) this is blocking, instead listen for finished() signal | 211 | } |
367 | 237 | process.waitForFinished(1000); | 212 | |
368 | 238 | 213 | void ApplicationManager::onProcessStarted(const QString &appId) | |
369 | 239 | DLOG_IF(process.error(), "process 'start application' failed to start with error: %s", process.errorString().toLatin1().data()); | 214 | { |
370 | 240 | if (!process.error()) { | 215 | Application *application = findApplication(appId); |
371 | 241 | // fetch pid | 216 | |
372 | 242 | QString output = process.readAllStandardOutput(); | 217 | if (!application) { // if shell did not start this application, but upstart did |
373 | 243 | int lastSpace = output.lastIndexOf(' '); | 218 | application = new Application(appId, Application::Starting, QStringList(), this); |
374 | 244 | qint64 pid = output.remove(0, lastSpace).toInt(); | 219 | if (!application->isValid()) { |
375 | 245 | #else | 220 | DLOG("Unable to instantiate application with appId '%s'", qPrintable(appId)); |
376 | 246 | /* Launch manually */ | 221 | return; |
328 | 247 | |||
329 | 248 | // Format arguments. | ||
330 | 249 | // FIXME(loicm) Special field codes are simply ignored for now. | ||
331 | 250 | // http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s06.html | ||
332 | 251 | QStringList execArguments = desktopData->exec().split(" ", QString::SkipEmptyParts); | ||
333 | 252 | DASSERT(execArguments.size() > 0); | ||
334 | 253 | QString exec(execArguments[0]); | ||
335 | 254 | const int kSize = execArguments.size(); | ||
336 | 255 | for (int i = kSize - 1; i > 0; i--) { | ||
337 | 256 | if ((execArguments[i].size() == 2) && (execArguments[i][0].toLatin1() == '%')) { | ||
338 | 257 | const char kChar = execArguments[i][1].toLatin1(); | ||
339 | 258 | if (kChar == 'F' || kChar == 'u' || kChar == 'U' || kChar == 'd' || kChar == 'D' | ||
340 | 259 | || kChar == 'n' || kChar == 'N' || kChar == 'i' || kChar == 'c' || kChar == 'k' | ||
341 | 260 | || kChar == 'v' || kChar == 'm') { | ||
342 | 261 | continue; | ||
343 | 262 | } | ||
377 | 263 | } | 222 | } |
378 | 264 | arguments.prepend(execArguments[i]); | ||
379 | 265 | } | ||
380 | 266 | arguments.append(QString("--desktop_file_hint=") + desktopData->file()); | ||
381 | 267 | if (flags.testFlag(ApplicationManager::ForceMainStage)) | ||
382 | 268 | arguments.append(QString("--stage_hint=main_stage")); | ||
383 | 269 | else if (desktopData->stageHint() == "SideStage") | ||
384 | 270 | arguments.append(QString("--stage_hint=side_stage")); | ||
385 | 271 | |||
386 | 272 | // Start process - set correct environment | ||
387 | 273 | setenv("QT_QPA_PLATFORM", "ubuntumirclient", 1); | ||
388 | 274 | |||
389 | 275 | bool result; | ||
390 | 276 | qint64 pid = 0; | ||
391 | 277 | QString path = "/"; | ||
392 | 278 | // respect Path from .desktop file | ||
393 | 279 | if (desktopData->path() != "") { | ||
394 | 280 | path = desktopData->path(); | ||
395 | 281 | } else { | ||
396 | 282 | struct passwd* passwd = getpwuid(getuid()); | ||
397 | 283 | if (passwd) | ||
398 | 284 | path = passwd->pw_dir; | ||
399 | 285 | } | ||
400 | 286 | DLOG("current working directory: '%s'", path.toLatin1().data()); | ||
401 | 287 | QProcess builder; | ||
402 | 288 | builder.setProcessChannelMode(QProcess::ForwardedChannels); | ||
403 | 289 | result = builder.startDetached(exec, arguments, path, &pid); | ||
404 | 290 | |||
405 | 291 | DLOG_IF(result == false, "process failed to start"); | ||
406 | 292 | if (result) { | ||
407 | 293 | #endif | ||
408 | 294 | DLOG("process started with pid %lld, adding '%s' to application lists", pid, desktopData->name().toLatin1().data()); | ||
409 | 295 | arguments.prepend(exec); | ||
410 | 296 | Application* application = new Application( | ||
411 | 297 | desktopData, pid, | ||
412 | 298 | (flags.testFlag(ApplicationManager::ForceMainStage) | ||
413 | 299 | || desktopData->stageHint() != "SideStage") | ||
414 | 300 | ? Application::MainStage : Application::SideStage, | ||
415 | 301 | Application::Starting, //FIXME(greyback): assuming running immediately | ||
416 | 302 | arguments, | ||
417 | 303 | m_taskController | ||
418 | 304 | ); | ||
419 | 305 | |||
420 | 306 | add(application); | 223 | add(application); |
421 | 307 | return application; | ||
422 | 308 | } else { | ||
423 | 309 | return NULL; | ||
424 | 310 | } | 224 | } |
425 | 311 | } | 225 | } |
426 | 312 | 226 | ||
427 | @@ -316,35 +230,62 @@ | |||
428 | 316 | 230 | ||
429 | 317 | Application *application = findApplication(appId); | 231 | Application *application = findApplication(appId); |
430 | 318 | 232 | ||
455 | 319 | if (application != NULL) { | 233 | if (!application) { |
456 | 320 | if (application == m_focusedApplication) { | 234 | DLOG("No such running application '%s'", qPrintable(appId)); |
457 | 321 | // TODO(greyback) What to do?? Focus next app, or unfocus everything?? | 235 | return false; |
458 | 322 | m_focusedApplication = NULL; | 236 | } |
459 | 323 | Q_EMIT focusedApplicationIdChanged(); | 237 | |
460 | 324 | } | 238 | if (application == m_focusedApplication) { |
461 | 325 | remove(application); | 239 | // TODO(greyback) What to do?? Focus next app, or unfocus everything?? |
462 | 326 | m_dbusWindowStack->WindowDestroyed(0, application->name()); | 240 | m_focusedApplication = NULL; |
463 | 327 | 241 | Q_EMIT focusedApplicationIdChanged(); | |
464 | 328 | // Start process. | 242 | } |
465 | 329 | bool result; | 243 | |
466 | 330 | 244 | remove(application); | |
467 | 331 | #if USE_UPSTART | 245 | m_dbusWindowStack->WindowDestroyed(0, application->name()); |
468 | 332 | result = QProcess::startDetached("stop", QStringList() << "application" | 246 | |
469 | 333 | << ("APP_ID=" + application->appId())); | 247 | bool result = m_taskController->stop(application->appId()); |
470 | 334 | DLOG_IF(result == false, "process 'stop application' failed to execute"); | 248 | |
471 | 335 | #else | 249 | LOG_IF(result == false, "FAILED to ask Upstart to stop application '%s'", qPrintable(application->appId())); |
472 | 336 | result = QProcess::startDetached("/bin/kill", QStringList() << "-9" << QString::number(application->m_pid)); | 250 | delete application; |
449 | 337 | DLOG_IF(result == false, "process '/bin/kill -9 %lld' failed to execute", application->m_pid); | ||
450 | 338 | #endif | ||
451 | 339 | LOG_IF(!result, "ApplicationManager: Unable to stop process '%s'", | ||
452 | 340 | application->name().toLatin1().constData()); | ||
453 | 341 | delete application; | ||
454 | 342 | } | ||
473 | 343 | 251 | ||
474 | 344 | // FIXME(dandrader): lying here. The operation is async. So we will only know whether | 252 | // FIXME(dandrader): lying here. The operation is async. So we will only know whether |
475 | 345 | // the focusing was successful once the server replies. Maybe the API in unity-api should | 253 | // the focusing was successful once the server replies. Maybe the API in unity-api should |
476 | 346 | // reflect that? | 254 | // reflect that? |
478 | 347 | return true; | 255 | return result; |
479 | 256 | } | ||
480 | 257 | |||
481 | 258 | void ApplicationManager::onProcessStopped(const QString &appId) | ||
482 | 259 | { | ||
483 | 260 | Application *application = findApplication(appId); | ||
484 | 261 | |||
485 | 262 | // if shell did not stop the application, but upstart says it died, we assume the process has been | ||
486 | 263 | // killed, so it can be respawned later. Only exception is if that application is focused or running | ||
487 | 264 | // as then it most likely crashed. Update this logic when upstart gives some failure info. | ||
488 | 265 | if (application) { | ||
489 | 266 | bool removeApplication = false; | ||
490 | 267 | |||
491 | 268 | if (application == m_focusedApplication) { | ||
492 | 269 | // Very bad case where focused application dies. Remove from list. Should give error message | ||
493 | 270 | m_focusedApplication = nullptr; | ||
494 | 271 | Q_EMIT focusedApplicationIdChanged(); | ||
495 | 272 | removeApplication = true; | ||
496 | 273 | } | ||
497 | 274 | |||
498 | 275 | if (application->state() == Application::Running) { | ||
499 | 276 | // Application probably crashed, else OOM killer struck. Either way state wasn't saved | ||
500 | 277 | // so just remove application | ||
501 | 278 | removeApplication = true; | ||
502 | 279 | } else if (application->state() == Application::Suspended) { | ||
503 | 280 | application->setState(Application::Stopped); | ||
504 | 281 | } | ||
505 | 282 | |||
506 | 283 | if (removeApplication) { | ||
507 | 284 | remove(application); | ||
508 | 285 | m_dbusWindowStack->WindowDestroyed(0, application->name()); | ||
509 | 286 | delete application; | ||
510 | 287 | } | ||
511 | 288 | } | ||
512 | 348 | } | 289 | } |
513 | 349 | 290 | ||
514 | 350 | /************************************* Mir-side methods *************************************/ | 291 | /************************************* Mir-side methods *************************************/ |
515 | @@ -354,12 +295,14 @@ | |||
516 | 354 | authorized = false; //to be proven wrong | 295 | authorized = false; //to be proven wrong |
517 | 355 | 296 | ||
518 | 356 | DLOG("ApplicationManager::authorizeSession (this=%p, pid=%lld)", this, pid); | 297 | DLOG("ApplicationManager::authorizeSession (this=%p, pid=%lld)", this, pid); |
525 | 357 | Application* application = findApplicationWithPid(pid); | 298 | |
526 | 358 | if (application) { | 299 | for (Application *app : m_applications) { |
527 | 359 | QString name = application->appId() + QString::number(qrand()); | 300 | if (app->state() == Application::Starting |
528 | 360 | application->setSessionName(name); | 301 | && m_taskController->appIdHasProcessId(app->appId(), pid)) { |
529 | 361 | authorized = true; | 302 | app->setPid(pid); |
530 | 362 | return; | 303 | authorized = true; |
531 | 304 | return; | ||
532 | 305 | } | ||
533 | 363 | } | 306 | } |
534 | 364 | 307 | ||
535 | 365 | /* | 308 | /* |
536 | @@ -403,13 +346,13 @@ | |||
537 | 403 | 346 | ||
538 | 404 | // some naughty applications use a script to launch the actual application. Check for the | 347 | // some naughty applications use a script to launch the actual application. Check for the |
539 | 405 | // case where shell actually launched the script. | 348 | // case where shell actually launched the script. |
541 | 406 | application = findApplication(desktopData->appId()); | 349 | Application *application = findApplication(desktopData->appId()); |
542 | 407 | if (application && application->state() == Application::Starting) { | 350 | if (application && application->state() == Application::Starting) { |
543 | 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", |
544 | 409 | pid, application->appId().toLatin1().data()); | 352 | pid, application->appId().toLatin1().data()); |
545 | 410 | delete desktopData; | 353 | delete desktopData; |
548 | 411 | QString name = application->appId() + QString::number(qrand()); | 354 | application->setSessionName(application->appId()); |
549 | 412 | application->setSessionName(name); | 355 | application->setPid(pid); |
550 | 413 | authorized = true; | 356 | authorized = true; |
551 | 414 | return; | 357 | return; |
552 | 415 | } | 358 | } |
553 | @@ -428,7 +371,9 @@ | |||
554 | 428 | 371 | ||
555 | 429 | QString argStr(command.data()); | 372 | QString argStr(command.data()); |
556 | 430 | QStringList arguments(argStr.split(' ')); | 373 | QStringList arguments(argStr.split(' ')); |
558 | 431 | application = new Application(desktopData, pid, stage, Application::Starting, arguments, m_taskController); | 374 | application = new Application(desktopData, Application::Starting, arguments, this); |
559 | 375 | application->setPid(pid); | ||
560 | 376 | application->setStage(stage); | ||
561 | 432 | add(application); | 377 | add(application); |
562 | 433 | authorized = true; | 378 | authorized = true; |
563 | 434 | } | 379 | } |
564 | @@ -538,8 +483,11 @@ | |||
565 | 538 | return nullptr; | 483 | return nullptr; |
566 | 539 | } | 484 | } |
567 | 540 | 485 | ||
569 | 541 | Application* ApplicationManager::findApplicationWithPid(const int pid) | 486 | Application* ApplicationManager::findApplicationWithPid(const qint64 pid) |
570 | 542 | { | 487 | { |
571 | 488 | if (pid <= 0) | ||
572 | 489 | return nullptr; | ||
573 | 490 | |||
574 | 543 | for (Application *app : m_applications) { | 491 | for (Application *app : m_applications) { |
575 | 544 | if (app->m_pid == pid) { | 492 | if (app->m_pid == pid) { |
576 | 545 | return app; | 493 | return app; |
577 | 546 | 494 | ||
578 | === modified file 'src/modules/Unity/Application/application_manager.h' | |||
579 | --- src/modules/Unity/Application/application_manager.h 2013-09-11 13:20:08 +0000 | |||
580 | +++ src/modules/Unity/Application/application_manager.h 2013-09-27 15:13:07 +0000 | |||
581 | @@ -27,12 +27,13 @@ | |||
582 | 27 | // Unity API | 27 | // Unity API |
583 | 28 | #include <unity/shell/application/ApplicationManagerInterface.h> | 28 | #include <unity/shell/application/ApplicationManagerInterface.h> |
584 | 29 | 29 | ||
586 | 30 | #include "taskcontroller.h" | 30 | // local |
587 | 31 | #include "application.h" | ||
588 | 31 | 32 | ||
589 | 32 | class Application; | ||
590 | 33 | class ShellServerConfiguration; | 33 | class ShellServerConfiguration; |
591 | 34 | class DBusWindowStack; | 34 | class DBusWindowStack; |
592 | 35 | class MirSurfaceManager; | 35 | class MirSurfaceManager; |
593 | 36 | class TaskController; | ||
594 | 36 | 37 | ||
595 | 37 | namespace mir { | 38 | namespace mir { |
596 | 38 | namespace shell { | 39 | namespace shell { |
597 | @@ -82,7 +83,7 @@ | |||
598 | 82 | Q_INVOKABLE void move(int from, int to); | 83 | Q_INVOKABLE void move(int from, int to); |
599 | 83 | 84 | ||
600 | 84 | const QList<Application*> &list() const { return m_applications; } | 85 | const QList<Application*> &list() const { return m_applications; } |
602 | 85 | Application* findApplicationWithPid(const int pid); | 86 | Application* findApplicationWithPid(const qint64 pid); |
603 | 86 | 87 | ||
604 | 87 | public Q_SLOTS: | 88 | public Q_SLOTS: |
605 | 88 | void authorizeSession(const quint64 pid, bool &authorized); | 89 | void authorizeSession(const quint64 pid, bool &authorized); |
606 | @@ -94,6 +95,9 @@ | |||
607 | 94 | 95 | ||
608 | 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&); |
609 | 96 | 97 | ||
610 | 98 | void onProcessStarted(const QString& appId); | ||
611 | 99 | void onProcessStopped(const QString& appId); | ||
612 | 100 | |||
613 | 97 | Q_SIGNALS: | 101 | Q_SIGNALS: |
614 | 98 | void focusRequested(FavoriteApplication favoriteApplication); | 102 | void focusRequested(FavoriteApplication favoriteApplication); |
615 | 99 | 103 | ||
616 | @@ -101,7 +105,6 @@ | |||
617 | 101 | void setFocused(Application *application); | 105 | void setFocused(Application *application); |
618 | 102 | void add(Application *application); | 106 | void add(Application *application); |
619 | 103 | void remove(Application* application); | 107 | void remove(Application* application); |
620 | 104 | Application* respawnApplication(Application* application); | ||
621 | 105 | Application* findApplicationWithSession(const std::shared_ptr<mir::shell::Session> &session); | 108 | Application* findApplicationWithSession(const std::shared_ptr<mir::shell::Session> &session); |
622 | 106 | Application* findApplicationWithSession(const mir::shell::Session *session); | 109 | Application* findApplicationWithSession(const mir::shell::Session *session); |
623 | 107 | Application* findLastExecutedApplication(); | 110 | Application* findLastExecutedApplication(); |
624 | @@ -111,11 +114,10 @@ | |||
625 | 111 | Application* m_focusedApplication; // remove as Mir has API for this | 114 | Application* m_focusedApplication; // remove as Mir has API for this |
626 | 112 | ShellServerConfiguration* m_mirServer; | 115 | ShellServerConfiguration* m_mirServer; |
627 | 113 | DBusWindowStack* m_dbusWindowStack; | 116 | DBusWindowStack* m_dbusWindowStack; |
629 | 114 | TaskController* m_taskController; | 117 | QScopedPointer<TaskController> m_taskController; |
630 | 115 | static ApplicationManager* the_application_manager; | 118 | static ApplicationManager* the_application_manager; |
631 | 116 | 119 | ||
632 | 117 | friend class DBusWindowStack; | 120 | friend class DBusWindowStack; |
633 | 118 | friend class TaskController; | ||
634 | 119 | friend class MirSurfaceManager; | 121 | friend class MirSurfaceManager; |
635 | 120 | }; | 122 | }; |
636 | 121 | 123 | ||
637 | 122 | 124 | ||
638 | === modified file 'src/modules/Unity/Application/applicationscreenshotprovider.cpp' | |||
639 | --- src/modules/Unity/Application/applicationscreenshotprovider.cpp 2013-09-24 18:48:11 +0000 | |||
640 | +++ src/modules/Unity/Application/applicationscreenshotprovider.cpp 2013-09-27 15:13:07 +0000 | |||
641 | @@ -67,11 +67,18 @@ | |||
642 | 67 | return QImage(); | 67 | return QImage(); |
643 | 68 | } | 68 | } |
644 | 69 | 69 | ||
645 | 70 | // TODO: if app not ready, return an app-provided splash image. If app has been stopped with saved state | ||
646 | 71 | // return the screenshot that was saved to disk. | ||
647 | 70 | if (!app->session() || !app->session()->default_surface()) { | 72 | if (!app->session() || !app->session()->default_surface()) { |
648 | 71 | LOG("ApplicationScreenshotProvider - app session not found - taking screenshot too early"); | 73 | LOG("ApplicationScreenshotProvider - app session not found - taking screenshot too early"); |
649 | 72 | return QImage(); | 74 | return QImage(); |
650 | 73 | } | 75 | } |
651 | 74 | 76 | ||
652 | 77 | if (app->state() == Application::Stopped || app->state() == Application::Starting) { | ||
653 | 78 | LOG("ApplicationScreenshotProvider - unable to take screenshot of stopped/not-ready applications"); | ||
654 | 79 | return QImage(); | ||
655 | 80 | } | ||
656 | 81 | |||
657 | 75 | /* Workaround for bug https://bugs.launchpad.net/qtubuntu/+bug/1209216 - currently all qtubuntu | 82 | /* Workaround for bug https://bugs.launchpad.net/qtubuntu/+bug/1209216 - currently all qtubuntu |
658 | 76 | * based applications are allocated a fullscreen Mir surface, but draw in a geometry excluding | 83 | * based applications are allocated a fullscreen Mir surface, but draw in a geometry excluding |
659 | 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. |
660 | 78 | 85 | ||
661 | === modified file 'src/modules/Unity/Application/desktopfilereader.cpp' | |||
662 | --- src/modules/Unity/Application/desktopfilereader.cpp 2013-09-17 17:52:54 +0000 | |||
663 | +++ src/modules/Unity/Application/desktopfilereader.cpp 2013-09-27 15:13:07 +0000 | |||
664 | @@ -98,7 +98,8 @@ | |||
665 | 98 | const unsigned int kAllEntriesMask = | 98 | const unsigned int kAllEntriesMask = |
666 | 99 | (1 << DesktopFileReader::kNameIndex) | (1 << DesktopFileReader::kCommentIndex) | 99 | (1 << DesktopFileReader::kNameIndex) | (1 << DesktopFileReader::kCommentIndex) |
667 | 100 | | (1 << DesktopFileReader::kIconIndex) | (1 << DesktopFileReader::kExecIndex) | 100 | | (1 << DesktopFileReader::kIconIndex) | (1 << DesktopFileReader::kExecIndex) |
669 | 101 | | (1 << DesktopFileReader::kPathIndex) | (1 << DesktopFileReader::kStageHintIndex); const unsigned int kMandatoryEntriesMask = | 101 | | (1 << DesktopFileReader::kPathIndex) | (1 << DesktopFileReader::kStageHintIndex); |
670 | 102 | const unsigned int kMandatoryEntriesMask = | ||
671 | 102 | (1 << DesktopFileReader::kNameIndex) | (1 << DesktopFileReader::kIconIndex) | 103 | (1 << DesktopFileReader::kNameIndex) | (1 << DesktopFileReader::kIconIndex) |
672 | 103 | | (1 << DesktopFileReader::kExecIndex); | 104 | | (1 << DesktopFileReader::kExecIndex); |
673 | 104 | const int kEntriesCount = ARRAY_SIZE(kEntryNames); | 105 | const int kEntriesCount = ARRAY_SIZE(kEntryNames); |
674 | 105 | 106 | ||
675 | === modified file 'src/modules/Unity/Application/taskcontroller.cpp' | |||
676 | --- src/modules/Unity/Application/taskcontroller.cpp 2013-08-27 13:31:20 +0000 | |||
677 | +++ src/modules/Unity/Application/taskcontroller.cpp 2013-09-27 15:13:07 +0000 | |||
678 | @@ -16,42 +16,116 @@ | |||
679 | 16 | * Authored by: Ricardo Mendoza <ricardo.mendoza@canonical.com> | 16 | * Authored by: Ricardo Mendoza <ricardo.mendoza@canonical.com> |
680 | 17 | */ | 17 | */ |
681 | 18 | 18 | ||
683 | 19 | #include "logging.h" | 19 | // local |
684 | 20 | #include "taskcontroller.h" | 20 | #include "taskcontroller.h" |
686 | 21 | #include "application_manager.h" | 21 | |
687 | 22 | // unity-mir | ||
688 | 23 | #include <logging.h> | ||
689 | 24 | |||
690 | 25 | // Qt | ||
691 | 26 | #include <QStringList> | ||
692 | 27 | |||
693 | 28 | // glib | ||
694 | 29 | #include <glib.h> | ||
695 | 30 | |||
696 | 31 | // std | ||
697 | 22 | #include <signal.h> | 32 | #include <signal.h> |
698 | 23 | #include <unistd.h> | 33 | #include <unistd.h> |
699 | 24 | 34 | ||
701 | 25 | TaskController::TaskController(ApplicationManager *parent) : | 35 | |
702 | 36 | TaskController* TaskController::m_theTaskController = nullptr; | ||
703 | 37 | |||
704 | 38 | TaskController* TaskController::singleton() | ||
705 | 39 | { | ||
706 | 40 | if (!m_theTaskController) { | ||
707 | 41 | m_theTaskController = new TaskController(); | ||
708 | 42 | } | ||
709 | 43 | return m_theTaskController; | ||
710 | 44 | } | ||
711 | 45 | |||
712 | 46 | TaskController::TaskController(QObject *parent) : | ||
713 | 26 | QObject(parent) | 47 | QObject(parent) |
714 | 27 | { | 48 | { |
716 | 28 | DLOG("TaskController::TaskController (this=%p)", this); | 49 | startCallback = [](const gchar * appId, gpointer userData) { |
717 | 50 | Q_UNUSED(userData) | ||
718 | 51 | Q_EMIT TaskController::singleton()->processStarted(QString(appId)); | ||
719 | 52 | }; | ||
720 | 53 | |||
721 | 54 | stopCallback = [](const gchar * appId, gpointer userData) { | ||
722 | 55 | Q_UNUSED(userData) | ||
723 | 56 | Q_EMIT TaskController::singleton()->processStopped(QString(appId)); | ||
724 | 57 | }; | ||
725 | 58 | |||
726 | 59 | upstart_app_launch_observer_add_app_start(startCallback, nullptr); | ||
727 | 60 | upstart_app_launch_observer_add_app_stop(stopCallback, nullptr); | ||
728 | 29 | } | 61 | } |
729 | 30 | 62 | ||
730 | 31 | TaskController::~TaskController() | 63 | TaskController::~TaskController() |
731 | 32 | { | 64 | { |
755 | 33 | DLOG("TaskController::~TaskController (this=%p)", this); | 65 | upstart_app_launch_observer_delete_app_start(startCallback, nullptr); |
756 | 34 | } | 66 | upstart_app_launch_observer_delete_app_stop(stopCallback, nullptr); |
757 | 35 | 67 | } | |
758 | 36 | void TaskController::do_suspend(Application* application) | 68 | |
759 | 37 | { | 69 | bool TaskController::start(const QString& appId, const QStringList& arguments) |
760 | 38 | DLOG("TaskController::do_suspend (this=%p, application=%p)", this, application); | 70 | { |
761 | 39 | if (application->state() == Application::Suspended) | 71 | DLOG("TaskController::start appId='%s'", qPrintable(appId)); |
762 | 40 | kill(application->pid(), SIGSTOP); | 72 | gchar ** upstartArgs = nullptr; |
763 | 41 | } | 73 | bool result = false; |
764 | 42 | 74 | ||
765 | 43 | void TaskController::do_resume(Application* application) | 75 | // Convert arguments QStringList into format suitable for upstart-app-launch |
766 | 44 | { | 76 | upstartArgs = g_new0(gchar *, arguments.length()); |
767 | 45 | DLOG("TaskController::do_resume (this=%p, application=%p)", this, application); | 77 | |
768 | 46 | if (application->state() == Application::Suspended) | 78 | for (int i=0; i<arguments.length(); i++) { |
769 | 47 | kill(application->pid(), SIGCONT); | 79 | upstartArgs[i] = arguments.at(i).toLatin1().data(); |
770 | 48 | } | 80 | } |
771 | 49 | 81 | ||
772 | 50 | void TaskController::do_respawn(Application* application) | 82 | result = upstart_app_launch_start_application(appId.toLatin1().constData(), |
773 | 51 | { | 83 | static_cast<const gchar * const *>(upstartArgs)); |
774 | 52 | DLOG("TaskController::do_respwn (this=%p, application=%p)", this, application); | 84 | g_free(upstartArgs); |
775 | 53 | if (application->state() == Application::Stopped) { | 85 | |
776 | 54 | ApplicationManager *appMgr = static_cast<ApplicationManager*>(parent()); | 86 | DLOG_IF(!result, "TaskController::startApplication appId='%s' FAILED", qPrintable(appId)); |
777 | 55 | appMgr->respawnApplication(application); | 87 | return result; |
778 | 88 | } | ||
779 | 89 | |||
780 | 90 | bool TaskController::stop(const QString& appId) | ||
781 | 91 | { | ||
782 | 92 | DLOG("TaskController::stop appId='%s'", qPrintable(appId)); | ||
783 | 93 | bool result = false; | ||
784 | 94 | |||
785 | 95 | result = upstart_app_launch_stop_application(appId.toLatin1().constData()); | ||
786 | 96 | |||
787 | 97 | DLOG_IF(!result, "TaskController::stopApplication appId='%s' FAILED", qPrintable(appId)); | ||
788 | 98 | return result; | ||
789 | 99 | } | ||
790 | 100 | |||
791 | 101 | bool TaskController::appIdHasProcessId(const QString& appId, const quint64 pid) | ||
792 | 102 | { | ||
793 | 103 | DLOG("TaskController::isApplicationPid appId='%s', pid=%lld", qPrintable(appId), pid); | ||
794 | 104 | return upstart_app_launch_pid_in_app_id(pid, appId.toLatin1().constData()); | ||
795 | 105 | } | ||
796 | 106 | |||
797 | 107 | bool TaskController::suspend(const QString& appId) | ||
798 | 108 | { | ||
799 | 109 | DLOG("TaskController::suspend (this=%p, application=%p)", this, qPrintable(appId)); | ||
800 | 110 | pid_t pid = upstart_app_launch_get_primary_pid(appId.toLatin1().constData()); | ||
801 | 111 | |||
802 | 112 | if (pid) { | ||
803 | 113 | kill(pid, SIGSTOP); | ||
804 | 114 | return true; | ||
805 | 115 | } else { | ||
806 | 116 | return false; | ||
807 | 117 | } | ||
808 | 118 | } | ||
809 | 119 | |||
810 | 120 | bool TaskController::resume(const QString& appId) | ||
811 | 121 | { | ||
812 | 122 | DLOG("TaskController::resume (this=%p, application=%p)", this, qPrintable(appId)); | ||
813 | 123 | pid_t pid = upstart_app_launch_get_primary_pid(appId.toLatin1().constData()); | ||
814 | 124 | |||
815 | 125 | if (pid) { | ||
816 | 126 | kill(pid, SIGCONT); | ||
817 | 127 | return true; | ||
818 | 128 | } else { | ||
819 | 129 | return false; | ||
820 | 56 | } | 130 | } |
821 | 57 | } | 131 | } |
822 | 58 | 132 | ||
823 | === modified file 'src/modules/Unity/Application/taskcontroller.h' | |||
824 | --- src/modules/Unity/Application/taskcontroller.h 2013-08-27 13:31:20 +0000 | |||
825 | +++ src/modules/Unity/Application/taskcontroller.h 2013-09-27 15:13:07 +0000 | |||
826 | @@ -23,17 +23,35 @@ | |||
827 | 23 | 23 | ||
828 | 24 | #include "application.h" | 24 | #include "application.h" |
829 | 25 | 25 | ||
831 | 26 | class ApplicationManager; | 26 | // upstart |
832 | 27 | extern "C" { | ||
833 | 28 | #include "upstart-app-launch.h" | ||
834 | 29 | } | ||
835 | 30 | |||
836 | 27 | class TaskController : public QObject | 31 | class TaskController : public QObject |
837 | 28 | { | 32 | { |
838 | 29 | Q_OBJECT | 33 | Q_OBJECT |
839 | 30 | public: | 34 | public: |
841 | 31 | explicit TaskController(ApplicationManager* parent); | 35 | static TaskController* singleton(); |
842 | 32 | ~TaskController(); | 36 | ~TaskController(); |
843 | 33 | 37 | ||
847 | 34 | void do_resume(Application* application); | 38 | bool start(const QString& appId, const QStringList& args); |
848 | 35 | void do_suspend(Application* application); | 39 | bool stop(const QString& appId); |
849 | 36 | void do_respawn(Application* application); | 40 | |
850 | 41 | bool suspend(const QString& appId); | ||
851 | 42 | bool resume(const QString& appId); | ||
852 | 43 | |||
853 | 44 | bool appIdHasProcessId(const QString& appId, const quint64 pid); | ||
854 | 45 | |||
855 | 46 | Q_SIGNALS: | ||
856 | 47 | void processStarted(const QString& appId); | ||
857 | 48 | void processStopped(const QString& appId); | ||
858 | 49 | |||
859 | 50 | private: | ||
860 | 51 | TaskController(QObject *parent = 0); | ||
861 | 52 | |||
862 | 53 | static TaskController* m_theTaskController; | ||
863 | 54 | upstart_app_launch_app_observer_t startCallback, stopCallback; | ||
864 | 37 | }; | 55 | }; |
865 | 38 | 56 | ||
866 | 39 | #endif // TASKCONTROLLER_H | 57 | #endif // TASKCONTROLLER_H |
PASSED: Continuous integration, rev:96 jenkins. qa.ubuntu. com/job/ unity-mir- ci/74/ jenkins. qa.ubuntu. com/job/ unity-mir- saucy-amd64- ci/16 jenkins. qa.ubuntu. com/job/ unity-mir- saucy-armhf- ci/74 jenkins. qa.ubuntu. com/job/ unity-mir- saucy-armhf- ci/74/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ unity-mir- saucy-i386- ci/74
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins: 8080/job/ unity-mir- ci/74/rebuild
http://