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