2 * Copyright (c) 2017 TOYOTA MOTOR CORPORATION
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
24 #include "window_manager.hpp"
25 #include "json_helper.hpp"
29 #include <afb/afb-binding.h>
30 #include <systemd/sd-event.h>
33 typedef struct WMClientCtxt
37 WMClientCtxt(const char *appName, const char* appRole)
46 wm::WindowManager wmgr;
48 afb_instance() : wmgr() {}
49 ~afb_instance() = default;
54 struct afb_instance *g_afb_instance;
57 int afb_instance::init()
59 return this->wmgr.init();
64 HMI_NOTICE("WinMan ver. %s", WINMAN_VERSION_STRING);
66 g_afb_instance = new afb_instance;
68 if (g_afb_instance->init() == -1)
70 HMI_ERROR("Could not connect to compositor");
74 atexit([] { delete g_afb_instance; });
79 delete g_afb_instance;
80 g_afb_instance = nullptr;
84 int binding_init() noexcept
88 return _binding_init();
90 catch (std::exception &e)
92 HMI_ERROR("Uncaught exception in binding_init(): %s", e.what());
97 static void cbRemoveClientCtxt(void *data)
99 WMClientCtxt *ctxt = (WMClientCtxt *)data;
104 HMI_DEBUG("remove app %s", ctxt->name.c_str());
106 // Policy Manager does not know this app was killed,
107 // so notify it by deactivate request.
108 g_afb_instance->wmgr.api_deactivate_window(
109 ctxt->name.c_str(), ctxt->role.c_str(),
110 [](const char *) {});
112 g_afb_instance->wmgr.removeClient(ctxt->name);
116 static void createSecurityContext(afb_req req, const char* appid, const char* role)
118 WMClientCtxt *ctxt = (WMClientCtxt *)afb_req_context_get(req);
121 // Create Security Context at first time
122 const char *new_role = g_afb_instance->wmgr.convertRoleOldToNew(role);
123 WMClientCtxt *ctxt = new WMClientCtxt(appid, new_role);
124 HMI_DEBUG("create session for %s", ctxt->name.c_str());
125 afb_req_session_set_LOA(req, 1);
126 afb_req_context_set(req, ctxt, cbRemoveClientCtxt);
130 void windowmanager_requestsurface(afb_req req) noexcept
132 std::lock_guard<std::mutex> guard(binding_m);
133 if (g_afb_instance == nullptr)
135 afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
141 const char *a_drawing_name = afb_req_value(req, "drawing_name");
144 afb_req_fail(req, "failed", "Need char const* argument drawing_name");
148 char *appid = afb_req_get_application_id(req);
151 auto ret = g_afb_instance->wmgr.api_request_surface(
152 appid, a_drawing_name);
155 afb_req_fail(req, "failed", ret.unwrap_err());
159 createSecurityContext(req, appid, a_drawing_name);
160 afb_req_success(req, json_object_new_int(ret.unwrap()), "success");
166 afb_req_fail(req, "failed", nullptr);
169 catch (std::exception &e)
171 afb_req_fail_f(req, "failed", "Uncaught exception while calling requestsurface: %s", e.what());
176 void windowmanager_requestsurfacexdg(afb_req req) noexcept
178 std::lock_guard<std::mutex> guard(binding_m);
179 if (g_afb_instance == nullptr)
181 afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
187 json_object *jreq = afb_req_json(req);
189 json_object *j_drawing_name = nullptr;
190 if (!json_object_object_get_ex(jreq, "drawing_name", &j_drawing_name))
192 afb_req_fail(req, "failed", "Need char const* argument drawing_name");
195 char const *a_drawing_name = json_object_get_string(j_drawing_name);
197 json_object *j_ivi_id = nullptr;
198 if (!json_object_object_get_ex(jreq, "ivi_id", &j_ivi_id))
200 afb_req_fail(req, "failed", "Need char const* argument ivi_id");
203 char const *a_ivi_id = json_object_get_string(j_ivi_id);
204 char *appid = afb_req_get_application_id(req);
207 auto ret = g_afb_instance->wmgr.api_request_surface(
208 appid, a_drawing_name, a_ivi_id);
212 afb_req_fail(req, "failed", ret);
216 createSecurityContext(req, appid, a_drawing_name);
217 afb_req_success(req, NULL, "success");
222 catch (std::exception &e)
224 afb_req_fail_f(req, "failed", "Uncaught exception while calling requestsurfacexdg: %s", e.what());
229 void windowmanager_setrole(afb_req req) noexcept
231 std::lock_guard<std::mutex> guard(binding_m);
232 if (g_afb_instance == nullptr)
234 afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
239 json_object *jreq = afb_req_json(req);
241 json_object *j_role = nullptr;
242 if (!json_object_object_get_ex(jreq, "role", &j_role))
244 afb_req_fail(req, "failed", "Need char const* argument role");
247 char const *a_role = json_object_get_string(j_role);
248 char *appid = afb_req_get_application_id(req);
252 auto ret = g_afb_instance->wmgr.api_set_role(appid, a_role);
255 afb_req_fail(req, "failed", "Couldn't register");
259 createSecurityContext(req, appid, a_role);
260 afb_req_success(req, NULL, "success");
265 catch (std::exception &e)
267 afb_req_fail_f(req, "failed", "Uncaught exception while calling requestsurfacexdg: %s", e.what());
272 void windowmanager_activatewindow(afb_req req) noexcept
274 std::lock_guard<std::mutex> guard(binding_m);
275 if (g_afb_instance == nullptr)
277 afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
283 const char *a_drawing_name = afb_req_value(req, "drawing_name");
286 afb_req_fail(req, "failed", "Need char const* argument drawing_name");
290 const char *a_drawing_area = afb_req_value(req, "drawing_area");
293 afb_req_fail(req, "failed", "Need char const* argument drawing_area");
297 char* appid = afb_req_get_application_id(req);
300 auto reply = [&req](const char *errmsg) {
301 if (errmsg != nullptr)
304 afb_req_fail(req, "failed", errmsg);
307 afb_req_success(req, NULL, "success");
310 if (!g_afb_instance->wmgr.wmcon.isRemoteArea(a_drawing_area))
312 g_afb_instance->wmgr.api_activate_window(
313 appid, a_drawing_name, a_drawing_area, reply);
317 std::string ecu_name;
318 ecu_name = g_afb_instance->wmgr.wmcon.getAreaToEcuName(a_drawing_area);
321 if (!g_afb_instance->wmgr.wmcon.isConnectionMode())
323 HMI_ERROR("WM Standalone Mode");
324 afb_req_fail(req, "failed", "Standalone Mode");
328 // If Window Manager is slave and this request is for master,
329 // request activateWindow to master
330 g_afb_instance->wmgr.api_activate_surface_to_master(
331 appid, a_drawing_name, a_drawing_area, reply);
337 catch (std::exception &e)
339 HMI_WARNING("failed: Uncaught exception while calling activatesurface: %s", e.what());
340 g_afb_instance->wmgr.exceptionProcessForTransition();
345 void windowmanager_deactivatewindow(afb_req req) noexcept
347 std::lock_guard<std::mutex> guard(binding_m);
348 if (g_afb_instance == nullptr)
350 afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
356 const char *a_drawing_name = afb_req_value(req, "drawing_name");
359 afb_req_fail(req, "failed", "Need char const* argument drawing_name");
363 char* appid = afb_req_get_application_id(req);
366 auto reply = [&req](const char *errmsg) {
367 if (errmsg != nullptr)
370 afb_req_fail(req, "failed", errmsg);
373 afb_req_success(req, NULL, "success");
376 // TODO: Check whether role is tbtnavi to request remote invisible
377 if (g_afb_instance->wmgr.wmcon.getAppIdToEcuName(appid) == "" ||
378 ("tbtnavi" != std::string(a_drawing_name)))
380 g_afb_instance->wmgr.api_deactivate_window(
381 appid, a_drawing_name, reply);
385 // If Window Manager is slave and this request is for master,
386 // request deactivateWindow to master
387 g_afb_instance->wmgr.api_deactivate_surface_to_master(
388 appid, a_drawing_name, reply);
393 catch (std::exception &e)
395 HMI_WARNING("failed: Uncaught exception while calling deactivatesurface: %s", e.what());
396 g_afb_instance->wmgr.exceptionProcessForTransition();
401 void windowmanager_enddraw(afb_req req) noexcept
403 std::lock_guard<std::mutex> guard(binding_m);
404 if (g_afb_instance == nullptr)
406 afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
412 const char *a_drawing_name = afb_req_value(req, "drawing_name");
415 afb_req_fail(req, "failed", "Need char const* argument drawing_name");
418 afb_req_success(req, NULL, "success");
420 char* appid = afb_req_get_application_id(req);
424 if (!g_afb_instance->wmgr.wmcon.isRemoteEcu(appid) ||
425 !g_afb_instance->wmgr.wmcon.isSyncDrawingForRemote(appid))
427 g_afb_instance->wmgr.api_enddraw(appid, a_drawing_name);
431 // If Window Manager is slave and requesting app is syncDrawing,
432 // request endDraw to master
433 g_afb_instance->wmgr.api_enddraw_for_remote(appid, a_drawing_name);
438 catch (std::exception &e)
440 HMI_WARNING("failed: Uncaught exception while calling enddraw: %s", e.what());
441 g_afb_instance->wmgr.exceptionProcessForTransition();
446 void windowmanager_getdisplayinfo_thunk(afb_req req) noexcept
448 std::lock_guard<std::mutex> guard(binding_m);
449 if (g_afb_instance == nullptr)
451 afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
457 auto ret = g_afb_instance->wmgr.api_get_display_info();
460 afb_req_fail(req, "failed", ret.unwrap_err());
464 afb_req_success(req, ret.unwrap(), "success");
466 catch (std::exception &e)
468 afb_req_fail_f(req, "failed", "Uncaught exception while calling getdisplayinfo: %s", e.what());
473 void windowmanager_getareainfo_thunk(afb_req req) noexcept
475 std::lock_guard<std::mutex> guard(binding_m);
476 if (g_afb_instance == nullptr)
478 afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
484 json_object *jreq = afb_req_json(req);
486 json_object *j_drawing_name = nullptr;
487 if (!json_object_object_get_ex(jreq, "drawing_name", &j_drawing_name))
489 afb_req_fail(req, "failed", "Need char const* argument drawing_name");
492 char const *a_drawing_name = json_object_get_string(j_drawing_name);
494 auto ret = g_afb_instance->wmgr.api_get_area_info(a_drawing_name);
497 afb_req_fail(req, "failed", ret.unwrap_err());
501 afb_req_success(req, ret.unwrap(), "success");
503 catch (std::exception &e)
505 afb_req_fail_f(req, "failed", "Uncaught exception while calling getareainfo: %s", e.what());
510 void windowmanager_getcarinfo_thunk(afb_req req) noexcept
512 std::lock_guard<std::mutex> guard(binding_m);
513 if (g_afb_instance == nullptr)
515 afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
521 json_object *jreq = afb_req_json(req);
523 json_object *j_label = nullptr;
524 if (! json_object_object_get_ex(jreq, "label", &j_label))
526 afb_req_fail(req, "failed", "Need char const* argument label");
529 char const* a_label = json_object_get_string(j_label);
531 auto ret = g_afb_instance->wmgr.api_get_car_info(a_label);
534 afb_req_fail(req, "failed", ret.unwrap_err());
538 afb_req_success(req, ret.unwrap(), "success");
540 catch (std::exception &e)
542 afb_req_fail_f(req, "failed", "Uncaught exception while calling getcarinfo: %s", e.what());
547 void windowmanager_set_render_order(afb_req req) noexcept
549 std::lock_guard<std::mutex> guard(binding_m);
550 if (g_afb_instance == nullptr)
552 afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
556 char* appid = afb_req_get_application_id(req);
559 json_object *jreq = afb_req_json(req);
560 json_object *j_ro; // Do not free this. binder frees jreq, then free j_ro
561 if (json_object_object_get_ex(jreq, "render_order", &j_ro))
563 int size = json_object_array_length(j_ro);
564 std::vector<std::string> ro(size);
565 for(int i = 0; i < size; i++)
567 ro[i] = json_object_get_string(json_object_array_get_idx(j_ro, i));
570 auto ret = g_afb_instance->wmgr.api_client_set_render_order(appid, ro);
573 afb_req_fail(req, "failed", nullptr);
577 afb_req_success(req, nullptr, nullptr);
584 afb_req_fail(req, "failed", nullptr);
588 void windowmanager_attach_app(afb_req req) noexcept
590 std::lock_guard<std::mutex> guard(binding_m);
591 if (g_afb_instance == nullptr)
593 afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
597 char* appid = afb_req_get_application_id(req);
600 json_object *jreq = afb_req_json(req);
601 json_object *j_dest, *j_id; // Do not free this. binder frees jreq, then free j_ro
602 if (json_object_object_get_ex(jreq, "destination", &j_dest) &&
603 json_object_object_get_ex(jreq, "service_surface", &j_id))
605 const char* dest_app = json_object_get_string(j_dest);
606 const char* service = json_object_get_string(j_id);
608 std::string uuid = g_afb_instance->wmgr.api_client_attach_service_surface(appid, dest_app, service);
611 afb_req_fail(req, "failed", nullptr);
615 json_object *resp = json_object_new_object();
616 json_object_object_add(resp, "uuid", json_object_new_string(uuid.c_str()));
617 afb_req_success(req, resp, nullptr);
624 afb_req_fail(req, "failed", nullptr);
628 void windowmanager_get_area_list(afb_req req) noexcept
630 std::lock_guard<std::mutex> guard(binding_m);
631 json_object* ret = g_afb_instance->wmgr.api_get_area_list();
632 afb_req_success(req, ret, nullptr);
635 void windowmanager_change_area_size(afb_req req) noexcept
637 std::lock_guard<std::mutex> guard(binding_m);
638 if (g_afb_instance == nullptr)
640 afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
644 char* appid = afb_req_get_application_id(req);
647 ChangeAreaReq change_req;
648 change_req.appname = appid;
649 change_req.save = false;
650 json_object *jreq = afb_req_json(req);
651 json_object *jsave, *jareas;
652 HMI_INFO("json_check, %s", json_object_get_string(jreq));
653 if(json_object_object_get_ex(jreq, "save", &jsave))
655 change_req.save = json_object_get_boolean(jsave);
657 if (json_object_object_get_ex(jreq, "areas", &jareas))
659 int size = json_object_array_length(jareas);
660 for(int i = 0; i < size; i++)
662 json_object* elem = json_object_array_get_idx(jareas, i);
664 std::string name = jh::getStringFromJson(elem, "name");
666 if(json_object_object_get_ex(elem, "rect", &jrect))
668 rect.x = jh::getIntFromJson(jrect, "x");
669 rect.y = jh::getIntFromJson(jrect, "y");
670 rect.w = jh::getIntFromJson(jrect, "w");
671 rect.h = jh::getIntFromJson(jrect, "h");
675 HMI_ERROR("bad request @area name :%s", name.c_str());
676 afb_req_fail(req, "failed", "bad request");
679 change_req.area_req[name] = rect;
681 if(change_req.area_req.size() != 0)
683 g_afb_instance->wmgr.api_change_area_size(change_req);
685 afb_req_success(req, nullptr, nullptr);
691 afb_req_fail(req, "failed", nullptr);
695 void windowmanager_wm_subscribe(afb_req req) noexcept
697 std::lock_guard<std::mutex> guard(binding_m);
698 if (g_afb_instance == nullptr)
700 afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
706 json_object *jreq = afb_req_json(req);
707 json_object *j = nullptr;
708 if (!json_object_object_get_ex(jreq, "event", &j))
710 afb_req_fail(req, "failed", "Need char const* argument event");
713 int event_id = json_object_get_int(j);
714 int ret = g_afb_instance->wmgr.api_subscribe(req, event_id);
718 afb_req_fail(req, "failed", "Error: afb_req_subscribe()");
721 afb_req_success(req, NULL, "success");
723 g_afb_instance->wmgr.setSubscribed(true);
725 catch (std::exception &e)
727 afb_req_fail_f(req, "failed", "Uncaught exception while calling wm_subscribe: %s", e.what());
732 void windowmanager_connect(afb_req req) noexcept
734 std::lock_guard<std::mutex> guard(binding_m);
736 HMI_DEBUG("WM - HS Connect");
738 if (g_afb_instance == nullptr)
740 afb_req_fail(req, "Failed", "Not Start WindowManager");
745 afb_req_success(req, NULL, "success");
749 void windowmanager_ping(afb_req req) noexcept
751 std::lock_guard<std::mutex> guard(binding_m);
753 if (g_afb_instance == nullptr)
755 afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
760 afb_req_success(req, NULL, "success");
764 void windowmanager_debug_terminate(afb_req req) noexcept
766 std::lock_guard<std::mutex> guard(binding_m);
767 if (g_afb_instance == nullptr)
769 afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
776 if (getenv("WINMAN_DEBUG_TERMINATE") != nullptr)
778 raise(SIGKILL); // afb-daemon kills it's pgroup using TERM, which
779 // doesn't play well with perf
782 afb_req_success(req, NULL, "success");
784 catch (std::exception &e)
786 afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_terminate: %s", e.what());
791 void on_event(const char *event, struct json_object *object)
793 g_afb_instance->wmgr.analyzeReceivedEvent(event, object);
796 const struct afb_verb_v2 windowmanager_verbs[] = {
797 {"requestSurface", windowmanager_requestsurface, nullptr, nullptr, AFB_SESSION_NONE},
798 {"requestSurfaceXdg", windowmanager_requestsurfacexdg, nullptr, nullptr, AFB_SESSION_NONE},
799 {"setRole", windowmanager_setrole, nullptr, nullptr, AFB_SESSION_NONE},
800 {"activateWindow", windowmanager_activatewindow, nullptr, nullptr, AFB_SESSION_NONE},
801 {"deactivateWindow", windowmanager_deactivatewindow, nullptr, nullptr, AFB_SESSION_NONE},
802 {"endDraw", windowmanager_enddraw, nullptr, nullptr, AFB_SESSION_NONE},
803 {"getDisplayInfo", windowmanager_getdisplayinfo_thunk, nullptr, nullptr, AFB_SESSION_NONE},
804 {"getAreaInfo", windowmanager_getareainfo_thunk, nullptr, nullptr, AFB_SESSION_NONE},
805 {"getCarInfo", windowmanager_getcarinfo_thunk, nullptr, nullptr, AFB_SESSION_NONE },
806 {"setRenderOrder", windowmanager_set_render_order, nullptr, nullptr, AFB_SESSION_NONE},
807 {"changeAreaSize", windowmanager_change_area_size, nullptr, nullptr, AFB_SESSION_NONE},
808 {"getAreaList", windowmanager_get_area_list, nullptr, nullptr, AFB_SESSION_NONE},
809 {"attachApp", windowmanager_attach_app, nullptr, nullptr, AFB_SESSION_NONE},
810 {"wm_subscribe", windowmanager_wm_subscribe, nullptr, nullptr, AFB_SESSION_NONE},
811 {"wm_connect", windowmanager_connect, nullptr, nullptr, AFB_SESSION_NONE},
812 {"ping", windowmanager_ping, nullptr, nullptr, AFB_SESSION_NONE},
813 {"debug_terminate", windowmanager_debug_terminate, nullptr, nullptr, AFB_SESSION_NONE},
816 extern "C" const struct afb_binding_v2 afbBindingV2 = {
817 "windowmanager", nullptr, nullptr, windowmanager_verbs, nullptr, binding_init, on_event, 0};