@@ -0,0 +1,197 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "ShellApplication.h"
18+
19+// Qt
20+#include <QLibrary>
21+#include <QScreen>
22+
23+#include <libintl.h>
24+
25+// libandroid-properties
26+#include <hybris/properties/properties.h>
27+
28+// local
29+#include <paths.h>
30+#include "CachingNetworkManagerFactory.h"
31+#include "MouseTouchAdaptor.h"
32+#include "UnityCommandLineParser.h"
33+
34+ShellApplication::ShellApplication(int & argc, char ** argv, bool isMirServer)
35+ : QGuiApplication(argc, argv)
36+ , m_shellView(nullptr)
37+ , m_secondaryWindow(nullptr)
38+ , m_mouseTouchAdaptor(nullptr)
39+ , m_qmlEngine(nullptr)
40+{
41+
42+ setApplicationName(QStringLiteral("unity8"));
43+
44+ connect(this, &QGuiApplication::screenAdded, this, &ShellApplication::onScreenAdded);
45+
46+ setupQmlEngine(isMirServer);
47+
48+ UnityCommandLineParser parser(*this);
49+
50+ if (!parser.deviceName().isEmpty()) {
51+ m_deviceName = parser.deviceName();
52+ } else {
53+ char buffer[200];
54+ property_get("ro.product.device", buffer /* value */, "desktop" /* default_value*/);
55+ m_deviceName = QString(buffer);
56+ }
57+ m_qmlArgs.setDeviceName(m_deviceName);
58+
59+ m_qmlArgs.setMode(parser.mode());
60+
61+ // The testability driver is only loaded by QApplication but not by QGuiApplication.
62+ // However, QApplication depends on QWidget which would add some unneeded overhead => Let's load the testability driver on our own.
63+ if (parser.hasTestability() || getenv("QT_LOAD_TESTABILITY")) {
64+ QLibrary testLib(QStringLiteral("qttestability"));
65+ if (testLib.load()) {
66+ typedef void (*TasInitialize)(void);
67+ TasInitialize initFunction = (TasInitialize)testLib.resolve("qt_testability_init");
68+ if (initFunction) {
69+ initFunction();
70+ } else {
71+ qCritical("Library qttestability resolve failed!");
72+ }
73+ } else {
74+ qCritical("Library qttestability load failed!");
75+ }
76+ }
77+
78+ bindtextdomain("unity8", translationDirectory().toUtf8().data());
79+ textdomain("unity8");
80+
81+ m_shellView = new ShellView(m_qmlEngine, &m_qmlArgs);
82+
83+ if (parser.windowGeometry().isValid()) {
84+ m_shellView->setWidth(parser.windowGeometry().width());
85+ m_shellView->setHeight(parser.windowGeometry().height());
86+ }
87+
88+ if (parser.hasFrameless()) {
89+ m_shellView->setFlags(Qt::FramelessWindowHint);
90+ }
91+
92+ // You will need this if you want to interact with touch-only components using a mouse
93+ // Needed only when manually testing on a desktop.
94+ if (parser.hasMouseToTouch()) {
95+ m_mouseTouchAdaptor = MouseTouchAdaptor::instance();
96+ }
97+
98+
99+ // Some hard-coded policy for now.
100+ // NB: We don't support more than two screens at the moment
101+ //
102+ // TODO: Support an arbitrary number of screens and different policies
103+ // (eg cloned desktop, several desktops, etc)
104+ if (isMirServer && screens().count() == 2) {
105+ m_shellView->setScreen(screens().at(1));
106+ m_qmlArgs.setDeviceName("desktop");
107+
108+ m_secondaryWindow = new SecondaryWindow(m_qmlEngine);
109+ m_secondaryWindow->setScreen(screens().at(0));
110+ // QWindow::showFullScreen() also calls QWindow::requestActivate() and we don't want that!
111+ m_secondaryWindow->setWindowState(Qt::WindowFullScreen);
112+ m_secondaryWindow->setVisible(true);
113+ }
114+
115+ if (isMirServer || parser.hasFullscreen()) {
116+ m_shellView->showFullScreen();
117+ } else {
118+ m_shellView->show();
119+ }
120+}
121+
122+ShellApplication::~ShellApplication()
123+{
124+ destroyResources();
125+}
126+
127+void ShellApplication::destroyResources()
128+{
129+ // Deletion order is important. Don't use QScopedPointers and the like
130+ // Otherwise the process will hang on shutdown (bug somewhere I guess).
131+ delete m_shellView;
132+ m_shellView = nullptr;
133+
134+ delete m_secondaryWindow;
135+ m_secondaryWindow = nullptr;
136+
137+ delete m_mouseTouchAdaptor;
138+ m_mouseTouchAdaptor = nullptr;
139+
140+ delete m_qmlEngine;
141+ m_qmlEngine = nullptr;
142+}
143+
144+void ShellApplication::setupQmlEngine(bool isMirServer)
145+{
146+ m_qmlEngine = new QQmlEngine(this);
147+
148+ m_qmlEngine->setBaseUrl(QUrl::fromLocalFile(::qmlDirectory()));
149+
150+ prependImportPaths(m_qmlEngine, ::overrideImportPaths());
151+ if (!isMirServer) {
152+ prependImportPaths(m_qmlEngine, ::nonMirImportPaths());
153+ }
154+ appendImportPaths(m_qmlEngine, ::fallbackImportPaths());
155+
156+ m_qmlEngine->setNetworkAccessManagerFactory(new CachingNetworkManagerFactory);
157+
158+ QObject::connect(m_qmlEngine, &QQmlEngine::quit, this, &QGuiApplication::quit);
159+}
160+
161+void ShellApplication::onScreenAdded(QScreen * /*screen*/)
162+{
163+ // TODO: Support an arbitrary number of screens and different policies
164+ // (eg cloned desktop, several desktops, etc)
165+ if (screens().count() == 2) {
166+ m_shellView->setScreen(screens().at(1));
167+ m_qmlArgs.setDeviceName("desktop");
168+ // Changing the QScreen where a QWindow is drawn makes it also lose focus (besides having
169+ // its backing QPlatformWindow recreated). So lets refocus it.
170+ m_shellView->requestActivate();
171+
172+ m_secondaryWindow = new SecondaryWindow(m_qmlEngine);
173+ m_secondaryWindow->setScreen(screens().at(0));
174+
175+ // QWindow::showFullScreen() also calls QWindow::requestActivate() and we don't want that!
176+ m_secondaryWindow->setWindowState(Qt::WindowFullScreen);
177+ m_secondaryWindow->setVisible(true);
178+ }
179+}
180+
181+void ShellApplication::onScreenAboutToBeRemoved(QScreen *screen)
182+{
183+ // TODO: Support an arbitrary number of screens and different policies
184+ // (eg cloned desktop, several desktops, etc)
185+ if (screen == m_shellView->screen()) {
186+ Q_ASSERT(screens().count() > 1);
187+ Q_ASSERT(screens().at(0) != screen);
188+ Q_ASSERT(m_secondaryWindow);
189+ delete m_secondaryWindow;
190+ m_secondaryWindow = nullptr;
191+ m_shellView->setScreen(screens().first());
192+ m_qmlArgs.setDeviceName(m_deviceName);
193+ // Changing the QScreen where a QWindow is drawn makes it also lose focus (besides having
194+ // its backing QPlatformWindow recreated). So lets refocus it.
195+ m_shellView->requestActivate();
196+ }
197+}