@@ -0,0 +1,166 @@
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+{
37+
38+ setApplicationName(QStringLiteral("unity8"));
39+
40+ connect(this, &QGuiApplication::screenAdded, this, &ShellApplication::onScreenAdded);
41+
42+ setupQmlEngine(isMirServer);
43+
44+ UnityCommandLineParser parser(*this);
45+
46+ if (!parser.deviceName().isEmpty()) {
47+ m_qmlArgs.setDeviceName(parser.deviceName());
48+ } else {
49+ char buffer[200];
50+ property_get("ro.product.device", buffer /* value */, "desktop" /* default_value*/);
51+ m_qmlArgs.setDeviceName(QString(buffer));
52+ }
53+
54+ m_qmlArgs.setMode(parser.mode());
55+
56+ // The testability driver is only loaded by QApplication but not by QGuiApplication.
57+ // However, QApplication depends on QWidget which would add some unneeded overhead => Let's load the testability driver on our own.
58+ if (parser.hasTestability() || getenv("QT_LOAD_TESTABILITY")) {
59+ QLibrary testLib(QStringLiteral("qttestability"));
60+ if (testLib.load()) {
61+ typedef void (*TasInitialize)(void);
62+ TasInitialize initFunction = (TasInitialize)testLib.resolve("qt_testability_init");
63+ if (initFunction) {
64+ initFunction();
65+ } else {
66+ qCritical("Library qttestability resolve failed!");
67+ }
68+ } else {
69+ qCritical("Library qttestability load failed!");
70+ }
71+ }
72+
73+ bindtextdomain("unity8", translationDirectory().toUtf8().data());
74+ textdomain("unity8");
75+
76+ m_shellView.reset(new ShellView(m_qmlEngine, &m_qmlArgs));
77+
78+ if (parser.windowGeometry().isValid()) {
79+ m_shellView->setWidth(parser.windowGeometry().width());
80+ m_shellView->setHeight(parser.windowGeometry().height());
81+ }
82+
83+ if (parser.hasFrameless()) {
84+ m_shellView->setFlags(Qt::FramelessWindowHint);
85+ }
86+
87+ // You will need this if you want to interact with touch-only components using a mouse
88+ // Needed only when manually testing on a desktop.
89+ if (parser.hasMouseToTouch()) {
90+ m_mouseTouchAdaptor.reset(MouseTouchAdaptor::instance());
91+ }
92+
93+
94+ // Some hard-coded policy for now.
95+ // NB: We don't support more than two screens at the moment
96+ //
97+ // TODO: Support an arbitrary number of screens and different policies
98+ // (eg cloned desktop, several desktops, etc)
99+ if (isMirServer && screens().count() == 2) {
100+ m_shellView->setScreen(screens().at(1));
101+
102+ m_secondaryWindow.reset(new SecondaryWindow(m_qmlEngine));
103+ m_secondaryWindow->setScreen(screens().at(0));
104+ // QWindow::showFullScreen() also calls QWindow::requestActivate() and we don't want that!
105+ m_secondaryWindow->setWindowState(Qt::WindowFullScreen);
106+ m_secondaryWindow->setVisible(true);
107+ }
108+
109+ if (isMirServer || parser.hasFullscreen()) {
110+ m_shellView->showFullScreen();
111+ } else {
112+ m_shellView->show();
113+ }
114+}
115+
116+void ShellApplication::setupQmlEngine(bool isMirServer)
117+{
118+ m_qmlEngine = new QQmlEngine(this);
119+
120+ m_qmlEngine->setBaseUrl(QUrl::fromLocalFile(::qmlDirectory()));
121+
122+ prependImportPaths(m_qmlEngine, ::overrideImportPaths());
123+ if (!isMirServer) {
124+ prependImportPaths(m_qmlEngine, ::nonMirImportPaths());
125+ }
126+ appendImportPaths(m_qmlEngine, ::fallbackImportPaths());
127+
128+ m_qmlEngine->setNetworkAccessManagerFactory(new CachingNetworkManagerFactory);
129+
130+ QObject::connect(m_qmlEngine, &QQmlEngine::quit, this, &QGuiApplication::quit);
131+}
132+
133+void ShellApplication::onScreenAdded(QScreen * /*screen*/)
134+{
135+ // TODO: Support an arbitrary number of screens and different policies
136+ // (eg cloned desktop, several desktops, etc)
137+ if (screens().count() == 2) {
138+ m_shellView->setScreen(screens().at(1));
139+ // Changing the QScreen where a QWindow is drawn makes it also lose focus (besides having
140+ // its backing QPlatformWindow recreated). So lets refocus it.
141+ m_shellView->requestActivate();
142+
143+ m_secondaryWindow.reset(new SecondaryWindow(m_qmlEngine));
144+ m_secondaryWindow->setScreen(screens().at(0));
145+
146+ // QWindow::showFullScreen() also calls QWindow::requestActivate() and we don't want that!
147+ m_secondaryWindow->setWindowState(Qt::WindowFullScreen);
148+ m_secondaryWindow->setVisible(true);
149+ }
150+}
151+
152+void ShellApplication::onScreenAboutToBeRemoved(QScreen *screen)
153+{
154+ // TODO: Support an arbitrary number of screens and different policies
155+ // (eg cloned desktop, several desktops, etc)
156+ if (screen == m_shellView->screen()) {
157+ Q_ASSERT(screens().count() > 1);
158+ Q_ASSERT(screens().at(0) != screen);
159+ Q_ASSERT(!m_secondaryWindow.isNull());
160+ m_secondaryWindow.reset();
161+ m_shellView->setScreen(screens().first());
162+ // Changing the QScreen where a QWindow is drawn makes it also lose focus (besides having
163+ // its backing QPlatformWindow recreated). So lets refocus it.
164+ m_shellView->requestActivate();
165+ }
166+}