f7c6dd0389bc36e9c5828e9d7aa9e7474388e433
[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 <json.hpp>
22 #include "app.hpp"
23 #include "result.hpp"
24 #include "json_helper.hpp"
25 #include "util.hpp"
26 #include "wayland_ivi_wm.hpp"
27
28 extern "C"
29 {
30 #include <afb/afb-binding.h>
31 #include <systemd/sd-event.h>
32 }
33
34 typedef struct WMClientCtxt
35 {
36     std::string name;
37     std::string role;
38     WMClientCtxt(const char *appName, const char* appRole)
39     {
40         name = appName;
41         role = appRole;
42     }
43 } WMClientCtxt;
44
45 struct afb_instance
46 {
47     std::unique_ptr<wl::display> display;
48     wm::App app;
49
50     afb_instance() : display{new wl::display}, app{this->display.get()} {}
51
52     int init();
53 };
54
55 struct afb_instance *g_afb_instance;
56 std::mutex binding_m;
57
58 int afb_instance::init()
59 {
60     return this->app.init();
61 }
62
63 int display_event_callback(sd_event_source *evs, int /*fd*/, uint32_t events,
64                            void * /*data*/)
65 {
66     ST();
67
68     if ((events & EPOLLHUP) != 0)
69     {
70         HMI_ERROR("wm", "The compositor hung up, dying now.");
71         delete g_afb_instance;
72         g_afb_instance = nullptr;
73         goto error;
74     }
75
76     if ((events & EPOLLIN) != 0u)
77     {
78         {
79             STN(display_read_events);
80             g_afb_instance->app.display->read_events();
81             g_afb_instance->app.set_pending_events();
82         }
83         {
84             // We want do dispatch pending wayland events from within
85             // the API context
86             STN(winman_ping_api_call);
87             afb_service_call("windowmanager", "ping", json_object_new_object(),
88                             [](void *c, int st, json_object *j) {
89                                 STN(winman_ping_api_call_return);
90                             },
91                             nullptr);
92         }
93     }
94
95     return 0;
96
97 error:
98     sd_event_source_unref(evs);
99     if (getenv("WINMAN_EXIT_ON_HANGUP") != nullptr)
100     {
101         exit(1);
102     }
103     return -1;
104 }
105
106 int _binding_init()
107 {
108     HMI_NOTICE("wm", "WinMan ver. %s", WINMAN_VERSION_STRING);
109
110     if (g_afb_instance != nullptr)
111     {
112         HMI_ERROR("wm", "Wayland context already initialized?");
113         return 0;
114     }
115
116     if (getenv("XDG_RUNTIME_DIR") == nullptr)
117     {
118         HMI_ERROR("wm", "Environment variable XDG_RUNTIME_DIR not set");
119         goto error;
120     }
121
122     {
123         // wait until wayland compositor starts up.
124         int cnt = 0;
125         g_afb_instance = new afb_instance;
126         while (!g_afb_instance->display->ok())
127         {
128             cnt++;
129             if (20 <= cnt)
130             {
131                 HMI_ERROR("wm", "Could not connect to compositor");
132                 goto error;
133             }
134             HMI_ERROR("wm", "Wait to start weston ...");
135             sleep(1);
136             delete g_afb_instance;
137             g_afb_instance = new afb_instance;
138         }
139     }
140
141     if (g_afb_instance->init() == -1)
142     {
143         HMI_ERROR("wm", "Could not connect to compositor");
144         goto error;
145     }
146
147     {
148         int ret = sd_event_add_io(afb_daemon_get_event_loop(), nullptr,
149                                   g_afb_instance->display->get_fd(), EPOLLIN,
150                                   display_event_callback, g_afb_instance);
151         if (ret < 0)
152         {
153             HMI_ERROR("wm", "Could not initialize afb_instance event handler: %d", -ret);
154             goto error;
155         }
156     }
157
158     atexit([] { delete g_afb_instance; });
159
160     return 0;
161
162 error:
163     delete g_afb_instance;
164     g_afb_instance = nullptr;
165     return -1;
166 }
167
168 int binding_init() noexcept
169 {
170     try
171     {
172         return _binding_init();
173     }
174     catch (std::exception &e)
175     {
176         HMI_ERROR("wm", "Uncaught exception in binding_init(): %s", e.what());
177     }
178     return -1;
179 }
180
181 static bool checkFirstReq(afb_req req)
182 {
183     WMClientCtxt *ctxt = (WMClientCtxt *)afb_req_context_get(req);
184     return (ctxt) ? false : true;
185 }
186
187 static void cbRemoveClientCtxt(void *data)
188 {
189     WMClientCtxt *ctxt = (WMClientCtxt *)data;
190     if (ctxt == nullptr)
191     {
192         return;
193     }
194     HMI_DEBUG("wm", "remove app %s", ctxt->name.c_str());
195     // Lookup surfaceID and remove it because App is dead.
196     auto pSid = g_afb_instance->app.id_alloc.lookup(ctxt->role.c_str());
197     if (pSid)
198     {
199         auto sid = *pSid;
200         g_afb_instance->app.id_alloc.remove_id(sid);
201         g_afb_instance->app.layers.remove_surface(sid);
202         g_afb_instance->app.controller->sprops.erase(sid);
203         g_afb_instance->app.controller->surfaces.erase(sid);
204         HMI_DEBUG("wm", "delete surfaceID %d", sid);
205     }
206     delete ctxt;
207 }
208
209 void windowmanager_requestsurface(afb_req req) noexcept
210 {
211     std::lock_guard<std::mutex> guard(binding_m);
212 #ifdef ST
213     ST();
214 #endif
215     if (g_afb_instance == nullptr)
216     {
217         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
218         return;
219     }
220
221     try
222     {
223         const char *a_drawing_name = afb_req_value(req, "drawing_name");
224         if (!a_drawing_name)
225         {
226             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
227             return;
228         }
229
230         /* Create Security Context */
231         bool isFirstReq = checkFirstReq(req);
232         if (!isFirstReq)
233         {
234             WMClientCtxt *ctxt = (WMClientCtxt *)afb_req_context_get(req);
235             HMI_DEBUG("wm", "You're %s.", ctxt->name.c_str());
236             if (ctxt->name != std::string(a_drawing_name))
237             {
238                 afb_req_fail_f(req, "failed", "Don't request with other name: %s for now", a_drawing_name);
239                 HMI_DEBUG("wm", "Don't request with other name: %s for now", a_drawing_name);
240                 return;
241             }
242         }
243
244         auto ret = g_afb_instance->app.api_request_surface(a_drawing_name);
245
246         if (isFirstReq)
247         {
248             WMClientCtxt *ctxt = new WMClientCtxt(afb_req_get_application_id(req), a_drawing_name);
249             HMI_DEBUG("wm", "create session for %s", ctxt->name.c_str());
250             afb_req_session_set_LOA(req, 1);
251             afb_req_context_set(req, ctxt, cbRemoveClientCtxt);
252         }
253         else
254         {
255             HMI_DEBUG("wm", "session already created for %s", a_drawing_name);
256         }
257
258         if (ret.is_err())
259         {
260             afb_req_fail(req, "failed", ret.unwrap_err());
261             return;
262         }
263
264         afb_req_success(req, json_object_new_int(ret.unwrap()), "success");
265     }
266     catch (std::exception &e)
267     {
268         afb_req_fail_f(req, "failed", "Uncaught exception while calling requestsurface: %s", e.what());
269         return;
270     }
271 }
272
273 void windowmanager_requestsurfacexdg(afb_req req) noexcept
274 {
275     std::lock_guard<std::mutex> guard(binding_m);
276 #ifdef ST
277     ST();
278 #endif
279     if (g_afb_instance == nullptr)
280     {
281         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
282         return;
283     }
284
285     try
286     {
287         json_object *jreq = afb_req_json(req);
288
289         json_object *j_drawing_name = nullptr;
290         if (!json_object_object_get_ex(jreq, "drawing_name", &j_drawing_name))
291         {
292             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
293             return;
294         }
295         char const *a_drawing_name = json_object_get_string(j_drawing_name);
296
297         json_object *j_ivi_id = nullptr;
298         if (!json_object_object_get_ex(jreq, "ivi_id", &j_ivi_id))
299         {
300             afb_req_fail(req, "failed", "Need char const* argument ivi_id");
301             return;
302         }
303         char const *a_ivi_id = json_object_get_string(j_ivi_id);
304
305         auto ret = g_afb_instance->app.api_request_surface(a_drawing_name, a_ivi_id);
306         if (ret != nullptr)
307         {
308             afb_req_fail(req, "failed", ret);
309             return;
310         }
311
312         afb_req_success(req, NULL, "success");
313     }
314     catch (std::exception &e)
315     {
316         afb_req_fail_f(req, "failed", "Uncaught exception while calling requestsurfacexdg: %s", e.what());
317         return;
318     }
319 }
320
321 void windowmanager_activatesurface(afb_req req) noexcept
322 {
323     std::lock_guard<std::mutex> guard(binding_m);
324 #ifdef ST
325     ST();
326 #endif
327     if (g_afb_instance == nullptr)
328     {
329         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
330         return;
331     }
332
333     try
334     {
335         const char *a_drawing_name = afb_req_value(req, "drawing_name");
336         if (!a_drawing_name)
337         {
338             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
339             return;
340         }
341
342         const char *a_drawing_area = afb_req_value(req, "drawing_area");
343         if (!a_drawing_area)
344         {
345             afb_req_fail(req, "failed", "Need char const* argument drawing_area");
346             return;
347         }
348
349         g_afb_instance->app.api_activate_surface(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         return;
364     }
365 }
366
367 void windowmanager_deactivatesurface(afb_req req) noexcept
368 {
369     std::lock_guard<std::mutex> guard(binding_m);
370 #ifdef ST
371     ST();
372 #endif
373     if (g_afb_instance == nullptr)
374     {
375         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
376         return;
377     }
378
379     try
380     {
381         const char *a_drawing_name = afb_req_value(req, "drawing_name");
382         if (!a_drawing_name)
383         {
384             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
385             return;
386         }
387
388         g_afb_instance->app.api_deactivate_surface(a_drawing_name,
389                     [&req](const char *errmsg) {
390                         if (errmsg != nullptr)
391                         {
392                             HMI_ERROR("wm", errmsg);
393                             afb_req_fail(req, "failed", errmsg);
394                             return;
395                         }
396                         afb_req_success(req, NULL, "success");
397                     });
398     }
399     catch (std::exception &e)
400     {
401         HMI_WARNING("wm", "Uncaught exception while calling deactivatesurface: %s", e.what());
402         return;
403     }
404 }
405
406 void windowmanager_enddraw(afb_req req) noexcept
407 {
408     std::lock_guard<std::mutex> guard(binding_m);
409 #ifdef ST
410     ST();
411 #endif
412     if (g_afb_instance == nullptr)
413     {
414         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
415         return;
416     }
417
418     try
419     {
420         const char *a_drawing_name = afb_req_value(req, "drawing_name");
421         if (!a_drawing_name)
422         {
423             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
424             return;
425         }
426         afb_req_success(req, NULL, "success");
427
428         g_afb_instance->app.api_enddraw(a_drawing_name);
429     }
430     catch (std::exception &e)
431     {
432         HMI_WARNING("wm", "failed", "Uncaught exception while calling enddraw: %s", e.what());
433         return;
434     }
435 }
436
437 void windowmanager_getdisplayinfo_thunk(afb_req req) noexcept
438 {
439     std::lock_guard<std::mutex> guard(binding_m);
440 #ifdef ST
441     ST();
442 #endif
443     if (g_afb_instance == nullptr)
444     {
445         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
446         return;
447     }
448
449     try
450     {
451         auto ret = g_afb_instance->app.api_get_display_info();
452         if (ret.is_err())
453         {
454             afb_req_fail(req, "failed", ret.unwrap_err());
455             return;
456         }
457
458         afb_req_success(req, ret.unwrap(), "success");
459     }
460     catch (std::exception &e)
461     {
462         afb_req_fail_f(req, "failed", "Uncaught exception while calling getdisplayinfo: %s", e.what());
463         return;
464     }
465 }
466
467 void windowmanager_getareainfo_thunk(afb_req req) noexcept
468 {
469     std::lock_guard<std::mutex> guard(binding_m);
470 #ifdef ST
471     ST();
472 #endif
473     if (g_afb_instance == nullptr)
474     {
475         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
476         return;
477     }
478
479     try
480     {
481         json_object *jreq = afb_req_json(req);
482
483         json_object *j_drawing_name = nullptr;
484         if (!json_object_object_get_ex(jreq, "drawing_name", &j_drawing_name))
485         {
486             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
487             return;
488         }
489         char const *a_drawing_name = json_object_get_string(j_drawing_name);
490
491         auto ret = g_afb_instance->app.api_get_area_info(a_drawing_name);
492         if (ret.is_err())
493         {
494             afb_req_fail(req, "failed", ret.unwrap_err());
495             return;
496         }
497
498         afb_req_success(req, ret.unwrap(), "success");
499     }
500     catch (std::exception &e)
501     {
502         afb_req_fail_f(req, "failed", "Uncaught exception while calling getareainfo: %s", e.what());
503         return;
504     }
505 }
506
507 void windowmanager_wm_subscribe(afb_req req) noexcept
508 {
509     std::lock_guard<std::mutex> guard(binding_m);
510 #ifdef ST
511     ST();
512 #endif
513     if (g_afb_instance == nullptr)
514     {
515         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
516         return;
517     }
518
519     try
520     {
521         json_object *jreq = afb_req_json(req);
522         json_object *j = nullptr;
523         if (!json_object_object_get_ex(jreq, "event", &j))
524         {
525             afb_req_fail(req, "failed", "Need char const* argument event");
526             return;
527         }
528         int event_type = json_object_get_int(j);
529         const char *event_name = g_afb_instance->app.kListEventName[event_type];
530         struct afb_event event = g_afb_instance->app.map_afb_event[event_name];
531         int ret = afb_req_subscribe(req, event);
532         if (ret)
533         {
534             afb_req_fail(req, "failed", "Error: afb_req_subscribe()");
535             return;
536         }
537         afb_req_success(req, NULL, "success");
538     }
539     catch (std::exception &e)
540     {
541         afb_req_fail_f(req, "failed", "Uncaught exception while calling wm_subscribe: %s", e.what());
542         return;
543     }
544 }
545
546 void windowmanager_list_drawing_names(afb_req req) noexcept
547 {
548     std::lock_guard<std::mutex> guard(binding_m);
549 #ifdef ST
550     ST();
551 #endif
552     if (g_afb_instance == nullptr)
553     {
554         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
555         return;
556     }
557
558     try
559     {
560
561         nlohmann::json j = g_afb_instance->app.id_alloc.name2id;
562         auto ret = wm::Ok(json_tokener_parse(j.dump().c_str()));
563         if (ret.is_err())
564         {
565             afb_req_fail(req, "failed", ret.unwrap_err());
566             return;
567         }
568
569         afb_req_success(req, ret.unwrap(), "success");
570     }
571     catch (std::exception &e)
572     {
573         afb_req_fail_f(req, "failed", "Uncaught exception while calling list_drawing_names: %s", e.what());
574         return;
575     }
576 }
577
578 void windowmanager_ping(afb_req req) noexcept
579 {
580     std::lock_guard<std::mutex> guard(binding_m);
581 #ifdef ST
582     ST();
583 #endif
584     if (g_afb_instance == nullptr)
585     {
586         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
587         return;
588     }
589
590     try
591     {
592
593         g_afb_instance->app.api_ping();
594
595         afb_req_success(req, NULL, "success");
596     }
597     catch (std::exception &e)
598     {
599         afb_req_fail_f(req, "failed", "Uncaught exception while calling ping: %s", e.what());
600         return;
601     }
602 }
603
604 void windowmanager_debug_status(afb_req req) noexcept
605 {
606     std::lock_guard<std::mutex> guard(binding_m);
607 #ifdef ST
608     ST();
609 #endif
610     if (g_afb_instance == nullptr)
611     {
612         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
613         return;
614     }
615
616     try
617     {
618
619         json_object *jr = json_object_new_object();
620         json_object_object_add(jr, "surfaces",
621                                to_json(g_afb_instance->app.controller->sprops));
622         json_object_object_add(jr, "layers", to_json(g_afb_instance->app.controller->lprops));
623
624         afb_req_success(req, jr, "success");
625     }
626     catch (std::exception &e)
627     {
628         afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_status: %s", e.what());
629         return;
630     }
631 }
632
633 void windowmanager_debug_layers(afb_req req) noexcept
634 {
635     std::lock_guard<std::mutex> guard(binding_m);
636 #ifdef ST
637     ST();
638 #endif
639     if (g_afb_instance == nullptr)
640     {
641         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
642         return;
643     }
644
645     try
646     {
647         auto ret = wm::Ok(json_tokener_parse(g_afb_instance->app.layers.to_json().dump().c_str()));
648
649         afb_req_success(req, ret, "success");
650     }
651     catch (std::exception &e)
652     {
653         afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_layers: %s", e.what());
654         return;
655     }
656 }
657
658 void windowmanager_debug_surfaces(afb_req req) noexcept
659 {
660     std::lock_guard<std::mutex> guard(binding_m);
661 #ifdef ST
662     ST();
663 #endif
664     if (g_afb_instance == nullptr)
665     {
666         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
667         return;
668     }
669
670     try
671     {
672
673         auto ret = wm::Ok(to_json(g_afb_instance->app.controller->sprops));
674         if (ret.is_err())
675         {
676             afb_req_fail(req, "failed", ret.unwrap_err());
677             return;
678         }
679
680         afb_req_success(req, ret.unwrap(), "success");
681     }
682     catch (std::exception &e)
683     {
684         afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_surfaces: %s", e.what());
685         return;
686     }
687 }
688
689 void windowmanager_debug_terminate(afb_req req) noexcept
690 {
691     std::lock_guard<std::mutex> guard(binding_m);
692 #ifdef ST
693     ST();
694 #endif
695     if (g_afb_instance == nullptr)
696     {
697         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
698         return;
699     }
700
701     try
702     {
703
704         if (getenv("WINMAN_DEBUG_TERMINATE") != nullptr)
705         {
706             raise(SIGKILL); // afb-daemon kills it's pgroup using TERM, which
707                             // doesn't play well with perf
708         }
709
710         afb_req_success(req, NULL, "success");
711     }
712     catch (std::exception &e)
713     {
714         afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_terminate: %s", e.what());
715         return;
716     }
717 }
718
719 const struct afb_verb_v2 windowmanager_verbs[] = {
720     {"requestsurface", windowmanager_requestsurface, nullptr, nullptr, AFB_SESSION_NONE},
721     {"requestsurfacexdg", windowmanager_requestsurfacexdg, nullptr, nullptr, AFB_SESSION_NONE},
722     {"activatesurface", windowmanager_activatesurface, nullptr, nullptr, AFB_SESSION_NONE},
723     {"deactivatesurface", windowmanager_deactivatesurface, nullptr, nullptr, AFB_SESSION_NONE},
724     {"enddraw", windowmanager_enddraw, nullptr, nullptr, AFB_SESSION_NONE},
725     {"getdisplayinfo", windowmanager_getdisplayinfo_thunk, nullptr, nullptr, AFB_SESSION_NONE},
726     {"getareainfo", windowmanager_getareainfo_thunk, nullptr, nullptr, AFB_SESSION_NONE},
727     {"wm_subscribe", windowmanager_wm_subscribe, nullptr, nullptr, AFB_SESSION_NONE},
728     {"list_drawing_names", windowmanager_list_drawing_names, nullptr, nullptr, AFB_SESSION_NONE},
729     {"ping", windowmanager_ping, nullptr, nullptr, AFB_SESSION_NONE},
730     {"debug_status", windowmanager_debug_status, nullptr, nullptr, AFB_SESSION_NONE},
731     {"debug_layers", windowmanager_debug_layers, nullptr, nullptr, AFB_SESSION_NONE},
732     {"debug_surfaces", windowmanager_debug_surfaces, nullptr, nullptr, AFB_SESSION_NONE},
733     {"debug_terminate", windowmanager_debug_terminate, nullptr, nullptr, AFB_SESSION_NONE},
734     {}};
735
736 extern "C" const struct afb_binding_v2 afbBindingV2 = {
737     "windowmanager", nullptr, nullptr, windowmanager_verbs, nullptr, binding_init, nullptr, 0};