261df8b73e8f98cc47db06c9125c63423a0f746f
[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 typedef struct WMClientCtxt
33 {
34     std::string name;
35     std::string role;
36     WMClientCtxt(const char *appName, const char* appRole)
37     {
38         name = appName;
39         role = appRole;
40     }
41 } WMClientCtxt;
42
43 struct afb_instance
44 {
45     std::unique_ptr<wl::display> display;
46     wm::WindowManager wmgr;
47
48     afb_instance() : display{new wl::display}, wmgr{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->wmgr.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->wmgr.display->read_events();
79             g_afb_instance->wmgr.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 void cbRemoveClientCtxt(void *data)
180 {
181     WMClientCtxt *ctxt = (WMClientCtxt *)data;
182     if (ctxt == nullptr)
183     {
184         return;
185     }
186     HMI_DEBUG("wm", "remove app %s", ctxt->name.c_str());
187     // Lookup surfaceID and remove it because App is dead.
188     auto pSid = g_afb_instance->wmgr.id_alloc.lookup(ctxt->role.c_str());
189     if (pSid)
190     {
191         auto sid = *pSid;
192         auto o_state = *g_afb_instance->wmgr.layers.get_layout_state(sid);
193         if (o_state != nullptr)
194         {
195             if (o_state->main == sid)
196             {
197                 o_state->main = -1;
198             }
199             else if (o_state->sub == sid)
200             {
201                 o_state->sub = -1;
202             }
203         }
204         g_afb_instance->wmgr.id_alloc.remove_id(sid);
205         g_afb_instance->wmgr.layers.remove_surface(sid);
206         HMI_DEBUG("wm", "delete surfaceID %d", sid);
207     }
208     g_afb_instance->wmgr.removeClient(ctxt->name);
209     delete ctxt;
210 }
211
212 static void createSecurityContext(afb_req req, const char* appid, const char* role)
213 {
214     WMClientCtxt *ctxt = (WMClientCtxt *)afb_req_context_get(req);
215     if (!ctxt)
216     {
217         // Create Security Context at first time
218         const char *new_role = g_afb_instance->wmgr.convertRoleOldToNew(role);
219         WMClientCtxt *ctxt = new WMClientCtxt(appid, new_role);
220         HMI_DEBUG("wm", "create session for %s", ctxt->name.c_str());
221         afb_req_session_set_LOA(req, 1);
222         afb_req_context_set(req, ctxt, cbRemoveClientCtxt);
223     }
224 }
225
226 void windowmanager_requestsurface(afb_req req) noexcept
227 {
228     std::lock_guard<std::mutex> guard(binding_m);
229 #ifdef ST
230     ST();
231 #endif
232     if (g_afb_instance == nullptr)
233     {
234         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
235         return;
236     }
237
238     try
239     {
240         const char *a_drawing_name = afb_req_value(req, "drawing_name");
241         if (!a_drawing_name)
242         {
243             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
244             return;
245         }
246
247         const char *appid = afb_req_get_application_id(req);
248         auto ret = g_afb_instance->wmgr.api_request_surface(
249             appid, a_drawing_name);
250         if (ret.is_err())
251         {
252             afb_req_fail(req, "failed", ret.unwrap_err());
253             return;
254         }
255
256         createSecurityContext(req, appid, a_drawing_name);
257
258         afb_req_success(req, json_object_new_int(ret.unwrap()), "success");
259     }
260     catch (std::exception &e)
261     {
262         afb_req_fail_f(req, "failed", "Uncaught exception while calling requestsurface: %s", e.what());
263         return;
264     }
265 }
266
267 void windowmanager_requestsurfacexdg(afb_req req) noexcept
268 {
269     std::lock_guard<std::mutex> guard(binding_m);
270 #ifdef ST
271     ST();
272 #endif
273     if (g_afb_instance == nullptr)
274     {
275         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
276         return;
277     }
278
279     try
280     {
281         json_object *jreq = afb_req_json(req);
282
283         json_object *j_drawing_name = nullptr;
284         if (!json_object_object_get_ex(jreq, "drawing_name", &j_drawing_name))
285         {
286             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
287             return;
288         }
289         char const *a_drawing_name = json_object_get_string(j_drawing_name);
290
291         json_object *j_ivi_id = nullptr;
292         if (!json_object_object_get_ex(jreq, "ivi_id", &j_ivi_id))
293         {
294             afb_req_fail(req, "failed", "Need char const* argument ivi_id");
295             return;
296         }
297         char const *a_ivi_id = json_object_get_string(j_ivi_id);
298         char const *appid = afb_req_get_application_id(req);
299         auto ret = g_afb_instance->wmgr.api_request_surface(
300             appid, a_drawing_name, a_ivi_id);
301
302         if (ret != nullptr)
303         {
304             afb_req_fail(req, "failed", ret);
305             return;
306         }
307
308         createSecurityContext(req, appid, a_drawing_name);
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_activatewindow(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->wmgr.api_activate_surface(
348             afb_req_get_application_id(req),
349             a_drawing_name, a_drawing_area,
350             [&req](const char *errmsg) {
351                 if (errmsg != nullptr)
352                 {
353                     HMI_ERROR("wm", errmsg);
354                     afb_req_fail(req, "failed", errmsg);
355                     return;
356                 }
357                 afb_req_success(req, NULL, "success");
358             });
359     }
360     catch (std::exception &e)
361     {
362         HMI_WARNING("wm", "failed: Uncaught exception while calling activatesurface: %s", e.what());
363         g_afb_instance->wmgr.exceptionProcessForTransition();
364         return;
365     }
366 }
367
368 void windowmanager_deactivatewindow(afb_req req) noexcept
369 {
370     std::lock_guard<std::mutex> guard(binding_m);
371 #ifdef ST
372     ST();
373 #endif
374     if (g_afb_instance == nullptr)
375     {
376         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
377         return;
378     }
379
380     try
381     {
382         const char *a_drawing_name = afb_req_value(req, "drawing_name");
383         if (!a_drawing_name)
384         {
385             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
386             return;
387         }
388
389         g_afb_instance->wmgr.api_deactivate_surface(
390             afb_req_get_application_id(req), a_drawing_name,
391             [&req](const char *errmsg) {
392                 if (errmsg != nullptr)
393                 {
394                     HMI_ERROR("wm", errmsg);
395                     afb_req_fail(req, "failed", errmsg);
396                     return;
397                 }
398                 afb_req_success(req, NULL, "success");
399             });
400     }
401     catch (std::exception &e)
402     {
403         HMI_WARNING("wm", "failed: Uncaught exception while calling deactivatesurface: %s", e.what());
404         g_afb_instance->wmgr.exceptionProcessForTransition();
405         return;
406     }
407 }
408
409 void windowmanager_enddraw(afb_req req) noexcept
410 {
411     std::lock_guard<std::mutex> guard(binding_m);
412 #ifdef ST
413     ST();
414 #endif
415     if (g_afb_instance == nullptr)
416     {
417         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
418         return;
419     }
420
421     try
422     {
423         const char *a_drawing_name = afb_req_value(req, "drawing_name");
424         if (!a_drawing_name)
425         {
426             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
427             return;
428         }
429         afb_req_success(req, NULL, "success");
430
431         g_afb_instance->wmgr.api_enddraw(
432             afb_req_get_application_id(req), a_drawing_name);
433     }
434     catch (std::exception &e)
435     {
436         HMI_WARNING("wm", "failed: Uncaught exception while calling enddraw: %s", e.what());
437         g_afb_instance->wmgr.exceptionProcessForTransition();
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->wmgr.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->wmgr.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->wmgr.kListEventName[event_type];
535         struct afb_event event = g_afb_instance->wmgr.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->wmgr.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->wmgr.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->wmgr.controller->sprops));
627         json_object_object_add(jr, "layers", to_json(g_afb_instance->wmgr.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->wmgr.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->wmgr.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     {"activatewindow", windowmanager_activatewindow, nullptr, nullptr, AFB_SESSION_NONE},
728     {"deactivatewindow", windowmanager_deactivatewindow, nullptr, nullptr, AFB_SESSION_NONE},
729     {"enddraw", windowmanager_enddraw, nullptr, nullptr, AFB_SESSION_NONE},
730     {"getdisplayinfo", windowmanager_getdisplayinfo_thunk, nullptr, nullptr, AFB_SESSION_NONE},
731     {"getareainfo", windowmanager_getareainfo_thunk, nullptr, nullptr, AFB_SESSION_NONE},
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};