Improve window manager
[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 <stdlib.h>
22 #include <vector>
23 #include "window_manager.hpp"
24 #include "json_helper.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     wm::WindowManager wmgr;
46
47     afb_instance() : wmgr() {}
48     ~afb_instance() = default;
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 _binding_init()
62 {
63     HMI_NOTICE("WinMan ver. %s", WINMAN_VERSION_STRING);
64
65     g_afb_instance = new afb_instance;
66
67     if (g_afb_instance->init() == -1)
68     {
69         HMI_ERROR("Could not connect to compositor");
70         goto error;
71     }
72
73     atexit([] { delete g_afb_instance; });
74
75     return 0;
76
77 error:
78     delete g_afb_instance;
79     g_afb_instance = nullptr;
80     return -1;
81 }
82
83 int binding_init() noexcept
84 {
85     try
86     {
87         return _binding_init();
88     }
89     catch (std::exception &e)
90     {
91         HMI_ERROR("Uncaught exception in binding_init(): %s", e.what());
92     }
93     return -1;
94 }
95
96 static void cbRemoveClientCtxt(void *data)
97 {
98     WMClientCtxt *ctxt = (WMClientCtxt *)data;
99     if (ctxt == nullptr)
100     {
101         return;
102     }
103     HMI_DEBUG("remove app %s", ctxt->name.c_str());
104
105     // Policy Manager does not know this app was killed,
106     // so notify it by deactivate request.
107     g_afb_instance->wmgr.api_deactivate_window(
108         ctxt->name.c_str(), ctxt->role.c_str(),
109         [](const char *) {});
110
111     g_afb_instance->wmgr.removeClient(ctxt->name);
112     delete ctxt;
113 }
114
115 static void createSecurityContext(afb_req req, const char* appid, const char* role)
116 {
117     WMClientCtxt *ctxt = (WMClientCtxt *)afb_req_context_get(req);
118     if (!ctxt)
119     {
120         // Create Security Context at first time
121         const char *new_role = g_afb_instance->wmgr.convertRoleOldToNew(role);
122         WMClientCtxt *ctxt = new WMClientCtxt(appid, new_role);
123         HMI_DEBUG("create session for %s", ctxt->name.c_str());
124         afb_req_session_set_LOA(req, 1);
125         afb_req_context_set(req, ctxt, cbRemoveClientCtxt);
126     }
127 }
128
129 void windowmanager_requestsurface(afb_req req) noexcept
130 {
131     std::lock_guard<std::mutex> guard(binding_m);
132     if (g_afb_instance == nullptr)
133     {
134         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
135         return;
136     }
137
138     try
139     {
140         const char *a_drawing_name = afb_req_value(req, "drawing_name");
141         if (!a_drawing_name)
142         {
143             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
144             return;
145         }
146
147         char *appid = afb_req_get_application_id(req);
148         if(appid)
149         {
150             auto ret = g_afb_instance->wmgr.api_request_surface(
151                 appid, a_drawing_name);
152             if (ret.is_err())
153             {
154                 afb_req_fail(req, "failed", ret.unwrap_err());
155             }
156             else
157             {
158                 createSecurityContext(req, appid, a_drawing_name);
159                 afb_req_success(req, json_object_new_int(ret.unwrap()), "success");
160             }
161             free(appid);
162         }
163         else
164         {
165             afb_req_fail(req, "failed", nullptr);
166         }
167     }
168     catch (std::exception &e)
169     {
170         afb_req_fail_f(req, "failed", "Uncaught exception while calling requestsurface: %s", e.what());
171         return;
172     }
173 }
174
175 void windowmanager_requestsurfacexdg(afb_req req) noexcept
176 {
177     std::lock_guard<std::mutex> guard(binding_m);
178     if (g_afb_instance == nullptr)
179     {
180         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
181         return;
182     }
183
184     try
185     {
186         json_object *jreq = afb_req_json(req);
187
188         json_object *j_drawing_name = nullptr;
189         if (!json_object_object_get_ex(jreq, "drawing_name", &j_drawing_name))
190         {
191             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
192             return;
193         }
194         char const *a_drawing_name = json_object_get_string(j_drawing_name);
195
196         json_object *j_ivi_id = nullptr;
197         if (!json_object_object_get_ex(jreq, "ivi_id", &j_ivi_id))
198         {
199             afb_req_fail(req, "failed", "Need char const* argument ivi_id");
200             return;
201         }
202         char const *a_ivi_id = json_object_get_string(j_ivi_id);
203         char *appid = afb_req_get_application_id(req);
204         if(appid)
205         {
206             auto ret = g_afb_instance->wmgr.api_request_surface(
207                 appid, a_drawing_name, a_ivi_id);
208
209             if (ret != nullptr)
210             {
211                 afb_req_fail(req, "failed", ret);
212             }
213             else
214             {
215                 createSecurityContext(req, appid, a_drawing_name);
216                 afb_req_success(req, NULL, "success");
217             }
218             free(appid);
219         }
220     }
221     catch (std::exception &e)
222     {
223         afb_req_fail_f(req, "failed", "Uncaught exception while calling requestsurfacexdg: %s", e.what());
224         return;
225     }
226 }
227
228 void windowmanager_setrole(afb_req req) noexcept
229 {
230     std::lock_guard<std::mutex> guard(binding_m);
231     if (g_afb_instance == nullptr)
232     {
233         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
234         return;
235     }
236     try
237     {
238         json_object *jreq = afb_req_json(req);
239
240         json_object *j_role = nullptr;
241         if (!json_object_object_get_ex(jreq, "role", &j_role))
242         {
243             afb_req_fail(req, "failed", "Need char const* argument role");
244             return;
245         }
246         char const *a_role = json_object_get_string(j_role);
247         char *appid = afb_req_get_application_id(req);
248
249         if(appid)
250         {
251             auto ret = g_afb_instance->wmgr.api_set_role(appid, a_role);
252             if (!ret)
253             {
254                 afb_req_fail(req, "failed", "Couldn't register");
255             }
256             else
257             {
258                 createSecurityContext(req, appid, a_role);
259                 afb_req_success(req, NULL, "success");
260             }
261             free(appid);
262         }
263     }
264     catch (std::exception &e)
265     {
266         afb_req_fail_f(req, "failed", "Uncaught exception while calling requestsurfacexdg: %s", e.what());
267         return;
268     }
269 }
270
271 void windowmanager_activatewindow(afb_req req) noexcept
272 {
273     std::lock_guard<std::mutex> guard(binding_m);
274     if (g_afb_instance == nullptr)
275     {
276         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
277         return;
278     }
279
280     try
281     {
282         const char *a_drawing_name = afb_req_value(req, "drawing_name");
283         if (!a_drawing_name)
284         {
285             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
286             return;
287         }
288
289         const char *a_drawing_area = afb_req_value(req, "drawing_area");
290         if (!a_drawing_area)
291         {
292             afb_req_fail(req, "failed", "Need char const* argument drawing_area");
293             return;
294         }
295
296         char* appid = afb_req_get_application_id(req);
297         if(appid)
298         {
299             auto reply = [&req](const char *errmsg) {
300                 if (errmsg != nullptr)
301                 {
302                     HMI_ERROR(errmsg);
303                     afb_req_fail(req, "failed", errmsg);
304                     return;
305                 }
306                 afb_req_success(req, NULL, "success");
307             };
308
309             if (g_afb_instance->wmgr.wmcon.isMasterMode() ||
310                 !g_afb_instance->wmgr.wmcon.isMasterArea(a_drawing_area))
311             {
312                 g_afb_instance->wmgr.api_activate_window(
313                     appid, a_drawing_name, a_drawing_area, reply);
314             }
315             else
316             {
317                 // TODO: temporarily
318                 if (!g_afb_instance->wmgr.wmcon.isConnecting())
319                 {
320                     g_afb_instance->wmgr.wmcon.connectToMaster();
321                 }
322
323                 // If Window Manager is slave and this request is for master,
324                 // request activateWindow to master
325                 g_afb_instance->wmgr.api_activate_surface_to_master(
326                     appid, a_drawing_name, a_drawing_area, reply);
327             }
328             free(appid);
329         }
330     }
331     catch (std::exception &e)
332     {
333         HMI_WARNING("failed: Uncaught exception while calling activatesurface: %s", e.what());
334         g_afb_instance->wmgr.exceptionProcessForTransition();
335         return;
336     }
337 }
338
339 void windowmanager_deactivatewindow(afb_req req) noexcept
340 {
341     std::lock_guard<std::mutex> guard(binding_m);
342     if (g_afb_instance == nullptr)
343     {
344         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
345         return;
346     }
347
348     try
349     {
350         const char *a_drawing_name = afb_req_value(req, "drawing_name");
351         if (!a_drawing_name)
352         {
353             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
354             return;
355         }
356
357         char* appid = afb_req_get_application_id(req);
358         if(appid)
359         {
360             auto reply = [&req](const char *errmsg) {
361                 if (errmsg != nullptr)
362                 {
363                     HMI_ERROR(errmsg);
364                     afb_req_fail(req, "failed", errmsg);
365                     return;
366                 }
367                 afb_req_success(req, NULL, "success");
368             };
369
370             // TODO: Check whether role is tbtnavi to request remote invisible
371             if (g_afb_instance->wmgr.wmcon.isMasterMode() ||
372                 ("tbtnavi" != std::string(a_drawing_name)))
373             {
374                 g_afb_instance->wmgr.api_deactivate_window(
375                     appid, a_drawing_name, reply);
376             }
377             else
378             {
379                 // If Window Manager is slave and this request is for master,
380                 // request deactivateWindow to master
381                 g_afb_instance->wmgr.api_deactivate_surface_to_master(
382                     appid, a_drawing_name, reply);
383             }
384             free(appid);
385         }
386     }
387     catch (std::exception &e)
388     {
389         HMI_WARNING("failed: Uncaught exception while calling deactivatesurface: %s", e.what());
390         g_afb_instance->wmgr.exceptionProcessForTransition();
391         return;
392     }
393 }
394
395 void windowmanager_enddraw(afb_req req) noexcept
396 {
397     std::lock_guard<std::mutex> guard(binding_m);
398     if (g_afb_instance == nullptr)
399     {
400         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
401         return;
402     }
403
404     try
405     {
406         const char *a_drawing_name = afb_req_value(req, "drawing_name");
407         if (!a_drawing_name)
408         {
409             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
410             return;
411         }
412         afb_req_success(req, NULL, "success");
413
414         char* appid = afb_req_get_application_id(req);
415         if(appid)
416         {
417             if (g_afb_instance->wmgr.wmcon.isMasterMode() ||
418                 !g_afb_instance->wmgr.wmcon.isSyncDrawingForRemote(appid))
419             {
420                 g_afb_instance->wmgr.api_enddraw(appid, a_drawing_name);
421             }
422             else
423             {
424                 // If Window Manager is slave and requesting app is syncDrawing,
425                 // request endDraw to master
426                 g_afb_instance->wmgr.api_enddraw_for_remote(appid, a_drawing_name);
427             }
428            free(appid);
429         }
430     }
431     catch (std::exception &e)
432     {
433         HMI_WARNING("failed: Uncaught exception while calling enddraw: %s", e.what());
434         g_afb_instance->wmgr.exceptionProcessForTransition();
435         return;
436     }
437 }
438
439 void windowmanager_getdisplayinfo_thunk(afb_req req) noexcept
440 {
441     std::lock_guard<std::mutex> guard(binding_m);
442     if (g_afb_instance == nullptr)
443     {
444         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
445         return;
446     }
447
448     try
449     {
450         auto ret = g_afb_instance->wmgr.api_get_display_info();
451         if (ret.is_err())
452         {
453             afb_req_fail(req, "failed", ret.unwrap_err());
454             return;
455         }
456
457         afb_req_success(req, ret.unwrap(), "success");
458     }
459     catch (std::exception &e)
460     {
461         afb_req_fail_f(req, "failed", "Uncaught exception while calling getdisplayinfo: %s", e.what());
462         return;
463     }
464 }
465
466 void windowmanager_getareainfo_thunk(afb_req req) noexcept
467 {
468     std::lock_guard<std::mutex> guard(binding_m);
469     if (g_afb_instance == nullptr)
470     {
471         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
472         return;
473     }
474
475     try
476     {
477         json_object *jreq = afb_req_json(req);
478
479         json_object *j_drawing_name = nullptr;
480         if (!json_object_object_get_ex(jreq, "drawing_name", &j_drawing_name))
481         {
482             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
483             return;
484         }
485         char const *a_drawing_name = json_object_get_string(j_drawing_name);
486
487         auto ret = g_afb_instance->wmgr.api_get_area_info(a_drawing_name);
488         if (ret.is_err())
489         {
490             afb_req_fail(req, "failed", ret.unwrap_err());
491             return;
492         }
493
494         afb_req_success(req, ret.unwrap(), "success");
495     }
496     catch (std::exception &e)
497     {
498         afb_req_fail_f(req, "failed", "Uncaught exception while calling getareainfo: %s", e.what());
499         return;
500     }
501 }
502
503 void windowmanager_getcarinfo_thunk(afb_req req) noexcept
504 {
505     std::lock_guard<std::mutex> guard(binding_m);
506     if (g_afb_instance == nullptr)
507     {
508         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
509         return;
510     }
511
512     try
513     {
514         json_object *jreq = afb_req_json(req);
515
516         json_object *j_label = nullptr;
517         if (! json_object_object_get_ex(jreq, "label", &j_label))
518         {
519             afb_req_fail(req, "failed", "Need char const* argument label");
520             return;
521         }
522         char const* a_label = json_object_get_string(j_label);
523
524         auto ret = g_afb_instance->wmgr.api_get_car_info(a_label);
525         if (ret.is_err())
526         {
527             afb_req_fail(req, "failed", ret.unwrap_err());
528             return;
529         }
530
531         afb_req_success(req, ret.unwrap(), "success");
532     }
533     catch (std::exception &e)
534     {
535         afb_req_fail_f(req, "failed", "Uncaught exception while calling getcarinfo: %s", e.what());
536         return;
537     }
538 }
539
540 void windowmanager_set_render_order(afb_req req) noexcept
541 {
542     std::lock_guard<std::mutex> guard(binding_m);
543     if (g_afb_instance == nullptr)
544     {
545         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
546         return;
547     }
548
549     char* appid = afb_req_get_application_id(req);
550     if(appid)
551     {
552         json_object *jreq = afb_req_json(req);
553         json_object *j_ro; // Do not free this. binder frees jreq, then free j_ro
554         if (json_object_object_get_ex(jreq, "render_order", &j_ro))
555         {
556             int size = json_object_array_length(j_ro);
557             std::vector<std::string> ro(size);
558             for(int i = 0; i < size; i++)
559             {
560                 ro[i] = json_object_get_string(json_object_array_get_idx(j_ro, i));
561             }
562
563             auto ret = g_afb_instance->wmgr.api_client_set_render_order(appid, ro);
564             if (!ret)
565             {
566                 afb_req_fail(req, "failed", nullptr);
567             }
568             else
569             {
570                 afb_req_success(req, nullptr, nullptr);
571             }
572         }
573         free(appid);
574     }
575     else
576     {
577         afb_req_fail(req, "failed", nullptr);
578     }
579 }
580
581 void windowmanager_attach_app(afb_req req) noexcept
582 {
583     std::lock_guard<std::mutex> guard(binding_m);
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     char* appid = afb_req_get_application_id(req);
591     if(appid)
592     {
593         json_object *jreq = afb_req_json(req);
594         json_object *j_dest, *j_id; // Do not free this. binder frees jreq, then free j_ro
595         if (json_object_object_get_ex(jreq, "destination", &j_dest) &&
596             json_object_object_get_ex(jreq, "service_surface", &j_id))
597         {
598             const char* dest_app = json_object_get_string(j_dest);
599             const char* service = json_object_get_string(j_id);
600
601             std::string uuid = g_afb_instance->wmgr.api_client_attach_service_surface(appid, dest_app, service);
602             if (uuid.empty())
603             {
604                 afb_req_fail(req, "failed", nullptr);
605             }
606             else
607             {
608                 json_object *resp = json_object_new_object();
609                 json_object_object_add(resp, "uuid", json_object_new_string(uuid.c_str()));
610                 afb_req_success(req, resp, nullptr);
611             }
612         }
613         free(appid);
614     }
615     else
616     {
617         afb_req_fail(req, "failed", nullptr);
618     }
619 }
620
621 void windowmanager_wm_subscribe(afb_req req) noexcept
622 {
623     std::lock_guard<std::mutex> guard(binding_m);
624     if (g_afb_instance == nullptr)
625     {
626         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
627         return;
628     }
629
630     try
631     {
632         json_object *jreq = afb_req_json(req);
633         json_object *j = nullptr;
634         if (!json_object_object_get_ex(jreq, "event", &j))
635         {
636             afb_req_fail(req, "failed", "Need char const* argument event");
637             return;
638         }
639         int event_id = json_object_get_int(j);
640         int ret = g_afb_instance->wmgr.api_subscribe(req, event_id);
641
642         if (ret)
643         {
644             afb_req_fail(req, "failed", "Error: afb_req_subscribe()");
645             return;
646         }
647         afb_req_success(req, NULL, "success");
648     }
649     catch (std::exception &e)
650     {
651         afb_req_fail_f(req, "failed", "Uncaught exception while calling wm_subscribe: %s", e.what());
652         return;
653     }
654 }
655
656 void windowmanager_ping(afb_req req) noexcept
657 {
658     std::lock_guard<std::mutex> guard(binding_m);
659
660     if (g_afb_instance == nullptr)
661     {
662         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
663         return;
664     }
665     else
666     {
667         afb_req_success(req, NULL, "success");
668     }
669 }
670
671 void windowmanager_debug_terminate(afb_req req) noexcept
672 {
673     std::lock_guard<std::mutex> guard(binding_m);
674     if (g_afb_instance == nullptr)
675     {
676         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
677         return;
678     }
679
680     try
681     {
682
683         if (getenv("WINMAN_DEBUG_TERMINATE") != nullptr)
684         {
685             raise(SIGKILL); // afb-daemon kills it's pgroup using TERM, which
686                             // doesn't play well with perf
687         }
688
689         afb_req_success(req, NULL, "success");
690     }
691     catch (std::exception &e)
692     {
693         afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_terminate: %s", e.what());
694         return;
695     }
696 }
697
698 void on_event(const char *event, struct json_object *object)
699 {
700     g_afb_instance->wmgr.analyzeReceivedEvent(event, object);
701 }
702
703 const struct afb_verb_v2 windowmanager_verbs[] = {
704     {"requestSurface", windowmanager_requestsurface, nullptr, nullptr, AFB_SESSION_NONE},
705     {"requestSurfaceXdg", windowmanager_requestsurfacexdg, nullptr, nullptr, AFB_SESSION_NONE},
706     {"setRole", windowmanager_setrole, nullptr, nullptr, AFB_SESSION_NONE},
707     {"activateWindow", windowmanager_activatewindow, nullptr, nullptr, AFB_SESSION_NONE},
708     {"deactivateWindow", windowmanager_deactivatewindow, nullptr, nullptr, AFB_SESSION_NONE},
709     {"endDraw", windowmanager_enddraw, nullptr, nullptr, AFB_SESSION_NONE},
710     {"getDisplayInfo", windowmanager_getdisplayinfo_thunk, nullptr, nullptr, AFB_SESSION_NONE},
711     {"getAreaInfo", windowmanager_getareainfo_thunk, nullptr, nullptr, AFB_SESSION_NONE},
712     {"getCarInfo", windowmanager_getcarinfo_thunk, nullptr, nullptr, AFB_SESSION_NONE },
713     {"setRenderOrder", windowmanager_set_render_order, nullptr, nullptr, AFB_SESSION_NONE},
714     {"attachApp", windowmanager_attach_app, nullptr, nullptr, AFB_SESSION_NONE},
715     {"wm_subscribe", windowmanager_wm_subscribe, nullptr, nullptr, AFB_SESSION_NONE},
716     {"ping", windowmanager_ping, nullptr, nullptr, AFB_SESSION_NONE},
717     {"debug_terminate", windowmanager_debug_terminate, nullptr, nullptr, AFB_SESSION_NONE},
718     {}};
719
720 extern "C" const struct afb_binding_v2 afbBindingV2 = {
721     "windowmanager", nullptr, nullptr, windowmanager_verbs, nullptr, binding_init, on_event, 0};