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