Format source codes
[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 <json.hpp>
22 #include "app.hpp"
23 #include "result.hpp"
24 #include "json_helper.hpp"
25 #include "util.hpp"
26 #include "wayland_ivi_wm.hpp"
27
28 extern "C"
29 {
30 #include <afb/afb-binding.h>
31 #include <systemd/sd-event.h>
32 }
33
34 typedef struct wmClientCtxt
35 {
36     std::string name;
37     wmClientCtxt(const char *appName)
38     {
39         name = appName;
40     }
41 } wmClientCtxt;
42
43 struct afb_instance
44 {
45     std::unique_ptr<wl::display> display;
46     wm::App app;
47
48     afb_instance() : display{new wl::display}, app{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->app.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->app.display->read_events();
79             g_afb_instance->app.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 bool checkFirstReq(afb_req req)
180 {
181     wmClientCtxt *ctxt = (wmClientCtxt *)afb_req_context_get(req);
182     return (ctxt) ? false : true;
183 }
184
185 static void cbRemoveClientCtxt(void *data)
186 {
187     wmClientCtxt *ctxt = (wmClientCtxt *)data;
188     if (ctxt == nullptr)
189     {
190         return;
191     }
192     HMI_DEBUG("wm", "remove app %s", ctxt->name.c_str());
193     // Lookup surfaceID and remove it because App is dead.
194     auto pSid = g_afb_instance->app.id_alloc.lookup(ctxt->name.c_str());
195     if (pSid)
196     {
197         auto sid = *pSid;
198         g_afb_instance->app.id_alloc.remove_id(sid);
199         g_afb_instance->app.layers.remove_surface(sid);
200         g_afb_instance->app.controller->sprops.erase(sid);
201         g_afb_instance->app.controller->surfaces.erase(sid);
202         HMI_DEBUG("wm", "delete surfaceID %d", sid);
203     }
204     delete ctxt;
205 }
206
207 void windowmanager_requestsurface(afb_req req) noexcept
208 {
209     std::lock_guard<std::mutex> guard(binding_m);
210 #ifdef ST
211     ST();
212 #endif
213     if (g_afb_instance == nullptr)
214     {
215         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
216         return;
217     }
218
219     try
220     {
221         const char *a_drawing_name = afb_req_value(req, "drawing_name");
222         if (!a_drawing_name)
223         {
224             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
225             return;
226         }
227
228         /* Create Security Context */
229         bool isFirstReq = checkFirstReq(req);
230         if (!isFirstReq)
231         {
232             wmClientCtxt *ctxt = (wmClientCtxt *)afb_req_context_get(req);
233             HMI_DEBUG("wm", "You're %s.", ctxt->name.c_str());
234             if (ctxt->name != std::string(a_drawing_name))
235             {
236                 afb_req_fail_f(req, "failed", "Don't request with other name: %s for now", a_drawing_name);
237                 HMI_DEBUG("wm", "Don't request with other name: %s for now", a_drawing_name);
238                 return;
239             }
240         }
241
242         auto ret = g_afb_instance->app.api_request_surface(a_drawing_name);
243
244         if (isFirstReq)
245         {
246             wmClientCtxt *ctxt = new wmClientCtxt(a_drawing_name);
247             HMI_DEBUG("wm", "create session for %s", ctxt->name.c_str());
248             afb_req_session_set_LOA(req, 1);
249             afb_req_context_set(req, ctxt, cbRemoveClientCtxt);
250         }
251         else
252         {
253             HMI_DEBUG("wm", "session already created for %s", a_drawing_name);
254         }
255
256         if (ret.is_err())
257         {
258             afb_req_fail(req, "failed", ret.unwrap_err());
259             return;
260         }
261
262         afb_req_success(req, json_object_new_int(ret.unwrap()), "success");
263     }
264     catch (std::exception &e)
265     {
266         afb_req_fail_f(req, "failed", "Uncaught exception while calling requestsurface: %s", e.what());
267         return;
268     }
269 }
270
271 void windowmanager_requestsurfacexdg(afb_req req) noexcept
272 {
273     std::lock_guard<std::mutex> guard(binding_m);
274 #ifdef ST
275     ST();
276 #endif
277     if (g_afb_instance == nullptr)
278     {
279         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
280         return;
281     }
282
283     try
284     {
285         json_object *jreq = afb_req_json(req);
286
287         json_object *j_drawing_name = nullptr;
288         if (!json_object_object_get_ex(jreq, "drawing_name", &j_drawing_name))
289         {
290             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
291             return;
292         }
293         char const *a_drawing_name = json_object_get_string(j_drawing_name);
294
295         json_object *j_ivi_id = nullptr;
296         if (!json_object_object_get_ex(jreq, "ivi_id", &j_ivi_id))
297         {
298             afb_req_fail(req, "failed", "Need char const* argument ivi_id");
299             return;
300         }
301         char const *a_ivi_id = json_object_get_string(j_ivi_id);
302
303         auto ret = g_afb_instance->app.api_request_surface(a_drawing_name, a_ivi_id);
304         if (ret != nullptr)
305         {
306             afb_req_fail(req, "failed", ret);
307             return;
308         }
309
310         afb_req_success(req, NULL, "success");
311     }
312     catch (std::exception &e)
313     {
314         afb_req_fail_f(req, "failed", "Uncaught exception while calling requestsurfacexdg: %s", e.what());
315         return;
316     }
317 }
318
319 void windowmanager_activatesurface(afb_req req) noexcept
320 {
321     std::lock_guard<std::mutex> guard(binding_m);
322 #ifdef ST
323     ST();
324 #endif
325     if (g_afb_instance == nullptr)
326     {
327         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
328         return;
329     }
330
331     try
332     {
333         const char *a_drawing_name = afb_req_value(req, "drawing_name");
334         if (!a_drawing_name)
335         {
336             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
337             return;
338         }
339
340         const char *a_drawing_area = afb_req_value(req, "drawing_area");
341         if (!a_drawing_area)
342         {
343             afb_req_fail(req, "failed", "Need char const* argument drawing_area");
344             return;
345         }
346
347         g_afb_instance->app.api_activate_surface(a_drawing_name, a_drawing_area,
348                     [&req](const char *errmsg) {
349                         if (errmsg != nullptr)
350                         {
351                             HMI_ERROR("wm", errmsg);
352                             afb_req_fail(req, "failed", errmsg);
353                             return;
354                         }
355                         afb_req_success(req, NULL, "success");
356                     });
357     }
358     catch (std::exception &e)
359     {
360         HMI_WARNING("wm", "failed", "Uncaught exception while calling activatesurface: %s", e.what());
361         return;
362     }
363 }
364
365 void windowmanager_deactivatesurface(afb_req req) noexcept
366 {
367     std::lock_guard<std::mutex> guard(binding_m);
368 #ifdef ST
369     ST();
370 #endif
371     if (g_afb_instance == nullptr)
372     {
373         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
374         return;
375     }
376
377     try
378     {
379         const char *a_drawing_name = afb_req_value(req, "drawing_name");
380         if (!a_drawing_name)
381         {
382             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
383             return;
384         }
385
386         g_afb_instance->app.api_deactivate_surface(a_drawing_name,
387                     [&req](const char *errmsg) {
388                         if (errmsg != nullptr)
389                         {
390                             HMI_ERROR("wm", errmsg);
391                             afb_req_fail(req, "failed", errmsg);
392                             return;
393                         }
394                         afb_req_success(req, NULL, "success");
395                     });
396     }
397     catch (std::exception &e)
398     {
399         HMI_WARNING("wm", "Uncaught exception while calling deactivatesurface: %s", e.what());
400         return;
401     }
402 }
403
404 void windowmanager_enddraw(afb_req req) noexcept
405 {
406     std::lock_guard<std::mutex> guard(binding_m);
407 #ifdef ST
408     ST();
409 #endif
410     if (g_afb_instance == nullptr)
411     {
412         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
413         return;
414     }
415
416     try
417     {
418         const char *a_drawing_name = afb_req_value(req, "drawing_name");
419         if (!a_drawing_name)
420         {
421             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
422             return;
423         }
424         afb_req_success(req, NULL, "success");
425
426         g_afb_instance->app.api_enddraw(a_drawing_name);
427     }
428     catch (std::exception &e)
429     {
430         HMI_WARNING("wm", "failed", "Uncaught exception while calling enddraw: %s", e.what());
431         return;
432     }
433 }
434
435 void windowmanager_getdisplayinfo_thunk(afb_req req) noexcept
436 {
437     std::lock_guard<std::mutex> guard(binding_m);
438 #ifdef ST
439     ST();
440 #endif
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->app.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 #ifdef ST
469     ST();
470 #endif
471     if (g_afb_instance == nullptr)
472     {
473         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
474         return;
475     }
476
477     try
478     {
479         json_object *jreq = afb_req_json(req);
480
481         json_object *j_drawing_name = nullptr;
482         if (!json_object_object_get_ex(jreq, "drawing_name", &j_drawing_name))
483         {
484             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
485             return;
486         }
487         char const *a_drawing_name = json_object_get_string(j_drawing_name);
488
489         auto ret = g_afb_instance->app.api_get_area_info(a_drawing_name);
490         if (ret.is_err())
491         {
492             afb_req_fail(req, "failed", ret.unwrap_err());
493             return;
494         }
495
496         afb_req_success(req, ret.unwrap(), "success");
497     }
498     catch (std::exception &e)
499     {
500         afb_req_fail_f(req, "failed", "Uncaught exception while calling getareainfo: %s", e.what());
501         return;
502     }
503 }
504
505 void windowmanager_wm_subscribe(afb_req req) noexcept
506 {
507     std::lock_guard<std::mutex> guard(binding_m);
508 #ifdef ST
509     ST();
510 #endif
511     if (g_afb_instance == nullptr)
512     {
513         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
514         return;
515     }
516
517     try
518     {
519         json_object *jreq = afb_req_json(req);
520         json_object *j = nullptr;
521         if (!json_object_object_get_ex(jreq, "event", &j))
522         {
523             afb_req_fail(req, "failed", "Need char const* argument event");
524             return;
525         }
526         int event_type = json_object_get_int(j);
527         const char *event_name = g_afb_instance->app.kListEventName[event_type];
528         struct afb_event event = g_afb_instance->app.map_afb_event[event_name];
529         int ret = afb_req_subscribe(req, event);
530         if (ret)
531         {
532             afb_req_fail(req, "failed", "Error: afb_req_subscribe()");
533             return;
534         }
535         afb_req_success(req, NULL, "success");
536     }
537     catch (std::exception &e)
538     {
539         afb_req_fail_f(req, "failed", "Uncaught exception while calling wm_subscribe: %s", e.what());
540         return;
541     }
542 }
543
544 void windowmanager_list_drawing_names(afb_req req) noexcept
545 {
546     std::lock_guard<std::mutex> guard(binding_m);
547 #ifdef ST
548     ST();
549 #endif
550     if (g_afb_instance == nullptr)
551     {
552         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
553         return;
554     }
555
556     try
557     {
558
559         nlohmann::json j = g_afb_instance->app.id_alloc.name2id;
560         auto ret = wm::Ok(json_tokener_parse(j.dump().c_str()));
561         if (ret.is_err())
562         {
563             afb_req_fail(req, "failed", ret.unwrap_err());
564             return;
565         }
566
567         afb_req_success(req, ret.unwrap(), "success");
568     }
569     catch (std::exception &e)
570     {
571         afb_req_fail_f(req, "failed", "Uncaught exception while calling list_drawing_names: %s", e.what());
572         return;
573     }
574 }
575
576 void windowmanager_ping(afb_req req) noexcept
577 {
578     std::lock_guard<std::mutex> guard(binding_m);
579 #ifdef ST
580     ST();
581 #endif
582     if (g_afb_instance == nullptr)
583     {
584         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
585         return;
586     }
587
588     try
589     {
590
591         g_afb_instance->app.api_ping();
592
593         afb_req_success(req, NULL, "success");
594     }
595     catch (std::exception &e)
596     {
597         afb_req_fail_f(req, "failed", "Uncaught exception while calling ping: %s", e.what());
598         return;
599     }
600 }
601
602 void windowmanager_debug_status(afb_req req) noexcept
603 {
604     std::lock_guard<std::mutex> guard(binding_m);
605 #ifdef ST
606     ST();
607 #endif
608     if (g_afb_instance == nullptr)
609     {
610         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
611         return;
612     }
613
614     try
615     {
616
617         json_object *jr = json_object_new_object();
618         json_object_object_add(jr, "surfaces",
619                                to_json(g_afb_instance->app.controller->sprops));
620         json_object_object_add(jr, "layers", to_json(g_afb_instance->app.controller->lprops));
621
622         afb_req_success(req, jr, "success");
623     }
624     catch (std::exception &e)
625     {
626         afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_status: %s", e.what());
627         return;
628     }
629 }
630
631 void windowmanager_debug_layers(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         auto ret = wm::Ok(json_tokener_parse(g_afb_instance->app.layers.to_json().dump().c_str()));
646
647         afb_req_success(req, ret, "success");
648     }
649     catch (std::exception &e)
650     {
651         afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_layers: %s", e.what());
652         return;
653     }
654 }
655
656 void windowmanager_debug_surfaces(afb_req req) noexcept
657 {
658     std::lock_guard<std::mutex> guard(binding_m);
659 #ifdef ST
660     ST();
661 #endif
662     if (g_afb_instance == nullptr)
663     {
664         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
665         return;
666     }
667
668     try
669     {
670
671         auto ret = wm::Ok(to_json(g_afb_instance->app.controller->sprops));
672         if (ret.is_err())
673         {
674             afb_req_fail(req, "failed", ret.unwrap_err());
675             return;
676         }
677
678         afb_req_success(req, ret.unwrap(), "success");
679     }
680     catch (std::exception &e)
681     {
682         afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_surfaces: %s", e.what());
683         return;
684     }
685 }
686
687 void windowmanager_debug_terminate(afb_req req) noexcept
688 {
689     std::lock_guard<std::mutex> guard(binding_m);
690 #ifdef ST
691     ST();
692 #endif
693     if (g_afb_instance == nullptr)
694     {
695         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
696         return;
697     }
698
699     try
700     {
701
702         if (getenv("WINMAN_DEBUG_TERMINATE") != nullptr)
703         {
704             raise(SIGKILL); // afb-daemon kills it's pgroup using TERM, which
705                             // doesn't play well with perf
706         }
707
708         afb_req_success(req, NULL, "success");
709     }
710     catch (std::exception &e)
711     {
712         afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_terminate: %s", e.what());
713         return;
714     }
715 }
716
717 const struct afb_verb_v2 windowmanager_verbs[] = {
718     {"requestsurface", windowmanager_requestsurface, nullptr, nullptr, AFB_SESSION_NONE},
719     {"requestsurfacexdg", windowmanager_requestsurfacexdg, nullptr, nullptr, AFB_SESSION_NONE},
720     {"activatesurface", windowmanager_activatesurface, nullptr, nullptr, AFB_SESSION_NONE},
721     {"deactivatesurface", windowmanager_deactivatesurface, nullptr, nullptr, AFB_SESSION_NONE},
722     {"enddraw", windowmanager_enddraw, nullptr, nullptr, AFB_SESSION_NONE},
723     {"getdisplayinfo", windowmanager_getdisplayinfo_thunk, nullptr, nullptr, AFB_SESSION_NONE},
724     {"getareainfo", windowmanager_getareainfo_thunk, nullptr, nullptr, AFB_SESSION_NONE},
725     {"wm_subscribe", windowmanager_wm_subscribe, nullptr, nullptr, AFB_SESSION_NONE},
726     {"list_drawing_names", windowmanager_list_drawing_names, nullptr, nullptr, AFB_SESSION_NONE},
727     {"ping", windowmanager_ping, nullptr, nullptr, AFB_SESSION_NONE},
728     {"debug_status", windowmanager_debug_status, nullptr, nullptr, AFB_SESSION_NONE},
729     {"debug_layers", windowmanager_debug_layers, nullptr, nullptr, AFB_SESSION_NONE},
730     {"debug_surfaces", windowmanager_debug_surfaces, nullptr, nullptr, AFB_SESSION_NONE},
731     {"debug_terminate", windowmanager_debug_terminate, nullptr, nullptr, AFB_SESSION_NONE},
732     {}};
733
734 extern "C" const struct afb_binding_v2 afbBindingV2 = {
735     "windowmanager", nullptr, nullptr, windowmanager_verbs, nullptr, binding_init, nullptr, 0};