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