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