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