Merge lp:~mzanetti/unity-mir/appimage-sourcesize-rotate-n7 into lp:unity-mir

Proposed by Michael Zanetti
Status: Superseded
Proposed branch: lp:~mzanetti/unity-mir/appimage-sourcesize-rotate-n7
Merge into: lp:unity-mir
Diff against target: 1970 lines (+1001/-273)
25 files modified
CMakeLists.txt (+1/-0)
cmake/LinuxCrossCompile.cmake (+39/-0)
cross-compile-chroot.sh (+85/-0)
debian/control (+1/-0)
scripts/fix-qt-cmake.sh (+10/-0)
scripts/setup-partial-armhf-chroot.sh (+67/-0)
src/modules/Unity/Application/ApplicationImage.qml (+12/-5)
src/modules/Unity/Application/CMakeLists.txt (+5/-6)
src/modules/Unity/Application/application.cpp (+23/-1)
src/modules/Unity/Application/application.h (+6/-0)
src/modules/Unity/Application/application_manager.cpp (+254/-171)
src/modules/Unity/Application/application_manager.h (+25/-4)
src/modules/Unity/Application/applicationscreenshotprovider.cpp (+0/-1)
src/modules/Unity/Application/applicationscreenshotprovider.h (+0/-1)
src/modules/Unity/Application/inputarea.cpp (+32/-6)
src/modules/Unity/Application/proc_info.cpp (+52/-0)
src/modules/Unity/Application/proc_info.h (+48/-0)
src/unity-mir/CMakeLists.txt (+3/-5)
tests/CMakeLists.txt (+1/-0)
tests/application_manager_test.cpp (+186/-64)
tests/auto/modules/Unity/Application/CMakeLists.txt (+3/-3)
tests/auto/modules/Unity/Application/main.cpp (+5/-6)
tests/mock_focus_controller.h (+37/-0)
tests/mock_proc_info.h (+37/-0)
tests/mock_session.h (+69/-0)
To merge this branch: bzr merge lp:~mzanetti/unity-mir/appimage-sourcesize-rotate-n7
Reviewer Review Type Date Requested Status
Mir development team Pending
Review via email: mp+207701@code.launchpad.net

This proposal has been superseded by a proposal from 2014-02-21.

Commit message

export AppImage's sourceSize

To post a comment you must log in.
180. By Michael Zanetti

merge upstream

181. By Michael Zanetti

keep the aspect ratio also when cropping

Unmerged revisions

181. By Michael Zanetti

keep the aspect ratio also when cropping

180. By Michael Zanetti

merge upstream

179. By Michael Zanetti

export ApplicationImage's sourceSize

178. By Gerry Boland

Fix for OSK on Manta

177. By Gerry Boland

Hacky fix for InputArea positioning with a rotated shell

176. By Gerry Boland

Fix OSK positioning on manta & flo

175. By Gerry Boland

Merge trunk

174. By Daniel d'Andrada

ApplicationImage - rotate image to compensate for shell rotation

173. By Daniel d'Andrada

Take app window placement information from given QQuickItems

Benefits:
 - no more hardcoded values (like panel height)
 - supports unity shell in any rotation

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2014-02-14 15:42:49 +0000
3+++ CMakeLists.txt 2014-02-21 16:48:46 +0000
4@@ -59,6 +59,7 @@
5 find_package(Qt5Core REQUIRED)
6 find_package(Qt5Quick REQUIRED)
7 find_package(Qt5DBus REQUIRED)
8+find_package(Boost 1.50 COMPONENTS system REQUIRED)
9
10 find_package(Protobuf REQUIRED)
11 if(PROTOBUF_PROTOC_EXECUTABLE STREQUAL "PROTOBUF_PROTOC_EXECUTABLE-NOTFOUND")
12
13=== added directory 'cmake'
14=== added file 'cmake/LinuxCrossCompile.cmake'
15--- cmake/LinuxCrossCompile.cmake 1970-01-01 00:00:00 +0000
16+++ cmake/LinuxCrossCompile.cmake 2014-02-21 16:48:46 +0000
17@@ -0,0 +1,39 @@
18+set(CMAKE_SYSTEM_NAME Linux)
19+set(CMAKE_SYSTEM_VERSION 1)
20+
21+set(UNITYMIR_CHROOT_DIR $ENV{UNITYMIR_CHROOT_DIR} CACHE STRING "directory containing partial chroot for unity-mir cross-compilation")
22+set(UNITYMIR_ARM_EABI "arm-linux-gnueabihf")
23+
24+set(CMAKE_C_COMPILER /usr/bin/${UNITYMIR_ARM_EABI}-gcc)
25+set(CMAKE_CXX_COMPILER /usr/bin/${UNITYMIR_ARM_EABI}-g++)
26+
27+# where to look to find dependencies in the target environment
28+set(CMAKE_FIND_ROOT_PATH "${UNITYMIR_CHROOT_DIR}")
29+
30+#treat the chroot's includes as system includes
31+include_directories(SYSTEM "${UNITYMIR_CHROOT_DIR}/usr/include" "${UNITYMIR_CHROOT_DIR}/usr/include/${UNITYMIR_ARM_EABI}")
32+
33+list(APPEND CMAKE_SYSTEM_INCLUDE_PATH "${UNITYMIR_CHROOT_DIR}/usr/include" "${UNITYMIR_CHROOT_DIR}/usr/include/${UNITYMIR_ARM_EABI}" )
34+
35+# Add the chroot libraries as system libraries
36+list(APPEND CMAKE_SYSTEM_LIBRARY_PATH
37+ "${UNITYMIR_CHROOT_DIR}/lib"
38+ "${UNITYMIR_CHROOT_DIR}/lib/${UNITYMIR_ARM_EABI}"
39+ "${UNITYMIR_CHROOT_DIR}/usr/lib"
40+ "${UNITYMIR_CHROOT_DIR}/usr/lib/${UNITYMIR_ARM_EABI}"
41+)
42+
43+set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE)
44+set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
45+set(CMAKE_EXECUTABLE_RUNTIME_C_FLAG "-Wl,-rpath-link,")
46+set(CMAKE_EXECUTABLE_RUNTIME_CXX_FLAG "-Wl,-rpath-link,")
47+set(CMAKE_INSTALL_RPATH "${UNITYMIR_CHROOT_DIR}/lib:${UNITYMIR_CHROOT_DIR}/lib/${UNITYMIR_ARM_EABI}:${UNITYMIR_CHROOT_DIR}/usr/lib:${UNITYMIR_CHROOT_DIR}/usr/lib/${UNITYMIR_ARM_EABI}:${UNITYMIR_CHROOT_DIR}/usr/lib/${UNITYMIR_ARM_EABI}/mesa-egl")
48+
49+set(ENV{PKG_CONFIG_PATH} "${UNITYMIR_CHROOT_DIR}/usr/lib/pkgconfig:${UNITYMIR_CHROOT_DIR}/usr/lib/${UNITYMIR_ARM_EABI}/pkgconfig")
50+set(ENV{PKG_CONFIG_SYSROOT_DIR} "${UNITYMIR_CHROOT_DIR}")
51+
52+#use only the cross compile system
53+set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
54+set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
55+set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
56+
57
58=== added file 'cross-compile-chroot.sh'
59--- cross-compile-chroot.sh 1970-01-01 00:00:00 +0000
60+++ cross-compile-chroot.sh 2014-02-21 16:48:46 +0000
61@@ -0,0 +1,85 @@
62+#!/bin/bash
63+# build script to compile unity-mir for armhf devices
64+
65+set -e
66+
67+usage() {
68+ echo "usage: $(basename $0) [-c] [-u]"
69+ echo "-c clean before building"
70+ echo "-u update partial chroot directory"
71+ echo "-h this message"
72+}
73+
74+clean_build_dir() {
75+ rm -rf ${1}
76+ mkdir ${1}
77+}
78+
79+BUILD_DIR=build-android-arm
80+NUM_JOBS=$(( $(grep -c ^processor /proc/cpuinfo) + 1 ))
81+_do_update_chroot=0
82+
83+while getopts "cuh" OPTNAME
84+do
85+ case $OPTNAME in
86+ c )
87+ clean_build_dir ${BUILD_DIR}
88+ ;;
89+ u )
90+ _do_update_chroot=1
91+ ;;
92+ h )
93+ usage
94+ exit 0
95+ ;;
96+ * )
97+ echo "invalid option specified"
98+ usage
99+ exit 1
100+ ;;
101+ esac
102+done
103+
104+
105+if [ "${UNITYMIR_CHROOT_DIR}" = "" ]; then
106+ export UNITYMIR_CHROOT_DIR=$(pwd)/partial-armhf-chroot
107+fi
108+
109+if [ ! -d ${UNITYMIR_CHROOT_DIR} ]; then
110+ echo "no partial chroot dir detected. attempting to create one"
111+ _do_update_chroot=1
112+fi
113+
114+if [ ! -d ${BUILD_DIR} ]; then
115+ mkdir ${BUILD_DIR}
116+fi
117+
118+if [ ${_do_update_chroot} -eq 1 ] ; then
119+ pushd scripts > /dev/null
120+ ./setup-partial-armhf-chroot.sh ${UNITYMIR_CHROOT_DIR}
121+ popd > /dev/null
122+ # force a clean build after an update, since CMake cache maybe out of date
123+ clean_build_dir ${BUILD_DIR}
124+fi
125+
126+echo "Using UNITYMIR_CHROOT_DIR: ${UNITYMIR_CHROOT_DIR}"
127+
128+pushd ${BUILD_DIR} > /dev/null
129+
130+ export CMAKE_PREFIX_PATH=${UNITYMIR_CHROOT_DIR}/usr/lib/arm-linux-gnueabihf/cmake
131+ export PKG_CONFIG_PATH="${UNITYMIR_CHROOT_DIR}/usr/lib/pkgconfig:${UNITYMIR_CHROOT_DIR}/usr/lib/arm-linux-gnueabihf/pkgconfig"
132+ export PKG_CONFIG_ALLOW_SYSTEM_CFLAGS=1
133+ export PKG_CONFIG_ALLOW_SYSTEM_LIBS=1
134+ export PKG_CONFIG_SYSROOT_DIR=${UNITYMIR_CHROOT_DIR}
135+ export PKG_CONFIG_EXECUTABLE=`which pkg-config`
136+ echo "Using PKG_CONFIG_PATH: $PKG_CONFIG_PATH"
137+ echo "Using PKG_CONFIG_EXECUTABLE: $PKG_CONFIG_EXECUTABLE"
138+
139+ # These are used to make cmake select the host machine QT MOC compiler in the AutoMocInfo module
140+ export DEB_HOST_MULTIARCH=arm-linux-gnueabihf
141+ export DEB_BUILD_MULTIARCH=$(gcc -dumpmachine)
142+
143+ cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/LinuxCrossCompile.cmake ..
144+ make -j${NUM_JOBS}
145+
146+popd ${BUILD_DIR} > /dev/null
147
148=== modified file 'debian/control'
149--- debian/control 2014-02-14 15:42:49 +0000
150+++ debian/control 2014-02-21 16:48:46 +0000
151@@ -6,6 +6,7 @@
152 cmake,
153 google-mock (>= 1.6.0+svn437),
154 pkg-config,
155+ libboost-dev,
156 libboost-system-dev,
157 libplatform-api1-dev,
158 libmirserver-dev (>= 0.1.5),
159
160=== added directory 'scripts'
161=== added file 'scripts/fix-qt-cmake.sh'
162--- scripts/fix-qt-cmake.sh 1970-01-01 00:00:00 +0000
163+++ scripts/fix-qt-cmake.sh 2014-02-21 16:48:46 +0000
164@@ -0,0 +1,10 @@
165+#!/bin/bash
166+
167+# $1 must contain path to QT cmake files
168+# Given that path, all hardcoded root paths are fixed up
169+for file in $(find ${1} -type f -name \*.cmake)
170+do
171+ echo "fixing $file"
172+ sed -i 's/NO_DEFAULT_PATH/ONLY_CMAKE_FIND_ROOT_PATH/g' $file
173+ sed -i 's/\/usr/${CMAKE_FIND_ROOT_PATH}\/usr/g' $file
174+done
175
176=== added file 'scripts/setup-partial-armhf-chroot.sh'
177--- scripts/setup-partial-armhf-chroot.sh 1970-01-01 00:00:00 +0000
178+++ scripts/setup-partial-armhf-chroot.sh 2014-02-21 16:48:46 +0000
179@@ -0,0 +1,67 @@
180+#!/bin/bash
181+
182+set -e
183+
184+if [ -z ${1} ]; then
185+ echo "please supply directory to create partial chroot in. (eg, ./setup-partial-armhf-chroot.sh mychroot-dir)"
186+ exit
187+fi
188+
189+echo "creating phablet-compatible armhf partial chroot for unity-mir compilation in directory ${1}"
190+
191+if [ ! -d ${1} ]; then
192+ mkdir -p ${1}
193+fi
194+
195+DEBCONTROL=$(pwd)/../debian/control
196+
197+pushd ${1} > /dev/null
198+
199+# Empty dpkg status file, so that ALL dependencies are listed with dpkg-checkbuilddeps
200+echo "" > status
201+
202+# Manual error code checking is needed for dpkg-checkbuilddeps
203+set +e
204+
205+# Parse dependencies from debian/control
206+# dpkg-checkbuilddeps returns 1 when dependencies are not met and the list is sent to stderr
207+builddeps=$(dpkg-checkbuilddeps -a armhf --admindir=. ${DEBCONTROL} 2>&1 )
208+if [ $? -ne 1 ] ; then
209+ echo "${builddeps}"
210+ exit 2
211+fi
212+
213+# now turn exit on error option
214+set -e
215+
216+# Sanitize dependencies list for submission to debootstrap
217+# build-essential is not needed as we are cross-compiling
218+builddeps=$(echo ${builddeps} | sed -e 's/dpkg-checkbuilddeps://g' -e 's/Unmet build dependencies://g' -e 's/build-essential:native//g')
219+builddeps=$(echo ${builddeps} | sed 's/([^)]*)//g')
220+# TODO: figure out why debootstrap is not finding libunity-api-dev package - it doesn't seem to be needed for cross-compilation - is it needed in debian/control?
221+builddeps=$(echo ${builddeps} | sed 's/libunity-api-dev//g')
222+builddeps=$(echo ${builddeps} | sed 's/ /,/g')
223+
224+fakeroot debootstrap --include=${builddeps} --arch=armhf --download-only --variant=buildd trusty .
225+
226+# Remove libc libraries that confuse the cross-compiler
227+rm var/cache/apt/archives/libc-dev*.deb
228+rm var/cache/apt/archives/libc6*.deb
229+
230+for deb in var/cache/apt/archives/* ; do
231+if [ ! -d ${deb} ] ; then
232+ echo "unpacking: ${deb}"
233+ dpkg -x ${deb} .
234+fi
235+done
236+
237+# Fix up symlinks which asssumed the usual root path
238+for broken_symlink in $(find . -name \*.so -type l -xtype l) ; do
239+ ln -sf $(pwd)$(readlink ${broken_symlink}) ${broken_symlink}
240+done
241+
242+popd > /dev/null
243+
244+# QT CMake files have hardcoded root paths - fix them up
245+./fix-qt-cmake.sh ${1}/usr/lib/arm-linux-gnueabihf/cmake/
246+
247
248=== modified file 'src/modules/Unity/Application/ApplicationImage.qml'
249--- src/modules/Unity/Application/ApplicationImage.qml 2013-10-01 17:45:26 +0000
250+++ src/modules/Unity/Application/ApplicationImage.qml 2014-02-21 16:48:46 +0000
251@@ -15,11 +15,14 @@
252 */
253
254 import QtQuick 2.0
255+import QtQuick.Window 2.0
256+import Ubuntu.Components 0.1
257
258 Item {
259 id: root
260 property var source: null
261 readonly property bool ready: source && (image.status == Image.Ready)
262+ property var sourceSize: orientationHelper.orientationAngle == 0 ? image.sourceSize : Qt.size(image.sourceSize.height, image.sourceSize.width)
263
264 function scheduleUpdate() {
265 image.source = "";
266@@ -34,10 +37,14 @@
267 scheduleUpdate();
268 }
269
270- Image {
271- id: image
272- anchors.fill: parent
273- source: (root.source) ? "image://screenshot/" + root.source.appId : ""
274- cache: false
275+ OrientationHelper {
276+ orientationAngle: Screen.angleBetween(Screen.primaryOrientation, nativeOrientation) // compensate for Shell rotation
277+ transitionEnabled: false
278+ Image {
279+ id: image
280+ anchors.fill: parent
281+ source: (root.source) ? "image://screenshot/" + root.source.appId : ""
282+ cache: false
283+ }
284 }
285 }
286
287=== modified file 'src/modules/Unity/Application/CMakeLists.txt'
288--- src/modules/Unity/Application/CMakeLists.txt 2014-01-27 11:29:44 +0000
289+++ src/modules/Unity/Application/CMakeLists.txt 2014-02-21 16:48:46 +0000
290@@ -31,6 +31,7 @@
291 inputfilterarea.cpp
292 processcontroller.h
293 processcontroller.cpp
294+ proc_info.cpp
295 shellinputarea.cpp
296 ubuntukeyboardinfo.cpp
297
298@@ -74,12 +75,10 @@
299 ${CMAKE_THREAD_LIBS_INIT}
300
301 ${GLIB_LDFLAGS}
302- ${UBUNTU_PLATFORM_API_LIBRARIES}
303- ${MIRCOMMON_LIBRARIES}
304- ${MIRSERVER_LIBRARIES}
305- ${PROCESS_CPP_LIBRARIES}
306- ${UBUNTU_PLATFORM_API_LIBRARIES}
307- ${UPSTART_APP_LAUNCH_LIBRARIES}
308+ ${UBUNTU_PLATFORM_API_LDFLAGS}
309+ ${MIRSERVER_LDFLAGS}
310+ ${PROCESS_CPP_LDFLAGS}
311+ ${UPSTART_APP_LAUNCH_LDFLAGS}
312
313 ubuntu_application_api_mirserver
314 )
315
316=== modified file 'src/modules/Unity/Application/application.cpp'
317--- src/modules/Unity/Application/application.cpp 2014-01-27 11:29:44 +0000
318+++ src/modules/Unity/Application/application.cpp 2014-02-21 16:48:46 +0000
319@@ -40,6 +40,7 @@
320 , m_state(state)
321 , m_focused(false)
322 , m_fullscreen(false)
323+ , m_visible(false)
324 , m_arguments(arguments)
325 , m_suspendTimer(new QTimer(this))
326 {
327@@ -52,7 +53,7 @@
328
329 Application::~Application()
330 {
331- DLOG("Application::~Application");
332+ DLOG("Application::~Application (this=%p)", this);
333 delete m_desktopData;
334 }
335
336@@ -125,6 +126,11 @@
337 return m_session;
338 }
339
340+bool Application::visible() const
341+{
342+ return m_visible;
343+}
344+
345 pid_t Application::pid() const
346 {
347 return m_pid;
348@@ -141,6 +147,7 @@
349
350 // TODO(greyback) what if called with new surface?
351 m_session = session;
352+ m_visible = true; // bit of an assumption that, but no other way to deduce an actual Surface has been created
353 }
354
355 void Application::setSessionName(const QString& name)
356@@ -216,6 +223,21 @@
357 }
358 }
359
360+void Application::setVisible(const bool visible)
361+{
362+ DLOG("Application::setVisible (this=%p, visible=%s)", this, visible ? "yes" : "no");
363+ // FIXME: this is bad, as should a MirSurface of this app exist, it won't be notified of the visiblity change.
364+ if (visible != m_visible) {
365+ if (visible) {
366+ m_session->show();
367+ } else {
368+ m_session->hide();
369+ }
370+ m_visible = visible;
371+ Q_EMIT visibleChanged();
372+ }
373+}
374+
375 void Application::suspend()
376 {
377 DLOG("Application::suspend (this=%p)", this);
378
379=== modified file 'src/modules/Unity/Application/application.h'
380--- src/modules/Unity/Application/application.h 2014-01-27 11:29:44 +0000
381+++ src/modules/Unity/Application/application.h 2014-02-21 16:48:46 +0000
382@@ -38,6 +38,7 @@
383 Q_PROPERTY(QString desktopFile READ desktopFile CONSTANT)
384 Q_PROPERTY(QString exec READ exec CONSTANT)
385 Q_PROPERTY(bool fullscreen READ fullscreen NOTIFY fullscreenChanged)
386+ Q_PROPERTY(bool visible READ visible WRITE setVisible NOTIFY visibleChanged)
387 Q_PROPERTY(Stage stage READ stage WRITE setStage NOTIFY stageChanged)
388
389 public:
390@@ -57,7 +58,10 @@
391 State state() const override;
392 bool focused() const override;
393
394+ bool visible() const;
395+
396 void setStage(Stage stage);
397+ void setVisible(const bool);
398
399 bool isValid() const;
400 QString desktopFile() const;
401@@ -74,6 +78,7 @@
402 Q_SIGNALS:
403 void fullscreenChanged();
404 void stageChanged(Stage stage);
405+ void visibleChanged();
406
407 private:
408 void setPid(pid_t pid);
409@@ -90,6 +95,7 @@
410 State m_state;
411 bool m_focused;
412 bool m_fullscreen;
413+ bool m_visible; // duplicating internal Mir data :(
414 std::shared_ptr<mir::shell::Session> m_session;
415 QString m_sessionName;
416 QStringList m_arguments;
417
418=== modified file 'src/modules/Unity/Application/application_manager.cpp'
419--- src/modules/Unity/Application/application_manager.cpp 2014-02-11 09:47:56 +0000
420+++ src/modules/Unity/Application/application_manager.cpp 2014-02-21 16:48:46 +0000
421@@ -16,6 +16,7 @@
422
423 // local
424 #include "application_manager.h"
425+#include "proc_info.h"
426 #include "application.h"
427 #include "desktopfilereader.h"
428 #include "dbuswindowstack.h"
429@@ -44,33 +45,134 @@
430 // Qt
431 #include <QCoreApplication>
432
433-// Default element sizes
434-#define SIDE_STAGE_WIDTH_GU 40
435-
436 namespace msh = mir::shell;
437
438 using namespace unity::shell::application;
439
440-ApplicationManager *ApplicationManager::the_application_manager = nullptr;
441+namespace
442+{
443+
444+QSize get_display_size(std::shared_ptr<mir::graphics::Display> const& display) {
445+ // Obtain display size
446+ mir::geometry::Rectangles view_area;
447+ display->for_each_display_buffer(
448+ [&view_area](mir::graphics::DisplayBuffer const& db)
449+ {
450+ view_area.add(db.view_area());
451+ });
452+
453+ return QSize(
454+ view_area.bounding_rectangle().size.width.as_uint32_t(),
455+ view_area.bounding_rectangle().size.height.as_uint32_t()
456+ );
457+}
458+
459+
460+void connectToSessionListener(ApplicationManager * manager, SessionListener * listener)
461+{
462+
463+ QObject::connect(listener, &SessionListener::sessionStarting,
464+ manager, &ApplicationManager::onSessionStarting);
465+ QObject::connect(listener, &SessionListener::sessionStopping,
466+ manager, &ApplicationManager::onSessionStopping);
467+ QObject::connect(listener, &SessionListener::sessionFocused,
468+ manager, &ApplicationManager::onSessionFocused, Qt::QueuedConnection);
469+ QObject::connect(listener, &SessionListener::sessionUnfocused,
470+ manager, &ApplicationManager::onSessionUnfocused);
471+ QObject::connect(listener, &SessionListener::sessionCreatedSurface,
472+ manager, &ApplicationManager::onSessionCreatedSurface);
473+ QObject::connect(listener, &SessionListener::sessionStarting,
474+ manager, &ApplicationManager::onSessionStarting);
475+ QObject::connect(listener, &SessionListener::sessionStopping,
476+ manager, &ApplicationManager::onSessionStopping);
477+ QObject::connect(listener, &SessionListener::sessionFocused,
478+ manager, &ApplicationManager::onSessionFocused, Qt::QueuedConnection);
479+ QObject::connect(listener, &SessionListener::sessionUnfocused,
480+ manager, &ApplicationManager::onSessionUnfocused);
481+ QObject::connect(listener, &SessionListener::sessionCreatedSurface,
482+ manager, &ApplicationManager::onSessionCreatedSurface);
483+}
484+
485+void connectToSessionAuthorizer(ApplicationManager * manager, SessionAuthorizer * authorizer)
486+{
487+ QObject::connect(authorizer, &SessionAuthorizer::requestAuthorizationForSession,
488+ manager, &ApplicationManager::authorizeSession, Qt::BlockingQueuedConnection);
489+}
490+
491+
492+void connectToPlacementStrategy(ApplicationManager * manager, InitialSurfacePlacementStrategy * strategy)
493+{
494+ QObject::connect(strategy, &InitialSurfacePlacementStrategy::requestPlacementForSession,
495+ manager, &ApplicationManager::placeSession, Qt::DirectConnection);
496+
497+}
498+
499+void connectToTaskController(ApplicationManager * manager, TaskController * controller)
500+{
501+ QObject::connect(controller, &TaskController::processStartReport,
502+ manager, &ApplicationManager::onProcessStartReportReceived);
503+ QObject::connect(controller, &TaskController::processStopped,
504+ manager, &ApplicationManager::onProcessStopped);
505+ QObject::connect(controller, &TaskController::requestFocus,
506+ manager, &ApplicationManager::onFocusRequested);
507+ QObject::connect(controller, &TaskController::requestResume,
508+ manager, &ApplicationManager::onResumeRequested);
509+
510+}
511+}
512+
513+QSharedPointer<ApplicationManager> ApplicationManager::Factory::Factory::create()
514+{
515+ QMirServerApplication* mirServerApplication = dynamic_cast<QMirServerApplication*>(QCoreApplication::instance());
516+ if (mirServerApplication == NULL) {
517+ LOG("Need to use QMirServerApplication");
518+ QCoreApplication::quit();
519+ return QSharedPointer<ApplicationManager>(nullptr);
520+ }
521+
522+ ShellServerConfiguration * mirServer = mirServerApplication->server();
523+
524+ QSize displaySize{get_display_size(mirServer->the_display())};
525+
526+ QSharedPointer<upstart::ApplicationController> appController(new upstart::ApplicationController());
527+ QSharedPointer<TaskController> taskController(new TaskController(nullptr, appController));
528+ QSharedPointer<DesktopFileReader::Factory> fileReaderFactory(new DesktopFileReader::Factory());
529+ QSharedPointer<ProcInfo> procInfo(new ProcInfo());
530+ QSharedPointer<ApplicationManager> appManager(
531+ new ApplicationManager(
532+ taskController,
533+ fileReaderFactory,
534+ procInfo,
535+ mirServer->the_focus_controller(),
536+ displaySize
537+ )
538+ );
539+
540+
541+ connectToSessionListener(appManager.data(), mirServer->sessionListener());
542+ connectToSessionAuthorizer(appManager.data(), mirServer->sessionAuthorizer());
543+ connectToPlacementStrategy(appManager.data(), mirServer->placementStrategy());
544+ connectToTaskController(appManager.data(), taskController.data());
545+
546+ return appManager;
547+}
548
549 ApplicationManager* ApplicationManager::singleton()
550 {
551- if (!the_application_manager) {
552- the_application_manager = new ApplicationManager(
553- QSharedPointer<TaskController>(
554- new TaskController(
555- nullptr,
556- QSharedPointer<ApplicationController>(
557- new upstart::ApplicationController()))),
558- QSharedPointer<DesktopFileReader::Factory>(
559- new DesktopFileReader::Factory()));
560+ static QSharedPointer<ApplicationManager> instance;
561+ if (!instance) {
562+ Factory appFactory;
563+ instance = appFactory.create();
564 }
565- return the_application_manager;
566+ return instance.data();
567 }
568
569 ApplicationManager::ApplicationManager(
570 const QSharedPointer<TaskController>& taskController,
571 const QSharedPointer<DesktopFileReader::Factory>& desktopFileReaderFactory,
572+ const QSharedPointer<ProcInfo>& procInfo,
573+ const std::shared_ptr<mir::shell::FocusController> & controller,
574+ const QSize & displaySize,
575 QObject *parent)
576 : ApplicationManagerInterface(parent)
577 , m_focusedApplication(nullptr)
578@@ -79,63 +181,18 @@
579 , m_msApplicationToBeFocused(nullptr)
580 , m_ssApplicationToBeFocused(nullptr)
581 , m_lifecycleExceptions(QStringList() << "com.ubuntu.music")
582+ , m_focusController(controller)
583+ , m_dbusWindowStack(new DBusWindowStack(this))
584 , m_taskController(taskController)
585 , m_desktopFileReaderFactory(desktopFileReaderFactory)
586+ , m_procInfo(procInfo)
587 , m_gridUnitPx(8)
588 , m_fenceNext(false)
589+ , m_displaySize(displaySize)
590 , m_panelHeight(54)
591 {
592 DLOG("ApplicationManager::ApplicationManager (this=%p)", this);
593
594- QMirServerApplication* mirServerApplication = dynamic_cast<QMirServerApplication*>(QCoreApplication::instance());
595- if (mirServerApplication == NULL) {
596- LOG("Need to use QMirServerApplication");
597- QCoreApplication::quit();
598- return;
599- }
600- m_mirServer = mirServerApplication->server();
601-
602- QObject::connect(m_mirServer->sessionListener(), &SessionListener::sessionStarting,
603- this, &ApplicationManager::onSessionStarting);
604- QObject::connect(m_mirServer->sessionListener(), &SessionListener::sessionStopping,
605- this, &ApplicationManager::onSessionStopping);
606- QObject::connect(m_mirServer->sessionListener(), &SessionListener::sessionFocused,
607- this, &ApplicationManager::onSessionFocused, Qt::QueuedConnection);
608- QObject::connect(m_mirServer->sessionListener(), &SessionListener::sessionUnfocused,
609- this, &ApplicationManager::onSessionUnfocused);
610- QObject::connect(m_mirServer->sessionListener(), &SessionListener::sessionCreatedSurface,
611- this, &ApplicationManager::onSessionCreatedSurface);
612- QObject::connect(m_mirServer->sessionAuthorizer(), &SessionAuthorizer::requestAuthorizationForSession,
613- this, &ApplicationManager::authorizeSession, Qt::BlockingQueuedConnection);
614- QObject::connect(m_mirServer->placementStrategy(), &InitialSurfacePlacementStrategy::requestPlacementForSession,
615- this, &ApplicationManager::placeSession, Qt::DirectConnection);
616-
617- QObject::connect(m_taskController.data(), &TaskController::processStartReport,
618- this, &ApplicationManager::onProcessStartReportReceived);
619- QObject::connect(m_taskController.data(), &TaskController::processStopped,
620- this, &ApplicationManager::onProcessStopped);
621- QObject::connect(m_taskController.data(), &TaskController::requestFocus,
622- this, &ApplicationManager::onFocusRequested);
623- QObject::connect(m_taskController.data(), &TaskController::requestResume,
624- this, &ApplicationManager::onResumeRequested);
625-
626- m_dbusWindowStack = new DBusWindowStack(this);
627-
628- std::shared_ptr<mir::graphics::Display> mirDisplay = m_mirServer->the_display();
629-
630- // Obtain display size
631- mir::geometry::Rectangles view_area;
632- mirDisplay->for_each_display_buffer(
633- [&view_area](mir::graphics::DisplayBuffer const& db)
634- {
635- view_area.add(db.view_area());
636- });
637-
638- m_displaySize = QSize(
639- view_area.bounding_rectangle().size.width.as_uint32_t(),
640- view_area.bounding_rectangle().size.height.as_uint32_t()
641- );
642-
643 // Setup panel height
644 QByteArray gridUnitString = qgetenv("GRID_UNIT_PX");
645 if (!gridUnitString.isEmpty()) {
646@@ -143,12 +200,9 @@
647 int value = gridUnitString.toInt(&ok);
648 if (ok) {
649 m_gridUnitPx = value;
650+ m_panelHeight = (3 * m_gridUnitPx) + static_cast<int>(2 * qFloor(static_cast<float>(m_gridUnitPx) / 8));
651 }
652 }
653-
654- int densityPixelPx = qFloor( (float)m_gridUnitPx / 8 );
655-
656- m_panelHeight = 3 * m_gridUnitPx + 2 * densityPixelPx;
657 }
658
659 ApplicationManager::~ApplicationManager()
660@@ -156,11 +210,6 @@
661 DLOG("ApplicationManager::~ApplicationManager");
662 }
663
664-int ApplicationManager::panelHeight()
665-{
666- return m_panelHeight;
667-}
668-
669 int ApplicationManager::rowCount(const QModelIndex &parent) const
670 {
671 return !parent.isValid() ? m_applications.size() : 0;
672@@ -222,6 +271,7 @@
673 if (application == nullptr)
674 return;
675
676+ DLOG("ApplicationManager::suspend(this=%p, application(%p)->appId(%s) )",this, application, qPrintable(application->appId()));
677 // Present in exceptions list, return.
678 if (!m_lifecycleExceptions.filter(application->appId().section('_',0,0)).empty())
679 return;
680@@ -232,8 +282,8 @@
681
682 bool ApplicationManager::focusApplication(const QString &appId)
683 {
684- DLOG("ApplicationManager::focusApplication (this=%p, appId=%s)", this, qPrintable(appId));
685 Application *application = findApplication(appId);
686+ DLOG("ApplicationManager::focusApplication (this=%p, application=%p, appId=%s)", this, application, qPrintable(appId));
687
688 if (!application) {
689 DLOG("No such running application '%s'", qPrintable(appId));
690@@ -256,7 +306,7 @@
691 move(from, m_applications.length()-1);
692 } else {
693 if (application->session())
694- m_mirServer->the_focus_controller()->set_focus_to(application->session());
695+ m_focusController->set_focus_to(application->session());
696 }
697
698 // FIXME(dandrader): lying here. The operation is async. So we will only know whether
699@@ -275,7 +325,7 @@
700 // Clear both stages
701 m_msApplicationToBeFocused = nullptr;
702 m_ssApplicationToBeFocused = nullptr;
703- m_mirServer->the_focus_controller()->set_focus_to(NULL); //FIXME(greyback)
704+ m_focusController->set_focus_to(NULL); //FIXME(greyback)
705 }
706
707 Application* ApplicationManager::startApplication(const QString &appId,
708@@ -294,6 +344,16 @@
709 return nullptr;
710 }
711
712+ {
713+ Application * application = findApplication(appId);
714+ if (application)
715+ {
716+ DLOG("ApplicationManager::startApplication - application already "
717+ "exists: (this=%p, app=%p, appId=%s)",
718+ this, application, qPrintable(appId));
719+ }
720+ }
721+
722 Application* application = new Application(
723 m_taskController,
724 m_desktopFileReaderFactory->createInstanceForAppId(appId),
725@@ -320,7 +380,9 @@
726 this, qPrintable(appId), (failure) ? 'Y' : 'N');
727
728 if (failure) {
729- onProcessStopped(appId, true);
730+ DLOG("ApplicationManager::onProcessStartReportReceived handling failure:");
731+ stopStartingApplication(appId);
732+ return;
733 }
734
735 Application *application = findApplication(appId);
736@@ -338,30 +400,23 @@
737 add(application);
738 Q_EMIT focusRequested(appId);
739 }
740+ else {
741+ DLOG("ApplicationManager::onProcessStartReportReceived application already found: (app=%p, appId=%s)", application, qPrintable(appId));
742+ }
743 }
744
745 bool ApplicationManager::stopApplication(const QString &appId)
746 {
747- DLOG("ApplicationManager::stopApplication (this=%p, appId=%s)", this, qPrintable(appId));
748-
749 Application *application = findApplication(appId);
750+ DLOG("ApplicationManager::stopApplication (this=%p, application=%p, appId=%s)", this, application, qPrintable(appId));
751
752 if (!application) {
753 DLOG("No such running application '%s'", qPrintable(appId));
754 return false;
755 }
756
757- if (application == m_focusedApplication) {
758- // TODO(greyback) What to do?? Focus next app, or unfocus everything??
759- m_focusedApplication = NULL;
760- Q_EMIT focusedApplicationIdChanged();
761- }
762+ checkFocusOnRemovedApplication(application);
763
764- if (application == m_mainStageApplication)
765- m_mainStageApplication = nullptr;
766- if (application == m_sideStageApplication)
767- m_sideStageApplication = nullptr;
768-
769 remove(application);
770 m_dbusWindowStack->WindowDestroyed(0, application->appId());
771
772@@ -376,37 +431,28 @@
773 return result;
774 }
775
776+void ApplicationManager::stopStartingApplication(const QString &appId)
777+{
778+ Application *application = findApplication(appId);
779+
780+ if (application && application->state() == Application::Starting) {
781+ shutdownApplication(application);
782+ }
783+ else if (application) {
784+ DLOG("onProcessStartReportReceived failure - but application=%p, appId=%s is not in Starting state",application, qPrintable(appId));
785+ }
786+}
787+
788 void ApplicationManager::onProcessStopped(const QString &appId, const bool unexpected)
789 {
790 Application *application = findApplication(appId);
791+ DLOG("ApplicationManager::onProcessStopped (this=%p, application=%p, appId=%s)", this, application, qPrintable(appId));
792
793 // if shell did not stop the application, but upstart says it died, we assume the process has been
794 // killed, so it can be respawned later. Only exception is if that application is focused or running
795 // as then it most likely crashed. Update this logic when upstart gives some failure info.
796 if (application) {
797- bool removeApplication = false;
798-
799- if (application == m_focusedApplication) {
800- // Very bad case where focused application dies. Remove from list. Should give error message
801- m_focusedApplication = nullptr;
802- Q_EMIT focusedApplicationIdChanged();
803- removeApplication = true;
804- }
805-
806- if (application->state() == Application::Running || application->state() == Application::Starting) {
807- // Application probably crashed, else OOM killer struck. Either way state wasn't saved
808- // so just remove application
809- removeApplication = true;
810- } else if (application->state() == Application::Suspended) {
811- application->setState(Application::Stopped);
812- application->setSession(nullptr);
813- }
814-
815- if (removeApplication) {
816- remove(application);
817- m_dbusWindowStack->WindowDestroyed(0, application->appId());
818- delete application;
819- }
820+ shutdownApplication(application);
821 }
822
823 if (unexpected) {
824@@ -415,6 +461,27 @@
825 }
826 }
827
828+void ApplicationManager::shutdownApplication(Application* application)
829+{
830+ bool removeApplication = checkFocusOnRemovedApplication(application);
831+
832+ if (application->state() == Application::Running || application->state() == Application::Starting) {
833+ // Application probably crashed, else OOM killer struck. Either way state wasn't saved
834+ // so just remove application
835+ removeApplication = true;
836+ } else if (application->state() == Application::Suspended) {
837+ application->setState(Application::Stopped);
838+ application->setSession(nullptr);
839+ }
840+
841+ if (removeApplication) {
842+ remove(application);
843+ m_dbusWindowStack->WindowDestroyed(0, application->appId());
844+ delete application;
845+ }
846+
847+}
848+
849 void ApplicationManager::onFocusRequested(const QString& appId)
850 {
851 DLOG("ApplicationManager::onFocusRequested (this=%p, appId=%s)", this, qPrintable(appId));
852@@ -424,9 +491,9 @@
853
854 void ApplicationManager::onResumeRequested(const QString& appId)
855 {
856- DLOG("ApplicationManager::onResumeRequested (this=%p, appId=%s)", this, qPrintable(appId));
857-
858 Application *application = findApplication(appId);
859+ DLOG("ApplicationManager::onResumeRequested (this=%p, application=%p, appId=%s)", this, application, qPrintable(appId));
860+
861
862 if (!application) {
863 DLOG("ApplicationManager::onResumeRequested: No such running application '%s'", qPrintable(appId));
864@@ -452,6 +519,7 @@
865 if (app->state() == Application::Starting
866 && m_taskController->appIdHasProcessId(app->appId(), pid)) {
867 app->setPid(pid);
868+ DLOG("ApplicationManager::authorizeSession - connecting: application=%p and pid=%lld", app, pid);
869 authorized = true;
870 return;
871 }
872@@ -463,41 +531,35 @@
873 * notify shell it is starting an application and so shell should allow it. Also reads
874 * the --stage parameter to determine the desired stage
875 */
876- QFile cmdline(QString("/proc/%1/cmdline").arg(pid));
877- if (!cmdline.open(QIODevice::ReadOnly | QIODevice::Text)) {
878+ std::unique_ptr<ProcInfo::CommandLine> info = m_procInfo->command_line(pid);
879+ if (!info) {
880 DLOG("ApplicationManager REJECTED connection from app with pid %lld as unable to read process command", pid);
881 return;
882 }
883
884- QByteArray command = cmdline.readLine().replace('\0', ' ');
885-
886- // FIXME: special exception for the OSK - maliit-server - not very secure
887- if (command.startsWith("maliit-server") || command.startsWith("/usr/lib/arm-linux-gnueabihf/qt5/libexec/QtWebProcess")
888- || command.startsWith("/usr/bin/signon-ui")) {
889+ if (info->starts_with("maliit-server") || info->starts_with("/usr/lib/arm-linux-gnueabihf/qt5/libexec/QtWebProcess")
890+ || info->starts_with("/usr/bin/signon-ui")) {
891 authorized = true;
892 m_fenceNext = true;
893 return;
894 }
895
896- QString pattern = QRegularExpression::escape("--desktop_file_hint=") + "(\\S+)";
897- QRegularExpression regExp(pattern);
898- QRegularExpressionMatch regExpMatch = regExp.match(command);
899+ boost::optional<QString> desktopFileName{ info->get_parameter("--desktop_file_hint=") };
900
901- if (!regExpMatch.hasMatch()) {
902+ if (!desktopFileName) {
903 LOG("ApplicationManager REJECTED connection from app with pid %lld as no desktop_file_hint specified", pid);
904 return;
905 }
906
907- QString desktopFileName = regExpMatch.captured(1);
908- DLOG("Process supplied desktop_file_hint, loading '%s'", desktopFileName.toLatin1().data());
909+ DLOG("Process supplied desktop_file_hint, loading '%s'", desktopFileName.get().toLatin1().data());
910
911 // FIXME: right now we support --desktop_file_hint=appId for historical reasons. So let's try that in
912 // case we didn't get an existing .desktop file path
913 DesktopFileReader* desktopData;
914- if (QFileInfo(desktopFileName).exists()) {
915- desktopData = m_desktopFileReaderFactory->createInstanceForDesktopFile(QFileInfo(desktopFileName));
916+ if (QFileInfo(desktopFileName.get()).exists()) {
917+ desktopData = m_desktopFileReaderFactory->createInstanceForDesktopFile(QFileInfo(desktopFileName.get()));
918 } else {
919- desktopData = m_desktopFileReaderFactory->createInstanceForAppId(desktopFileName);
920+ desktopData = m_desktopFileReaderFactory->createInstanceForAppId(desktopFileName.get());
921 }
922
923 if (!desktopData->loaded()) {
924@@ -521,18 +583,15 @@
925
926 // if stage supplied in CLI, fetch that
927 Application::Stage stage = Application::MainStage;
928- pattern = QRegularExpression::escape("--stage_hint=") + "(\\S+)";
929- regExp.setPattern(pattern);
930- regExpMatch = regExp.match(command);
931+ boost::optional<QString> stageParam = info->get_parameter("--stage_hint=");
932
933- if (regExpMatch.hasMatch() && regExpMatch.captured(1) == "side_stage") {
934+ if (stageParam && stageParam.get() == "side_stage") {
935 stage = Application::SideStage;
936 }
937
938 DLOG("Existing process with pid %lld appeared, adding '%s' to application lists", pid, desktopData->name().toLatin1().data());
939
940- QString argStr(command.data());
941- QStringList arguments(argStr.split(' '));
942+ QStringList arguments(info->as_string_list());
943 application = new Application(m_taskController, desktopData, Application::Starting, arguments, this);
944 application->setPid(pid);
945 application->setStage(stage);
946@@ -542,44 +601,50 @@
947
948 void ApplicationManager::placeSession(msh::Session const* session, uint32_t &x, uint32_t &y)
949 {
950- DLOG("ApplicationManager::placeSession (this=%p, session=%p)", this, session);
951-
952 Application* application = findApplicationWithSession(session);
953+ DLOG("ApplicationManager::placeSession (this=%p, application=%p, session=%p, name=%s)", this, application, session, session?(session->name().c_str()):"null");
954
955 // Application defaults
956 x = 0;
957- y = m_panelHeight;
958+ y = 0;
959
960- // Shell client override
961- if (!session) {
962- y = 0;
963- return;
964+ if (application && !application->fullscreen() && session) {
965+ if (application->stage() == Application::SideStage && !m_sideStageAppRect.isNull()) {
966+ QRectF localRect(0., 0., m_sideStageAppRect->width(), m_sideStageAppRect->height());
967+ QRectF sceneRect = m_sideStageAppRect->mapRectToScene(localRect);
968+ x = (uint32_t)(sceneRect.x());
969+ y = (uint32_t)(sceneRect.y());
970+ } else if (!m_mainStageAppRect.isNull()) {
971+ QRectF localRect(0., 0., m_sideStageAppRect->width(), m_mainStageAppRect->height());
972+ QRectF sceneRect = m_mainStageAppRect->mapRectToScene(localRect);
973+ x = (uint32_t)(sceneRect.x());
974+ y = (uint32_t)(sceneRect.y());
975+ }
976+ } else if (session) { // if a valid session, but no application associated, it must be a helper like OSK
977+ if (qgetenv("NATIVE_ORIENTATION") == "landscape") {
978+ x = 0;
979+ y = 0;
980+ } else {
981+ x = 0;
982+ y = m_panelHeight;
983+ }
984 }
985
986- // Fullscreen applications override
987- if (application && application->fullscreen())
988- y = 0;
989-
990- // SideStage override
991- if (application && application->stage() == Application::SideStage)
992- x = m_displaySize.width() - (SIDE_STAGE_WIDTH_GU * m_gridUnitPx);
993-
994 DLOG("ApplicationManager::placeSession (x=%d, y=%d)", x, y);
995 }
996
997 void ApplicationManager::onSessionStarting(std::shared_ptr<msh::Session> const& session)
998 {
999- DLOG("ApplicationManager::onSessionStarting (this=%p, application=%s)", this, session->name().c_str());
1000+ DLOG("ApplicationManager::onSessionStarting (this=%p, application=%s)", this, session?session->name().c_str():"null");
1001
1002 if (m_fenceNext) {
1003 m_fenceNext = false;
1004 return;
1005 }
1006
1007- //FIXME(greyback) Mir not supplying any identifier that we can use to link the PID to the session
1008- // so am assuming that the *most recently* launched application session is the one that connects
1009- Application* application = findLastExecutedApplication();
1010- if (application && application->state() != Application::Running) {
1011+ Application* application = findApplicationWithPid(session->process_id());
1012+ if (application && application->state() != Application::Running &&
1013+ !application->session()) {
1014 application->setSession(session);
1015 if (application->stage() == Application::MainStage)
1016 m_msApplicationToBeFocused = application;
1017@@ -592,10 +657,11 @@
1018
1019 void ApplicationManager::onSessionStopping(std::shared_ptr<msh::Session> const& session)
1020 {
1021- DLOG("ApplicationManager::onSessionStopping (this=%p, application=%s)", this, session->name().c_str());
1022-
1023 // in case application closed not by hand of shell, check again here:
1024 Application* application = findApplicationWithSession(session);
1025+
1026+ DLOG("ApplicationManager::onSessionStopping (this=%p, application=%p, appId=%s, session name=%s)", this, application, application?qPrintable(application->appId()):"null", session?session->name().c_str():"null");
1027+
1028 if (application) {
1029 bool removeApplication = true;
1030
1031@@ -604,7 +670,7 @@
1032 application->setSession(nullptr);
1033 m_dbusWindowStack->WindowDestroyed(0, application->appId());
1034 if (application != m_focusedApplication) {
1035- removeApplication = false;
1036+ removeApplication = false;
1037 }
1038 }
1039
1040@@ -626,8 +692,8 @@
1041
1042 void ApplicationManager::onSessionFocused(std::shared_ptr<msh::Session> const& session)
1043 {
1044- DLOG("ApplicationManager::onSessionFocused (this=%p, session=%p)", this, session.get());
1045 Application* application = findApplicationWithSession(session);
1046+ DLOG("ApplicationManager::onSessionFocused (this=%p, application=%p, appId=%s, session name=%s)", this, application, application?qPrintable(application->appId()):"null", session?session->name().c_str():"null");
1047
1048 // Don't give application focus until it has created it's surface, when it is set as state "Running"
1049 // and only notify shell of focus changes that it actually expects
1050@@ -649,7 +715,7 @@
1051
1052 void ApplicationManager::onSessionUnfocused()
1053 {
1054- DLOG("ApplicationManager::onSessionUnfocused (this=%p)", this);
1055+ DLOG("ApplicationManager::onSessionUnfocused (this=%p, application=%p)", this, m_focusedApplication);
1056 if (NULL != m_focusedApplication) {
1057 Q_ASSERT(m_focusedApplication->focused());
1058 m_focusedApplication->setFocused(false);
1059@@ -666,7 +732,7 @@
1060 }
1061
1062 void ApplicationManager::onSessionCreatedSurface(msh::Session const* session,
1063- std::shared_ptr<msh::Surface> const& surface)
1064+ std::shared_ptr<msh::Surface> const& surface)
1065 {
1066 DLOG("ApplicationManager::onSessionCreatedSurface (this=%p)", this);
1067 Q_UNUSED(surface);
1068@@ -683,7 +749,7 @@
1069
1070 void ApplicationManager::setFocused(Application *application)
1071 {
1072- DLOG("ApplicationManager::setFocused (appId=%s)", qPrintable(application->appId()));
1073+ DLOG("ApplicationManager::setFocused (application=%p, appId=%s)", application, qPrintable(application->appId()));
1074
1075 if (application == m_focusedApplication)
1076 return;
1077@@ -734,15 +800,6 @@
1078 return nullptr;
1079 }
1080
1081-Application* ApplicationManager::findLastExecutedApplication()
1082-{
1083- if (m_applications.length() > 0) {
1084- return m_applications.last();
1085- } else {
1086- return NULL;
1087- }
1088-}
1089-
1090 Application* ApplicationManager::applicationForStage(Application::Stage stage)
1091 {
1092 DLOG("ApplicationManager::focusedApplicationForStage(this=%p)", this);
1093@@ -769,6 +826,11 @@
1094 DASSERT(application != NULL);
1095 DLOG("ApplicationManager::remove (this=%p, application='%s')", this, qPrintable(application->name()));
1096
1097+ if (application == m_sideStageApplication)
1098+ m_sideStageApplication = nullptr;
1099+ if (application == m_mainStageApplication)
1100+ m_mainStageApplication = nullptr;
1101+
1102 int i = m_applications.indexOf(application);
1103 if (i != -1) {
1104 beginRemoveRows(QModelIndex(), i, i);
1105@@ -793,6 +855,16 @@
1106 }
1107 }
1108
1109+void ApplicationManager::setMainStageAppRect(QQuickItem *item)
1110+{
1111+ m_mainStageAppRect = item;
1112+}
1113+
1114+void ApplicationManager::setSideStageAppRect(QQuickItem *item)
1115+{
1116+ m_sideStageAppRect = item;
1117+}
1118+
1119 QModelIndex ApplicationManager::findIndex(Application* application)
1120 {
1121 for (int i = 0; i < m_applications.size(); ++i) {
1122@@ -803,3 +875,14 @@
1123
1124 return QModelIndex();
1125 }
1126+
1127+bool ApplicationManager::checkFocusOnRemovedApplication(Application * application)
1128+{
1129+ if (application == m_focusedApplication) {
1130+ // TODO(greyback) What to do?? Focus next app, or unfocus everything??
1131+ m_focusedApplication = nullptr;
1132+ Q_EMIT focusedApplicationIdChanged();
1133+ return true;
1134+ }
1135+ return false;
1136+}
1137
1138=== modified file 'src/modules/Unity/Application/application_manager.h'
1139--- src/modules/Unity/Application/application_manager.h 2014-01-27 11:29:44 +0000
1140+++ src/modules/Unity/Application/application_manager.h 2014-02-21 16:48:46 +0000
1141@@ -25,6 +25,8 @@
1142
1143 // Qt
1144 #include <QObject>
1145+#include <QPointer>
1146+#include <QQuickItem>
1147 #include <QSharedPointer>
1148 #include <QStringList>
1149
1150@@ -38,6 +40,7 @@
1151 class DBusWindowStack;
1152 class MirSurfaceManager;
1153 class TaskController;
1154+class ProcInfo;
1155
1156 namespace mir {
1157 namespace geometry {
1158@@ -46,6 +49,7 @@
1159 namespace shell {
1160 class Session;
1161 class Surface;
1162+ class FocusController;
1163 }
1164 }
1165
1166@@ -55,6 +59,11 @@
1167 Q_FLAGS(ExecFlags)
1168
1169 public:
1170+ class Factory
1171+ {
1172+ public:
1173+ QSharedPointer<ApplicationManager> create();
1174+ };
1175 // Mapping enums to Ubuntu Platform API enums.
1176 enum Flag {
1177 NoFlag = 0x0,
1178@@ -66,6 +75,9 @@
1179
1180 explicit ApplicationManager(const QSharedPointer<TaskController>& taskController,
1181 const QSharedPointer<DesktopFileReader::Factory>& desktopFileReaderFactory,
1182+ const QSharedPointer<ProcInfo>& processInfo,
1183+ std::shared_ptr<mir::shell::FocusController> const& controller,
1184+ QSize const& displaySize,
1185 QObject *parent = 0);
1186 virtual ~ApplicationManager();
1187
1188@@ -86,12 +98,14 @@
1189 const QStringList &arguments = QStringList());
1190 Q_INVOKABLE void move(int from, int to);
1191
1192+ Q_INVOKABLE void setMainStageAppRect(QQuickItem *item);
1193+ Q_INVOKABLE void setSideStageAppRect(QQuickItem *item);
1194+
1195 const QList<Application*> &list() const { return m_applications; }
1196 Application* findApplicationWithPid(const qint64 pid);
1197
1198 // Internal helpers
1199 void suspendApplication(Application *application);
1200- int panelHeight();
1201 QSize displaySize() const { return m_displaySize; }
1202
1203 public Q_SLOTS:
1204@@ -119,9 +133,11 @@
1205 void remove(Application* application);
1206 Application* findApplicationWithSession(const std::shared_ptr<mir::shell::Session> &session);
1207 Application* findApplicationWithSession(const mir::shell::Session *session);
1208- Application* findLastExecutedApplication();
1209 Application* applicationForStage(Application::Stage stage);
1210 QModelIndex findIndex(Application* application);
1211+ bool checkFocusOnRemovedApplication(Application* application);
1212+ void shutdownApplication(Application* application);
1213+ void stopStartingApplication(const QString &appId);
1214
1215 QList<Application*> m_applications;
1216 Application* m_focusedApplication; // remove as Mir has API for this
1217@@ -130,16 +146,21 @@
1218 Application* m_msApplicationToBeFocused; // placeholder store for async focusing
1219 Application* m_ssApplicationToBeFocused; // placeholder store for async focusing
1220 QStringList m_lifecycleExceptions;
1221- ShellServerConfiguration* m_mirServer;
1222+ std::shared_ptr<mir::shell::FocusController> m_focusController;
1223 DBusWindowStack* m_dbusWindowStack;
1224 QSharedPointer<TaskController> m_taskController;
1225 QSharedPointer<DesktopFileReader::Factory> m_desktopFileReaderFactory;
1226- static ApplicationManager* the_application_manager;
1227+ QSharedPointer<ProcInfo> m_procInfo;
1228 int m_gridUnitPx;
1229 bool m_fenceNext;
1230 QSize m_displaySize;
1231 int m_panelHeight;
1232
1233+ // The rectangular area that should be occupied by an application on the main
1234+ // or side stage.
1235+ QPointer<QQuickItem> m_mainStageAppRect;
1236+ QPointer<QQuickItem> m_sideStageAppRect;
1237+
1238 friend class DBusWindowStack;
1239 friend class MirSurfaceManager;
1240 };
1241
1242=== modified file 'src/modules/Unity/Application/applicationscreenshotprovider.cpp'
1243--- src/modules/Unity/Application/applicationscreenshotprovider.cpp 2013-12-20 03:38:43 +0000
1244+++ src/modules/Unity/Application/applicationscreenshotprovider.cpp 2014-02-21 16:48:46 +0000
1245@@ -28,7 +28,6 @@
1246 ApplicationScreenshotProvider::ApplicationScreenshotProvider(ApplicationManager *appManager)
1247 : QQuickImageProvider(QQuickImageProvider::Image)
1248 , m_appManager(appManager)
1249- , m_panelHeight(m_appManager->panelHeight())
1250 {
1251 }
1252
1253
1254=== modified file 'src/modules/Unity/Application/applicationscreenshotprovider.h'
1255--- src/modules/Unity/Application/applicationscreenshotprovider.h 2013-08-14 14:24:17 +0000
1256+++ src/modules/Unity/Application/applicationscreenshotprovider.h 2014-02-21 16:48:46 +0000
1257@@ -30,7 +30,6 @@
1258
1259 private:
1260 ApplicationManager* m_appManager;
1261- int m_panelHeight;
1262 };
1263
1264 #endif // APPLICATIONSCREENSHOTPROVIDER_H
1265
1266=== modified file 'src/modules/Unity/Application/inputarea.cpp'
1267--- src/modules/Unity/Application/inputarea.cpp 2013-11-26 08:56:08 +0000
1268+++ src/modules/Unity/Application/inputarea.cpp 2014-02-21 16:48:46 +0000
1269@@ -176,12 +176,38 @@
1270 {
1271 DLOG("InputArea::setMirInputArea (this=%p, x=%lf, y=%lf, width=%lf, height=%lf)", this, relativeGeometry.x(), relativeGeometry.y(), relativeGeometry.width(), relativeGeometry.height());
1272 using namespace mir::geometry;
1273- const QRect rect = parentItem()->mapRectToScene(relativeGeometry).toRect();
1274-
1275- m_mirInputArea.top_left.x = X{rect.x()};
1276- m_mirInputArea.top_left.y = Y{rect.y()};
1277- m_mirInputArea.size.width = Width{rect.width()};
1278- m_mirInputArea.size.height = Height{rect.height()};
1279+ QQuickItem* parent = parentItem();
1280+ bool shellFound = false;
1281+ const bool landscape = qgetenv("NATIVE_ORIENTATION") == "landscape";
1282+
1283+ // FIXME: It seems Qt's mapRectToScene/Item returns incorrect position coordinates once rotations are performed.
1284+ // As a work around, we try to find the "shell" Item, find the InputArea geometry relative to that, and then
1285+ // manually perform the rotation for landscape mode.
1286+ while (landscape && parent) {
1287+ if (parent->objectName() == "shell") {
1288+ shellFound = true;
1289+ break;
1290+ }
1291+ parent = parent->parentItem();
1292+ }
1293+
1294+ QRect rect;
1295+
1296+ if (landscape && shellFound) {
1297+ rect = mapRectToItem(parent, relativeGeometry).toRect();
1298+
1299+ m_mirInputArea.top_left.x = X{parent->height() - rect.y() - rect.height()};
1300+ m_mirInputArea.top_left.y = Y{rect.x()};
1301+ m_mirInputArea.size.width = Width{rect.height()};
1302+ m_mirInputArea.size.height = Height{rect.width()};
1303+ } else { // position relative to scene as usual
1304+ rect = parentItem()->mapRectToScene(relativeGeometry).toRect();
1305+
1306+ m_mirInputArea.top_left.x = X{rect.x()};
1307+ m_mirInputArea.top_left.y = Y{rect.y()};
1308+ m_mirInputArea.size.width = Width{rect.width()};
1309+ m_mirInputArea.size.height = Height{rect.height()};
1310+ }
1311
1312 if (m_surface) {
1313 m_surface->installInputArea(this);
1314
1315=== added file 'src/modules/Unity/Application/proc_info.cpp'
1316--- src/modules/Unity/Application/proc_info.cpp 1970-01-01 00:00:00 +0000
1317+++ src/modules/Unity/Application/proc_info.cpp 2014-02-21 16:48:46 +0000
1318@@ -0,0 +1,52 @@
1319+/*
1320+ * Copyright (C) 2014 Canonical, Ltd.
1321+ *
1322+ * This program is free software: you can redistribute it and/or modify it under
1323+ * the terms of the GNU Lesser General Public License version 3, as published by
1324+ * the Free Software Foundation.
1325+ *
1326+ * This program is distributed in the hope that it will be useful, but WITHOUT
1327+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1328+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1329+ * Lesser General Public License for more details.
1330+ *
1331+ * You should have received a copy of the GNU Lesser General Public License
1332+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1333+ */
1334+
1335+#include "proc_info.h"
1336+
1337+#include <QFile>
1338+#include <QRegularExpression>
1339+
1340+ProcInfo::~ProcInfo() {
1341+}
1342+
1343+std::unique_ptr<ProcInfo::CommandLine> ProcInfo::command_line(quint64 pid) {
1344+ QFile cmdline(QString("/proc/%1/cmdline").arg(pid));
1345+ if (!cmdline.open(QIODevice::ReadOnly | QIODevice::Text)) {
1346+ return nullptr;
1347+ }
1348+
1349+ return std::unique_ptr<CommandLine>(new CommandLine{ cmdline.readLine().replace('\0', ' ') });
1350+}
1351+QStringList ProcInfo::CommandLine::as_string_list() const {
1352+ return QString(command.data()).split(' ');
1353+}
1354+
1355+bool ProcInfo::CommandLine::starts_with(char const* prefix) const {
1356+ return command.startsWith(prefix);
1357+}
1358+
1359+boost::optional<QString> ProcInfo::CommandLine::get_parameter(const char* name) const {
1360+ QString pattern = QRegularExpression::escape(name) + "(\\S+)";
1361+ QRegularExpression regExp(pattern);
1362+ QRegularExpressionMatch regExpMatch = regExp.match(command);
1363+
1364+ if (!regExpMatch.hasMatch()) {
1365+ return boost::optional<QString>{};
1366+ }
1367+
1368+ return boost::optional<QString>{regExpMatch.captured(1)};
1369+}
1370+
1371
1372=== added file 'src/modules/Unity/Application/proc_info.h'
1373--- src/modules/Unity/Application/proc_info.h 1970-01-01 00:00:00 +0000
1374+++ src/modules/Unity/Application/proc_info.h 2014-02-21 16:48:46 +0000
1375@@ -0,0 +1,48 @@
1376+/*
1377+ * Copyright (C) 2014 Canonical, Ltd.
1378+ *
1379+ * This program is free software: you can redistribute it and/or modify it under
1380+ * the terms of the GNU Lesser General Public License version 3, as published by
1381+ * the Free Software Foundation.
1382+ *
1383+ * This program is distributed in the hope that it will be useful, but WITHOUT
1384+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1385+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1386+ * Lesser General Public License for more details.
1387+ *
1388+ * You should have received a copy of the GNU Lesser General Public License
1389+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1390+ */
1391+
1392+// Process Information
1393+
1394+#ifndef PROC_INFO_H
1395+#define PROC_INFO_H
1396+
1397+#include <memory>
1398+
1399+#include <boost/optional.hpp>
1400+
1401+#include <QByteArray>
1402+#include <QStringList>
1403+
1404+class QString;
1405+
1406+class ProcInfo
1407+{
1408+public:
1409+ class CommandLine
1410+ {
1411+ public:
1412+ QByteArray command;
1413+
1414+ bool starts_with(const char* prefix) const;
1415+ boost::optional<QString> get_parameter(const char* name) const;
1416+ QStringList as_string_list() const;
1417+ };
1418+ virtual std::unique_ptr<CommandLine> command_line(quint64 pid);
1419+ virtual ~ProcInfo();
1420+};
1421+
1422+#endif
1423+
1424
1425=== modified file 'src/unity-mir/CMakeLists.txt'
1426--- src/unity-mir/CMakeLists.txt 2014-02-07 16:10:12 +0000
1427+++ src/unity-mir/CMakeLists.txt 2014-02-21 16:48:46 +0000
1428@@ -74,12 +74,10 @@
1429
1430 ${CMAKE_THREAD_LIBS_INIT}
1431
1432- ${UBUNTU_PLATFORM_API_LIBRARIES}
1433- ${MIRCOMMON_LIBRARIES}
1434- ${MIRSERVER_LIBRARIES}
1435+ ${UBUNTU_PLATFORM_API_LDFLAGS}
1436+ ${MIRSERVER_LDFLAGS}
1437 ${PROTOBUF_LIBRARIES}
1438-
1439- boost_system
1440+ ${Boost_SYSTEM_LIBRARY_RELEASE}
1441
1442 ubuntu_application_api_mirserver)
1443
1444
1445=== modified file 'tests/CMakeLists.txt'
1446--- tests/CMakeLists.txt 2014-01-27 11:29:44 +0000
1447+++ tests/CMakeLists.txt 2014-02-21 16:48:46 +0000
1448@@ -20,6 +20,7 @@
1449 ${CMAKE_SOURCE_DIR}/src/modules
1450 ${GMOCK_INCLUDE_DIR}
1451 ${GTEST_INCLUDE_DIR}
1452+ ${MIRSERVER_INCLUDE_DIRS}
1453 )
1454
1455 add_executable(
1456
1457=== modified file 'tests/application_manager_test.cpp'
1458--- tests/application_manager_test.cpp 2014-01-27 11:29:44 +0000
1459+++ tests/application_manager_test.cpp 2014-02-21 16:48:46 +0000
1460@@ -19,6 +19,7 @@
1461
1462 #include <Unity/Application/applicationcontroller.h>
1463 #include <Unity/Application/taskcontroller.h>
1464+#include <Unity/Application/proc_info.h>
1465
1466 #include <core/posix/linux/proc/process/oom_score_adj.h>
1467
1468@@ -29,43 +30,54 @@
1469 #include "mock_desktop_file_reader.h"
1470 #include "mock_oom_controller.h"
1471 #include "mock_process_controller.h"
1472-
1473-TEST(ApplicationManager, SuspendingAndResumingARunningApplicationResultsInOomScoreAdjustment)
1474+#include "mock_proc_info.h"
1475+#include "mock_session.h"
1476+#include "mock_focus_controller.h"
1477+
1478+
1479+class ApplicationManagerTests : public ::testing::Test
1480 {
1481- using namespace ::testing;
1482-
1483- const QString appId("com.canonical.does.not.exist");
1484-
1485- NiceMock<testing::MockOomController> oomController;
1486- QSharedPointer<ProcessController::OomController> oomControllerPtr(
1487+public:
1488+ ApplicationManagerTests()
1489+ : processController{
1490+ QSharedPointer<ProcessController::OomController> (
1491 &oomController,
1492- [](ProcessController::OomController*){});
1493-
1494- NiceMock<testing::MockProcessController> processController(oomControllerPtr);
1495- QSharedPointer<ProcessController> processControllerPtr(
1496- &processController,
1497- [](ProcessController*){});
1498-
1499- NiceMock<testing::MockApplicationController> appController;
1500- QSharedPointer<ApplicationController> appControllerPtr(
1501- &appController,
1502- [](ApplicationController*){});
1503-
1504- QSharedPointer<TaskController> taskController(
1505+ [](ProcessController::OomController*){})
1506+ },
1507+ applicationManager{
1508+ QSharedPointer<TaskController>{
1509 new TaskController(
1510 nullptr,
1511- appControllerPtr,
1512- processControllerPtr
1513- ));
1514-
1515- NiceMock<MockDesktopFileReaderFactory> desktopFileReaderFactory;
1516- QSharedPointer<DesktopFileReader::Factory> desktopFileReaderFactoryPtr(
1517+ QSharedPointer<ApplicationController>(
1518+ &appController,
1519+ [](ApplicationController*){}),
1520+ QSharedPointer<ProcessController>(
1521+ &processController,
1522+ [](ProcessController*){})
1523+ )},
1524+ QSharedPointer<DesktopFileReader::Factory>(
1525 &desktopFileReaderFactory,
1526- [](DesktopFileReader::Factory*){});
1527-
1528- ApplicationManager applicationManager(
1529- taskController,
1530- desktopFileReaderFactoryPtr);
1531+ [](DesktopFileReader::Factory*){}),
1532+ QSharedPointer<ProcInfo>(&procInfo,[](ProcInfo *){}),
1533+ std::shared_ptr<mir::shell::FocusController>(&focusController, [](void*){}),
1534+ QSize(400,400)
1535+ }
1536+ {
1537+ }
1538+ testing::NiceMock<testing::MockOomController> oomController;
1539+ testing::NiceMock<testing::MockProcessController> processController;
1540+ testing::NiceMock<testing::MockApplicationController> appController;
1541+ testing::NiceMock<testing::MockProcInfo> procInfo;
1542+ testing::NiceMock<testing::MockDesktopFileReaderFactory> desktopFileReaderFactory;
1543+ testing::NiceMock<testing::MockFocusController> focusController;
1544+ ApplicationManager applicationManager;
1545+};
1546+
1547+TEST_F(ApplicationManagerTests, SuspendingAndResumingARunningApplicationResultsInOomScoreAdjustment)
1548+{
1549+ using namespace ::testing;
1550+
1551+ const QString appId("com.canonical.does.not.exist");
1552
1553 EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(_, _)).Times(1);
1554
1555@@ -86,43 +98,12 @@
1556 }
1557
1558 // Currently disabled as we need to make sure that we have a corresponding mir session, too.
1559-TEST(ApplicationManager, DISABLED_FocusingRunningApplicationResultsInOomScoreAdjustment)
1560+TEST_F(ApplicationManagerTests, DISABLED_FocusingRunningApplicationResultsInOomScoreAdjustment)
1561 {
1562 using namespace ::testing;
1563
1564 const QString appId("com.canonical.does.not.exist");
1565
1566- NiceMock<testing::MockOomController> oomController;
1567- QSharedPointer<ProcessController::OomController> oomControllerPtr(
1568- &oomController,
1569- [](ProcessController::OomController*){});
1570-
1571- NiceMock<testing::MockProcessController> processController(oomControllerPtr);
1572- QSharedPointer<ProcessController> processControllerPtr(
1573- &processController,
1574- [](ProcessController*){});
1575-
1576- NiceMock<testing::MockApplicationController> appController;
1577- QSharedPointer<ApplicationController> appControllerPtr(
1578- &appController,
1579- [](ApplicationController*){});
1580-
1581- QSharedPointer<TaskController> taskController(
1582- new TaskController(
1583- nullptr,
1584- appControllerPtr,
1585- processControllerPtr
1586- ));
1587-
1588- NiceMock<MockDesktopFileReaderFactory> desktopFileReaderFactory;
1589- QSharedPointer<DesktopFileReader::Factory> desktopFileReaderFactoryPtr(
1590- &desktopFileReaderFactory,
1591- [](DesktopFileReader::Factory*){});
1592-
1593- ApplicationManager applicationManager(
1594- taskController,
1595- desktopFileReaderFactoryPtr);
1596-
1597 QSet<QString> appIds;
1598
1599 for (unsigned int i = 0; i < 50; i++)
1600@@ -135,6 +116,9 @@
1601 ApplicationManager::NoFlag,
1602 QStringList());
1603
1604+ std::shared_ptr<mir::shell::Session> mirSession = std::make_shared<MockSession>(appIdFormat.toStdString(), i);
1605+ applicationManager.onSessionStarting( mirSession );
1606+
1607 EXPECT_NE(nullptr, application);
1608
1609 appIds.insert(appId);
1610@@ -149,3 +133,141 @@
1611 applicationManager.focusApplication(appId);
1612 }
1613 }
1614+
1615+
1616+TEST_F(ApplicationManagerTests,bug_case_1240400_second_dialer_app_fails_to_authorize_and_gets_mixed_up_with_first_one)
1617+{
1618+ using namespace ::testing;
1619+ std::shared_ptr<mir::shell::Surface> aSurface(nullptr);
1620+ quint64 firstProcId = 5921;
1621+ quint64 secondProcId = 5922;
1622+ const char dialer_app_id[] = "dialer-app";
1623+ QByteArray cmdLine( "/usr/bin/dialer-app --desktop_file_hint=dialer-app");
1624+ QByteArray secondcmdLine( "/usr/bin/dialer-app");
1625+
1626+ EXPECT_CALL(procInfo,command_line_(firstProcId))
1627+ .Times(1)
1628+ .WillOnce(Return(cmdLine));
1629+ EXPECT_CALL(procInfo,command_line_(secondProcId))
1630+ .Times(1)
1631+ .WillOnce(Return(secondcmdLine));
1632+
1633+ bool authed = true;
1634+
1635+ std::shared_ptr<mir::shell::Session> mirSession = std::make_shared<MockSession>(dialer_app_id, firstProcId);
1636+ applicationManager.authorizeSession(firstProcId, authed);
1637+ EXPECT_EQ(true, authed);
1638+ applicationManager.onSessionStarting(mirSession);
1639+ applicationManager.onSessionCreatedSurface(mirSession.get(),aSurface);
1640+ Application * app = applicationManager.findApplication(dialer_app_id);
1641+ EXPECT_NE(nullptr,app);
1642+
1643+ // now a second session without desktop file is launched:
1644+ applicationManager.authorizeSession(secondProcId, authed);
1645+ applicationManager.onProcessStartReportReceived(dialer_app_id, true);
1646+
1647+ EXPECT_EQ(false,authed);
1648+ EXPECT_EQ(app,applicationManager.findApplication(dialer_app_id));
1649+ EXPECT_EQ(QString(dialer_app_id),applicationManager.focusedApplicationId());
1650+}
1651+
1652+TEST_F(ApplicationManagerTests,application_dies_while_starting)
1653+{
1654+ using namespace ::testing;
1655+ quint64 procId = 5921;
1656+ const char app_id[] = "my-app";
1657+ QByteArray cmdLine( "/usr/bin/my-app --desktop_file_hint=my-app");
1658+
1659+ EXPECT_CALL(procInfo,command_line_(procId))
1660+ .Times(1)
1661+ .WillOnce(Return(cmdLine));
1662+
1663+ bool authed = true;
1664+
1665+ std::shared_ptr<mir::shell::Session> mirSession = std::make_shared<MockSession>(app_id, procId);
1666+ applicationManager.authorizeSession(procId, authed);
1667+ applicationManager.onSessionStarting(mirSession);
1668+ Application * beforeFailure = applicationManager.findApplication(app_id);
1669+ applicationManager.onProcessStartReportReceived(app_id,true);
1670+ Application * afterFailure = applicationManager.findApplication(app_id);
1671+
1672+ EXPECT_EQ(true, authed);
1673+ EXPECT_NE(nullptr, beforeFailure);
1674+ EXPECT_EQ(nullptr, afterFailure);
1675+}
1676+
1677+TEST_F(ApplicationManagerTests,application_start_failure_after_starting)
1678+{
1679+ using namespace ::testing;
1680+ quint64 procId = 5921;
1681+ std::shared_ptr<mir::shell::Surface> aSurface(nullptr);
1682+ const char app_id[] = "my-app";
1683+ QByteArray cmdLine( "/usr/bin/my-app --desktop_file_hint=my-app");
1684+
1685+ EXPECT_CALL(procInfo,command_line_(procId))
1686+ .Times(1)
1687+ .WillOnce(Return(cmdLine));
1688+
1689+ bool authed = true;
1690+
1691+ std::shared_ptr<mir::shell::Session> mirSession = std::make_shared<MockSession>(app_id, procId);
1692+ applicationManager.authorizeSession(procId, authed);
1693+ applicationManager.onSessionStarting(mirSession);
1694+ Application * beforeFailure = applicationManager.findApplication(app_id);
1695+ applicationManager.onSessionCreatedSurface(mirSession.get(), aSurface);
1696+ applicationManager.onProcessStartReportReceived(app_id, true);
1697+ Application * afterFailure = applicationManager.findApplication(app_id);
1698+
1699+ EXPECT_EQ(true, authed);
1700+ EXPECT_NE(nullptr, beforeFailure);
1701+ EXPECT_EQ(beforeFailure, afterFailure);
1702+}
1703+
1704+TEST_F(ApplicationManagerTests,bug_case_1281075_session_ptrs_always_distributed_to_last_started_app)
1705+{
1706+ using namespace ::testing;
1707+ quint64 first_procId = 5921;
1708+ quint64 second_procId = 5922;
1709+ quint64 third_procId = 5923;
1710+ std::shared_ptr<mir::shell::Surface> aSurface(nullptr);
1711+ const char first_app_id[] = "app1";
1712+ QByteArray first_cmdLine( "/usr/bin/app1 --desktop_file_hint=app1");
1713+ const char second_app_id[] = "app2";
1714+ QByteArray second_cmdLine( "/usr/bin/app2--desktop_file_hint=app2");
1715+ const char third_app_id[] = "app3";
1716+ QByteArray third_cmdLine( "/usr/bin/app3 --desktop_file_hint=app3");
1717+
1718+ EXPECT_CALL(procInfo,command_line_(first_procId))
1719+ .Times(1)
1720+ .WillOnce(Return(first_cmdLine));
1721+
1722+ ON_CALL(appController,appIdHasProcessId(_,_)).WillByDefault(Return(false));
1723+
1724+ EXPECT_CALL(procInfo,command_line_(second_procId))
1725+ .Times(1)
1726+ .WillOnce(Return(second_cmdLine));
1727+
1728+ EXPECT_CALL(procInfo,command_line_(third_procId))
1729+ .Times(1)
1730+ .WillOnce(Return(third_cmdLine));
1731+
1732+ bool authed = true;
1733+
1734+ std::shared_ptr<mir::shell::Session> first_session = std::make_shared<MockSession>("Oo", first_procId);
1735+ std::shared_ptr<mir::shell::Session> second_session = std::make_shared<MockSession>("oO", second_procId);
1736+ std::shared_ptr<mir::shell::Session> third_session = std::make_shared<MockSession>("OO", third_procId);
1737+ applicationManager.authorizeSession(first_procId, authed);
1738+ applicationManager.authorizeSession(second_procId, authed);
1739+ applicationManager.authorizeSession(third_procId, authed);
1740+ applicationManager.onSessionStarting(first_session);
1741+ applicationManager.onSessionStarting(third_session);
1742+ applicationManager.onSessionStarting(second_session);
1743+
1744+ Application * firstApp = applicationManager.findApplication(first_app_id);
1745+ Application * secondApp = applicationManager.findApplication(second_app_id);
1746+ Application * thirdApp = applicationManager.findApplication(third_app_id);
1747+
1748+ EXPECT_EQ(first_session, firstApp->session());
1749+ EXPECT_EQ(second_session, secondApp->session());
1750+ EXPECT_EQ(third_session, thirdApp->session());
1751+}
1752
1753=== modified file 'tests/auto/modules/Unity/Application/CMakeLists.txt'
1754--- tests/auto/modules/Unity/Application/CMakeLists.txt 2014-01-27 11:29:44 +0000
1755+++ tests/auto/modules/Unity/Application/CMakeLists.txt 2014-02-21 16:48:46 +0000
1756@@ -11,7 +11,7 @@
1757 unity-mir-test-app
1758 main.cpp)
1759
1760-qt5_use_modules(unity-mir-test-app Test DBus Gui)
1761+qt5_use_modules(unity-mir-test-app Test DBus Gui Quick)
1762
1763 target_link_libraries(
1764 unity-mir-test-app
1765@@ -19,8 +19,8 @@
1766 unity-mir
1767 unityapplicationplugin
1768
1769- ${MIRSERVER_LIBRARIES}
1770- ${UPSTART_APP_LAUNCH_LIBRARIES})
1771+ ${MIRSERVER_LDFLAGS}
1772+ ${UPSTART_APP_LAUNCH_LDFLAGS})
1773
1774 install(
1775 TARGETS unity-mir-test-app
1776
1777=== modified file 'tests/auto/modules/Unity/Application/main.cpp'
1778--- tests/auto/modules/Unity/Application/main.cpp 2014-01-27 11:29:44 +0000
1779+++ tests/auto/modules/Unity/Application/main.cpp 2014-02-21 16:48:46 +0000
1780@@ -18,6 +18,7 @@
1781
1782 #include "application_manager.h"
1783 #include "processcontroller.h"
1784+#include "proc_info.h"
1785 #include "taskcontroller.h"
1786 #include "upstart/applicationcontroller.h"
1787
1788@@ -44,12 +45,10 @@
1789
1790 void ApplicationManagerTests::testStartStop()
1791 {
1792- QSharedPointer<upstart::ApplicationController> appController(new upstart::ApplicationController());
1793- QSharedPointer<TaskController> taskController(new TaskController(nullptr, appController));
1794- QSharedPointer<DesktopFileReader::Factory> fileReaderFactory(new DesktopFileReader::Factory());
1795+ ApplicationManager::Factory appFactory;
1796+ QSharedPointer<ApplicationManager> manager{appFactory.create()};
1797
1798- ApplicationManager manager(taskController, fileReaderFactory);
1799- Application *app = manager.startApplication("unity-mir-test-helper-app", QStringList());
1800+ Application *app = manager->startApplication("unity-mir-test-helper-app", QStringList());
1801 QVERIFY(app);
1802 QCOMPARE(app->desktopFile(), QString("/usr/share/applications/unity-mir-test-helper-app.desktop"));
1803 QCOMPARE(app->name(), QString("My Fake App"));
1804@@ -61,7 +60,7 @@
1805 QString simplifiedCommand = pidCommandLine(app->pid());
1806 QCOMPARE(simplifiedCommand, QString("unity-mir-test-helper-app"));
1807
1808- manager.stopApplication(app->appId());
1809+ manager->stopApplication(app->appId());
1810
1811 QDir d;
1812 QTRY_VERIFY(!d.exists(QString("/proc/%1").arg(app->pid())));
1813
1814=== added file 'tests/mock_focus_controller.h'
1815--- tests/mock_focus_controller.h 1970-01-01 00:00:00 +0000
1816+++ tests/mock_focus_controller.h 2014-02-21 16:48:46 +0000
1817@@ -0,0 +1,37 @@
1818+/*
1819+ * Copyright (C) 2014 Canonical, Ltd.
1820+ *
1821+ * This program is free software: you can redistribute it and/or modify it under
1822+ * the terms of the GNU Lesser General Public License version 3, as published by
1823+ * the Free Software Foundation.
1824+ *
1825+ * This program is distributed in the hope that it will be useful, but WITHOUT
1826+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1827+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1828+ * Lesser General Public License for more details.
1829+ *
1830+ * You should have received a copy of the GNU Lesser General Public License
1831+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1832+ *
1833+ */
1834+
1835+#ifndef MOCK_MIR_SHELL_FOCUS_CONTROLLER_H
1836+#define MOCK_MIR_SHELL_FOCUS_CONTROLLER_H
1837+
1838+#include <mir/shell/focus_controller.h>
1839+#include <gmock/gmock.h>
1840+
1841+#include <string>
1842+
1843+namespace testing
1844+{
1845+class MockFocusController : public mir::shell::FocusController
1846+{
1847+public:
1848+ MOCK_METHOD0(focus_next, void());
1849+ MOCK_CONST_METHOD0(focussed_application, std::weak_ptr<mir::shell::Session>());
1850+ MOCK_METHOD1(set_focus_to, void(std::shared_ptr<mir::shell::Session>const&));
1851+};
1852+}
1853+
1854+#endif // MOCK_MIR_SHELL_FOCUS_CONTROLLER_H_
1855
1856=== added file 'tests/mock_proc_info.h'
1857--- tests/mock_proc_info.h 1970-01-01 00:00:00 +0000
1858+++ tests/mock_proc_info.h 2014-02-21 16:48:46 +0000
1859@@ -0,0 +1,37 @@
1860+/*
1861+ * Copyright (C) 2014 Canonical, Ltd.
1862+ *
1863+ * This program is free software: you can redistribute it and/or modify it under
1864+ * the terms of the GNU Lesser General Public License version 3, as published by
1865+ * the Free Software Foundation.
1866+ *
1867+ * This program is distributed in the hope that it will be useful, but WITHOUT
1868+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1869+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1870+ * Lesser General Public License for more details.
1871+ *
1872+ * You should have received a copy of the GNU Lesser General Public License
1873+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1874+ *
1875+ */
1876+
1877+#ifndef MOCK_PROC_INFO_H
1878+#define MOCK_PROC_INFO_H
1879+
1880+#include <Unity/Application/proc_info.h>
1881+
1882+#include <gmock/gmock.h>
1883+
1884+namespace testing
1885+{
1886+struct MockProcInfo : public ProcInfo
1887+{
1888+ MOCK_METHOD1(command_line_, QByteArray(quint64));
1889+ std::unique_ptr<CommandLine> command_line(quint64 pid)
1890+ {
1891+ return std::unique_ptr<CommandLine>(new CommandLine{command_line_(pid)});
1892+ }
1893+};
1894+}
1895+
1896+#endif // MOCK_OOM_CONTROLLER_H
1897
1898=== added file 'tests/mock_session.h'
1899--- tests/mock_session.h 1970-01-01 00:00:00 +0000
1900+++ tests/mock_session.h 2014-02-21 16:48:46 +0000
1901@@ -0,0 +1,69 @@
1902+/*
1903+ * Copyright (C) 2014 Canonical, Ltd.
1904+ *
1905+ * This program is free software: you can redistribute it and/or modify it under
1906+ * the terms of the GNU Lesser General Public License version 3, as published by
1907+ * the Free Software Foundation.
1908+ *
1909+ * This program is distributed in the hope that it will be useful, but WITHOUT
1910+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1911+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1912+ * Lesser General Public License for more details.
1913+ *
1914+ * You should have received a copy of the GNU Lesser General Public License
1915+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1916+ *
1917+ */
1918+
1919+#ifndef MOCK_MIR_SHELL_SESSION_H
1920+#define MOCK_MIR_SHELL_SESSION_H
1921+
1922+#include <mir/shell/session.h>
1923+#include <mir/graphics/display_configuration.h>
1924+#include <mir/shell/surface_creation_parameters.h>
1925+#include <gmock/gmock.h>
1926+
1927+#include <string>
1928+
1929+namespace testing
1930+{
1931+struct MockSession : public mir::shell::Session
1932+{
1933+ MockSession() {}
1934+ MockSession(std::string const& sessionName, pid_t processId)
1935+ : m_sessionName(sessionName), m_sessionId(processId)
1936+ {}
1937+
1938+ std::string name() const override
1939+ {
1940+ return m_sessionName;
1941+ }
1942+
1943+ pid_t process_id() const override
1944+ {
1945+ return m_sessionId;
1946+ }
1947+
1948+ typedef mir::frontend::SurfaceId SurfaceId;
1949+
1950+ MOCK_METHOD0(force_requests_to_complete, void());
1951+
1952+ MOCK_CONST_METHOD0(default_surface, std::shared_ptr<mir::shell::Surface>());
1953+ MOCK_CONST_METHOD1(get_surface, std::shared_ptr<mir::frontend::Surface>(SurfaceId));
1954+
1955+ MOCK_METHOD1(take_snapshot, void(mir::shell::SnapshotCallback const&));
1956+ MOCK_METHOD1(set_lifecycle_state, void(MirLifecycleState));
1957+ MOCK_METHOD1(create_surface, SurfaceId(mir::shell::SurfaceCreationParameters const&));
1958+ MOCK_METHOD1(destroy_surface, void (SurfaceId));
1959+
1960+ MOCK_METHOD0(hide, void());
1961+ MOCK_METHOD0(show, void());
1962+ MOCK_METHOD1(send_display_config, void(mir::graphics::DisplayConfiguration const&));
1963+ MOCK_METHOD3(configure_surface, int(SurfaceId, MirSurfaceAttrib, int));
1964+private:
1965+ std::string m_sessionName;
1966+ pid_t m_sessionId;
1967+};
1968+}
1969+
1970+#endif // MOCK_MIR_SHELL_SESSION_H

Subscribers

People subscribed via source and target branches