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