Add gitlab issue/merge request templates
[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 "window_manager.hpp"
22 #include "json_helper.hpp"
23
24 extern "C"
25 {
26 #include <afb/afb-binding.h>
27 #include <systemd/sd-event.h>
28 }
29
30 typedef struct WMClientCtxt
31 {
32     std::string name;
33     std::string role;
34     WMClientCtxt(const char *appName, const char* appRole)
35     {
36         name = appName;
37         role = appRole;
38     }
39 } WMClientCtxt;
40
41 struct afb_instance
42 {
43     wm::WindowManager wmgr;
44
45     afb_instance() : wmgr() {}
46     ~afb_instance() = default;
47
48     int init();
49 };
50
51 struct afb_instance *g_afb_instance;
52 std::mutex binding_m;
53
54 int afb_instance::init()
55 {
56     return this->wmgr.init();
57 }
58
59 static int _binding_init()
60 {
61     HMI_NOTICE("WinMan ver. %s", WINMAN_VERSION_STRING);
62
63     g_afb_instance = new afb_instance;
64
65     if (g_afb_instance->init() == -1)
66     {
67         HMI_ERROR("Could not connect to compositor");
68         goto error;
69     }
70
71     atexit([] { delete g_afb_instance; });
72
73     return 0;
74
75 error:
76     delete g_afb_instance;
77     g_afb_instance = nullptr;
78     return -1;
79 }
80
81 static int binding_init (afb_api_t api) noexcept
82 {
83     try
84     {
85         return _binding_init();
86     }
87     catch (std::exception &e)
88     {
89         HMI_ERROR("Uncaught exception in binding_init(): %s", e.what());
90     }
91     return -1;
92 }
93
94 static void cbRemoveClientCtxt(void *data)
95 {
96     WMClientCtxt *ctxt = (WMClientCtxt *)data;
97     if (ctxt == nullptr)
98     {
99         return;
100     }
101     HMI_DEBUG("remove app %s", ctxt->name.c_str());
102
103     // Policy Manager does not know this app was killed,
104     // so notify it by deactivate request.
105     g_afb_instance->wmgr.api_deactivate_window(
106         ctxt->name.c_str(), ctxt->role.c_str(),
107         [](const char *) {});
108
109     g_afb_instance->wmgr.removeClient(ctxt->name);
110     delete ctxt;
111 }
112
113 static void createSecurityContext(afb_req_t req, const char* appid, const char* role)
114 {
115     WMClientCtxt *ctxt = (WMClientCtxt *)afb_req_context_get(req);
116     if (!ctxt)
117     {
118         // Create Security Context at first time
119         WMClientCtxt *ctxt = new WMClientCtxt(appid, role);
120         HMI_DEBUG("create session for %s", ctxt->name.c_str());
121         afb_req_session_set_LOA(req, 1);
122         afb_req_context_set(req, ctxt, cbRemoveClientCtxt);
123     }
124 }
125
126 void windowmanager_requestsurface(afb_req_t req) noexcept
127 {
128     std::lock_guard<std::mutex> guard(binding_m);
129     if (g_afb_instance == nullptr)
130     {
131         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
132         return;
133     }
134
135     try
136     {
137         const char *a_drawing_name = afb_req_value(req, "drawing_name");
138         if (!a_drawing_name)
139         {
140             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
141             return;
142         }
143
144         char *appid = afb_req_get_application_id(req);
145         if(appid)
146         {
147             auto ret = g_afb_instance->wmgr.api_request_surface(
148                 appid, a_drawing_name);
149             if (ret.is_err())
150             {
151                 afb_req_fail(req, "failed", ret.unwrap_err());
152             }
153             else
154             {
155                 createSecurityContext(req, appid, a_drawing_name);
156                 afb_req_success(req, json_object_new_int(ret.unwrap()), "success");
157             }
158             free(appid);
159         }
160         else
161         {
162             afb_req_fail(req, "failed", nullptr);
163         }
164     }
165     catch (std::exception &e)
166     {
167         afb_req_fail_f(req, "failed", "Uncaught exception while calling requestsurface: %s", e.what());
168         return;
169     }
170 }
171
172 void windowmanager_requestsurfacexdg(afb_req_t req) noexcept
173 {
174     std::lock_guard<std::mutex> guard(binding_m);
175     if (g_afb_instance == nullptr)
176     {
177         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
178         return;
179     }
180
181     try
182     {
183         json_object *jreq = afb_req_json(req);
184
185         json_object *j_drawing_name = nullptr;
186         if (!json_object_object_get_ex(jreq, "drawing_name", &j_drawing_name))
187         {
188             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
189             return;
190         }
191         char const *a_drawing_name = json_object_get_string(j_drawing_name);
192
193         json_object *j_ivi_id = nullptr;
194         if (!json_object_object_get_ex(jreq, "ivi_id", &j_ivi_id))
195         {
196             afb_req_fail(req, "failed", "Need char const* argument ivi_id");
197             return;
198         }
199         char const *a_ivi_id = json_object_get_string(j_ivi_id);
200         char *appid = afb_req_get_application_id(req);
201         if(appid)
202         {
203             auto ret = g_afb_instance->wmgr.api_request_surface(
204                 appid, a_drawing_name, a_ivi_id);
205
206             if (ret != nullptr)
207             {
208                 afb_req_fail(req, "failed", ret);
209             }
210             else
211             {
212                 createSecurityContext(req, appid, a_drawing_name);
213                 afb_req_success(req, NULL, "success");
214             }
215             free(appid);
216         }
217     }
218     catch (std::exception &e)
219     {
220         afb_req_fail_f(req, "failed", "Uncaught exception while calling requestsurfacexdg: %s", e.what());
221         return;
222     }
223 }
224
225 void windowmanager_activatewindow(afb_req_t req) noexcept
226 {
227     std::lock_guard<std::mutex> guard(binding_m);
228     if (g_afb_instance == nullptr)
229     {
230         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
231         return;
232     }
233
234     try
235     {
236         const char *a_drawing_name = afb_req_value(req, "drawing_name");
237         if (!a_drawing_name)
238         {
239             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
240             return;
241         }
242
243         const char *a_drawing_area = afb_req_value(req, "drawing_area");
244         if (!a_drawing_area)
245         {
246             afb_req_fail(req, "failed", "Need char const* argument drawing_area");
247             return;
248         }
249
250         char* appid = afb_req_get_application_id(req);
251         if(appid)
252         {
253             g_afb_instance->wmgr.api_activate_window(
254                 appid, a_drawing_name, a_drawing_area,
255                 [&req](const char *errmsg) {
256                     if (errmsg != nullptr)
257                     {
258                         HMI_ERROR(errmsg);
259                         afb_req_fail(req, "failed", errmsg);
260                         return;
261                     }
262                     afb_req_success(req, NULL, "success");
263                 });
264             free(appid);
265         }
266     }
267     catch (std::exception &e)
268     {
269         HMI_WARNING("failed: Uncaught exception while calling activatesurface: %s", e.what());
270         g_afb_instance->wmgr.exceptionProcessForTransition();
271         return;
272     }
273 }
274
275 void windowmanager_deactivatewindow(afb_req_t req) noexcept
276 {
277     std::lock_guard<std::mutex> guard(binding_m);
278     if (g_afb_instance == nullptr)
279     {
280         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
281         return;
282     }
283
284     try
285     {
286         const char *a_drawing_name = afb_req_value(req, "drawing_name");
287         if (!a_drawing_name)
288         {
289             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
290             return;
291         }
292
293         char* appid = afb_req_get_application_id(req);
294         if(appid)
295         {
296             g_afb_instance->wmgr.api_deactivate_window(
297                 appid, a_drawing_name,
298                 [&req](const char *errmsg) {
299                     if (errmsg != nullptr)
300                     {
301                         HMI_ERROR(errmsg);
302                         afb_req_fail(req, "failed", errmsg);
303                         return;
304                     }
305                     afb_req_success(req, NULL, "success");
306                 });
307             free(appid);
308         }
309     }
310     catch (std::exception &e)
311     {
312         HMI_WARNING("failed: Uncaught exception while calling deactivatesurface: %s", e.what());
313         g_afb_instance->wmgr.exceptionProcessForTransition();
314         return;
315     }
316 }
317
318 void windowmanager_enddraw(afb_req_t req) noexcept
319 {
320     std::lock_guard<std::mutex> guard(binding_m);
321     if (g_afb_instance == nullptr)
322     {
323         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
324         return;
325     }
326
327     try
328     {
329         const char *a_drawing_name = afb_req_value(req, "drawing_name");
330         if (!a_drawing_name)
331         {
332             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
333             return;
334         }
335         afb_req_success(req, NULL, "success");
336
337         char* appid = afb_req_get_application_id(req);
338         if(appid)
339         {
340             g_afb_instance->wmgr.api_enddraw(appid, a_drawing_name);
341             free(appid);
342         }
343     }
344     catch (std::exception &e)
345     {
346         HMI_WARNING("failed: Uncaught exception while calling enddraw: %s", e.what());
347         g_afb_instance->wmgr.exceptionProcessForTransition();
348         return;
349     }
350 }
351
352 void windowmanager_getdisplayinfo_thunk(afb_req_t req) noexcept
353 {
354     std::lock_guard<std::mutex> guard(binding_m);
355     if (g_afb_instance == nullptr)
356     {
357         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
358         return;
359     }
360
361     try
362     {
363         auto ret = g_afb_instance->wmgr.api_get_display_info();
364         if (ret.is_err())
365         {
366             afb_req_fail(req, "failed", ret.unwrap_err());
367             return;
368         }
369
370         afb_req_success(req, ret.unwrap(), "success");
371     }
372     catch (std::exception &e)
373     {
374         afb_req_fail_f(req, "failed", "Uncaught exception while calling getdisplayinfo: %s", e.what());
375         return;
376     }
377 }
378
379 void windowmanager_getareainfo_thunk(afb_req_t req) noexcept
380 {
381     std::lock_guard<std::mutex> guard(binding_m);
382     if (g_afb_instance == nullptr)
383     {
384         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
385         return;
386     }
387
388     try
389     {
390         json_object *jreq = afb_req_json(req);
391
392         json_object *j_drawing_name = nullptr;
393         if (!json_object_object_get_ex(jreq, "drawing_name", &j_drawing_name))
394         {
395             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
396             return;
397         }
398         char const *a_drawing_name = json_object_get_string(j_drawing_name);
399
400         auto ret = g_afb_instance->wmgr.api_get_area_info(a_drawing_name);
401         if (ret.is_err())
402         {
403             afb_req_fail(req, "failed", ret.unwrap_err());
404             return;
405         }
406
407         afb_req_success(req, ret.unwrap(), "success");
408     }
409     catch (std::exception &e)
410     {
411         afb_req_fail_f(req, "failed", "Uncaught exception while calling getareainfo: %s", e.what());
412         return;
413     }
414 }
415
416 void windowmanager_get_area_list(afb_req_t req) noexcept
417 {
418     std::lock_guard<std::mutex> guard(binding_m);
419     json_object* ret = g_afb_instance->wmgr.api_get_area_list();
420     afb_req_success(req, ret, nullptr);
421 }
422
423 void windowmanager_change_area_size(afb_req_t req) noexcept
424 {
425     std::lock_guard<std::mutex> guard(binding_m);
426     if (g_afb_instance == nullptr)
427     {
428         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
429         return;
430     }
431
432     char* appid = afb_req_get_application_id(req);
433     if(appid)
434     {
435         ChangeAreaReq change_req;
436         change_req.appname = appid;
437         change_req.save = false;
438         json_object *jreq = afb_req_json(req);
439         json_object *jsave, *jareas;
440         HMI_INFO("json_check, %s", json_object_get_string(jreq));
441         if(json_object_object_get_ex(jreq, "save", &jsave))
442         {
443             change_req.save = json_object_get_boolean(jsave);
444         }
445         if (json_object_object_get_ex(jreq, "areas", &jareas))
446         {
447             int size = json_object_array_length(jareas);
448             for(int i = 0; i < size; i++)
449             {
450                 json_object* elem = json_object_array_get_idx(jareas, i);
451                 struct rect rect;
452                 std::string name = jh::getStringFromJson(elem, "name");
453                 json_object* jrect;
454                 if(json_object_object_get_ex(elem, "rect", &jrect))
455                 {
456                     rect.x = jh::getIntFromJson(jrect, "x");
457                     rect.y = jh::getIntFromJson(jrect, "y");
458                     rect.w = jh::getIntFromJson(jrect, "w");
459                     rect.h = jh::getIntFromJson(jrect, "h");
460                 }
461                 else
462                 {
463                     HMI_ERROR("bad request @area name :%s", name.c_str());
464                     afb_req_fail(req, "failed", "bad request");
465                     return;
466                 }
467                 change_req.area_req[name] = rect;
468             }
469             if(change_req.area_req.size() != 0)
470             {
471                 g_afb_instance->wmgr.api_change_area_size(change_req);
472             }
473             afb_req_success(req, nullptr, nullptr);
474         }
475         free(appid);
476     }
477     else
478     {
479         afb_req_fail(req, "failed", nullptr);
480     }
481 }
482
483 void windowmanager_wm_subscribe(afb_req_t req) noexcept
484 {
485     std::lock_guard<std::mutex> guard(binding_m);
486     if (g_afb_instance == nullptr)
487     {
488         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
489         return;
490     }
491
492     try
493     {
494         json_object *jreq = afb_req_json(req);
495         json_object *j = nullptr;
496         if (!json_object_object_get_ex(jreq, "event", &j))
497         {
498             afb_req_fail(req, "failed", "Need char const* argument event");
499             return;
500         }
501         wm::WindowManager::EventType event_id = (wm::WindowManager::EventType)json_object_get_int(j);
502         bool ret = g_afb_instance->wmgr.api_subscribe(req, event_id);
503
504         if (!ret)
505         {
506             afb_req_fail(req, "failed", "Error: afb_req_subscribe()");
507             return;
508         }
509         afb_req_success(req, NULL, "success");
510     }
511     catch (std::exception &e)
512     {
513         afb_req_fail_f(req, "failed", "Uncaught exception while calling wm_subscribe: %s", e.what());
514         return;
515     }
516 }
517
518 void windowmanager_ping(afb_req_t req) noexcept
519 {
520     std::lock_guard<std::mutex> guard(binding_m);
521
522     if (g_afb_instance == nullptr)
523     {
524         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
525         return;
526     }
527     else
528     {
529         afb_req_success(req, NULL, "success");
530     }
531 }
532
533 void windowmanager_debug_terminate(afb_req_t req) noexcept
534 {
535     std::lock_guard<std::mutex> guard(binding_m);
536     if (g_afb_instance == nullptr)
537     {
538         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
539         return;
540     }
541
542     try
543     {
544
545         if (getenv("WINMAN_DEBUG_TERMINATE") != nullptr)
546         {
547             raise(SIGKILL); // afb-daemon kills it's pgroup using TERM, which
548                             // doesn't play well with perf
549         }
550
551         afb_req_success(req, NULL, "success");
552     }
553     catch (std::exception &e)
554     {
555         afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_terminate: %s", e.what());
556         return;
557     }
558 }
559
560 const afb_verb_t windowmanager_verbs[] = {
561     { .verb = "requestSurface", .callback = windowmanager_requestsurface },
562     { .verb = "requestSurfaceXDG", .callback = windowmanager_requestsurfacexdg },
563     { .verb = "activateWindow", .callback = windowmanager_activatewindow },
564     { .verb = "deactivateWindow", .callback = windowmanager_deactivatewindow },
565     { .verb = "endDraw", .callback = windowmanager_enddraw },
566     { .verb = "getDisplayInfo", .callback = windowmanager_getdisplayinfo_thunk },
567     { .verb = "getAreaInfo", .callback = windowmanager_getareainfo_thunk },
568     { .verb = "changeAreaSize", .callback = windowmanager_change_area_size },
569     { .verb = "getAreaList", .callback = windowmanager_get_area_list },
570     { .verb = "wm_subscribe", .callback = windowmanager_wm_subscribe },
571     { .verb = "ping", .callback = windowmanager_ping },
572     { .verb = "debug_terminate", .callback = windowmanager_debug_terminate },
573     {} };
574
575 extern "C" const afb_binding_t afbBindingExport = {
576   .api = "windowmanager",
577   .specification = "windowmanager",
578   .info = "windowmanager",
579   .verbs = windowmanager_verbs,
580   .preinit = nullptr,
581   .init = binding_init,
582   .onevent = nullptr,
583   .userdata = nullptr,
584   .provide_class = nullptr,
585   .require_class = nullptr,
586   .require_api = nullptr,
587   .noconcurrency = 0
588 };