4a136079ec1a7da867cd130b41263edf2a89e5a1
[apps/homescreen.git] / homescreen / src / main.cpp
1 // SPDX-License-Identifier: Apache-2.0
2 /*
3  * Copyright (C) 2016, 2017 Mentor Graphics Development (Deutschland) GmbH
4  * Copyright (c) 2017, 2018 TOYOTA MOTOR CORPORATION
5  * Copyright (c) 2022 Konsulko Group
6  */
7
8 #include <QGuiApplication>
9 #include <QCommandLineParser>
10 #include <QtCore/QUrlQuery>
11 #include <QtGui/QGuiApplication>
12 #include <QtQml/QQmlApplicationEngine>
13 #include <QtQml/QQmlContext>
14 #include <QtQml/QQmlComponent>
15 #include <QtQml/qqml.h>
16 #include <QQuickWindow>
17 #include <QTimer>
18
19 #include <weather.h>
20 #include <bluetooth.h>
21
22 #include "applicationlauncher.h"
23 #include "statusbarmodel.h"
24 #include "mastervolume.h"
25 #include "homescreenhandler.h"
26 #include "hmi-debug.h"
27
28 #include <qpa/qplatformnativeinterface.h>
29 #include <wayland-client.h>
30
31 #include "wayland-agl-shell-client-protocol.h"
32 #include "wayland-agl-shell-desktop-client-protocol.h"
33 #include "shell.h"
34
35 #ifndef MIN
36 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
37 #endif
38
39 struct shell_data {
40         struct agl_shell *shell;
41         struct agl_shell_desktop *shell_desktop;
42         bool wait_for_bound;
43         bool bound_ok;
44         int ver;
45 };
46
47 static void
48 agl_shell_desktop_application(void *data,
49                               struct agl_shell_desktop *agl_shell_desktop,
50                               const char *app_id)
51 {
52         HomescreenHandler *homescreenHandler = static_cast<HomescreenHandler *>(data);
53
54         if (homescreenHandler)
55                 homescreenHandler->addAppToStack(app_id);
56 }
57
58 static void
59 agl_shell_desktop_state_app(void *data,
60                             struct agl_shell_desktop *agl_shell_desktop,
61                             const char *app_id,
62                             const char *app_data,
63                             uint32_t state,
64                             uint32_t role)
65 {
66         HomescreenHandler *homescreenHandler = static_cast<HomescreenHandler *>(data);
67
68         if (homescreenHandler && state == AGL_SHELL_DESKTOP_APP_STATE_DESTROYED)
69                 homescreenHandler->deactivateApp(app_id);
70 }
71
72 static void
73 agl_shell_bound_ok(void *data, struct agl_shell *agl_shell)
74 {
75         struct shell_data *shell_data = static_cast<struct shell_data *>(data);
76         shell_data->wait_for_bound = false;
77
78         shell_data->bound_ok = true;
79 }
80
81 static void
82 agl_shell_bound_fail(void *data, struct agl_shell *agl_shell)
83 {
84         struct shell_data *shell_data = static_cast<struct shell_data *>(data);
85         shell_data->wait_for_bound = false;
86
87         shell_data->bound_ok = false;
88 }
89
90 #ifdef AGL_SHELL_BOUND_OK_SINCE_VERSION
91 static const struct agl_shell_listener shell_listener = {
92         agl_shell_bound_ok,
93         agl_shell_bound_fail,
94 };
95 #endif
96
97 static const struct agl_shell_desktop_listener shell_desktop_listener = {
98    agl_shell_desktop_application,
99    agl_shell_desktop_state_app
100 };
101
102 static void
103 global_add(void *data, struct wl_registry *reg, uint32_t name,
104            const char *interface, uint32_t ver)
105 {
106         struct shell_data *shell_data = static_cast<struct shell_data *>(data);
107
108         if (!shell_data)
109                 return;
110
111         if (strcmp(interface, agl_shell_interface.name) == 0) {
112                 if (ver >= 2) {
113                         shell_data->shell =
114                                 static_cast<struct agl_shell *>(
115                                         wl_registry_bind(reg, name, &agl_shell_interface, MIN(2, ver)));
116 #ifdef AGL_SHELL_BOUND_OK_SINCE_VERSION
117                         agl_shell_add_listener(shell_data->shell, &shell_listener, data);
118 #endif
119                 } else {
120                         shell_data->shell =
121                                 static_cast<struct agl_shell *>(
122                                         wl_registry_bind(reg, name, &agl_shell_interface, 1));
123                 }
124                 shell_data->ver = ver;
125
126         }
127
128         if (strcmp(interface, agl_shell_desktop_interface.name) == 0) {
129                 shell_data->shell_desktop = static_cast<struct agl_shell_desktop *>(
130                         wl_registry_bind(reg, name, &agl_shell_desktop_interface, 1)
131                 );
132         }
133 }
134
135 static void
136 global_remove(void *data, struct wl_registry *reg, uint32_t id)
137 {
138         /* Don't care */
139         (void) data;
140         (void) reg;
141         (void) id;
142 }
143
144 static const struct wl_registry_listener registry_listener = {
145         global_add,
146         global_remove,
147 };
148
149 static struct wl_surface *
150 getWlSurface(QPlatformNativeInterface *native, QWindow *window)
151 {
152         void *surf = native->nativeResourceForWindow("surface", window);
153         return static_cast<struct ::wl_surface *>(surf);
154 }
155
156 static struct wl_output *
157 getWlOutput(QPlatformNativeInterface *native, QScreen *screen)
158 {
159         void *output = native->nativeResourceForScreen("output", screen);
160         return static_cast<struct ::wl_output*>(output);
161 }
162
163 static struct wl_display *
164 getWlDisplay(QPlatformNativeInterface *native)
165 {
166        return static_cast<struct wl_display *>(
167                native->nativeResourceForIntegration("display")
168        );
169 }
170
171
172 static void
173 register_agl_shell(QPlatformNativeInterface *native, struct shell_data *shell_data)
174 {
175         struct wl_display *wl;
176         struct wl_registry *registry;
177
178         wl = getWlDisplay(native);
179         registry = wl_display_get_registry(wl);
180
181         wl_registry_add_listener(registry, &registry_listener, shell_data);
182
183         /* Roundtrip to get all globals advertised by the compositor */
184         wl_display_roundtrip(wl);
185         wl_registry_destroy(registry);
186 }
187
188 static struct wl_surface *
189 create_component(QPlatformNativeInterface *native, QQmlComponent *comp,
190                  QScreen *screen, QObject **qobj)
191 {
192         QObject *obj = comp->create();
193         obj->setParent(screen);
194
195         QWindow *win = qobject_cast<QWindow *>(obj);
196         *qobj = obj;
197
198         return getWlSurface(native, win);
199 }
200
201 static QScreen *
202 find_screen(const char *screen_name)
203 {
204         QList<QScreen *> screens = qApp->screens();
205         QScreen *found = nullptr;
206         QString qstr_name = QString::fromUtf8(screen_name, -1);
207
208         for (QScreen *xscreen : screens) {
209                 if (qstr_name == xscreen->name()) {
210                         found = xscreen;
211                         break;
212                 }
213         }
214
215         return found;
216 }
217
218 static void
219 load_agl_shell_app(QPlatformNativeInterface *native,
220                    QQmlApplicationEngine *engine,
221                    struct agl_shell *agl_shell,
222                    const char *screen_name,
223                     bool is_demo)
224 {
225         struct wl_surface *bg, *top, *bottom;
226         struct wl_output *output;
227         QObject *qobj_bg, *qobj_top, *qobj_bottom;
228         QScreen *screen = nullptr;
229
230         if (is_demo) {
231                 QQmlComponent bg_comp(engine, QUrl("qrc:/background_demo.qml"));
232                 qInfo() << bg_comp.errors();
233
234                 QQmlComponent top_comp(engine, QUrl("qrc:/toppanel_demo.qml"));
235                 qInfo() << top_comp.errors();
236
237                 QQmlComponent bot_comp(engine, QUrl("qrc:/bottompanel_demo.qml"));
238                 qInfo() << bot_comp.errors();
239
240                 top = create_component(native, &top_comp, screen, &qobj_top);
241                 bottom = create_component(native, &bot_comp, screen, &qobj_bottom);
242                 bg = create_component(native, &bg_comp, screen, &qobj_bg);
243         } else {
244                 QQmlComponent bg_comp(engine, QUrl("qrc:/background.qml"));
245                 qInfo() << bg_comp.errors();
246
247                 QQmlComponent top_comp(engine, QUrl("qrc:/toppanel.qml"));
248                 qInfo() << top_comp.errors();
249
250                 QQmlComponent bot_comp(engine, QUrl("qrc:/bottompanel.qml"));
251                 qInfo() << bot_comp.errors();
252
253                 top = create_component(native, &top_comp, screen, &qobj_top);
254                 bottom = create_component(native, &bot_comp, screen, &qobj_bottom);
255                 bg = create_component(native, &bg_comp, screen, &qobj_bg);
256         }
257
258         if (!screen_name)
259                 screen = qApp->primaryScreen();
260         else
261                 screen = find_screen(screen_name);
262
263         if (!screen) {
264                 qDebug() << "No outputs present in the system.";
265                 return;
266         }
267
268         qDebug() << "found primary screen " << qApp->primaryScreen()->name() <<
269                 "first screen " << qApp->screens().first()->name();
270         output = getWlOutput(native, screen);
271
272         /* engine.rootObjects() works only if we had a load() */
273         StatusBarModel *statusBar = qobj_top->findChild<StatusBarModel *>("statusBar");
274         if (statusBar) {
275                 qDebug() << "got statusBar objectname, doing init()";
276                 statusBar->init(engine->rootContext());
277         }
278
279         agl_shell_set_panel(agl_shell, top, output, AGL_SHELL_EDGE_TOP);
280         agl_shell_set_panel(agl_shell, bottom, output, AGL_SHELL_EDGE_BOTTOM);
281         qDebug() << "Setting homescreen to screen  " << screen->name();
282
283         agl_shell_set_background(agl_shell, bg, output);
284
285         /* Delay the ready signal until after Qt has done all of its own setup
286          * in a.exec() */
287         QTimer::singleShot(500, [agl_shell](){
288                 agl_shell_ready(agl_shell);
289         });
290 }
291
292 int main(int argc, char *argv[])
293 {
294         setenv("QT_QPA_PLATFORM", "wayland", 1);
295         setenv("QT_QUICK_CONTROLS_STYLE", "AGL", 1);
296
297         QGuiApplication app(argc, argv);
298         const char *screen_name;
299         bool is_demo_val = false;
300         int ret = 0;
301         struct shell_data shell_data = { nullptr, nullptr, true, false, 0 };
302
303         QPlatformNativeInterface *native = qApp->platformNativeInterface();
304         screen_name = getenv("HOMESCREEN_START_SCREEN");
305
306         const char *is_demo = getenv("HOMESCREEN_DEMO_CI");
307         if (is_demo && strcmp(is_demo, "1") == 0)
308                 is_demo_val = true;
309
310         QCoreApplication::setOrganizationDomain("LinuxFoundation");
311         QCoreApplication::setOrganizationName("AutomotiveGradeLinux");
312         QCoreApplication::setApplicationName("HomeScreen");
313         QCoreApplication::setApplicationVersion("0.7.0");
314
315         // we need to have an app_id
316         app.setDesktopFileName("homescreen");
317
318         register_agl_shell(native, &shell_data);
319         if (!shell_data.shell) {
320                 fprintf(stderr, "agl_shell extension is not advertised. "
321                         "Are you sure that agl-compositor is running?\n");
322                 exit(EXIT_FAILURE);
323         }
324         if (!shell_data.shell_desktop) {
325                 fprintf(stderr, "agl_shell_desktop extension is not advertised. "
326                         "Are you sure that agl-compositor is running?\n");
327                 exit(EXIT_FAILURE);
328         }
329
330         qDebug() << "agl-shell interface is at version " << shell_data.ver;
331         if (shell_data.ver >= 2) {
332                 while (ret != -1 && shell_data.wait_for_bound) {
333                         ret = wl_display_dispatch(getWlDisplay(native));
334
335                         if (shell_data.wait_for_bound)
336                                 continue;
337                 }
338
339                 if (!shell_data.bound_ok) {
340                         qInfo() << "agl_shell extension already in use by other shell client.";
341                         exit(EXIT_FAILURE);
342                 }
343         }
344
345
346         std::shared_ptr<struct agl_shell> agl_shell{shell_data.shell, agl_shell_destroy};
347         Shell *aglShell = new Shell(agl_shell, &app);
348
349         // Import C++ class to QML
350         qmlRegisterType<StatusBarModel>("HomeScreen", 1, 0, "StatusBarModel");
351         qmlRegisterType<MasterVolume>("MasterVolume", 1, 0, "MasterVolume");
352
353         ApplicationLauncher *launcher = new ApplicationLauncher();
354         launcher->setCurrent(QStringLiteral("launcher"));
355         HomescreenHandler* homescreenHandler = new HomescreenHandler(aglShell, launcher);
356
357         agl_shell_desktop_add_listener(shell_data.shell_desktop, &shell_desktop_listener, homescreenHandler);
358
359         QQmlApplicationEngine engine;
360         QQmlContext *context = engine.rootContext();
361
362         context->setContextProperty("homescreenHandler", homescreenHandler);
363         context->setContextProperty("launcher", launcher);
364         context->setContextProperty("weather", new Weather());
365         context->setContextProperty("bluetooth", new Bluetooth(false, context));
366
367         // We add it here even if we don't use it
368         context->setContextProperty("shell", aglShell);
369
370         // Instead of loading main.qml we load one-by-one each of the QMLs,
371         // divided now between several surfaces: panels, background.
372         load_agl_shell_app(native, &engine, shell_data.shell, screen_name, is_demo_val);
373
374         return app.exec();
375 }