Migrate hmi-debug into util
[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         char *appid = afb_req_get_application_id(req);
231         if(appid)
232         {
233             auto ret = g_afb_instance->wmgr.api_request_surface(
234                 appid, a_drawing_name);
235             if (ret.is_err())
236             {
237                 afb_req_fail(req, "failed", ret.unwrap_err());
238             }
239             else
240             {
241                 createSecurityContext(req, appid, a_drawing_name);
242                 afb_req_success(req, json_object_new_int(ret.unwrap()), "success");
243             }
244             free(appid);
245         }
246         else
247         {
248             afb_req_fail(req, "failed", nullptr);
249         }
250     }
251     catch (std::exception &e)
252     {
253         afb_req_fail_f(req, "failed", "Uncaught exception while calling requestsurface: %s", e.what());
254         return;
255     }
256 }
257
258 void windowmanager_requestsurfacexdg(afb_req req) noexcept
259 {
260     std::lock_guard<std::mutex> guard(binding_m);
261     if (g_afb_instance == nullptr)
262     {
263         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
264         return;
265     }
266
267     try
268     {
269         json_object *jreq = afb_req_json(req);
270
271         json_object *j_drawing_name = nullptr;
272         if (!json_object_object_get_ex(jreq, "drawing_name", &j_drawing_name))
273         {
274             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
275             return;
276         }
277         char const *a_drawing_name = json_object_get_string(j_drawing_name);
278
279         json_object *j_ivi_id = nullptr;
280         if (!json_object_object_get_ex(jreq, "ivi_id", &j_ivi_id))
281         {
282             afb_req_fail(req, "failed", "Need char const* argument ivi_id");
283             return;
284         }
285         char const *a_ivi_id = json_object_get_string(j_ivi_id);
286         char *appid = afb_req_get_application_id(req);
287         if(appid)
288         {
289             auto ret = g_afb_instance->wmgr.api_request_surface(
290                 appid, a_drawing_name, a_ivi_id);
291
292             if (ret != nullptr)
293             {
294                 afb_req_fail(req, "failed", ret);
295             }
296             else
297             {
298                 createSecurityContext(req, appid, a_drawing_name);
299                 afb_req_success(req, NULL, "success");
300             }
301             free(appid);
302         }
303     }
304     catch (std::exception &e)
305     {
306         afb_req_fail_f(req, "failed", "Uncaught exception while calling requestsurfacexdg: %s", e.what());
307         return;
308     }
309 }
310
311 void windowmanager_activatewindow(afb_req req) noexcept
312 {
313     std::lock_guard<std::mutex> guard(binding_m);
314     if (g_afb_instance == nullptr)
315     {
316         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
317         return;
318     }
319
320     try
321     {
322         const char *a_drawing_name = afb_req_value(req, "drawing_name");
323         if (!a_drawing_name)
324         {
325             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
326             return;
327         }
328
329         const char *a_drawing_area = afb_req_value(req, "drawing_area");
330         if (!a_drawing_area)
331         {
332             afb_req_fail(req, "failed", "Need char const* argument drawing_area");
333             return;
334         }
335
336         char* appid = afb_req_get_application_id(req);
337         if(appid)
338         {
339             g_afb_instance->wmgr.api_activate_surface(
340                 appid, a_drawing_name, a_drawing_area,
341                 [&req](const char *errmsg) {
342                     if (errmsg != nullptr)
343                     {
344                         HMI_ERROR("wm", errmsg);
345                         afb_req_fail(req, "failed", errmsg);
346                         return;
347                     }
348                     afb_req_success(req, NULL, "success");
349                 });
350             free(appid);
351         }
352     }
353     catch (std::exception &e)
354     {
355         HMI_WARNING("failed: Uncaught exception while calling activatesurface: %s", e.what());
356         g_afb_instance->wmgr.exceptionProcessForTransition();
357         return;
358     }
359 }
360
361 void windowmanager_deactivatewindow(afb_req req) noexcept
362 {
363     std::lock_guard<std::mutex> guard(binding_m);
364     if (g_afb_instance == nullptr)
365     {
366         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
367         return;
368     }
369
370     try
371     {
372         const char *a_drawing_name = afb_req_value(req, "drawing_name");
373         if (!a_drawing_name)
374         {
375             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
376             return;
377         }
378
379         char* appid = afb_req_get_application_id(req);
380         if(appid)
381         {
382             g_afb_instance->wmgr.api_deactivate_surface(
383                 appid, a_drawing_name,
384                 [&req](const char *errmsg) {
385                     if (errmsg != nullptr)
386                     {
387                         HMI_ERROR("wm", errmsg);
388                         afb_req_fail(req, "failed", errmsg);
389                         return;
390                     }
391                     afb_req_success(req, NULL, "success");
392                 });
393             free(appid);
394         }
395     }
396     catch (std::exception &e)
397     {
398         HMI_WARNING("failed: Uncaught exception while calling deactivatesurface: %s", e.what());
399         g_afb_instance->wmgr.exceptionProcessForTransition();
400         return;
401     }
402 }
403
404 void windowmanager_enddraw(afb_req req) noexcept
405 {
406     std::lock_guard<std::mutex> guard(binding_m);
407     if (g_afb_instance == nullptr)
408     {
409         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
410         return;
411     }
412
413     try
414     {
415         const char *a_drawing_name = afb_req_value(req, "drawing_name");
416         if (!a_drawing_name)
417         {
418             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
419             return;
420         }
421         afb_req_success(req, NULL, "success");
422
423         char* appid = afb_req_get_application_id(req);
424         if(appid)
425         {
426             g_afb_instance->wmgr.api_enddraw(appid, a_drawing_name);
427             free(appid);
428         }
429     }
430     catch (std::exception &e)
431     {
432         HMI_WARNING("failed: Uncaught exception while calling enddraw: %s", e.what());
433         g_afb_instance->wmgr.exceptionProcessForTransition();
434         return;
435     }
436 }
437
438 void windowmanager_getdisplayinfo_thunk(afb_req req) noexcept
439 {
440     std::lock_guard<std::mutex> guard(binding_m);
441     if (g_afb_instance == nullptr)
442     {
443         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
444         return;
445     }
446
447     try
448     {
449         auto ret = g_afb_instance->wmgr.api_get_display_info();
450         if (ret.is_err())
451         {
452             afb_req_fail(req, "failed", ret.unwrap_err());
453             return;
454         }
455
456         afb_req_success(req, ret.unwrap(), "success");
457     }
458     catch (std::exception &e)
459     {
460         afb_req_fail_f(req, "failed", "Uncaught exception while calling getdisplayinfo: %s", e.what());
461         return;
462     }
463 }
464
465 void windowmanager_getareainfo_thunk(afb_req req) noexcept
466 {
467     std::lock_guard<std::mutex> guard(binding_m);
468     if (g_afb_instance == nullptr)
469     {
470         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
471         return;
472     }
473
474     try
475     {
476         json_object *jreq = afb_req_json(req);
477
478         json_object *j_drawing_name = nullptr;
479         if (!json_object_object_get_ex(jreq, "drawing_name", &j_drawing_name))
480         {
481             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
482             return;
483         }
484         char const *a_drawing_name = json_object_get_string(j_drawing_name);
485
486         auto ret = g_afb_instance->wmgr.api_get_area_info(a_drawing_name);
487         if (ret.is_err())
488         {
489             afb_req_fail(req, "failed", ret.unwrap_err());
490             return;
491         }
492
493         afb_req_success(req, ret.unwrap(), "success");
494     }
495     catch (std::exception &e)
496     {
497         afb_req_fail_f(req, "failed", "Uncaught exception while calling getareainfo: %s", e.what());
498         return;
499     }
500 }
501
502 void windowmanager_wm_subscribe(afb_req req) noexcept
503 {
504     std::lock_guard<std::mutex> guard(binding_m);
505     if (g_afb_instance == nullptr)
506     {
507         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
508         return;
509     }
510
511     try
512     {
513         json_object *jreq = afb_req_json(req);
514         json_object *j = nullptr;
515         if (!json_object_object_get_ex(jreq, "event", &j))
516         {
517             afb_req_fail(req, "failed", "Need char const* argument event");
518             return;
519         }
520         int event_type = json_object_get_int(j);
521         const char *event_name = g_afb_instance->wmgr.kListEventName[event_type];
522         struct afb_event event = g_afb_instance->wmgr.map_afb_event[event_name];
523         int ret = afb_req_subscribe(req, event);
524         if (ret)
525         {
526             afb_req_fail(req, "failed", "Error: afb_req_subscribe()");
527             return;
528         }
529         afb_req_success(req, NULL, "success");
530     }
531     catch (std::exception &e)
532     {
533         afb_req_fail_f(req, "failed", "Uncaught exception while calling wm_subscribe: %s", e.what());
534         return;
535     }
536 }
537
538 void windowmanager_list_drawing_names(afb_req req) noexcept
539 {
540     std::lock_guard<std::mutex> guard(binding_m);
541     if (g_afb_instance == nullptr)
542     {
543         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
544         return;
545     }
546
547     try
548     {
549
550         nlohmann::json j = g_afb_instance->wmgr.id_alloc.name2id;
551         auto ret = wm::Ok(json_tokener_parse(j.dump().c_str()));
552         if (ret.is_err())
553         {
554             afb_req_fail(req, "failed", ret.unwrap_err());
555             return;
556         }
557
558         afb_req_success(req, ret.unwrap(), "success");
559     }
560     catch (std::exception &e)
561     {
562         afb_req_fail_f(req, "failed", "Uncaught exception while calling list_drawing_names: %s", e.what());
563         return;
564     }
565 }
566
567 void windowmanager_ping(afb_req req) noexcept
568 {
569     std::lock_guard<std::mutex> guard(binding_m);
570     if (g_afb_instance == nullptr)
571     {
572         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
573         return;
574     }
575
576     try
577     {
578
579         g_afb_instance->wmgr.api_ping();
580
581         afb_req_success(req, NULL, "success");
582     }
583     catch (std::exception &e)
584     {
585         afb_req_fail_f(req, "failed", "Uncaught exception while calling ping: %s", e.what());
586         return;
587     }
588 }
589
590 void windowmanager_debug_status(afb_req req) noexcept
591 {
592     std::lock_guard<std::mutex> guard(binding_m);
593     if (g_afb_instance == nullptr)
594     {
595         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
596         return;
597     }
598
599     try
600     {
601
602         json_object *jr = json_object_new_object();
603         json_object_object_add(jr, "surfaces",
604                                to_json(g_afb_instance->wmgr.controller->sprops));
605         json_object_object_add(jr, "layers", to_json(g_afb_instance->wmgr.controller->lprops));
606
607         afb_req_success(req, jr, "success");
608     }
609     catch (std::exception &e)
610     {
611         afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_status: %s", e.what());
612         return;
613     }
614 }
615
616 void windowmanager_debug_layers(afb_req req) noexcept
617 {
618     std::lock_guard<std::mutex> guard(binding_m);
619     if (g_afb_instance == nullptr)
620     {
621         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
622         return;
623     }
624
625     try
626     {
627         auto ret = wm::Ok(json_tokener_parse(g_afb_instance->wmgr.layers.to_json().dump().c_str()));
628
629         afb_req_success(req, ret, "success");
630     }
631     catch (std::exception &e)
632     {
633         afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_layers: %s", e.what());
634         return;
635     }
636 }
637
638 void windowmanager_debug_surfaces(afb_req req) noexcept
639 {
640     std::lock_guard<std::mutex> guard(binding_m);
641     if (g_afb_instance == nullptr)
642     {
643         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
644         return;
645     }
646
647     try
648     {
649
650         auto ret = wm::Ok(to_json(g_afb_instance->wmgr.controller->sprops));
651         if (ret.is_err())
652         {
653             afb_req_fail(req, "failed", ret.unwrap_err());
654             return;
655         }
656
657         afb_req_success(req, ret.unwrap(), "success");
658     }
659     catch (std::exception &e)
660     {
661         afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_surfaces: %s", e.what());
662         return;
663     }
664 }
665
666 void windowmanager_debug_terminate(afb_req req) noexcept
667 {
668     std::lock_guard<std::mutex> guard(binding_m);
669     if (g_afb_instance == nullptr)
670     {
671         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
672         return;
673     }
674
675     try
676     {
677
678         if (getenv("WINMAN_DEBUG_TERMINATE") != nullptr)
679         {
680             raise(SIGKILL); // afb-daemon kills it's pgroup using TERM, which
681                             // doesn't play well with perf
682         }
683
684         afb_req_success(req, NULL, "success");
685     }
686     catch (std::exception &e)
687     {
688         afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_terminate: %s", e.what());
689         return;
690     }
691 }
692
693 const struct afb_verb_v2 windowmanager_verbs[] = {
694     {"requestsurface", windowmanager_requestsurface, nullptr, nullptr, AFB_SESSION_NONE},
695     {"requestsurfacexdg", windowmanager_requestsurfacexdg, nullptr, nullptr, AFB_SESSION_NONE},
696     {"activatewindow", windowmanager_activatewindow, nullptr, nullptr, AFB_SESSION_NONE},
697     {"deactivatewindow", windowmanager_deactivatewindow, nullptr, nullptr, AFB_SESSION_NONE},
698     {"enddraw", windowmanager_enddraw, nullptr, nullptr, AFB_SESSION_NONE},
699     {"getdisplayinfo", windowmanager_getdisplayinfo_thunk, nullptr, nullptr, AFB_SESSION_NONE},
700     {"getareainfo", windowmanager_getareainfo_thunk, nullptr, nullptr, AFB_SESSION_NONE},
701     {"wm_subscribe", windowmanager_wm_subscribe, nullptr, nullptr, AFB_SESSION_NONE},
702     {"list_drawing_names", windowmanager_list_drawing_names, nullptr, nullptr, AFB_SESSION_NONE},
703     {"ping", windowmanager_ping, nullptr, nullptr, AFB_SESSION_NONE},
704     {"debug_status", windowmanager_debug_status, nullptr, nullptr, AFB_SESSION_NONE},
705     {"debug_layers", windowmanager_debug_layers, nullptr, nullptr, AFB_SESSION_NONE},
706     {"debug_surfaces", windowmanager_debug_surfaces, nullptr, nullptr, AFB_SESSION_NONE},
707     {"debug_terminate", windowmanager_debug_terminate, nullptr, nullptr, AFB_SESSION_NONE},
708     {}};
709
710 extern "C" const struct afb_binding_v2 afbBindingV2 = {
711     "windowmanager", nullptr, nullptr, windowmanager_verbs, nullptr, binding_init, nullptr, 0};