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