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