Omit std::
[apps/agl-service-windowmanager.git] / src / main.cpp
1 /*
2  * Copyright (c) 2017 TOYOTA MOTOR CORPORATION
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <unistd.h>
18 #include <algorithm>
19 #include <mutex>
20 #include <json.h>
21 #include "window_manager.hpp"
22 #include "json_helper.hpp"
23 #include "wayland_ivi_wm.hpp"
24
25 extern "C"
26 {
27 #include <afb/afb-binding.h>
28 #include <systemd/sd-event.h>
29 }
30
31 typedef struct WMClientCtxt
32 {
33     std::string name;
34     std::string role;
35     WMClientCtxt(const char *appName, const char* appRole)
36     {
37         name = appName;
38         role = appRole;
39     }
40 } WMClientCtxt;
41
42 struct afb_instance
43 {
44     std::unique_ptr<wl::display> display;
45     wm::WindowManager wmgr;
46
47     afb_instance() : display{new wl::display}, wmgr{this->display.get()} {}
48
49     int init();
50 };
51
52 struct afb_instance *g_afb_instance;
53 std::mutex binding_m;
54
55 int afb_instance::init()
56 {
57     return this->wmgr.init();
58 }
59
60 int display_event_callback(sd_event_source *evs, int /*fd*/, uint32_t events,
61                            void * /*data*/)
62 {
63     if ((events & EPOLLHUP) != 0)
64     {
65         HMI_ERROR("The compositor hung up, dying now.");
66         delete g_afb_instance;
67         g_afb_instance = nullptr;
68         goto error;
69     }
70
71     if ((events & EPOLLIN) != 0u)
72     {
73         {
74             g_afb_instance->wmgr.display->read_events();
75             g_afb_instance->wmgr.set_pending_events();
76         }
77         {
78             // We want do dispatch pending wayland events from within
79             // the API context
80             afb_service_call("windowmanager", "ping", json_object_new_object(),
81                             [](void *c, int st, json_object *j) {
82                             },
83                             nullptr);
84         }
85     }
86
87     return 0;
88
89 error:
90     sd_event_source_unref(evs);
91     if (getenv("WINMAN_EXIT_ON_HANGUP") != nullptr)
92     {
93         exit(1);
94     }
95     return -1;
96 }
97
98 int _binding_init()
99 {
100     HMI_NOTICE("WinMan ver. %s", WINMAN_VERSION_STRING);
101
102     if (g_afb_instance != nullptr)
103     {
104         HMI_ERROR("Wayland context already initialized?");
105         return 0;
106     }
107
108     if (getenv("XDG_RUNTIME_DIR") == nullptr)
109     {
110         HMI_ERROR("Environment variable XDG_RUNTIME_DIR not set");
111         goto error;
112     }
113
114     {
115         // wait until wayland compositor starts up.
116         int cnt = 0;
117         g_afb_instance = new afb_instance;
118         while (!g_afb_instance->display->ok())
119         {
120             cnt++;
121             if (20 <= cnt)
122             {
123                 HMI_ERROR("Could not connect to compositor");
124                 goto error;
125             }
126             HMI_ERROR("Wait to start weston ...");
127             sleep(1);
128             delete g_afb_instance;
129             g_afb_instance = new afb_instance;
130         }
131     }
132
133     if (g_afb_instance->init() == -1)
134     {
135         HMI_ERROR("Could not connect to compositor");
136         goto error;
137     }
138
139     {
140         int ret = sd_event_add_io(afb_daemon_get_event_loop(), nullptr,
141                                   g_afb_instance->display->get_fd(), EPOLLIN,
142                                   display_event_callback, g_afb_instance);
143         if (ret < 0)
144         {
145             HMI_ERROR("Could not initialize afb_instance event handler: %d", -ret);
146             goto error;
147         }
148     }
149
150     atexit([] { delete g_afb_instance; });
151
152     return 0;
153
154 error:
155     delete g_afb_instance;
156     g_afb_instance = nullptr;
157     return -1;
158 }
159
160 int binding_init() noexcept
161 {
162     try
163     {
164         return _binding_init();
165     }
166     catch (std::exception &e)
167     {
168         HMI_ERROR("Uncaught exception in binding_init(): %s", e.what());
169     }
170     return -1;
171 }
172
173 static void cbRemoveClientCtxt(void *data)
174 {
175     WMClientCtxt *ctxt = (WMClientCtxt *)data;
176     if (ctxt == nullptr)
177     {
178         return;
179     }
180     HMI_DEBUG("remove app %s", ctxt->name.c_str());
181
182     // Policy Manager does not know this app was killed,
183     // so notify it by deactivate request.
184     g_afb_instance->wmgr.api_deactivate_surface(
185         ctxt->name.c_str(), ctxt->role.c_str(),
186         [](const char *errmsg) {
187             if (errmsg != nullptr)
188             {
189                 HMI_ERROR(errmsg);
190                 return;
191             }
192         });
193
194     g_afb_instance->wmgr.removeClient(ctxt->name);
195     delete ctxt;
196 }
197
198 static void createSecurityContext(afb_req req, const char* appid, const char* role)
199 {
200     WMClientCtxt *ctxt = (WMClientCtxt *)afb_req_context_get(req);
201     if (!ctxt)
202     {
203         // Create Security Context at first time
204         const char *new_role = g_afb_instance->wmgr.convertRoleOldToNew(role);
205         WMClientCtxt *ctxt = new WMClientCtxt(appid, new_role);
206         HMI_DEBUG("create session for %s", ctxt->name.c_str());
207         afb_req_session_set_LOA(req, 1);
208         afb_req_context_set(req, ctxt, cbRemoveClientCtxt);
209     }
210 }
211
212 void windowmanager_requestsurface(afb_req req) noexcept
213 {
214     std::lock_guard<std::mutex> guard(binding_m);
215     if (g_afb_instance == nullptr)
216     {
217         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
218         return;
219     }
220
221     try
222     {
223         const char *a_drawing_name = afb_req_value(req, "drawing_name");
224         if (!a_drawing_name)
225         {
226             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
227             return;
228         }
229
230         const char *appid = afb_req_get_application_id(req);
231         auto ret = g_afb_instance->wmgr.api_request_surface(
232             appid, a_drawing_name);
233         if (ret.is_err())
234         {
235             afb_req_fail(req, "failed", ret.unwrap_err());
236             return;
237         }
238
239         createSecurityContext(req, appid, a_drawing_name);
240
241         afb_req_success(req, json_object_new_int(ret.unwrap()), "success");
242     }
243     catch (std::exception &e)
244     {
245         afb_req_fail_f(req, "failed", "Uncaught exception while calling requestsurface: %s", e.what());
246         return;
247     }
248 }
249
250 void windowmanager_requestsurfacexdg(afb_req req) noexcept
251 {
252     std::lock_guard<std::mutex> guard(binding_m);
253     if (g_afb_instance == nullptr)
254     {
255         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
256         return;
257     }
258
259     try
260     {
261         json_object *jreq = afb_req_json(req);
262
263         json_object *j_drawing_name = nullptr;
264         if (!json_object_object_get_ex(jreq, "drawing_name", &j_drawing_name))
265         {
266             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
267             return;
268         }
269         char const *a_drawing_name = json_object_get_string(j_drawing_name);
270
271         json_object *j_ivi_id = nullptr;
272         if (!json_object_object_get_ex(jreq, "ivi_id", &j_ivi_id))
273         {
274             afb_req_fail(req, "failed", "Need char const* argument ivi_id");
275             return;
276         }
277         char const *a_ivi_id = json_object_get_string(j_ivi_id);
278         char const *appid = afb_req_get_application_id(req);
279         auto ret = g_afb_instance->wmgr.api_request_surface(
280             appid, a_drawing_name, a_ivi_id);
281
282         if (ret != nullptr)
283         {
284             afb_req_fail(req, "failed", ret);
285             return;
286         }
287
288         createSecurityContext(req, appid, a_drawing_name);
289
290         afb_req_success(req, NULL, "success");
291     }
292     catch (std::exception &e)
293     {
294         afb_req_fail_f(req, "failed", "Uncaught exception while calling requestsurfacexdg: %s", e.what());
295         return;
296     }
297 }
298
299 void windowmanager_activatewindow(afb_req req) noexcept
300 {
301     std::lock_guard<std::mutex> guard(binding_m);
302     if (g_afb_instance == nullptr)
303     {
304         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
305         return;
306     }
307
308     try
309     {
310         const char *a_drawing_name = afb_req_value(req, "drawing_name");
311         if (!a_drawing_name)
312         {
313             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
314             return;
315         }
316
317         const char *a_drawing_area = afb_req_value(req, "drawing_area");
318         if (!a_drawing_area)
319         {
320             afb_req_fail(req, "failed", "Need char const* argument drawing_area");
321             return;
322         }
323
324         g_afb_instance->wmgr.api_activate_surface(
325             afb_req_get_application_id(req),
326             a_drawing_name, a_drawing_area,
327             [&req](const char *errmsg) {
328                 if (errmsg != nullptr)
329                 {
330                     HMI_ERROR(errmsg);
331                     afb_req_fail(req, "failed", errmsg);
332                     return;
333                 }
334                 afb_req_success(req, NULL, "success");
335             });
336     }
337     catch (std::exception &e)
338     {
339         HMI_WARNING("failed: Uncaught exception while calling activatesurface: %s", e.what());
340         g_afb_instance->wmgr.exceptionProcessForTransition();
341         return;
342     }
343 }
344
345 void windowmanager_deactivatewindow(afb_req req) noexcept
346 {
347     std::lock_guard<std::mutex> guard(binding_m);
348     if (g_afb_instance == nullptr)
349     {
350         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
351         return;
352     }
353
354     try
355     {
356         const char *a_drawing_name = afb_req_value(req, "drawing_name");
357         if (!a_drawing_name)
358         {
359             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
360             return;
361         }
362
363         g_afb_instance->wmgr.api_deactivate_surface(
364             afb_req_get_application_id(req), a_drawing_name,
365             [&req](const char *errmsg) {
366                 if (errmsg != nullptr)
367                 {
368                     HMI_ERROR(errmsg);
369                     afb_req_fail(req, "failed", errmsg);
370                     return;
371                 }
372                 afb_req_success(req, NULL, "success");
373             });
374     }
375     catch (std::exception &e)
376     {
377         HMI_WARNING("failed: Uncaught exception while calling deactivatesurface: %s", e.what());
378         g_afb_instance->wmgr.exceptionProcessForTransition();
379         return;
380     }
381 }
382
383 void windowmanager_enddraw(afb_req req) noexcept
384 {
385     std::lock_guard<std::mutex> guard(binding_m);
386     if (g_afb_instance == nullptr)
387     {
388         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
389         return;
390     }
391
392     try
393     {
394         const char *a_drawing_name = afb_req_value(req, "drawing_name");
395         if (!a_drawing_name)
396         {
397             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
398             return;
399         }
400         afb_req_success(req, NULL, "success");
401
402         g_afb_instance->wmgr.api_enddraw(
403             afb_req_get_application_id(req), a_drawing_name);
404     }
405     catch (std::exception &e)
406     {
407         HMI_WARNING("failed: Uncaught exception while calling enddraw: %s", e.what());
408         g_afb_instance->wmgr.exceptionProcessForTransition();
409         return;
410     }
411 }
412
413 void windowmanager_getdisplayinfo_thunk(afb_req req) noexcept
414 {
415     std::lock_guard<std::mutex> guard(binding_m);
416     if (g_afb_instance == nullptr)
417     {
418         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
419         return;
420     }
421
422     try
423     {
424         auto ret = g_afb_instance->wmgr.api_get_display_info();
425         if (ret.is_err())
426         {
427             afb_req_fail(req, "failed", ret.unwrap_err());
428             return;
429         }
430
431         afb_req_success(req, ret.unwrap(), "success");
432     }
433     catch (std::exception &e)
434     {
435         afb_req_fail_f(req, "failed", "Uncaught exception while calling getdisplayinfo: %s", e.what());
436         return;
437     }
438 }
439
440 void windowmanager_getareainfo_thunk(afb_req req) noexcept
441 {
442     std::lock_guard<std::mutex> guard(binding_m);
443     if (g_afb_instance == nullptr)
444     {
445         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
446         return;
447     }
448
449     try
450     {
451         json_object *jreq = afb_req_json(req);
452
453         json_object *j_drawing_name = nullptr;
454         if (!json_object_object_get_ex(jreq, "drawing_name", &j_drawing_name))
455         {
456             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
457             return;
458         }
459         char const *a_drawing_name = json_object_get_string(j_drawing_name);
460
461         auto ret = g_afb_instance->wmgr.api_get_area_info(a_drawing_name);
462         if (ret.is_err())
463         {
464             afb_req_fail(req, "failed", ret.unwrap_err());
465             return;
466         }
467
468         afb_req_success(req, ret.unwrap(), "success");
469     }
470     catch (std::exception &e)
471     {
472         afb_req_fail_f(req, "failed", "Uncaught exception while calling getareainfo: %s", e.what());
473         return;
474     }
475 }
476
477 void windowmanager_wm_subscribe(afb_req req) noexcept
478 {
479     std::lock_guard<std::mutex> guard(binding_m);
480     if (g_afb_instance == nullptr)
481     {
482         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
483         return;
484     }
485
486     try
487     {
488         json_object *jreq = afb_req_json(req);
489         json_object *j = nullptr;
490         if (!json_object_object_get_ex(jreq, "event", &j))
491         {
492             afb_req_fail(req, "failed", "Need char const* argument event");
493             return;
494         }
495         int event_type = json_object_get_int(j);
496         const char *event_name = g_afb_instance->wmgr.kListEventName[event_type];
497         struct afb_event event = g_afb_instance->wmgr.map_afb_event[event_name];
498         int ret = afb_req_subscribe(req, event);
499         if (ret)
500         {
501             afb_req_fail(req, "failed", "Error: afb_req_subscribe()");
502             return;
503         }
504         afb_req_success(req, NULL, "success");
505     }
506     catch (std::exception &e)
507     {
508         afb_req_fail_f(req, "failed", "Uncaught exception while calling wm_subscribe: %s", e.what());
509         return;
510     }
511 }
512
513 void windowmanager_list_drawing_names(afb_req req) noexcept
514 {
515     std::lock_guard<std::mutex> guard(binding_m);
516     if (g_afb_instance == nullptr)
517     {
518         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
519         return;
520     }
521
522     try
523     {
524
525         nlohmann::json j = g_afb_instance->wmgr.id_alloc.name2id;
526         auto ret = wm::Ok(json_tokener_parse(j.dump().c_str()));
527         if (ret.is_err())
528         {
529             afb_req_fail(req, "failed", ret.unwrap_err());
530             return;
531         }
532
533         afb_req_success(req, ret.unwrap(), "success");
534     }
535     catch (std::exception &e)
536     {
537         afb_req_fail_f(req, "failed", "Uncaught exception while calling list_drawing_names: %s", e.what());
538         return;
539     }
540 }
541
542 void windowmanager_ping(afb_req req) noexcept
543 {
544     std::lock_guard<std::mutex> guard(binding_m);
545     if (g_afb_instance == nullptr)
546     {
547         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
548         return;
549     }
550
551     try
552     {
553
554         g_afb_instance->wmgr.api_ping();
555
556         afb_req_success(req, NULL, "success");
557     }
558     catch (std::exception &e)
559     {
560         afb_req_fail_f(req, "failed", "Uncaught exception while calling ping: %s", e.what());
561         return;
562     }
563 }
564
565 void windowmanager_debug_status(afb_req req) noexcept
566 {
567     std::lock_guard<std::mutex> guard(binding_m);
568     if (g_afb_instance == nullptr)
569     {
570         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
571         return;
572     }
573
574     try
575     {
576
577         json_object *jr = json_object_new_object();
578         json_object_object_add(jr, "surfaces",
579                                to_json(g_afb_instance->wmgr.controller->sprops));
580         json_object_object_add(jr, "layers", to_json(g_afb_instance->wmgr.controller->lprops));
581
582         afb_req_success(req, jr, "success");
583     }
584     catch (std::exception &e)
585     {
586         afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_status: %s", e.what());
587         return;
588     }
589 }
590
591 void windowmanager_debug_layers(afb_req req) noexcept
592 {
593     std::lock_guard<std::mutex> guard(binding_m);
594     if (g_afb_instance == nullptr)
595     {
596         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
597         return;
598     }
599
600     try
601     {
602         auto ret = wm::Ok(json_tokener_parse(g_afb_instance->wmgr.layers.to_json().dump().c_str()));
603
604         afb_req_success(req, ret, "success");
605     }
606     catch (std::exception &e)
607     {
608         afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_layers: %s", e.what());
609         return;
610     }
611 }
612
613 void windowmanager_debug_surfaces(afb_req req) noexcept
614 {
615     std::lock_guard<std::mutex> guard(binding_m);
616     if (g_afb_instance == nullptr)
617     {
618         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
619         return;
620     }
621
622     try
623     {
624
625         auto ret = wm::Ok(to_json(g_afb_instance->wmgr.controller->sprops));
626         if (ret.is_err())
627         {
628             afb_req_fail(req, "failed", ret.unwrap_err());
629             return;
630         }
631
632         afb_req_success(req, ret.unwrap(), "success");
633     }
634     catch (std::exception &e)
635     {
636         afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_surfaces: %s", e.what());
637         return;
638     }
639 }
640
641 void windowmanager_debug_terminate(afb_req req) noexcept
642 {
643     std::lock_guard<std::mutex> guard(binding_m);
644     if (g_afb_instance == nullptr)
645     {
646         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
647         return;
648     }
649
650     try
651     {
652
653         if (getenv("WINMAN_DEBUG_TERMINATE") != nullptr)
654         {
655             raise(SIGKILL); // afb-daemon kills it's pgroup using TERM, which
656                             // doesn't play well with perf
657         }
658
659         afb_req_success(req, NULL, "success");
660     }
661     catch (std::exception &e)
662     {
663         afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_terminate: %s", e.what());
664         return;
665     }
666 }
667
668 const struct afb_verb_v2 windowmanager_verbs[] = {
669     {"requestSurface", windowmanager_requestsurface, nullptr, nullptr, AFB_SESSION_NONE},
670     {"requestSurfaceXDG", windowmanager_requestsurfacexdg, nullptr, nullptr, AFB_SESSION_NONE},
671     {"activateWindow", windowmanager_activatewindow, nullptr, nullptr, AFB_SESSION_NONE},
672     {"deactivateWindow", windowmanager_deactivatewindow, nullptr, nullptr, AFB_SESSION_NONE},
673     {"endDraw", windowmanager_enddraw, nullptr, nullptr, AFB_SESSION_NONE},
674     {"getDisplayInfo", windowmanager_getdisplayinfo_thunk, nullptr, nullptr, AFB_SESSION_NONE},
675     {"getAreaInfo", windowmanager_getareainfo_thunk, nullptr, nullptr, AFB_SESSION_NONE},
676     {"wm_subscribe", windowmanager_wm_subscribe, nullptr, nullptr, AFB_SESSION_NONE},
677     {"list_drawing_names", windowmanager_list_drawing_names, nullptr, nullptr, AFB_SESSION_NONE},
678     {"ping", windowmanager_ping, nullptr, nullptr, AFB_SESSION_NONE},
679     {"debug_status", windowmanager_debug_status, nullptr, nullptr, AFB_SESSION_NONE},
680     {"debug_layers", windowmanager_debug_layers, nullptr, nullptr, AFB_SESSION_NONE},
681     {"debug_surfaces", windowmanager_debug_surfaces, nullptr, nullptr, AFB_SESSION_NONE},
682     {"debug_terminate", windowmanager_debug_terminate, nullptr, nullptr, AFB_SESSION_NONE},
683     {}};
684
685 extern "C" const struct afb_binding_v2 afbBindingV2 = {
686     "windowmanager", nullptr, nullptr, windowmanager_verbs, nullptr, binding_init, nullptr, 0};