From 5f9be1b9c8e6a4b6d25d64e6f5ec28deb0525854 Mon Sep 17 00:00:00 2001 From: zheng_wenlong Date: Wed, 14 Nov 2018 12:39:54 +0900 Subject: Add windowmanager-service and libraries Add windowmanager-service and libraries. Change-Id: I838b149ee8a62c0dbc6cb98e1fe8e45929e19140 Signed-off-by: zheng_wenlong --- .../agl-service-windowmanager/src/CMakeLists.txt | 124 ++ .../agl-service-windowmanager/src/applist.cpp | 635 ++++++ .../agl-service-windowmanager/src/applist.hpp | 99 + .../src/config/areas.json | 204 ++ .../src/config/connection.json | 5 + .../src/config/old_roles.json | 68 + .../src/controller_hooks.hpp | 42 + .../agl-service-windowmanager/src/json_helper.cpp | 149 ++ .../agl-service-windowmanager/src/json_helper.hpp | 34 + .../agl-service-windowmanager/src/layers.cpp | 382 ++++ .../agl-service-windowmanager/src/layers.hpp | 174 ++ .../src/low_can_client.cpp | 187 ++ .../src/low_can_client.hpp | 113 + .../common/agl-service-windowmanager/src/main.cpp | 844 +++++++ .../agl-service-windowmanager/src/pm_wrapper.cpp | 439 ++++ .../agl-service-windowmanager/src/pm_wrapper.hpp | 76 + .../agl-service-windowmanager/src/request.cpp | 51 + .../agl-service-windowmanager/src/request.hpp | 109 + .../agl-service-windowmanager/src/result.hpp | 86 + .../common/agl-service-windowmanager/src/util.cpp | 172 ++ .../common/agl-service-windowmanager/src/util.hpp | 125 ++ .../src/wayland_ivi_wm.cpp | 724 ++++++ .../src/wayland_ivi_wm.hpp | 327 +++ .../src/window_manager.cpp | 2326 ++++++++++++++++++++ .../src/window_manager.hpp | 346 +++ .../agl-service-windowmanager/src/wm_client.cpp | 271 +++ .../agl-service-windowmanager/src/wm_client.hpp | 92 + .../src/wm_connection.cpp | 457 ++++ .../src/wm_connection.hpp | 64 + .../agl-service-windowmanager/src/wm_error.cpp | 46 + .../agl-service-windowmanager/src/wm_error.hpp | 41 + .../agl-service-windowmanager/src/wm_layer.cpp | 271 +++ .../agl-service-windowmanager/src/wm_layer.hpp | 104 + .../src/wm_layer_control.cpp | 911 ++++++++ .../src/wm_layer_control.hpp | 118 + 35 files changed, 10216 insertions(+) create mode 100644 demo3/common/agl-service-windowmanager/src/CMakeLists.txt create mode 100644 demo3/common/agl-service-windowmanager/src/applist.cpp create mode 100644 demo3/common/agl-service-windowmanager/src/applist.hpp create mode 100644 demo3/common/agl-service-windowmanager/src/config/areas.json create mode 100644 demo3/common/agl-service-windowmanager/src/config/connection.json create mode 100644 demo3/common/agl-service-windowmanager/src/config/old_roles.json create mode 100644 demo3/common/agl-service-windowmanager/src/controller_hooks.hpp create mode 100644 demo3/common/agl-service-windowmanager/src/json_helper.cpp create mode 100644 demo3/common/agl-service-windowmanager/src/json_helper.hpp create mode 100644 demo3/common/agl-service-windowmanager/src/layers.cpp create mode 100644 demo3/common/agl-service-windowmanager/src/layers.hpp create mode 100644 demo3/common/agl-service-windowmanager/src/low_can_client.cpp create mode 100644 demo3/common/agl-service-windowmanager/src/low_can_client.hpp create mode 100644 demo3/common/agl-service-windowmanager/src/main.cpp create mode 100644 demo3/common/agl-service-windowmanager/src/pm_wrapper.cpp create mode 100644 demo3/common/agl-service-windowmanager/src/pm_wrapper.hpp create mode 100644 demo3/common/agl-service-windowmanager/src/request.cpp create mode 100644 demo3/common/agl-service-windowmanager/src/request.hpp create mode 100644 demo3/common/agl-service-windowmanager/src/result.hpp create mode 100644 demo3/common/agl-service-windowmanager/src/util.cpp create mode 100644 demo3/common/agl-service-windowmanager/src/util.hpp create mode 100644 demo3/common/agl-service-windowmanager/src/wayland_ivi_wm.cpp create mode 100644 demo3/common/agl-service-windowmanager/src/wayland_ivi_wm.hpp create mode 100644 demo3/common/agl-service-windowmanager/src/window_manager.cpp create mode 100644 demo3/common/agl-service-windowmanager/src/window_manager.hpp create mode 100644 demo3/common/agl-service-windowmanager/src/wm_client.cpp create mode 100644 demo3/common/agl-service-windowmanager/src/wm_client.hpp create mode 100644 demo3/common/agl-service-windowmanager/src/wm_connection.cpp create mode 100644 demo3/common/agl-service-windowmanager/src/wm_connection.hpp create mode 100644 demo3/common/agl-service-windowmanager/src/wm_error.cpp create mode 100644 demo3/common/agl-service-windowmanager/src/wm_error.hpp create mode 100644 demo3/common/agl-service-windowmanager/src/wm_layer.cpp create mode 100644 demo3/common/agl-service-windowmanager/src/wm_layer.hpp create mode 100644 demo3/common/agl-service-windowmanager/src/wm_layer_control.cpp create mode 100644 demo3/common/agl-service-windowmanager/src/wm_layer_control.hpp (limited to 'demo3/common/agl-service-windowmanager/src') diff --git a/demo3/common/agl-service-windowmanager/src/CMakeLists.txt b/demo3/common/agl-service-windowmanager/src/CMakeLists.txt new file mode 100644 index 0000000..9fa4b73 --- /dev/null +++ b/demo3/common/agl-service-windowmanager/src/CMakeLists.txt @@ -0,0 +1,124 @@ +# +# Copyright (c) 2017 TOYOTA MOTOR CORPORATION +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include(FindPkgConfig) +pkg_check_modules(AFB REQUIRED afb-daemon) +pkg_check_modules(ILM REQUIRED ilmControl ilmCommon) +pkg_check_modules(SD REQUIRED libsystemd>=222) + +# We do not want a prefix for our module +set(CMAKE_SHARED_MODULE_PREFIX "") + +set(TARGETS_WM windowmanager-service) + +add_library(${TARGETS_WM} MODULE + main.cpp + util.cpp + json_helper.cpp + applist.cpp + request.cpp + pm_wrapper.cpp + window_manager.cpp + wm_client.cpp + wm_error.cpp + wm_layer.cpp + wm_layer_control.cpp + wm_connection.cpp + low_can_client.cpp) + +target_include_directories(${TARGETS_WM} + PRIVATE + ${AFB_INCLUDE_DIRS} + ${SD_INCLUDE_DIRS} + ${ILM_INCLUDE_DIRS} + ../include + ../src + ../${PLUGIN_PM}) + +target_link_libraries(${TARGETS_WM} + PRIVATE + ${AFB_LIBRARIES} + ${WLC_LIBRARIES} + ${ILM_LIBRARIES} + ${SD_LIBRARIES} + ${PLUGIN_PM}) + +target_compile_definitions(${TARGETS_WM} + PRIVATE + AFB_BINDING_VERSION=2 + # We do not want source location of messages + AFB_BINDING_PRAGMA_NO_VERBOSE_DETAILS + WINMAN_VERSION_STRING="${PACKAGE_VERSION}" + _GNU_SOURCE) + +if(NOT ${CMAKE_BUILD_TYPE} STREQUAL "Release") + target_compile_definitions(${TARGETS_WM} + PRIVATE + _GLIBCXX_DEBUG) +endif() + +target_compile_options(${TARGETS_WM} + PRIVATE + -Wall -Wextra -Wno-unused-parameter -Wno-comment) + +set_target_properties(${TARGETS_WM} + PROPERTIES + # INTERPROCEDURAL_OPTIMIZATION ON + + CXX_EXTENSIONS OFF + CXX_STANDARD 14 + CXX_STANDARD_REQUIRED ON + + C_EXTENSIONS OFF + C_STANDARD 99 + C_STANDARD_REQUIRED ON + + LINK_FLAGS "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/../export.map -Wl,-rpath,'$ORIGIN'") + +if (LINK_LIBCXX) + set_target_properties(${TARGETS_WM} + PROPERTIES + LINK_FLAGS "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/../export.map -lc++ -Wl,-rpath,'$ORIGIN'") +endif() + +if (NOT ${SANITIZER_MODE} STREQUAL "none" AND NOT ${SANITIZER_MODE} STREQUAL "") + target_compile_options(${TARGETS_WM} + PRIVATE + -fsanitize=${SANITIZER_MODE} -g -fno-omit-frame-pointer) + set_target_properties(${TARGETS_WM} + PROPERTIES + LINK_FLAGS "-fsanitize=${SANITIZER_MODE} -g") +endif() + +if(NOT EXISTS ${PROJECT_BINARY_DIR}/package) + add_custom_command(TARGET ${TARGETS_WM} POST_BUILD + COMMAND cp -rf ${PROJECT_SOURCE_DIR}/package ${PROJECT_BINARY_DIR} + ) +endif() + +add_custom_command(TARGET ${TARGETS_WM} POST_BUILD + COMMAND mkdir -p ${PROJECT_BINARY_DIR}/package/root/lib + COMMAND cp -rf ${PROJECT_BINARY_DIR}/src/${TARGETS_WM}.so ${PROJECT_BINARY_DIR}/package/root/lib + COMMAND mkdir -p ${PROJECT_BINARY_DIR}/package/root/etc + COMMAND cp -f ${PROJECT_SOURCE_DIR}/layers_setting.json ${PROJECT_BINARY_DIR}/package/root/etc + COMMAND cp -f ${PROJECT_SOURCE_DIR}/src/config/old_roles.json ${PROJECT_BINARY_DIR}/package/root/etc + COMMAND cp -f ${PROJECT_SOURCE_DIR}/src/config/areas.json ${PROJECT_BINARY_DIR}/package/root/etc + COMMAND cp -f ${PROJECT_SOURCE_DIR}/src/config/connection.json ${PROJECT_BINARY_DIR}/package/root/etc +) + +add_custom_target(package DEPENDS ${PROJECT_BINARY_DIR}/package/root + COMMAND wgtpkg-pack -f -o ${PROJECT_BINARY_DIR}/package/${TARGETS_WM}-2017.wgt ${PROJECT_BINARY_DIR}/package/root +) diff --git a/demo3/common/agl-service-windowmanager/src/applist.cpp b/demo3/common/agl-service-windowmanager/src/applist.cpp new file mode 100644 index 0000000..44865f6 --- /dev/null +++ b/demo3/common/agl-service-windowmanager/src/applist.cpp @@ -0,0 +1,635 @@ +/* + * Copyright (c) 2017 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include "applist.hpp" +#include "util.hpp" + +using std::shared_ptr; +using std::string; +using std::vector; + +namespace wm +{ + +const static int kReserveClientSize = 100; +const static int kReserveReqSize = 10; + +/** + * AppList Constructor. + * + * Reserve the container size to avoid re-allocating memory. + * + * @note Size should be changed according to the system. + * If the number of applications becomes over the size, re-allocating memory will happen. + */ +AppList::AppList() + : req_list(), + app2client(), + current_req(1) +{ + this->app2client.reserve(kReserveClientSize); + this->req_list.reserve(kReserveReqSize); +} + +AppList::~AppList() {} + +// =================== Client Date container API =================== + +/** + * Add Client to the list + * + * Add Client to the list. + * The Client means application which has role, layer, surface + * This function should be called once for the app. + * Caller should take care not to be called more than once. + * + * @param string[in] Application id. This will be the key to withdraw the information. + * @param unsigned[in] Layer ID in which the application is + * @param unsigned[in] surface ID which the application has + * @param string[in] Role which means what behavior the application will do. + * @return None + * @attention This function should be called once for the app + * Caller should take care not to be called more than once. + */ +void AppList::addClient(const string &appid, unsigned layer, unsigned surface, const string &role) +{ + std::lock_guard lock(this->mtx); + shared_ptr client = std::make_shared(appid, layer, surface, role); + this->app2client[appid] = client; + this->clientDump(); +} + +void AppList::addClient(const string &appid, unsigned layer, const string &role) +{ + std::lock_guard lock(this->mtx); + shared_ptr client = std::make_shared(appid, layer, role); + this->app2client[appid] = client; + this->clientDump(); +} + +/** + * Remove WMClient from the list + * + * @param string[in] Application id. This will be the key to withdraw the information. + */ +void AppList::removeClient(const string &appid) +{ + std::lock_guard lock(this->mtx); + this->app2client.erase(appid); + HMI_INFO("Remove client %s", appid.c_str()); +} + +/** + * Check this class stores the appid. + * + * @param string[in] Application id. This will be the key to withdraw the information. + * @return true if the class has the requested key(appid) + */ +bool AppList::contains(const string &appid) const +{ + auto result = this->app2client.find(appid); + return (this->app2client.end() != result) ? true : false; +} + +/** + * Remove surface from client + * + * @param unsigned[in] surface id. + * @return None + */ +void AppList::removeSurface(unsigned surface){ + // This function may be very slow + std::lock_guard lock(this->mtx); + bool ret = false; + for (auto &x : this->app2client) + { + ret = x.second->removeSurfaceIfExist(surface); + if(ret){ + HMI_DEBUG("remove surface %d from Client %s finish", + surface, x.second->appID().c_str()); + break; + } + } + +} + +/** + * Get WMClient object. + * + * After get the WMClient object, caller can call the client method. + * Before call this function, caller must call "contains" + * to check the key is contained, otherwise, you have to take care of std::out_of_range. + * + * @param string[in] application id(key) + * @return WMClient object + * @attention Must call cantains to check appid is stored before this function. + */ +shared_ptr AppList::lookUpClient(const string &appid) +{ + if(this->app2client.count(appid) != 0) + { + return this->app2client.at(appid); + } + else + { + return nullptr; + } +} + +/** + * Count Client. + * + * Returns the number of client stored in the list. + * + * @param None + * @return The number of client + */ +int AppList::countClient() const +{ + return this->app2client.size(); +} + +/** + * Get AppID with surface and role. + * + * Returns AppID if found. + * + * @param unsigned[in] surfaceID + * @param string[in] role + * @param bool[in,out] AppID is found or not + * @return AppID + * @attention If AppID is not found, param found will be false. + */ +/* string AppList::getAppID(unsigned surface, const string& role, bool* found) const +{ + *found = false; + for (const auto &x : this->app2client) + { + if(x.second->surfaceID(role) == surface){ + *found = true; + return x.second->appID(); + } + } + return string(""); +} */ + +string AppList::getAppID(unsigned surface, bool* found) const +{ + *found = false; + for (const auto &x : this->app2client) + { + if(x.second->surfaceID() == surface){ + *found = true; + return x.second->appID(); + } + } + return string(""); +} + +WMError AppList::popFloatingSurface(unsigned pid, unsigned *surface) +{ + WMError ret = WMError::NO_ENTRY; + + auto fwd_itr = std::remove_if(this->floating_surfaces.begin(), this->floating_surfaces.end(), + [pid, surface, &ret](FloatingSurface x) { + if(pid == x.pid){ + *surface = x.surface_id; + ret = WMError::SUCCESS; + return true; + } + else{ + return false; + } + }); + if (fwd_itr != this->floating_surfaces.cend()) + { + HMI_INFO("pop floating surface: %d", *surface); + } + this->floating_surfaces.erase(fwd_itr, this->floating_surfaces.end()); + return ret; +} + +// =================== Floating(Temporary) surface/client API =================== + +// TODO: After testing setRole, remove these API + +WMError AppList::popFloatingSurface(const string &appid, unsigned *surface) +{ + HMI_ERROR("This function is not implemented"); + return WMError::SUCCESS; +} + +void AppList::addFloatingClient(const string &appid, unsigned layer, const string &role) +{ +} + +void AppList::addFloatingSurface(const string &appid, unsigned surface, unsigned pid) +{ + struct FloatingSurface fsurface{appid, surface, pid}; + this->floating_surfaces.push_back(fsurface); + this->dumpFloatingSurfaces(); +} + +void AppList::removeFloatingSurface(unsigned surface) +{ + this->dumpFloatingSurfaces(); + auto fwd_itr = std::remove_if( + this->floating_surfaces.begin(), this->floating_surfaces.end(), + [surface](FloatingSurface x) { + return x.surface_id == surface; + }); + if(fwd_itr != this->floating_surfaces.cend()){ + HMI_INFO("remove floating surface: %d", surface); + } + this->floating_surfaces.erase(fwd_itr, this->floating_surfaces.end()); +} + +// =================== Request Date container API =================== + +/** + * Get current request number + * + * Request number is the numeric ID to designate the request. + * But returned request number from this function doesn't mean the request exists. + * This number is used as key to withdraw the WMRequest object. + * + * @param None + * @return current request number. + * @note request number is more than 0. + */ +unsigned AppList::currentRequestNumber() const +{ + return this->current_req; +} + +/** + * Get request number + * + * Request number is the numeric ID to designate the request. + * But returned request number from this function doesn't mean the request exists. + * This number is used as key to withdraw the WMRequest object. + * + * @param None + * @return request number. + * @attention If returned value is 0, no request exists. + */ +unsigned AppList::getRequestNumber(const string &appid) const +{ + for (const auto &x : this->req_list) + { + // Since app will not request twice and more, comparing appid is enough? + if ((x.trigger.appid == appid)) + { + return x.req_num; + } + } + return 0; +} + +/** + * Add Request + * + * Request number is the numeric ID to designate the request. + * But returned request number from this function doesn't mean the request exists. + * This number is used as key to withdraw the WMRequest object. + * + * @param WMRequest[in] WMRequest object caller creates + * @return Request number + * @attention If the request number is different with curent request number, + * it means the previous request is not finished. + */ +unsigned AppList::addRequest(WMRequest req) +{ + std::lock_guard lock(this->mtx); + if (this->req_list.size() == 0) + { + req.req_num = current_req; + } + else + { + HMI_SEQ_INFO(this->current_req, "add: %d", this->req_list.back().req_num + 1); + req.req_num = this->req_list.back().req_num + 1; + } + this->req_list.push_back(req); + return req.req_num; +} + +/** + * Get trigger which the application requests + * + * WMTrigger contains which application requests what role and where to put(area) and task. + * This is used for input event to Window Policy Manager(state machine). + * + * @param unsigned[in] request number + * @param bool[in,out] Check request number of the parameter is valid. + * @return WMTrigger which associates with the request number + * @attention If the request number is not valid, parameter "found" is false + * and return value will be meaningless value. + * Caller can check the request parameter is valid. + */ +struct WMTrigger AppList::getRequest(unsigned req_num, bool *found) +{ + *found = false; + for (const auto &x : this->req_list) + { + if (req_num == x.req_num) + { + *found = true; + return x.trigger; + } + } + HMI_SEQ_ERROR(req_num, "Couldn't get request : %d", req_num); + return WMTrigger{"", "", "", Task::TASK_INVALID}; +} + +/** + * Get actions which the application requests + * + * WMAciton contains the information of state transition. + * In other words, it contains actions of Window Manager, + * which role should be put to the area. + * + * @param unsigned[in] request number + * @param bool[in,out] Check request number of the parameter is valid. + * @return WMTrigger which associates with the request number + * @attention If the request number is not valid, parameter "found" is false + * and return value will be no reference pointer. + * Caller must check the request parameter is valid. + */ +const vector &AppList::getActions(unsigned req_num, bool* found) +{ + *found = false; + for (auto &x : this->req_list) + { + if (req_num == x.req_num) + { + *found = true; + return x.sync_draw_req; + } + } + HMI_SEQ_ERROR(req_num, "Couldn't get action with the request : %d", req_num); +} + +/** + * Set actions to the request. + * + * Add actions to the request. + * This function can be called many times, and actions increase. + * This function is used for decision of actions of Window Manager + * according to the result of Window Policy Manager. + * + * @param unsigned[in] request number + * @param WMAction[in] Action of Window Manager. + * @return WMError If request number is not valid, FAIL will be returned. + */ +WMError AppList::setAction(unsigned req_num, const struct WMAction &action) +{ + std::lock_guard lock(this->mtx); + WMError result = WMError::FAIL; + for (auto &x : this->req_list) + { + if (req_num != x.req_num) + { + continue; + } + x.sync_draw_req.push_back(action); + result = WMError::SUCCESS; + break; + } + return result; +} + +/** + * Note: + * @note This function set action with parameters. + * If visible is true, it means app should be visible, so enddraw_finished parameter should be false. + * otherwise (visible is false) app should be invisible. Then enddraw_finished param is set to true. + * This function doesn't support actions for focus yet. + */ +/** + * Set actions to the request. + * + * This function is overload function. + * The feature is same as other one. + * + * @param unsigned[in] request number + * @param string[in] application id + * @param string[in] role + * @param string[in] area + * @param Task[in] the role should be visible or not. + * @return WMError If request number is not valid, FAIL will be returned. + * @attention This function set action with parameters, then caller doesn't need to create WMAction object. + * If visible is true, it means app should be visible, so enddraw_finished parameter will be false. + * otherwise (visible is false) app should be invisible. Then enddraw_finished param is set to true. + * This function doesn't support actions for focus yet. + */ +WMError AppList::setAction(unsigned req_num, shared_ptr client, const string &role, const string &area, TaskVisible visible) +{ + std::lock_guard lock(this->mtx); + WMError result = WMError::FAIL; + for (auto &x : req_list) + { + if (req_num != x.req_num) + { + continue; + } + // If visible task is not invisible, redraw is required -> true + bool edraw_f = (visible != TaskVisible::INVISIBLE) ? false : true; + WMAction action{req_num, client, role, area, visible, edraw_f, TaskCarState::NO_TASK}; + + x.sync_draw_req.push_back(action); + result = WMError::SUCCESS; + break; + } + return result; +} + +/** + * Set end_draw_finished param is true + * + * This function checks + * - req_num is equal to current request number + * - appid and role are equeal to the appid and role stored in action list + * If it is valid, set the action is finished. + * + * @param unsigned[in] request number + * @param string[in] application id + * @param string[in] role + * @return If the parameters are not valid in action list, returns false + */ +bool AppList::setEndDrawFinished(unsigned req_num, const string &appid, const string &role) +{ + std::lock_guard lock(this->mtx); + bool result = false; + for (auto &x : req_list) + { + if (req_num < x.req_num) + { + break; + } + if (req_num == x.req_num) + { + for (auto &y : x.sync_draw_req) + { + if (nullptr != y.client) + { + if (y.client->appID() == appid && y.role == role) + { + HMI_SEQ_INFO(req_num, "Role %s finish redraw", y.role.c_str()); + y.end_draw_finished = true; + result = true; + } + } + else + { + if (y.role == role) + { + HMI_SEQ_INFO(req_num, "Role %s finish redraw", y.role.c_str()); + y.end_draw_finished = true; + result = true; + } + } + } + } + } + this->reqDump(); + return result; +} + +/** + * Check all actions of the requested sequence is finished + * + * @param unsigned[in] request_number + * @return true if all action is set. + */ +bool AppList::endDrawFullfilled(unsigned req_num) +{ + bool result = false; + for (const auto &x : req_list) + { + if (req_num < x.req_num) + { + break; + } + if (req_num == x.req_num) + { + result = true; + for (const auto &y : x.sync_draw_req) + { + result &= y.end_draw_finished; + if (!result) + { + break; + } + } + } + } + return result; +} + +/** + * Finish the request, then remove it. + * + * @param unsigned[in] request_number + * @return None + * @note Please call next after this function to receive or process next request. + */ +void AppList::removeRequest(unsigned req_num) +{ + std::lock_guard lock(this->mtx); + this->req_list.erase(remove_if(this->req_list.begin(), this->req_list.end(), + [req_num](WMRequest x) { + return x.req_num == req_num; + })); +} + +/** + * Move the current request to next + * + * @param None + * @return None + */ +void AppList::next() +{ + std::lock_guard lock(this->mtx); + ++this->current_req; + if (0 == this->current_req) + { + this->current_req = 1; + } +} + +/** + * Check the request exists is in request list + * + * @param None + * @return true if WMRequest exists in the request list + */ +bool AppList::haveRequest() const +{ + return !this->req_list.empty(); +} + +void AppList::clientDump() +{ + DUMP("======= client dump ====="); + for (const auto &x : this->app2client) + { + const auto &y = x.second; + y->dumpInfo(); + } + DUMP("======= client dump end====="); +} + +void AppList::reqDump() +{ + DUMP("======= req dump ====="); + DUMP("current request : %d", current_req); + for (const auto &x : req_list) + { + DUMP("requested : %d", x.req_num); + DUMP("Trigger : (APPID :%s, ROLE :%s, AREA :%s, TASK: %d)", + x.trigger.appid.c_str(), + x.trigger.role.c_str(), + x.trigger.area.c_str(), + x.trigger.task); + + for (const auto &y : x.sync_draw_req) + { + DUMP( + "Action : (APPID :%s, ROLE :%s, AREA :%s, VISIBLE : %s, END_DRAW_FINISHED: %d)", + (y.client) ? y.client->appID().c_str() : "-", + y.role.c_str(), + y.area.c_str(), + (y.visible == TaskVisible::INVISIBLE) ? "invisible" : "visible", + y.end_draw_finished); + } + } + DUMP("======= req dump end ====="); +} + +void AppList::dumpFloatingSurfaces() +{ + DUMP("======= floating surface dump ====="); + for (const auto &x : this->floating_surfaces) + { + DUMP("surface : %d, pid : %d", x.surface_id, x.pid); + } + DUMP("======= floating surface dump end =====\n"); +} + +} // namespace wm diff --git a/demo3/common/agl-service-windowmanager/src/applist.hpp b/demo3/common/agl-service-windowmanager/src/applist.hpp new file mode 100644 index 0000000..36e0524 --- /dev/null +++ b/demo3/common/agl-service-windowmanager/src/applist.hpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2017 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ALLOCATE_LIST_HPP +#define ALLOCATE_LIST_HPP +#include +#include +#include +#include +#include +#include "wm_client.hpp" +#include "request.hpp" +#include "wm_error.hpp" + +namespace wm +{ + +/* using std::experimental::nullopt; +using std::experimental::optional; */ + +struct FloatingSurface +{ + std::string appid; + unsigned surface_id; + unsigned pid; +}; + +class AppList +{ + public: + AppList(); + virtual ~AppList(); + AppList(const AppList &obj) = delete; + + // Client Database Interface + /* TODO: consider, which is better WMClient as parameter or not + If the WMClient should be more flexible, I think this param should be WMClient class + */ + void addClient(const std::string &appid, unsigned layer, + unsigned surface, const std::string &role); + void addClient(const std::string &appid, unsigned layer, const std::string &role); + void removeClient(const std::string &appid); + bool contains(const std::string &appid) const; + int countClient() const; + std::shared_ptr lookUpClient(const std::string &appid); + void removeSurface(unsigned surface); + std::string getAppID(unsigned surface, bool* found) const; // TODO: remove + + + // Floating surface + void addFloatingClient(const std::string &appid, unsigned layer, const std::string &role); + void addFloatingSurface(const std::string &appid, unsigned surface, unsigned pid); + WMError popFloatingSurface(unsigned pid, unsigned *surface); + WMError popFloatingSurface(const std::string &appid, unsigned *surface); + void removeFloatingSurface(unsigned surface); + + // Request Interface + unsigned currentRequestNumber() const; + unsigned getRequestNumber(const std::string &appid) const; + unsigned addRequest(WMRequest req); + WMError setAction(unsigned req_num, const struct WMAction &action); + WMError setAction(unsigned req_num, std::shared_ptr client, + const std::string &role, const std::string &area, TaskVisible visible); + bool setEndDrawFinished(unsigned req_num, const std::string &appid, const std::string &role); + bool endDrawFullfilled(unsigned req_num); + void removeRequest(unsigned req_num); + void next(); + bool haveRequest() const; + + struct WMTrigger getRequest(unsigned req_num, bool* found); + const std::vector &getActions(unsigned req_num, bool* found); + + void clientDump(); + void reqDump(); + void dumpFloatingSurfaces(); + + private: + std::vector req_list; + std::unordered_map> app2client; + unsigned current_req; + std::mutex mtx; + std::vector floating_surfaces; +}; + +} // namespace wm +#endif // ALLOCATE_LIST_HPP \ No newline at end of file diff --git a/demo3/common/agl-service-windowmanager/src/config/areas.json b/demo3/common/agl-service-windowmanager/src/config/areas.json new file mode 100644 index 0000000..d4f3531 --- /dev/null +++ b/demo3/common/agl-service-windowmanager/src/config/areas.json @@ -0,0 +1,204 @@ +{ + "ecus": [ + { + "name": "master", + "screens": [ + { + "id": 0, + "areas": [ + { + "name": "fullscreen", + "rect": { + "x": 0, + "y": 0, + "w": 1920, + "h": 720 + } + }, + { + "name": "normal.full", + "rect": { + "x": 0, + "y": 0, + "w": 1920, + "h": 720 + } + }, + { + "name": "split.main", + "rect": { + "x": 0, + "y": 0, + "w": 1280, + "h": 720 + } + }, + { + "name": "split.sub", + "rect": { + "x": 1280, + "y": 0, + "w": 640, + "h": 720 + } + }, + { + "name": "software_keyboard", + "rect": { + "x": 0, + "y": 962, + "w": 1080, + "h": 744 + } + }, + { + "name": "restriction.normal", + "rect": { + "x": 0, + "y": 0, + "w": 1920, + "h": 1080 + } + }, + { + "name": "restriction.split.main", + "rect": { + "x": 0, + "y": 0, + "w": 1920, + "h": 540 + } + }, + { + "name": "restriction.split.sub", + "rect": { + "x": 0, + "y": 540, + "w": 1920, + "h": 540 + } + }, + { + "name": "on_screen", + "rect": { + "x": 1280, + "y": 0, + "w": 640, + "h": 720 + } + }, + { + "name": "master.split.sub", + "rect": { + "x": 1280, + "y": 0, + "w": 640, + "h": 720 + } + } + ] + } + ] + }, + { + "name": "slave", + "screens": [ + { + "id": 0, + "areas": [ + { + "name": "fullscreen", + "rect": { + "x": 0, + "y": 0, + "w": 1080, + "h": 1920 + } + }, + { + "name": "normal.full", + "rect": { + "x": 0, + "y": 218, + "w": 1080, + "h": 1488 + } + }, + { + "name": "split.main", + "rect": { + "x": 0, + "y": 218, + "w": 1080, + "h": 744 + } + }, + { + "name": "split.sub", + "rect": { + "x": 0, + "y": 962, + "w": 1080, + "h": 744 + } + }, + { + "name": "software_keyboard", + "rect": { + "x": 0, + "y": 962, + "w": 1080, + "h": 744 + } + }, + { + "name": "restriction.normal", + "rect": { + "x": 0, + "y": 218, + "w": 1080, + "h": 1488 + } + }, + { + "name": "restriction.split.main", + "rect": { + "x": 0, + "y": 218, + "w": 1080, + "h": 744 + } + }, + { + "name": "restriction.split.sub", + "rect": { + "x": 0, + "y": 962, + "w": 1080, + "h": 744 + } + }, + { + "name": "on_screen", + "rect": { + "x": 0, + "y": 218, + "w": 1080, + "h": 1488 + } + }, + { + "name": "master.split.sub", + "rect": { + "x": 0, + "y": 180, + "w": 640, + "h": 720 + } + } + ] + } + ] + } + ] +} diff --git a/demo3/common/agl-service-windowmanager/src/config/connection.json b/demo3/common/agl-service-windowmanager/src/config/connection.json new file mode 100644 index 0000000..3ee06c3 --- /dev/null +++ b/demo3/common/agl-service-windowmanager/src/config/connection.json @@ -0,0 +1,5 @@ +{ + "mode": "master", + "master_ip": "10.4.1.78", + "master_port": 54400 +} diff --git a/demo3/common/agl-service-windowmanager/src/config/old_roles.json b/demo3/common/agl-service-windowmanager/src/config/old_roles.json new file mode 100644 index 0000000..02a4c2d --- /dev/null +++ b/demo3/common/agl-service-windowmanager/src/config/old_roles.json @@ -0,0 +1,68 @@ +{ + "old_roles": [ + { + "name": "HomeScreen", + "new": "homescreen" + }, + { + "name": "Music", + "new": "music" + }, + { + "name": "MediaPlayer", + "new": "music" + }, + { + "name": "Video", + "new": "video" + }, + { + "name": "VideoPlayer", + "new": "video" + }, + { + "name": "WebBrowser", + "new": "browser" + }, + { + "name": "Radio", + "new": "radio" + }, + { + "name": "Phone", + "new": "phone" + }, + { + "name": "Navigation", + "new": "map" + }, + { + "name": "HVAC", + "new": "hvac" + }, + { + "name": "Settings", + "new": "settings" + }, + { + "name": "Dashboard", + "new": "dashboard" + }, + { + "name": "POI", + "new": "poi" + }, + { + "name": "Mixer", + "new": "mixer" + }, + { + "name": "Restriction", + "new": "restriction" + }, + { + "name": "^OnScreen.*", + "new": "on_screen" + } + ] +} diff --git a/demo3/common/agl-service-windowmanager/src/controller_hooks.hpp b/demo3/common/agl-service-windowmanager/src/controller_hooks.hpp new file mode 100644 index 0000000..ae88187 --- /dev/null +++ b/demo3/common/agl-service-windowmanager/src/controller_hooks.hpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2017 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TMCAGLWM_CONTROLLER_HOOKS_HPP +#define TMCAGLWM_CONTROLLER_HOOKS_HPP + +#include + +#include + +namespace wm +{ + +class WindowManager; + +struct controller_hooks +{ + WindowManager *wmgr; + + void surface_created(uint32_t surface_id); + void surface_removed(uint32_t surface_id); + void surface_visibility(uint32_t surface_id, uint32_t v); + void surface_destination_rectangle(uint32_t surface_id, uint32_t x, uint32_t y, uint32_t w, uint32_t h); + void surface_properties(uint32_t surface_id, uint32_t pid); +}; + +} // namespace wm + +#endif // TMCAGLWM_CONTROLLER_HOOKS_HPP diff --git a/demo3/common/agl-service-windowmanager/src/json_helper.cpp b/demo3/common/agl-service-windowmanager/src/json_helper.cpp new file mode 100644 index 0000000..d9cf5eb --- /dev/null +++ b/demo3/common/agl-service-windowmanager/src/json_helper.cpp @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2017 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "json_helper.hpp" +#include "util.hpp" + +template +json_object *to_json_(T const &s) +{ + auto a = json_object_new_array(); + + if (!s.empty()) + { + for (auto const &i : s) + { + json_object_array_add(a, to_json(i.second)); + } + } + + return a; +} + +json_object *to_json(std::vector const &v) +{ + auto a = json_object_new_array(); + for (const auto i : v) + { + json_object_array_add(a, json_object_new_int(i)); + } + return a; +} + +namespace jh { + +const char* getStringFromJson(json_object* obj, const char* key) +{ + json_object* tmp; + if (!json_object_object_get_ex(obj, key, &tmp)) + { + HMI_DEBUG("Not found key \"%s\"", key); + return nullptr; + } + + return json_object_get_string(tmp); +} + +int getIntFromJson(json_object *obj, const char *key) +{ + json_object *tmp; + if (!json_object_object_get_ex(obj, key, &tmp)) + { + HMI_DEBUG("Not found key \"%s\"", key); + return 0; + } + + return json_object_get_int(tmp); +} + +double getDoubleFromJson(json_object *obj, const char *key) +{ + json_object *tmp; + if (!json_object_object_get_ex(obj, key, &tmp)) + { + HMI_DEBUG("Not found key \"%s\"", key); + return 0; + } + + return json_object_get_double(tmp); +} + +json_bool getBoolFromJson(json_object *obj, const char *key) +{ + json_object *tmp; + if (!json_object_object_get_ex(obj, key, &tmp)) + { + HMI_DEBUG("Not found key \"%s\"", key); + return FALSE; + } + + return json_object_get_boolean(tmp); +} + +int inputJsonFilie(const char* file, json_object** obj) +{ + const int input_size = 128; + int ret = -1; + + HMI_DEBUG("Input file: %s", file); + + // Open json file + FILE *fp = fopen(file, "rb"); + if (nullptr == fp) + { + HMI_ERROR("Could not open file"); + return ret; + } + + // Parse file data + struct json_tokener *tokener = json_tokener_new(); + enum json_tokener_error json_error; + char buffer[input_size]; + int block_cnt = 1; + while (1) + { + size_t len = fread(buffer, sizeof(char), input_size, fp); + *obj = json_tokener_parse_ex(tokener, buffer, len); + if (nullptr != *obj) + { + HMI_DEBUG("File input is success"); + ret = 0; + break; + } + + json_error = json_tokener_get_error(tokener); + if ((json_tokener_continue != json_error) + || (input_size > len)) + { + HMI_ERROR("Failed to parse file (byte:%d err:%s)", + (input_size * block_cnt), json_tokener_error_desc(json_error)); + HMI_ERROR("\n%s", buffer); + *obj = nullptr; + break; + } + block_cnt++; + } + + // Close json file + fclose(fp); + + // Free json_tokener + json_tokener_free(tokener); + + return ret; +} + +} // namespace jh diff --git a/demo3/common/agl-service-windowmanager/src/json_helper.hpp b/demo3/common/agl-service-windowmanager/src/json_helper.hpp new file mode 100644 index 0000000..d4ae85a --- /dev/null +++ b/demo3/common/agl-service-windowmanager/src/json_helper.hpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2017 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef JSON_HELPER_HPP +#define JSON_HELPER_HPP + +#include +#include + +struct json_object; +json_object *to_json(std::vector const &v); + +namespace jh { +const char* getStringFromJson(json_object* obj, const char* key); +int getIntFromJson(json_object *obj, const char *key); +double getDoubleFromJson(json_object *obj, const char *key); +json_bool getBoolFromJson(json_object *obj, const char *key); +int inputJsonFilie(const char* file, json_object** obj); +} // namespace jh + +#endif // JSON_HELPER_HPP diff --git a/demo3/common/agl-service-windowmanager/src/layers.cpp b/demo3/common/agl-service-windowmanager/src/layers.cpp new file mode 100644 index 0000000..05d404d --- /dev/null +++ b/demo3/common/agl-service-windowmanager/src/layers.cpp @@ -0,0 +1,382 @@ +/* + * Copyright (c) 2017 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "layers.hpp" +#include "json_helper.hpp" +#include "util.hpp" + +namespace wm +{ + +using json = nlohmann::json; + +layer::layer(nlohmann::json const &j) +{ + this->role = j["role"]; + this->name = j["name"]; + this->layer_id = j["layer_id"]; + + // Init flag of normal layout only + this->is_normal_layout_only = true; + + auto split_layouts = j.find("split_layouts"); + if (split_layouts != j.end()) + { + + // Clear flag of normal layout only + this->is_normal_layout_only = false; + + auto &sls = j["split_layouts"]; + // this->layouts.reserve(sls.size()); + std::transform(std::cbegin(sls), std::cend(sls), + std::back_inserter(this->layouts), [this](json const &sl) { + struct split_layout l + { + sl["name"], sl["main_match"], sl["sub_match"] + }; + HMI_DEBUG("wm", + "layer %d add split_layout \"%s\" (main: \"%s\") (sub: " + "\"%s\")", + this->layer_id, + l.name.c_str(), l.main_match.c_str(), + l.sub_match.c_str()); + return l; + }); + } + HMI_DEBUG("layer_id:%d is_normal_layout_only:%d\n", + this->layer_id, this->is_normal_layout_only); +} + +struct result to_layer_map(nlohmann::json const &j) +{ + try + { + layer_map stl{}; + auto m = j["mappings"]; + + std::transform(std::cbegin(m), std::cend(m), + std::inserter(stl.mapping, stl.mapping.end()), + [](nlohmann::json const &j) { + return std::pair( + j.value("layer_id", -1), layer(j)); + }); + + // TODO: add sanity checks here? + // * check for double IDs + // * check for double names/roles + + stl.layers.reserve(m.size()); + std::transform(std::cbegin(stl.mapping), std::cend(stl.mapping), + std::back_inserter(stl.layers), + [&stl](std::pair const &k) { + stl.roles.emplace_back( + std::make_pair(k.second.role, k.second.layer_id)); + return unsigned(k.second.layer_id); + }); + + std::sort(stl.layers.begin(), stl.layers.end()); + + for (auto i : stl.mapping) + { + if (i.second.name.empty()) + { + return Err("Found mapping w/o name"); + } + if (i.second.layer_id == -1) + { + return Err("Found invalid/unset IDs in mapping"); + } + } + + auto msi = j.find("main_surface"); + if (msi != j.end()) + { + stl.main_surface_name = msi->value("surface_role", ""); + stl.main_surface = -1; + } + + return Ok(stl); + } + catch (std::exception &e) + { + return Err(e.what()); + } +} + +optional +layer_map::get_layer_id(int surface_id) +{ + auto i = this->surfaces.find(surface_id); + if (i != this->surfaces.end()) + { + return optional(i->second); + } + return nullopt; +} + +optional layer_map::get_layer_id(std::string const &role) +{ + for (auto const &r : this->roles) + { + auto re = std::regex(r.first); + if (std::regex_match(role, re)) + { + HMI_DEBUG("role %s matches layer %d", role.c_str(), r.second); + return optional(r.second); + } + } + HMI_DEBUG("role %s does NOT match any layer", role.c_str()); + return nullopt; +} + +json layer::to_json() const +{ + auto is_full = this->rect == compositor::full_rect; + + json r{}; + if (is_full) + { + r = {{"type", "full"}}; + } + else + { + r = {{"type", "rect"}, + {"rect", + {{"x", this->rect.x}, + {"y", this->rect.y}, + {"width", this->rect.w}, + {"height", this->rect.h}}}}; + } + + return { + {"name", this->name}, + {"role", this->role}, + {"layer_id", this->layer_id}, + {"area", r}, + }; +} + +json layer_map::to_json() const +{ + json j{}; + for (auto const &i : this->mapping) + { + j.push_back(i.second.to_json()); + } + return j; +} + +void layer_map::setupArea(double scaling) +{ + compositor::rect rct; + + rct = this->area2size["normal.full"]; + this->area2size["normalfull"] = rct; + this->area2size["normal"] = rct; + + for (auto &i : this->area2size) + { + i.second.x = static_cast(scaling * i.second.x + 0.5); + i.second.y = static_cast(scaling * i.second.y + 0.5); + i.second.w = static_cast(scaling * i.second.w + 0.5); + i.second.h = static_cast(scaling * i.second.h + 0.5); + + HMI_DEBUG("wm:lm", "area:%s size(after) : x:%d y:%d w:%d h:%d", + i.first.c_str(), i.second.x, i.second.y, i.second.w, i.second.h); + } +} + +compositor::rect layer_map::getAreaSize(const std::string &area) +{ + return area2size[area]; +} + +int layer_map::loadAreaDb() +{ + HMI_DEBUG("wm:lm", "Call"); + + // Get afm application installed dir + char const *afm_app_install_dir = getenv("AFM_APP_INSTALL_DIR"); + HMI_DEBUG("wm:lm", "afm_app_install_dir:%s", afm_app_install_dir); + + std::string file_name; + if (!afm_app_install_dir) + { + HMI_ERROR("wm:lm", "AFM_APP_INSTALL_DIR is not defined"); + } + else + { + file_name = std::string(afm_app_install_dir) + std::string("/etc/areas.db"); + } + + // Load area.db + json_object *json_obj; + int ret = jh::inputJsonFilie(file_name.c_str(), &json_obj); + if (0 > ret) + { + HMI_DEBUG("wm:lm", "Could not open area.db, so use default area information"); + json_obj = json_tokener_parse(kDefaultAreaDb); + } + HMI_DEBUG("wm:lm", "json_obj dump:%s", json_object_get_string(json_obj)); + + // Perse areas + HMI_DEBUG("wm:lm", "Perse areas"); + json_object *json_cfg; + if (!json_object_object_get_ex(json_obj, "areas", &json_cfg)) + { + HMI_ERROR("wm:lm", "Parse Error!!"); + return -1; + } + + int len = json_object_array_length(json_cfg); + HMI_DEBUG("wm:lm", "json_cfg len:%d", len); + HMI_DEBUG("wm:lm", "json_cfg dump:%s", json_object_get_string(json_cfg)); + + const char *area; + for (int i = 0; i < len; i++) + { + json_object *json_tmp = json_object_array_get_idx(json_cfg, i); + HMI_DEBUG("wm:lm", "> json_tmp dump:%s", json_object_get_string(json_tmp)); + + area = jh::getStringFromJson(json_tmp, "name"); + if (nullptr == area) + { + HMI_ERROR("wm:lm", "Parse Error!!"); + return -1; + } + HMI_DEBUG("wm:lm", "> area:%s", area); + + json_object *json_rect; + if (!json_object_object_get_ex(json_tmp, "rect", &json_rect)) + { + HMI_ERROR("wm:lm", "Parse Error!!"); + return -1; + } + HMI_DEBUG("wm:lm", "> json_rect dump:%s", json_object_get_string(json_rect)); + + compositor::rect area_size; + area_size.x = jh::getIntFromJson(json_rect, "x"); + area_size.y = jh::getIntFromJson(json_rect, "y"); + area_size.w = jh::getIntFromJson(json_rect, "w"); + area_size.h = jh::getIntFromJson(json_rect, "h"); + + this->area2size[area] = area_size; + } + + // Check + for (auto itr = this->area2size.begin(); + itr != this->area2size.end(); ++itr) + { + HMI_DEBUG("wm:lm", "area:%s x:%d y:%d w:%d h:%d", + itr->first.c_str(), itr->second.x, itr->second.y, + itr->second.w, itr->second.h); + } + + // Release json_object + json_object_put(json_obj); + + return 0; +} + +const char* layer_map::kDefaultAreaDb = "{ \ + \"areas\": [ \ + { \ + \"name\": \"fullscreen\", \ + \"rect\": { \ + \"x\": 0, \ + \"y\": 0, \ + \"w\": 1080, \ + \"h\": 1920 \ + } \ + }, \ + { \ + \"name\": \"normal.full\", \ + \"rect\": { \ + \"x\": 0, \ + \"y\": 218, \ + \"w\": 1080, \ + \"h\": 1488 \ + } \ + }, \ + { \ + \"name\": \"split.main\", \ + \"rect\": { \ + \"x\": 0, \ + \"y\": 218, \ + \"w\": 1080, \ + \"h\": 744 \ + } \ + }, \ + { \ + \"name\": \"split.sub\", \ + \"rect\": { \ + \"x\": 0, \ + \"y\": 962, \ + \"w\": 1080, \ + \"h\": 744 \ + } \ + }, \ + { \ + \"name\": \"software_keyboard\", \ + \"rect\": { \ + \"x\": 0, \ + \"y\": 962, \ + \"w\": 1080, \ + \"h\": 744 \ + } \ + }, \ + { \ + \"name\": \"restriction.normal\", \ + \"rect\": { \ + \"x\": 0, \ + \"y\": 218, \ + \"w\": 1080, \ + \"h\": 1488 \ + } \ + }, \ + { \ + \"name\": \"restriction.split.main\", \ + \"rect\": { \ + \"x\": 0, \ + \"y\": 218, \ + \"w\": 1080, \ + \"h\": 744 \ + } \ + }, \ + { \ + \"name\": \"restriction.split.sub\", \ + \"rect\": { \ + \"x\": 0, \ + \"y\": 962, \ + \"w\": 1080, \ + \"h\": 744 \ + } \ + }, \ + { \ + \"name\": \"on_screen\", \ + \"rect\": { \ + \"x\": 0, \ + \"y\": 218, \ + \"w\": 1080, \ + \"h\": 1488 \ + } \ + } \ + ] \ +}"; + +} // namespace wm diff --git a/demo3/common/agl-service-windowmanager/src/layers.hpp b/demo3/common/agl-service-windowmanager/src/layers.hpp new file mode 100644 index 0000000..f52886e --- /dev/null +++ b/demo3/common/agl-service-windowmanager/src/layers.hpp @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2017 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TMCAGLWM_LAYERS_H +#define TMCAGLWM_LAYERS_H + +#include + +#include "../include/json.hpp" +#include "layout.hpp" +#include "result.hpp" +#include "wayland_ivi_wm.hpp" + +namespace wm +{ + +struct split_layout +{ + std::string name; + std::string main_match; + std::string sub_match; +}; + +struct layer +{ + using json = nlohmann::json; + + // A more or less descriptive name? + std::string name = ""; + // The actual layer ID + int layer_id = -1; + // The rectangular region surfaces are allowed to draw on + // this layer, note however, width and hieght of the rect + // can be negative, in which case they specify that + // the actual value is computed using MAX + 1 - w + // That is; allow us to specify dimensions dependent on + // e.g. screen dimension, w/o knowing the actual screen size. + compositor::rect rect; + // Specify a role prefix for surfaces that should be + // put on this layer. + std::string role; + // TODO: perhaps a zorder is needed here? + std::vector layouts; + + mutable struct LayoutState state; + + // Flag of normal layout only + bool is_normal_layout_only; + + explicit layer(nlohmann::json const &j); + + json to_json() const; +}; + +struct layer_map +{ + using json = nlohmann::json; + + using storage_type = std::map; + using layers_type = std::vector; + using role_to_layer_map = std::vector>; + using addsurf_layer_map = std::map; + + storage_type mapping; // map surface_id to layer + layers_type layers; // the actual layer IDs we have + int main_surface; + std::string main_surface_name; + role_to_layer_map roles; + addsurf_layer_map surfaces; // additional surfaces on layers + + optional get_layer_id(int surface_id); + optional get_layer_id(std::string const &role); + optional get_layout_state(int surface_id) + { + int layer_id = *this->get_layer_id(surface_id); + auto i = this->mapping.find(layer_id); + return i == this->mapping.end() + ? nullopt + : optional(&i->second.state); + } + optional get_layer(int layer_id) + { + auto i = this->mapping.find(layer_id); + return i == this->mapping.end() ? nullopt + : optional(i->second); + } + + layers_type::size_type get_layers_count() const + { + return this->layers.size(); + } + + void add_surface(int surface_id, int layer_id) + { + this->surfaces[surface_id] = layer_id; + } + + void remove_surface(int surface_id) + { + this->surfaces.erase(surface_id); + } + + json to_json() const; + void setupArea(double scaling); + compositor::rect getAreaSize(const std::string &area); + int loadAreaDb(); + + private: + std::unordered_map area2size; + + static const char *kDefaultAreaDb; +}; + +struct result to_layer_map(nlohmann::json const &j); + +static const nlohmann::json default_layers_json = { + {"main_surface", { + {"surface_role", "HomeScreen"} + }}, + {"mappings", { + { + {"role", "^HomeScreen$"}, + {"name", "HomeScreen"}, + {"layer_id", 1000}, + {"area", { + {"type", "full"} + }} + }, + { + {"role", "MediaPlayer|Radio|Phone|Navigation|HVAC|Settings|Dashboard|POI|Mixer"}, + {"name", "apps"}, + {"layer_id", 1001}, + {"area", { + {"type", "rect"}, + {"rect", { + {"x", 0}, + {"y", 218}, + {"width", -1}, + {"height", -433} + }} + }} + }, + { + {"role", "^OnScreen.*"}, + {"name", "popups"}, + {"layer_id", 9999}, + {"area", { + {"type", "rect"}, + {"rect", { + {"x", 0}, + {"y", 760}, + {"width", -1}, + {"height", 400} + }} + }} + } + }} +}; +} // namespace wm + +#endif // TMCAGLWM_LAYERS_H diff --git a/demo3/common/agl-service-windowmanager/src/low_can_client.cpp b/demo3/common/agl-service-windowmanager/src/low_can_client.cpp new file mode 100644 index 0000000..090aa14 --- /dev/null +++ b/demo3/common/agl-service-windowmanager/src/low_can_client.cpp @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2018 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "low_can_client.hpp" +#include "json_helper.hpp" +#include "util.hpp" + +extern "C" +{ +#include +} + +namespace wm +{ + +LowCanClient::LowCanClient() + : vehicle_speed(0), + trans_gear_pos(0), + headlamp_status(FALSE), + parking_brake_status(TRUE), + accel_pedal_pos(0), + accel_pedal_stt(FALSE), + lightstatus_brake_status(TRUE), + is_changed_accel_pedal_stt(false) +{ +} + +void LowCanClient::initialize() +{ + int ret; + + // Require API "low-can" + ret = afb_daemon_require_api_v2("low-can", 1); + if (0 > ret) + { + HMI_INFO("Requirement API \"low-can\" failed"); + return; + } + + // Subscribe low-level-can + // low-can subscribe { "event": "vehicle.speed" } + // low-can subscribe { "event": "transmission_gear_position" } + // low-can subscribe { "event": "headlamp_status" } + // low-can subscribe { "event": "parking_brake_status" } + // low-can subscribe { "event": "accelerator.pedal.position" } + // low-can subscribe { "event": "lightstatus.brake" } + for (int i = SignalNoMin; i <= SignalNoMax; i++) + { + // Set Event + json_object *json_obj = json_object_new_object(); + json_object_object_add(json_obj, "event", + json_object_new_string(this->kSignalName[i])); + + // Set filter + if (0 != strcmp("", this->kFilterValue[i])) + { + json_object_object_add(json_obj, "filter", + json_tokener_parse(this->kFilterValue[i])); + } + HMI_DEBUG("subscribe message:%s", json_object_get_string(json_obj)); + + // Subscribe + afb_service_call("low-can", "subscribe", json_obj, + [](void *closure, int status, json_object *result) { + HMI_DEBUG("subscribe result:%s", json_object_get_string(result)); + }, + nullptr); + } + + return; +} + +const char *LowCanClient::analyzeCanSignal(struct json_object *object) +{ + HMI_DEBUG("object:%s", json_object_get_string(object)); + + const char *name = jh::getStringFromJson(object, "name"); + HMI_DEBUG("CAN signal name:%s", name); + + if (strstr(name, this->kSignalName[SignalNoVehicliSpeed])) + { + // Update vehicle speed + this->vehicle_speed = jh::getIntFromJson(object, "value"); + HMI_DEBUG("Update vehicle speed:%d", this->vehicle_speed); + } + else if (strstr(name, this->kSignalName[SignalNoTransGearPos])) + { + // Update transmission gear position + this->trans_gear_pos = jh::getIntFromJson(object, "value"); + HMI_DEBUG("Update transmission gear position:%d", this->trans_gear_pos); + } + else if (strstr(name, this->kSignalName[SignalNoHeadlame])) + { + // Update headlamp status + this->headlamp_status = jh::getBoolFromJson(object, "value"); + HMI_DEBUG("Update headlamp status:%d", this->headlamp_status); + } + else if (strstr(name, this->kSignalName[SignalNoParkingBrake])) + { + // Update parking gear status + this->parking_brake_status = jh::getBoolFromJson(object, "value"); + HMI_DEBUG("Update parking brake status:%d", this->parking_brake_status); + } + else if (strstr(name, this->kSignalName[SignalNoAccelPedalPos])) + { + // Clear flag for whether accel pedal state is changed + this->is_changed_accel_pedal_stt = false; + + // Update accelerator pedal status + this->accel_pedal_pos = jh::getDoubleFromJson(object, "value"); + HMI_DEBUG("Update accelerator pedal position:%lf", this->accel_pedal_pos); + + bool accel_pedal_stt; + if (0 != this->accel_pedal_pos) + { + accel_pedal_stt = true; + } + else + { + accel_pedal_stt = false; + } + + if (accel_pedal_stt != this->accel_pedal_stt) + { + this->is_changed_accel_pedal_stt = true; + this->accel_pedal_stt = accel_pedal_stt; + } + } + else if (strstr(name, this->kSignalName[SignalNoLightstatusBrake])) + { + // Update lightstatus brake status + this->lightstatus_brake_status = jh::getBoolFromJson(object, "value"); + HMI_DEBUG("Update lightstatus brake status:%d", this->lightstatus_brake_status); + } + + return name; +} + +bool LowCanClient::isChangedAccelPedalState() +{ + return this->is_changed_accel_pedal_stt; +} + +int LowCanClient::getCurrentTransGearState() +{ + return this->trans_gear_pos; +} + +bool LowCanClient::getCurrentHeadlampState() +{ + return (bool)this->headlamp_status; +} + +bool LowCanClient::getCurrentParkingBrakeState() +{ + return (bool)this->parking_brake_status; +} + +double LowCanClient::getCurrentAccelPedalPosition() +{ + return this->accel_pedal_pos; +} + +bool LowCanClient::getCurrentAccelPedalState() +{ + return this->accel_pedal_stt; +} + +bool LowCanClient::getCurrentLightstatusBrakeState() +{ + return (bool)this->lightstatus_brake_status; +} + +} // namespace wm diff --git a/demo3/common/agl-service-windowmanager/src/low_can_client.hpp b/demo3/common/agl-service-windowmanager/src/low_can_client.hpp new file mode 100644 index 0000000..9b7f509 --- /dev/null +++ b/demo3/common/agl-service-windowmanager/src/low_can_client.hpp @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2017 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TMCAGLWM_LOW_CAN_CLIENT_HPP +#define TMCAGLWM_LOW_CAN_CLIENT_HPP + +#include +#include +#include + +namespace wm +{ + +class LowCanClient +{ + + public: + explicit LowCanClient(); + ~LowCanClient() = default; + + enum SignalNo + { + SignalNoVehicliSpeed = 0, + SignalNoTransGearPos, + SignalNoHeadlame, + SignalNoParkingBrake, + SignalNoAccelPedalPos, + SignalNoLightstatusBrake, + + SignalNum, + + SignalNoMin = SignalNoVehicliSpeed, + SignalNoMax = SignalNum - 1, + }; + + const std::vector kSignalName{ + "vehicle.speed", + "transmission_gear_position", + "headlamp_status", + "parking_brake_status", + "accelerator.pedal.position", + "lightstatus.brake", + }; + + void initialize(); + const char *analyzeCanSignal(struct json_object *object); + + int getCurrentTransGearState(); + bool getCurrentHeadlampState(); + bool getCurrentParkingBrakeState(); + double getCurrentAccelPedalPosition(); + bool getCurrentAccelPedalState(); + bool getCurrentLightstatusBrakeState(); + + bool isChangedAccelPedalState(); + + private: + // Disable copy and move + LowCanClient(LowCanClient const &) = delete; + LowCanClient &operator=(LowCanClient const &) = delete; + LowCanClient(LowCanClient &&) = delete; + LowCanClient &operator=(LowCanClient &&) = delete; + + enum TransGearPosVal + { + TransGearPosValD1 = 1, + TransGearPosValD2, + TransGearPosValD3, + TransGearPosValD4, + TransGearPosValD5, + TransGearPosValD6, + TransGearPosValD7, + TransGearPosValD8, + TransGearPosValR, + TransGearPosValN, + }; + + const std::vector kFilterValue{ + "", // vehicle.speed + "", // transmission_gear_position + "", // headlamp_status + "", // parking_brake_status + "", // accelerator.pedal.position + "", // lightstatus.brake + }; + + int vehicle_speed; + int trans_gear_pos; + json_bool headlamp_status; + json_bool parking_brake_status; + double accel_pedal_pos; + bool accel_pedal_stt; + json_bool lightstatus_brake_status; + + bool is_changed_accel_pedal_stt; +}; + +} // namespace wm + +#endif // TMCAGLWM_LOW_CAN_CLIENT_HPP diff --git a/demo3/common/agl-service-windowmanager/src/main.cpp b/demo3/common/agl-service-windowmanager/src/main.cpp new file mode 100644 index 0000000..3766152 --- /dev/null +++ b/demo3/common/agl-service-windowmanager/src/main.cpp @@ -0,0 +1,844 @@ +/* + * Copyright (c) 2017 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include "window_manager.hpp" +#include "json_helper.hpp" + +extern "C" +{ +#include +#include +} + +typedef struct WMClientCtxt +{ + std::string name; + std::string role; + WMClientCtxt(const char *appName, const char* appRole) + { + name = appName; + role = appRole; + } +} WMClientCtxt; + +struct afb_instance +{ + wm::WindowManager wmgr; + + afb_instance() : wmgr() {} + ~afb_instance() = default; + + int init(); +}; + +struct afb_instance *g_afb_instance; +std::mutex binding_m; + +int afb_instance::init() +{ + return this->wmgr.init(); +} + +int _binding_init() +{ + HMI_NOTICE("WinMan ver. %s", WINMAN_VERSION_STRING); + + g_afb_instance = new afb_instance; + + if (g_afb_instance->init() == -1) + { + HMI_ERROR("Could not connect to compositor"); + goto error; + } + + atexit([] { delete g_afb_instance; }); + + return 0; + +error: + delete g_afb_instance; + g_afb_instance = nullptr; + return -1; +} + +int binding_init() noexcept +{ + try + { + return _binding_init(); + } + catch (std::exception &e) + { + HMI_ERROR("Uncaught exception in binding_init(): %s", e.what()); + } + return -1; +} + +static void cbRemoveClientCtxt(void *data) +{ + WMClientCtxt *ctxt = (WMClientCtxt *)data; + if (ctxt == nullptr) + { + return; + } + HMI_DEBUG("remove app %s", ctxt->name.c_str()); + + // Policy Manager does not know this app was killed, + // so notify it by deactivate request. + g_afb_instance->wmgr.api_deactivate_surface( + ctxt->name.c_str(), ctxt->role.c_str(), + [](const char *) {}); + + g_afb_instance->wmgr.removeClient(ctxt->name); + delete ctxt; +} + +static void createSecurityContext(afb_req req, const char* appid, const char* role) +{ + WMClientCtxt *ctxt = (WMClientCtxt *)afb_req_context_get(req); + if (!ctxt) + { + // Create Security Context at first time + const char *new_role = g_afb_instance->wmgr.convertRoleOldToNew(role); + WMClientCtxt *ctxt = new WMClientCtxt(appid, new_role); + HMI_DEBUG("create session for %s", ctxt->name.c_str()); + afb_req_session_set_LOA(req, 1); + afb_req_context_set(req, ctxt, cbRemoveClientCtxt); + } +} + +void windowmanager_requestsurface(afb_req req) noexcept +{ + std::lock_guard guard(binding_m); + + if (g_afb_instance == nullptr) + { + afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?"); + return; + } + + try + { + const char *a_drawing_name = afb_req_value(req, "drawing_name"); + if (!a_drawing_name) + { + afb_req_fail(req, "failed", "Need char const* argument drawing_name"); + return; + } + + char *appid = afb_req_get_application_id(req); + if(appid) + { + auto ret = g_afb_instance->wmgr.api_request_surface( + appid, a_drawing_name); + + if (ret.is_err()) + { + afb_req_fail(req, "failed", ret.unwrap_err()); + } + else + { + createSecurityContext(req, appid, a_drawing_name); + afb_req_success(req, json_object_new_int(ret.unwrap()), "success"); + } + free(appid); + } + else + { + afb_req_fail(req, "failed", nullptr); + } + } + catch (std::exception &e) + { + afb_req_fail_f(req, "failed", "Uncaught exception while calling requestsurface: %s", e.what()); + return; + } +} + +void windowmanager_requestsurfacexdg(afb_req req) noexcept +{ + std::lock_guard guard(binding_m); + + if (g_afb_instance == nullptr) + { + afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?"); + return; + } + + try + { + json_object *jreq = afb_req_json(req); + + json_object *j_drawing_name = nullptr; + if (!json_object_object_get_ex(jreq, "drawing_name", &j_drawing_name)) + { + afb_req_fail(req, "failed", "Need char const* argument drawing_name"); + return; + } + char const *a_drawing_name = json_object_get_string(j_drawing_name); + + json_object *j_ivi_id = nullptr; + if (!json_object_object_get_ex(jreq, "ivi_id", &j_ivi_id)) + { + afb_req_fail(req, "failed", "Need char const* argument ivi_id"); + return; + } + char const *a_ivi_id = json_object_get_string(j_ivi_id); + char *appid = afb_req_get_application_id(req); + if(appid) + { + auto ret = g_afb_instance->wmgr.api_request_surface( + appid, a_drawing_name, a_ivi_id); + if (ret != nullptr) + { + afb_req_fail(req, "failed", ret); + } + else + { + createSecurityContext(req, appid, a_drawing_name); + afb_req_success(req, NULL, "success"); + } + free(appid); + } + } + catch (std::exception &e) + { + afb_req_fail_f(req, "failed", "Uncaught exception while calling requestsurfacexdg: %s", e.what()); + return; + } +} + +void windowmanager_setrole(afb_req req) noexcept +{ + std::lock_guard guard(binding_m); + if (g_afb_instance == nullptr) + { + afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?"); + return; + } + try + { + json_object *jreq = afb_req_json(req); + + json_object *j_role = nullptr; + if (!json_object_object_get_ex(jreq, "role", &j_role)) + { + afb_req_fail(req, "failed", "Need char const* argument role"); + return; + } + char const *a_role = json_object_get_string(j_role); + char *appid = afb_req_get_application_id(req); + + if(appid) + { + auto ret = g_afb_instance->wmgr.api_set_role(appid, a_role); + if (!ret) + { + afb_req_fail(req, "failed", "Couldn't register"); + } + else + { + createSecurityContext(req, appid, a_role); + afb_req_success(req, NULL, "success"); + } + free(appid); + } + } + catch (std::exception &e) + { + afb_req_fail_f(req, "failed", "Uncaught exception while calling requestsurfacexdg: %s", e.what()); + return; + } +} + +void windowmanager_activatewindow(afb_req req) noexcept +{ + std::lock_guard guard(binding_m); + + if (g_afb_instance == nullptr) + { + afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?"); + return; + } + + try + { + const char *a_drawing_name = afb_req_value(req, "drawing_name"); + if (!a_drawing_name) + { + afb_req_fail(req, "failed", "Need char const* argument drawing_name"); + return; + } + + const char *a_drawing_area = afb_req_value(req, "drawing_area"); + if (!a_drawing_area) + { + afb_req_fail(req, "failed", "Need char const* argument drawing_area"); + return; + } + + char* appid = afb_req_get_application_id(req); + if(appid) + { + auto reply = [&req](const char *errmsg) { + if (errmsg != nullptr) + { + HMI_ERROR(errmsg); + afb_req_fail(req, "failed", errmsg); + return; + } + afb_req_success(req, NULL, "success"); + }; + + if (g_afb_instance->wmgr.wmcon.isMasterMode() || + !g_afb_instance->wmgr.wmcon.isMasterArea(a_drawing_area)) + { + g_afb_instance->wmgr.api_activate_surface( + appid, a_drawing_name, a_drawing_area, reply); + } + else + { + // TODO: temporarily + if (!g_afb_instance->wmgr.wmcon.isConnecting()) + { + g_afb_instance->wmgr.wmcon.connectToMaster(); + } + + // If Window Manager is slave and this request is for master, + // request activateWindow to master + g_afb_instance->wmgr.api_activate_surface_to_master( + appid, a_drawing_name, a_drawing_area, reply); + } + free(appid); + } + } + catch (std::exception &e) + { + HMI_WARNING("failed: Uncaught exception while calling activatesurface: %s", e.what()); + g_afb_instance->wmgr.exceptionProcessForTransition(); + return; + } +} + +void windowmanager_deactivatewindow(afb_req req) noexcept +{ + std::lock_guard guard(binding_m); + + if (g_afb_instance == nullptr) + { + afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?"); + return; + } + + try + { + const char *a_drawing_name = afb_req_value(req, "drawing_name"); + if (!a_drawing_name) + { + afb_req_fail(req, "failed", "Need char const* argument drawing_name"); + return; + } + + char* appid = afb_req_get_application_id(req); + if(appid) + { + auto reply = [&req](const char *errmsg) { + if (errmsg != nullptr) + { + HMI_ERROR(errmsg); + afb_req_fail(req, "failed", errmsg); + return; + } + afb_req_success(req, NULL, "success"); + }; + + // TODO: Check whether role is tbtnavi to request remote invisible + if (g_afb_instance->wmgr.wmcon.isMasterMode() || + ("tbtnavi" != std::string(a_drawing_name))) + { + g_afb_instance->wmgr.api_deactivate_surface( + appid, a_drawing_name, reply); + } + else + { + // If Window Manager is slave and this request is for master, + // request deactivateWindow to master + g_afb_instance->wmgr.api_deactivate_surface_to_master( + appid, a_drawing_name, reply); + } + free(appid); + } + } + catch (std::exception &e) + { + HMI_WARNING("failed: Uncaught exception while calling deactivatesurface: %s", e.what()); + g_afb_instance->wmgr.exceptionProcessForTransition(); + return; + } +} + +void windowmanager_enddraw(afb_req req) noexcept +{ + std::lock_guard guard(binding_m); + + if (g_afb_instance == nullptr) + { + afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?"); + return; + } + + try + { + const char *a_drawing_name = afb_req_value(req, "drawing_name"); + if (!a_drawing_name) + { + afb_req_fail(req, "failed", "Need char const* argument drawing_name"); + return; + } + afb_req_success(req, NULL, "success"); + + char* appid = afb_req_get_application_id(req); + if(appid) + { + if (g_afb_instance->wmgr.wmcon.isMasterMode() || + !g_afb_instance->wmgr.wmcon.isSyncDrawingForRemote(appid)) + { + g_afb_instance->wmgr.api_enddraw(appid, a_drawing_name); + } + else + { + // If Window Manager is slave and requesting app is syncDrawing, + // request endDraw to master + g_afb_instance->wmgr.api_enddraw_for_remote(appid, a_drawing_name); + } + free(appid); + } + } + catch (std::exception &e) + { + HMI_WARNING("failed: Uncaught exception while calling enddraw: %s", e.what()); + g_afb_instance->wmgr.exceptionProcessForTransition(); + return; + } +} + +void windowmanager_getdisplayinfo_thunk(afb_req req) noexcept +{ + std::lock_guard guard(binding_m); + + if (g_afb_instance == nullptr) + { + afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?"); + return; + } + + try + { + auto ret = g_afb_instance->wmgr.api_get_display_info(); + if (ret.is_err()) + { + afb_req_fail(req, "failed", ret.unwrap_err()); + return; + } + + afb_req_success(req, ret.unwrap(), "success"); + } + catch (std::exception &e) + { + afb_req_fail_f(req, "failed", "Uncaught exception while calling getdisplayinfo: %s", e.what()); + return; + } +} + +void windowmanager_getareainfo_thunk(afb_req req) noexcept +{ + std::lock_guard guard(binding_m); + + if (g_afb_instance == nullptr) + { + afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?"); + return; + } + + try + { + json_object *jreq = afb_req_json(req); + + json_object *j_drawing_name = nullptr; + if (!json_object_object_get_ex(jreq, "drawing_name", &j_drawing_name)) + { + afb_req_fail(req, "failed", "Need char const* argument drawing_name"); + return; + } + char const *a_drawing_name = json_object_get_string(j_drawing_name); + + auto ret = g_afb_instance->wmgr.api_get_area_info(a_drawing_name); + if (ret.is_err()) + { + afb_req_fail(req, "failed", ret.unwrap_err()); + return; + } + + afb_req_success(req, ret.unwrap(), "success"); + } + catch (std::exception &e) + { + afb_req_fail_f(req, "failed", "Uncaught exception while calling getareainfo: %s", e.what()); + return; + } +} + +void windowmanager_getcarinfo_thunk(afb_req req) noexcept +{ + std::lock_guard guard(binding_m); + if (g_afb_instance == nullptr) + { + afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?"); + return; + } + + try + { + json_object *jreq = afb_req_json(req); + + json_object *j_label = nullptr; + if (! json_object_object_get_ex(jreq, "label", &j_label)) + { + afb_req_fail(req, "failed", "Need char const* argument label"); + return; + } + char const* a_label = json_object_get_string(j_label); + + auto ret = g_afb_instance->wmgr.api_get_car_info(a_label); + if (ret.is_err()) + { + afb_req_fail(req, "failed", ret.unwrap_err()); + return; + } + + afb_req_success(req, ret.unwrap(), "success"); + } + catch (std::exception &e) + { + afb_req_fail_f(req, "failed", "Uncaught exception while calling getcarinfo: %s", e.what()); + return; + } +} + +void windowmanager_set_render_order(afb_req req) noexcept +{ + std::lock_guard guard(binding_m); + if (g_afb_instance == nullptr) + { + afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?"); + return; + } + + char* appid = afb_req_get_application_id(req); + if(appid) + { + json_object *jreq = afb_req_json(req); + json_object *j_ro; // Do not free this. binder frees jreq, then free j_ro + if (json_object_object_get_ex(jreq, "render_order", &j_ro)) + { + int size = json_object_array_length(j_ro); + std::vector ro(size); + for(int i = 0; i < size; i++) + { + ro[i] = json_object_get_string(json_object_array_get_idx(j_ro, i)); + } + + auto ret = g_afb_instance->wmgr.api_client_set_render_order(appid, ro); + if (!ret) + { + afb_req_fail(req, "failed", nullptr); + } + else + { + afb_req_success(req, nullptr, nullptr); + } + } + free(appid); + } + else + { + afb_req_fail(req, "failed", nullptr); + } +} + +void windowmanager_attach_app(afb_req req) noexcept +{ + std::lock_guard guard(binding_m); + if (g_afb_instance == nullptr) + { + afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?"); + return; + } + + char* appid = afb_req_get_application_id(req); + if(appid) + { + json_object *jreq = afb_req_json(req); + json_object *j_dest, *j_id; // Do not free this. binder frees jreq, then free j_ro + if (json_object_object_get_ex(jreq, "destination", &j_dest) && + json_object_object_get_ex(jreq, "service_surface", &j_id)) + { + const char* dest_app = json_object_get_string(j_dest); + const char* service = json_object_get_string(j_id); + + std::string uuid = g_afb_instance->wmgr.api_client_attach_service_surface(appid, dest_app, service); + if (uuid.empty()) + { + afb_req_fail(req, "failed", nullptr); + } + else + { + json_object *resp = json_object_new_object(); + json_object_object_add(resp, "uuid", json_object_new_string(uuid.c_str())); + afb_req_success(req, resp, nullptr); + } + } + free(appid); + } + else + { + afb_req_fail(req, "failed", nullptr); + } +} + +void windowmanager_wm_subscribe(afb_req req) noexcept +{ + std::lock_guard guard(binding_m); + + if (g_afb_instance == nullptr) + { + afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?"); + return; + } + + try + { + json_object *jreq = afb_req_json(req); + json_object *j = nullptr; + if (!json_object_object_get_ex(jreq, "event", &j)) + { + afb_req_fail(req, "failed", "Need char const* argument event"); + return; + } + int event_type = json_object_get_int(j); + const char *event_name = g_afb_instance->wmgr.kListEventName[event_type]; + struct afb_event event = g_afb_instance->wmgr.map_afb_event[event_name]; + int ret = afb_req_subscribe(req, event); + if (ret) + { + afb_req_fail(req, "failed", "Error: afb_req_subscribe()"); + return; + } + afb_req_success(req, NULL, "success"); + } + catch (std::exception &e) + { + afb_req_fail_f(req, "failed", "Uncaught exception while calling wm_subscribe: %s", e.what()); + return; + } +} + +void windowmanager_list_drawing_names(afb_req req) noexcept +{ + std::lock_guard guard(binding_m); + + /* if (g_afb_instance == nullptr) + { + afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?"); + return; + } + + try + { + + nlohmann::json j = g_afb_instance->wmgr.id_alloc.name2id; + auto ret = wm::Ok(json_tokener_parse(j.dump().c_str())); + if (ret.is_err()) + { + afb_req_fail(req, "failed", ret.unwrap_err()); + return; + } + + afb_req_success(req, ret.unwrap(), "success"); + } + catch (std::exception &e) + { + afb_req_fail_f(req, "failed", "Uncaught exception while calling list_drawing_names: %s", e.what()); + return; + } */ +} + +void windowmanager_ping(afb_req req) noexcept +{ + std::lock_guard guard(binding_m); + + if (g_afb_instance == nullptr) + { + afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?"); + return; + } + else + { + afb_req_success(req, NULL, "success"); + } +} + +void windowmanager_debug_status(afb_req req) noexcept +{ + std::lock_guard guard(binding_m); + + /* if (g_afb_instance == nullptr) + { + afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?"); + return; + } + + try + { + + json_object *jr = json_object_new_object(); + json_object_object_add(jr, "surfaces", + to_json(g_afb_instance->wmgr.controller->sprops)); + json_object_object_add(jr, "layers", to_json(g_afb_instance->wmgr.controller->lprops)); + + afb_req_success(req, jr, "success"); + } + catch (std::exception &e) + { + afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_status: %s", e.what()); + return; + } */ +} + +void windowmanager_debug_layers(afb_req req) noexcept +{ + std::lock_guard guard(binding_m); + + /* if (g_afb_instance == nullptr) + { + afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?"); + return; + } + + try + { + auto ret = wm::Ok(json_tokener_parse(g_afb_instance->wmgr.layers.to_json().dump().c_str())); + + afb_req_success(req, ret, "success"); + } + catch (std::exception &e) + { + afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_layers: %s", e.what()); + return; + } */ +} + +void windowmanager_debug_surfaces(afb_req req) noexcept +{ + std::lock_guard guard(binding_m); + + /* if (g_afb_instance == nullptr) + { + afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?"); + return; + } + + try + { + + auto ret = wm::Ok(to_json(g_afb_instance->wmgr.controller->sprops)); + if (ret.is_err()) + { + afb_req_fail(req, "failed", ret.unwrap_err()); + return; + } + + afb_req_success(req, ret.unwrap(), "success"); + } + catch (std::exception &e) + { + afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_surfaces: %s", e.what()); + return; + } */ +} + +void windowmanager_debug_terminate(afb_req req) noexcept +{ + std::lock_guard guard(binding_m); + + if (g_afb_instance == nullptr) + { + afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?"); + return; + } + + try + { + + if (getenv("WINMAN_DEBUG_TERMINATE") != nullptr) + { + raise(SIGKILL); // afb-daemon kills it's pgroup using TERM, which + // doesn't play well with perf + } + + afb_req_success(req, NULL, "success"); + } + catch (std::exception &e) + { + afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_terminate: %s", e.what()); + return; + } +} + +void on_event(const char *event, struct json_object *object) +{ + g_afb_instance->wmgr.analyzeReceivedEvent(event, object); +} + +const struct afb_verb_v2 windowmanager_verbs[] = { + {"requestSurface", windowmanager_requestsurface, nullptr, nullptr, AFB_SESSION_NONE}, + {"requestSurfaceXdg", windowmanager_requestsurfacexdg, nullptr, nullptr, AFB_SESSION_NONE}, + {"setRole", windowmanager_setrole, nullptr, nullptr, AFB_SESSION_NONE}, + {"activateWindow", windowmanager_activatewindow, nullptr, nullptr, AFB_SESSION_NONE}, + {"deactivateWindow", windowmanager_deactivatewindow, nullptr, nullptr, AFB_SESSION_NONE}, + {"endDraw", windowmanager_enddraw, nullptr, nullptr, AFB_SESSION_NONE}, + {"getDisplayInfo", windowmanager_getdisplayinfo_thunk, nullptr, nullptr, AFB_SESSION_NONE}, + {"getAreaInfo", windowmanager_getareainfo_thunk, nullptr, nullptr, AFB_SESSION_NONE}, + {"getCarInfo", windowmanager_getcarinfo_thunk, nullptr, nullptr, AFB_SESSION_NONE }, + {"setRenderOrder", windowmanager_set_render_order, nullptr, nullptr, AFB_SESSION_NONE}, + {"attachApp", windowmanager_attach_app, nullptr, nullptr, AFB_SESSION_NONE}, + {"wm_subscribe", windowmanager_wm_subscribe, nullptr, nullptr, AFB_SESSION_NONE}, + {"list_drawing_names", windowmanager_list_drawing_names, nullptr, nullptr, AFB_SESSION_NONE}, + {"ping", windowmanager_ping, nullptr, nullptr, AFB_SESSION_NONE}, + {"debug_status", windowmanager_debug_status, nullptr, nullptr, AFB_SESSION_NONE}, + {"debug_layers", windowmanager_debug_layers, nullptr, nullptr, AFB_SESSION_NONE}, + {"debug_surfaces", windowmanager_debug_surfaces, nullptr, nullptr, AFB_SESSION_NONE}, + {"debug_terminate", windowmanager_debug_terminate, nullptr, nullptr, AFB_SESSION_NONE}, + {}}; + +extern "C" const struct afb_binding_v2 afbBindingV2 = { + "windowmanager", nullptr, nullptr, windowmanager_verbs, nullptr, binding_init, on_event, 0}; diff --git a/demo3/common/agl-service-windowmanager/src/pm_wrapper.cpp b/demo3/common/agl-service-windowmanager/src/pm_wrapper.cpp new file mode 100644 index 0000000..7cf90f0 --- /dev/null +++ b/demo3/common/agl-service-windowmanager/src/pm_wrapper.cpp @@ -0,0 +1,439 @@ +/* + * Copyright (c) 2018 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "pm_wrapper.hpp" +#include "json_helper.hpp" +#include "util.hpp" + +namespace wm +{ + +static PMWrapper *g_context; + +namespace +{ + +static void onStateTransitioned(json_object *json_out) +{ + g_context->updateStates(json_out); +} + +static void onError(json_object *json_out) +{ + HMI_DEBUG("error message from PolicyManager:%s", + json_object_get_string(json_out)); + + g_context->processError(); +} + +} // namespace + +PMWrapper::PMWrapper() {} + +int PMWrapper::initialize(std::string ecu_name) +{ + int ret = 0; + + ret = this->pm.initialize(ecu_name); + if (0 > ret) + { + HMI_ERROR("wm:pmw", "Faild to initialize PolicyManager"); + } + + g_context = this; + + return ret; +} + +void PMWrapper::registerCallback(StateTransitionHandler on_state_transitioned, + ErrorHandler on_error) +{ + this->on_state_transitioned = on_state_transitioned; + this->on_error = on_error; + + PolicyManager::CallbackTable my_callback; + my_callback.onStateTransitioned = onStateTransitioned; + my_callback.onError = onError; + this->pm.registerCallback(my_callback); +} + +int PMWrapper::setInputEventData(Task task, std::string role, std::string area) +{ + const char* event; + if (Task::TASK_ALLOCATE == task) + { + event = "activate"; + } + else if (Task::TASK_RELEASE == task) + { + event = "deactivate"; + } + else if (Task::TASK_PARKING_BRAKE_OFF == task) + { + event = "parking_brake_off"; + } + else if (Task::TASK_PARKING_BRAKE_ON == task) + { + event = "parking_brake_on"; + } + else if (Task::TASK_ACCEL_PEDAL_OFF == task) + { + event = "accel_pedal_off"; + } + else if (Task::TASK_ACCEL_PEDAL_ON == task) + { + event = "accel_pedal_on"; + } + else if (Task::TASK_HEDLAMP_OFF == task) + { + event = "headlamp_off"; + } + else if (Task::TASK_HEDLAMP_ON == task) + { + event = "headlamp_on"; + } + else if (Task::TASK_LIGHTSTATUS_BRAKE_OFF == task) + { + event = "lightstatus_brake_off"; + } + else if (Task::TASK_LIGHTSTATUS_BRAKE_ON == task) + { + event = "lightstatus_brake_on"; + } + else + { + event = ""; + } + + json_object *json_in = json_object_new_object(); + json_object_object_add(json_in, "event", json_object_new_string(event)); + json_object_object_add(json_in, "role", json_object_new_string(role.c_str())); + json_object_object_add(json_in, "area", json_object_new_string(area.c_str())); + + int ret; + ret = this->pm.setInputEventData(json_in); + if (0 > ret) + { + HMI_ERROR("wm:pmw", "Faild to set input event data to PolicyManager"); + } + json_object_put(json_in); + + return ret; +} + +int PMWrapper::executeStateTransition() +{ + int ret; + ret = this->pm.executeStateTransition(); + if (0 > ret) + { + HMI_ERROR("wm:pmw", "Failed to execute state transition for PolicyManager"); + } + + return ret; +} + +void PMWrapper::undoState() +{ + this->pm.undoState(); + + this->crrlayer2rolestate = this->prvlayer2rolestate; +} + +void PMWrapper::updateStates(json_object *json_out) +{ + std::vector actions; + + HMI_DEBUG("json_out dump:%s", json_object_get_string(json_out)); + + this->createCarStateChangeAction(json_out, actions); + this->createLayoutChangeAction(json_out, actions); + + this->on_state_transitioned(actions); +} + +void PMWrapper::createCarStateChangeAction(json_object *json_out, std::vector &actions) +{ + json_object *json_car_ele; + if (!json_object_object_get_ex(json_out, "car_elements", &json_car_ele)) + { + HMI_DEBUG("Not found key \"car_elements\""); + return; + } + + int len = json_object_array_length(json_car_ele); + HMI_DEBUG("json_car_ele len:%d", len); + + for (int i = 0; i < len; i++) + { + json_object *json_tmp = json_object_array_get_idx(json_car_ele, i); + + std::string car_ele_name = jh::getStringFromJson(json_tmp, "name"); + std::string state = jh::getStringFromJson(json_tmp, "state"); + json_bool changed = jh::getBoolFromJson(json_tmp, "changed"); + HMI_DEBUG("car_element:%s changed:%d", car_ele_name.c_str(), changed); + + if (changed) + { + TaskCarState task = TaskCarState::NO_TASK; + if ("parking_brake" == car_ele_name) + { + if ("off" == state) + { + task = TaskCarState::PARKING_BRAKE_OFF; + } + else if ("on" == state) + { + task = TaskCarState::PARKING_BRAKE_ON; + } + else + { + HMI_DEBUG("Unknown parking brake state: %s", state.c_str()); + } + } + else if ("accel_pedal" == car_ele_name) + { + if ("off" == state) + { + task = TaskCarState::ACCEL_PEDAL_OFF; + } + else if ("on" == state) + { + task = TaskCarState::ACCEL_PEDAL_ON; + } + else + { + HMI_DEBUG("Unknown accel pedal state: %s", state.c_str()); + } + } + else if ("lamp" == car_ele_name) + { + if ("off" == state) + { + task = TaskCarState::HEDLAMP_OFF; + } + else if ("on" == state) + { + task = TaskCarState::HEDLAMP_ON; + } + else + { + HMI_DEBUG("Unknown lamp state: %s", state.c_str()); + } + } + else if ("lightstatus_brake" == car_ele_name) + { + if ("off" == state) + { + task = TaskCarState::LIGHTSTATUS_BRAKE_OFF; + } + else if ("on" == state) + { + task = TaskCarState::LIGHTSTATUS_BRAKE_ON; + } + else + { + HMI_DEBUG("Unknown lightstatus brake state: %s", state.c_str()); + } + } + else if ("running" == car_ele_name) + { + if ("stop" == state) + { + task = TaskCarState::CAR_STOP; + } + else if ("run" == state) + { + task = TaskCarState::CAR_RUN; + } + else + { + HMI_DEBUG("Unknown car state: %s", state.c_str()); + } + } + else if ("restriction_mode" == car_ele_name) + { + if ("off" == state) + { + task = TaskCarState::RESTRICTION_MODE_OFF; + } + else if ("on" == state) + { + task = TaskCarState::RESTRICTION_MODE_ON; + } + else + { + HMI_DEBUG("Unknown car state: %s", state.c_str()); + } + } + else + { + HMI_DEBUG("Unknown car element: %s", car_ele_name.c_str()); + } + + // Set action + if (TaskCarState::NO_TASK != task) + { + bool end_draw_finished = true; + WMAction act + { + 0, + nullptr, + "", + "", + TaskVisible::NO_CHANGE, + end_draw_finished, + task + }; + actions.push_back(act); + } + } + } +} + +void PMWrapper::createLayoutChangeAction(json_object *json_out, std::vector &actions) +{ + // Get displayed roles from previous layout + json_object *json_layers; + if (!json_object_object_get_ex(json_out, "layers", &json_layers)) + { + HMI_DEBUG("Not found key \"layers\""); + return; + } + + int len = json_object_array_length(json_layers); + HMI_DEBUG("json_layers len:%d", len); + + for (int i = 0; i < len; i++) + { + json_object *json_tmp = json_object_array_get_idx(json_layers, i); + + std::string layer_name = jh::getStringFromJson(json_tmp, "name"); + json_bool changed = jh::getBoolFromJson(json_tmp, "changed"); + HMI_DEBUG("layer:%s changed:%d", layer_name.c_str(), changed); + + if (changed) + { + json_object *json_areas; + if (!json_object_object_get_ex(json_tmp, "areas", &json_areas)) + { + HMI_DEBUG("Not found key \"areas\""); + return; + } + + int len = json_object_array_length(json_areas); + HMI_DEBUG("json_layers len:%d", len); + + // Store previous role state in this layer + this->prvlayer2rolestate[layer_name] = this->crrlayer2rolestate[layer_name]; + + RoleState crr_roles; + RoleState prv_roles = this->prvlayer2rolestate[layer_name]; + for (int j = 0; j < len; j++) + { + json_object *json_tmp2 = json_object_array_get_idx(json_areas, j); + + std::string area_name = jh::getStringFromJson(json_tmp2, "name"); + std::string role_name = jh::getStringFromJson(json_tmp2, "role"); + + crr_roles[role_name] = area_name; + + auto i_prv = prv_roles.find(role_name); + HMI_DEBUG("current role:%s area:%s", + role_name.c_str(), area_name.c_str()); + + // If current role does not exist in previous + if (prv_roles.end() == i_prv) + { + HMI_DEBUG("current role does not exist in previous"); + + // Set activate action + bool end_draw_finished = false; + WMAction act + { + 0, + nullptr, + role_name, + area_name, + TaskVisible::VISIBLE, + end_draw_finished, + TaskCarState::NO_TASK + }; + actions.push_back(act); + } + else + { + HMI_DEBUG("previous role:%s area:%s", + i_prv->first.c_str(), i_prv->second.c_str()); + + // If current role exists in previous and area is different with previous + if (area_name != i_prv->second) + { + HMI_DEBUG("current role exists in previous and area is different with previous"); + + // Set activate action + bool end_draw_finished = false; + WMAction act + { + 0, + nullptr, + role_name, + area_name, + TaskVisible::VISIBLE, + end_draw_finished, + TaskCarState::NO_TASK + }; + actions.push_back(act); + } + + // Remove role which exist in current list from previous list + prv_roles.erase(i_prv); + } + } + + // Deactivate roles which remains in previous list + // because these are not displayed in current layout + for (auto i_prv : prv_roles) + { + HMI_DEBUG("Deactivate role:%s", i_prv.first.c_str()); + + // Set deactivate action + bool end_draw_finished = true; + WMAction act + { + 0, + nullptr, + i_prv.first, + "", + TaskVisible::INVISIBLE, + end_draw_finished, + TaskCarState::NO_TASK + }; + actions.push_back(act); + } + + // Update previous role list + this->crrlayer2rolestate[layer_name] = crr_roles; + } + } +} + +void PMWrapper::processError() +{ + this->on_error(); +} + +} // namespace wm diff --git a/demo3/common/agl-service-windowmanager/src/pm_wrapper.hpp b/demo3/common/agl-service-windowmanager/src/pm_wrapper.hpp new file mode 100644 index 0000000..84d68df --- /dev/null +++ b/demo3/common/agl-service-windowmanager/src/pm_wrapper.hpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2018 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TMCAGLWM_PM_WRAPPER_HPP +#define TMCAGLWM_PM_WRAPPER_HPP + +#include +#include +#include +#include +#include "policy_manager.hpp" +#include "request.hpp" + +struct json_object; +struct sd_event; +struct sd_event_source; +struct StmState; + +namespace wm +{ + +class PMWrapper +{ + public: + explicit PMWrapper(); + ~PMWrapper() = default; + + using StateTransitionHandler = std::function)>; + using ErrorHandler = std::function; + + int initialize(std::string ecu_name); + void registerCallback(StateTransitionHandler on_state_transitioned, + ErrorHandler on_error); + int setInputEventData(Task task, std::string role, std::string area); + int executeStateTransition(); + void undoState(); + + // Do not use these functions + void updateStates(json_object *json_out); + void processError(); + + private: + // Disable copy and move + PMWrapper(PMWrapper const &) = delete; + PMWrapper &operator=(PMWrapper const &) = delete; + PMWrapper(PMWrapper &&) = delete; + PMWrapper &operator=(PMWrapper &&) = delete; + + typedef std::map RoleState; + + PolicyManager pm; + StateTransitionHandler on_state_transitioned; + ErrorHandler on_error; + std::map prvlayer2rolestate; + std::map crrlayer2rolestate; + + void createCarStateChangeAction(json_object *json_out, std::vector &actions); + void createLayoutChangeAction(json_object *json_out, std::vector &actions); +}; + +} // namespace wm + +#endif // TMCAGLWM_PM_WRAPPER_HPP diff --git a/demo3/common/agl-service-windowmanager/src/request.cpp b/demo3/common/agl-service-windowmanager/src/request.cpp new file mode 100644 index 0000000..0d8e5d4 --- /dev/null +++ b/demo3/common/agl-service-windowmanager/src/request.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2017 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "request.hpp" + +namespace wm +{ + +using std::string; + +WMRequest::WMRequest() {} + +WMRequest::WMRequest(string appid, string role, string area, Task task) + : req_num(0), + trigger{appid, role, area, task}, + sync_draw_req(0) +{ +} + +WMRequest::WMRequest(Task task) + : req_num(0), + trigger{"", "", "", task}, + sync_draw_req(0) +{ +} + +WMRequest::~WMRequest() +{ +} + +WMRequest::WMRequest(const WMRequest &obj) +{ + this->req_num = obj.req_num; + this->trigger = obj.trigger; + this->sync_draw_req = obj.sync_draw_req; +} + +} // namespace wm diff --git a/demo3/common/agl-service-windowmanager/src/request.hpp b/demo3/common/agl-service-windowmanager/src/request.hpp new file mode 100644 index 0000000..bb203e3 --- /dev/null +++ b/demo3/common/agl-service-windowmanager/src/request.hpp @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2017 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WMREQUEST_HPP +#define WMREQUEST_HPP + +#include +#include +#include + +namespace wm +{ + +class WMClient; + +enum Task +{ + TASK_ALLOCATE, + TASK_RELEASE, + TASK_PARKING_BRAKE_OFF, + TASK_PARKING_BRAKE_ON, + TASK_ACCEL_PEDAL_OFF, + TASK_ACCEL_PEDAL_ON, + TASK_HEDLAMP_OFF, + TASK_HEDLAMP_ON, + TASK_LIGHTSTATUS_BRAKE_OFF, + TASK_LIGHTSTATUS_BRAKE_ON, + TASK_RESTRICTION_MODE_OFF, + TASK_RESTRICTION_MODE_ON, + TASK_INVALID +}; + +enum TaskVisible +{ + VISIBLE, + INVISIBLE, + REQ_REMOTE_VISIBLE, + REQ_REMOTE_INVISIBLE, + REMOTE_VISIBLE, + REMOTE_INVISIBLE, + NO_CHANGE +}; + +enum TaskCarState +{ + PARKING_BRAKE_OFF, + PARKING_BRAKE_ON, + ACCEL_PEDAL_OFF, + ACCEL_PEDAL_ON, + HEDLAMP_OFF, + HEDLAMP_ON, + LIGHTSTATUS_BRAKE_OFF, + LIGHTSTATUS_BRAKE_ON, + CAR_STOP, + CAR_RUN, + RESTRICTION_MODE_OFF, + RESTRICTION_MODE_ON, + NO_TASK, +}; + +struct WMTrigger +{ + std::string appid; + std::string role; + std::string area; + Task task; +}; + +struct WMAction +{ + unsigned req_num; + std::shared_ptr client; + std::string role; + std::string area; + TaskVisible visible; + bool end_draw_finished; + TaskCarState car_state; +}; + +struct WMRequest +{ + WMRequest(); + explicit WMRequest(std::string appid, std::string role, + std::string area, Task task); + explicit WMRequest(Task task); + virtual ~WMRequest(); + WMRequest(const WMRequest &obj); + + unsigned req_num; + struct WMTrigger trigger; + std::vector sync_draw_req; +}; + +} // namespace wm + +#endif //WMREQUEST_HPP diff --git a/demo3/common/agl-service-windowmanager/src/result.hpp b/demo3/common/agl-service-windowmanager/src/result.hpp new file mode 100644 index 0000000..8a59a90 --- /dev/null +++ b/demo3/common/agl-service-windowmanager/src/result.hpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2017 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TMCAGLWM_RESULT_HPP +#define TMCAGLWM_RESULT_HPP + +#include +#include + +namespace wm +{ + +using std::experimental::nullopt; +using std::experimental::optional; + +// We only ever return a string as an error - so just parametrize +// this over result type T +template +struct result +{ + char const *e; + optional t; + + bool is_ok() const { return this->t != nullopt; } + bool is_err() const { return this->e != nullptr; } + + T unwrap() + { + if (this->e != nullptr) + { + throw std::logic_error(this->e); + } + return this->t.value(); + } + + operator T() { return this->unwrap(); } + + char const *unwrap_err() { return this->e; } + + optional const &ok() const { return this->t; } + optional err() const + { + return this->e ? optional(this->e) : nullopt; + } + + result map_err(std::function f); +}; + +template +struct result Err(char const *e) +{ + return result{e, nullopt}; +} + +template +struct result Ok(T t) +{ + return result{nullptr, t}; +} + +template +result result::map_err(std::function f) +{ + if (this->is_err()) + { + return Err(f(this->e)); + } + return *this; +} + +} // namespace wm + +#endif // TMCAGLWM_RESULT_HPP diff --git a/demo3/common/agl-service-windowmanager/src/util.cpp b/demo3/common/agl-service-windowmanager/src/util.cpp new file mode 100644 index 0000000..b22a704 --- /dev/null +++ b/demo3/common/agl-service-windowmanager/src/util.cpp @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2017 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "util.hpp" + +#include +#include +#include +#include +#include + +#include + +static char ERROR_FLAG[6][20] = {"NONE", "ERROR", "WARNING", "NOTICE", "INFO", "DEBUG"}; + +void rectangle::fit(unsigned long to_width, unsigned long to_height) +{ + // fit rect within (to_width x to_height) + + if (to_width <= width()) { + // scale to fit with + set_bottom(top() + (static_cast(to_width) * height() / width()) - 1); + set_right(left() + to_width - 1); + } else { + // scale to fit height + set_right(left() + (static_cast(to_height) * width () / height()) - 1); + set_bottom(top() + to_height - 1); + } +} + +void rectangle::center(unsigned long outer_w, unsigned long outer_h) +{ + long inner_w = width(); + long inner_h = height(); + + set_left((outer_w - inner_w) / 2); + set_right(left() + inner_w - 1); + set_top((outer_h - inner_h) / 2); + set_bottom(top() + inner_h - 1); +} + +void rectangle::set_aspect(double ratio) +{ + // aspect ratio is width:height (= width/height) + // e.g. Landscape of HD's ratio is 16:9 (= 1.777...) + // Portrait of HD's ratio is 9:16 (= 0.5625) + // + // width / height = ratio + // width * height = area + // + // width = sqrt(ratio * area) + // height = width / ratio + + long orig_w = width(); + long orig_h = height(); + + if (ratio >= 1) { + // width >= height + // try to keep width + set_right(left() + orig_w - 1); + set_bottom(top() + static_cast(orig_w / ratio + 0.5) - 1); + } else { + set_bottom(top() + orig_h - 1); + set_right(left() + static_cast(orig_h * ratio + 0.5) - 1); + } +} + +void _HMI_LOG(enum LOG_LEVEL level, const char* file, const char* func, const int line, const char* prefix, const char* log, ...) +{ + const int log_level = (getenv("USE_HMI_DEBUG") == NULL)?LOG_LEVEL_ERROR:atoi(getenv("USE_HMI_DEBUG")); + if(log_level < level) + { + return; + } + + char *message; + struct timespec tp; + unsigned int time; + + clock_gettime(CLOCK_REALTIME, &tp); + time = (tp.tv_sec * 1000000L) + (tp.tv_nsec / 1000); + + va_list args; + va_start(args, log); + if (log == NULL || vasprintf(&message, log, args) < 0) + message = NULL; + fprintf(stderr, "[%10.3f] [%s %s] [%s, %s(), Line:%d] >>> %s \n", time / 1000.0, prefix, ERROR_FLAG[level], file, func, line, message); + va_end(args); + free(message); +} + +void _HMI_SEQ_LOG(enum LOG_LEVEL level, const char* file, const char* func, const int line, unsigned seq_num, const char* log, ...){ + const int log_level = (getenv("USE_HMI_DEBUG") == NULL) ? LOG_LEVEL_ERROR:atoi(getenv("USE_HMI_DEBUG")); + if(log_level < level) + { + return; + } + + char *message; + struct timespec tp; + unsigned int time; + + clock_gettime(CLOCK_REALTIME, &tp); + time = (tp.tv_sec * 1000000L) + (tp.tv_nsec / 1000); + + va_list args; + va_start(args, log); + if (log == NULL || vasprintf(&message, log, args) < 0) + message = NULL; + fprintf(stderr, "[%10.3f] [wm %s] [%s, %s(), Line:%d] >>> req %d: %s \n", time / 1000.0, ERROR_FLAG[level], file, func, line, seq_num, message); + va_end(args); + free(message); +} + +void _DUMP(enum LOG_LEVEL level, const char *log, ...) +{ + const int log_level = (getenv("USE_HMI_DEBUG") == NULL) ? LOG_LEVEL_ERROR : atoi(getenv("USE_HMI_DEBUG")); + if (log_level < level) + { + return; + } + char *message; + va_list args; + va_start(args, log); + if (log == NULL || vasprintf(&message, log, args) < 0) + message = NULL; + fprintf(stderr, "%s \n", message); + va_end(args); + free(message); +} + +std::vector parseString(std::string str, char delimiter) +{ + // Parse string by delimiter + std::vector vct; + std::stringstream ss{str}; + std::string buf; + while (std::getline(ss, buf, delimiter)) + { + if (!buf.empty()) + { + // Delete space and push back to vector + vct.push_back(deleteSpace(buf)); + } + } + return vct; +} + +std::string deleteSpace(std::string str) +{ + std::string ret = str; + size_t pos; + while ((pos = ret.find_first_of(" ")) != std::string::npos) + { + ret.erase(pos, 1); + } + return ret; +} + diff --git a/demo3/common/agl-service-windowmanager/src/util.hpp b/demo3/common/agl-service-windowmanager/src/util.hpp new file mode 100644 index 0000000..d049fff --- /dev/null +++ b/demo3/common/agl-service-windowmanager/src/util.hpp @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2017 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WM_UTIL_HPP +#define WM_UTIL_HPP + +#include +#include +#include +#include +#include +#include + +#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) + +#define HMI_ERROR(args,...) _HMI_LOG(LOG_LEVEL_ERROR, __FILENAME__, __FUNCTION__, __LINE__,"wm",args, ##__VA_ARGS__) +#define HMI_WARNING(args,...) _HMI_LOG(LOG_LEVEL_WARNING, __FILENAME__, __FUNCTION__,__LINE__, "wm", args,##__VA_ARGS__) +#define HMI_NOTICE(args,...) _HMI_LOG(LOG_LEVEL_NOTICE, __FILENAME__, __FUNCTION__,__LINE__, "wm", args,##__VA_ARGS__) +#define HMI_INFO(args,...) _HMI_LOG(LOG_LEVEL_INFO, __FILENAME__, __FUNCTION__,__LINE__, "wm", args,##__VA_ARGS__) +#define HMI_DEBUG(args,...) _HMI_LOG(LOG_LEVEL_DEBUG, __FILENAME__, __FUNCTION__,__LINE__, "wm", args,##__VA_ARGS__) + +#define HMI_SEQ_ERROR(seq_num, args,...) _HMI_SEQ_LOG(LOG_LEVEL_ERROR, __FILENAME__, __FUNCTION__, __LINE__, seq_num, args, ##__VA_ARGS__) +#define HMI_SEQ_WARNING(seq_num, args,...) _HMI_SEQ_LOG(LOG_LEVEL_WARNING, __FILENAME__, __FUNCTION__, __LINE__, seq_num, args, ##__VA_ARGS__) +#define HMI_SEQ_NOTICE(seq_num, args,...) _HMI_SEQ_LOG(LOG_LEVEL_NOTICE, __FILENAME__, __FUNCTION__, __LINE__, seq_num, args, ##__VA_ARGS__) +#define HMI_SEQ_INFO(seq_num, args,...) _HMI_SEQ_LOG(LOG_LEVEL_INFO, __FILENAME__, __FUNCTION__, __LINE__, seq_num, args, ##__VA_ARGS__) +#define HMI_SEQ_DEBUG(seq_num, args,...) _HMI_SEQ_LOG(LOG_LEVEL_DEBUG, __FILENAME__, __FUNCTION__, __LINE__, seq_num, args, ##__VA_ARGS__) + +#define DUMP(args, ...) _DUMP(LOG_LEVEL_DEBUG, args, ##__VA_ARGS__) + +enum LOG_LEVEL{ + LOG_LEVEL_NONE = 0, + LOG_LEVEL_ERROR, + LOG_LEVEL_WARNING, + LOG_LEVEL_NOTICE, + LOG_LEVEL_INFO, + LOG_LEVEL_DEBUG, + LOG_LEVEL_MAX = LOG_LEVEL_DEBUG +}; + +void _HMI_LOG(enum LOG_LEVEL level, const char* file, const char* func, const int line, const char* prefix, const char* log, ...); +void _HMI_SEQ_LOG(enum LOG_LEVEL level, const char* file, const char* func, const int line, unsigned seq_num, const char* log, ...); +void _DUMP(enum LOG_LEVEL level, const char *log, ...); + +std::vector parseString(std::string str, char delimiter); +std::string deleteSpace(std::string str); + +struct rect +{ + int32_t w, h; + int32_t x, y; +}; + +struct size +{ + uint32_t w, h; +}; + +class rectangle +{ + public: + explicit rectangle(long wd, long ht) : _right(wd - 1), _bottom(ht - 1) {}; + + void set_left(long l) { + _left = l; + } + long left() const { return _left; }; + + void set_right(long r) { + _right = r; + } + long right() const { return _right; }; + + void set_top(long t) { + _top = t; + } + long top() const { return _top; }; + + void set_bottom(long b) { + _bottom = b; + } + long bottom() const { return _bottom; } + + long width() const { + if (is_valid()) + return 0; + else + return _right - _left + 1; + } + + long height() const { + if (is_valid()) + return 0; + else + return _bottom - _top + 1; + } + + void set_aspect(double ratio); + void fit(unsigned long to_width, unsigned long to_height); + void center(unsigned long outer_w, unsigned long outer_h); + + private: + bool is_valid() const { + return (_top > _bottom || _left > _right); + } + + long _left = 0; + long _top = 0; + long _right; + long _bottom; +}; + +#endif // !WM_UTIL_HPP diff --git a/demo3/common/agl-service-windowmanager/src/wayland_ivi_wm.cpp b/demo3/common/agl-service-windowmanager/src/wayland_ivi_wm.cpp new file mode 100644 index 0000000..28bd024 --- /dev/null +++ b/demo3/common/agl-service-windowmanager/src/wayland_ivi_wm.cpp @@ -0,0 +1,724 @@ +/* + * Copyright (c) 2017 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wayland_ivi_wm.hpp" + +/** + * namespace wl + */ +namespace wl +{ + +/** + * display + */ +display::display() + : d(std::unique_ptr( + wl_display_connect(nullptr), &wl_display_disconnect)), + r(d.get()) {} + +bool display::ok() const { return d && wl_display_get_error(d.get()) == 0; } + +void display::roundtrip() { wl_display_roundtrip(this->d.get()); } + +int display::dispatch() { return wl_display_dispatch(this->d.get()); } + +int display::dispatch_pending() { return wl_display_dispatch_pending(this->d.get()); } + +int display::read_events() +{ + ; + while (wl_display_prepare_read(this->d.get()) == -1) + { + if (wl_display_dispatch_pending(this->d.get()) == -1) + { + return -1; + } + } + + if (wl_display_flush(this->d.get()) == -1) + { + return -1; + } + + if (wl_display_read_events(this->d.get()) == -1) + { + wl_display_cancel_read(this->d.get()); + } + + return 0; +} + +void display::flush() { wl_display_flush(this->d.get()); } + +int display::get_fd() const { return wl_display_get_fd(this->d.get()); } + +int display::get_error() { return wl_display_get_error(this->d.get()); } + +/** + * registry + */ +namespace +{ +void registry_global_created(void *data, struct wl_registry * /*r*/, uint32_t name, + char const *iface, uint32_t v) +{ + static_cast(data)->global_created(name, iface, v); +} + +void registry_global_removed(void *data, struct wl_registry * /*r*/, + uint32_t name) +{ + static_cast(data)->global_removed(name); +} + +constexpr struct wl_registry_listener registry_listener = { + registry_global_created, registry_global_removed}; +} // namespace + +registry::registry(struct wl_display *d) + : wayland_proxy(d == nullptr ? nullptr : wl_display_get_registry(d)) +{ + if (this->proxy != nullptr) + { + wl_registry_add_listener(this->proxy.get(), ®istry_listener, this); + } +} + +void registry::add_global_handler(char const *iface, binder bind) +{ + this->bindings[iface] = std::move(bind); +} + +void registry::global_created(uint32_t name, char const *iface, uint32_t v) +{ + auto b = this->bindings.find(iface); + if (b != this->bindings.end()) + { + b->second(this->proxy.get(), name, v); + } + HMI_DEBUG("wl::registry @ %p global n %u i %s v %u", this->proxy.get(), name, + iface, v); +} + +void registry::global_removed(uint32_t /*name*/) {} + +/** + * output + */ +namespace +{ +void output_geometry(void *data, struct wl_output * /*wl_output*/, int32_t x, + int32_t y, int32_t physical_width, int32_t physical_height, + int32_t subpixel, const char *make, const char *model, + int32_t transform) +{ + static_cast(data)->geometry( + x, y, physical_width, physical_height, subpixel, make, model, transform); +} + +void output_mode(void *data, struct wl_output * /*wl_output*/, uint32_t flags, + int32_t width, int32_t height, int32_t refresh) +{ + static_cast(data)->mode(flags, width, height, refresh); +} + +void output_done(void *data, struct wl_output * /*wl_output*/) +{ + static_cast(data)->done(); +} + +void output_scale(void *data, struct wl_output * /*wl_output*/, + int32_t factor) +{ + static_cast(data)->scale(factor); +} + +constexpr struct wl_output_listener output_listener = { + output_geometry, output_mode, output_done, output_scale}; +} // namespace + +output::output(struct wl_registry *r, uint32_t name, uint32_t v) + : wayland_proxy(wl_registry_bind(r, name, &wl_output_interface, v)) +{ + wl_output_add_listener(this->proxy.get(), &output_listener, this); +} + +void output::geometry(int32_t x, int32_t y, int32_t pw, int32_t ph, + int32_t subpel, char const *make, char const *model, + int32_t tx) +{ + HMI_DEBUG("wm", + "wl::output %s @ %p x %i y %i w %i h %i spel %x make %s model %s tx %i", + __func__, this->proxy.get(), x, y, pw, ph, subpel, make, model, tx); + this->physical_width = pw; + this->physical_height = ph; + this->transform = tx; +} + +void output::mode(uint32_t flags, int32_t w, int32_t h, int32_t r) +{ + HMI_DEBUG("wl::output %s @ %p f %x w %i h %i r %i", __func__, + this->proxy.get(), flags, w, h, r); + if ((flags & WL_OUTPUT_MODE_CURRENT) != 0u) + { + this->width = w; + this->height = h; + this->refresh = r; + } +} + +void output::done() +{ + HMI_DEBUG("wl::output %s @ %p done", __func__, this->proxy.get()); + // Pivot and flipped + if (this->transform == WL_OUTPUT_TRANSFORM_90 || + this->transform == WL_OUTPUT_TRANSFORM_270 || + this->transform == WL_OUTPUT_TRANSFORM_FLIPPED_90 || + this->transform == WL_OUTPUT_TRANSFORM_FLIPPED_270) + { + std::swap(this->width, this->height); + std::swap(this->physical_width, this->physical_height); + } +} + +void output::scale(int32_t factor) +{ + HMI_DEBUG("wl::output %s @ %p f %i", __func__, this->proxy.get(), factor); +} +} // namespace wl + +/** + * namespace compositor + */ +namespace compositor +{ + +namespace +{ + +void surface_visibility_changed( + void *data, struct ivi_wm * /*ivi_wm*/, + uint32_t surface_id, int32_t visibility) +{ + auto c = static_cast(data); + c->surface_visibility_changed(surface_id, visibility); +} + +void surface_opacity_changed(void *data, struct ivi_wm * /*ivi_wm*/, + uint32_t surface_id, wl_fixed_t opacity) +{ + auto c = static_cast(data); + c->surface_opacity_changed(surface_id, float(wl_fixed_to_double(opacity))); +} + +void surface_source_rectangle_changed( + void *data, struct ivi_wm * /*ivi_wm*/, uint32_t surface_id, + int32_t x, int32_t y, int32_t width, int32_t height) +{ + auto c = static_cast(data); + c->surface_source_rectangle_changed(surface_id, x, y, width, height); +} + +void surface_destination_rectangle_changed( + void *data, struct ivi_wm * /*ivi_wm*/, uint32_t surface_id, + int32_t x, int32_t y, int32_t width, int32_t height) +{ + auto c = static_cast(data); + c->surface_destination_rectangle_changed(surface_id, x, y, width, height); +} + +void surface_created(void *data, struct ivi_wm * /*ivi_wm*/, + uint32_t id_surface) +{ + static_cast(data)->surface_created(id_surface); +} + +void surface_destroyed( + void *data, struct ivi_wm * /*ivi_wm*/, uint32_t surface_id) +{ + auto c = static_cast(data); + c->surface_destroyed(surface_id); +} + +void surface_error_detected(void *data, struct ivi_wm * /*ivi_wm*/, uint32_t object_id, + uint32_t error_code, const char *error_text) +{ + static_cast(data)->surface_error_detected( + object_id, error_code, error_text); +} + +void surface_size_changed( + void *data, struct ivi_wm * /*ivi_wm*/, uint32_t surface_id, + int32_t width, int32_t height) +{ + auto c = static_cast(data); + c->surface_size_changed(surface_id, width, height); +} + +void surface_stats_received(void *data, struct ivi_wm * /*ivi_wm*/, + uint32_t surface_id, uint32_t frame_count, uint32_t pid) +{ + auto c = static_cast(data); + c->surface_stats_received(surface_id, frame_count, pid); +} + +void surface_added_to_layer(void *data, struct ivi_wm * /*ivi_wm*/, + uint32_t layer_id, uint32_t surface_id) +{ + auto c = static_cast(data); + c->surface_added_to_layer(layer_id, surface_id); +} + +void layer_visibility_changed(void *data, struct ivi_wm * /*ivi_wm*/, + uint32_t layer_id, int32_t visibility) +{ + auto c = static_cast(data); + c->layer_visibility_changed(layer_id, visibility); +} + +void layer_opacity_changed(void *data, struct ivi_wm * /*ivi_wm*/, + uint32_t layer_id, wl_fixed_t opacity) +{ + auto c = static_cast(data); + c->layer_opacity_changed(layer_id, float(wl_fixed_to_double(opacity))); +} + +void layer_source_rectangle_changed( + void *data, struct ivi_wm * /*ivi_wm*/, uint32_t layer_id, + int32_t x, int32_t y, int32_t width, int32_t height) +{ + auto c = static_cast(data); + c->layer_source_rectangle_changed(layer_id, x, y, width, height); +} + +void layer_destination_rectangle_changed( + void *data, struct ivi_wm * /*ivi_wm*/, uint32_t layer_id, + int32_t x, int32_t y, int32_t width, int32_t height) +{ + auto c = static_cast(data); + c->layer_destination_rectangle_changed(layer_id, x, y, width, height); +} + +void layer_created(void *data, struct ivi_wm * /*ivi_wm*/, + uint32_t id_layer) +{ + static_cast(data)->layer_created(id_layer); +} + +void layer_destroyed(void *data, struct ivi_wm * /*ivi_wm*/, uint32_t layer_id) +{ + auto c = static_cast(data); + c->layer_destroyed(layer_id); +} + +void layer_error_detected(void *data, struct ivi_wm * /*ivi_wm*/, uint32_t object_id, + uint32_t error_code, const char *error_text) +{ + static_cast(data)->layer_error_detected( + object_id, error_code, error_text); +} + +constexpr struct ivi_wm_listener listener = { + surface_visibility_changed, + layer_visibility_changed, + surface_opacity_changed, + layer_opacity_changed, + surface_source_rectangle_changed, + layer_source_rectangle_changed, + surface_destination_rectangle_changed, + layer_destination_rectangle_changed, + surface_created, + layer_created, + surface_destroyed, + layer_destroyed, + surface_error_detected, + layer_error_detected, + surface_size_changed, + surface_stats_received, + surface_added_to_layer, +}; + +void screen_created(void *data, struct ivi_wm_screen *ivi_wm_screen, uint32_t id) +{ + static_cast(data)->screen_created((struct screen *)data, id); +} + +void layer_added(void *data, + struct ivi_wm_screen *ivi_wm_screen, + uint32_t layer_id) +{ + HMI_DEBUG("added layer_id:%d", layer_id); +} + +void connector_name(void *data, + struct ivi_wm_screen *ivi_wm_screen, + const char *process_name) +{ + HMI_DEBUG("process_name:%s", process_name); +} + +void screen_error(void *data, + struct ivi_wm_screen *ivi_wm_screen, + uint32_t error, + const char *message) +{ + HMI_DEBUG("screen error:%d message:%s", error, message); +} + +constexpr struct ivi_wm_screen_listener screen_listener = { + screen_created, + layer_added, + connector_name, + screen_error, +}; +} // namespace + +/** + * surface + */ +surface::surface(uint32_t i, struct controller *c) + : controller_child(c, i) +{ + this->parent->add_proxy_to_sid_mapping(this->parent->proxy.get(), i); +} + +void surface::set_visibility(uint32_t visibility) +{ + HMI_DEBUG("compositor::surface id:%d v:%d", this->id, visibility); + ivi_wm_set_surface_visibility(this->parent->proxy.get(), this->id, visibility); +} + +void surface::set_source_rectangle(int32_t x, int32_t y, + int32_t width, int32_t height) +{ + ivi_wm_set_surface_source_rectangle(this->parent->proxy.get(), this->id, + x, y, width, height); +} + +void surface::set_destination_rectangle(int32_t x, int32_t y, + int32_t width, int32_t height) +{ + ivi_wm_set_surface_destination_rectangle(this->parent->proxy.get(), this->id, + x, y, width, height); +} + +/** + * layer + */ +layer::layer(uint32_t i, struct controller *c) : layer(i, 0, 0, c) {} + +layer::layer(uint32_t i, int32_t w, int32_t h, struct controller *c) + : controller_child(c, i) +{ + this->parent->add_proxy_to_lid_mapping(this->parent->proxy.get(), i); + ivi_wm_create_layout_layer(c->proxy.get(), i, w, h); +} + +void layer::set_visibility(uint32_t visibility) +{ + ivi_wm_set_layer_visibility(this->parent->proxy.get(), this->id, visibility); +} + +void layer::set_destination_rectangle(int32_t x, int32_t y, + int32_t width, int32_t height) +{ + ivi_wm_set_layer_destination_rectangle(this->parent->proxy.get(), this->id, + x, y, width, height); +} + +void layer::add_surface(uint32_t surface_id) +{ + ivi_wm_layer_add_surface(this->parent->proxy.get(), this->id, surface_id); +} + +void layer::remove_surface(uint32_t surface_id) +{ + ivi_wm_layer_remove_surface(this->parent->proxy.get(), this->id, surface_id); +} + +/** + * screen + */ +screen::screen(uint32_t i, struct controller *c, struct wl_output *o) + : wayland_proxy(ivi_wm_create_screen(c->proxy.get(), o)), + controller_child(c, i) +{ + HMI_DEBUG("compositor::screen @ %p id %u o %p", this->proxy.get(), i, o); + + // Add listener for screen + ivi_wm_screen_add_listener(this->proxy.get(), &screen_listener, this); +} + +void screen::clear() { ivi_wm_screen_clear(this->proxy.get()); } + +void screen::screen_created(struct screen *screen, uint32_t id) +{ + HMI_DEBUG("compositor::screen @ %p screen %u (%x) @ %p", this->proxy.get(), + id, id, screen); + this->id = id; + this->parent->screens[id] = screen; +} + +void screen::set_render_order(std::vector const &ro) +{ + std::size_t i; + + // Remove all layers from the screen render order + ivi_wm_screen_clear(this->proxy.get()); + + for (i = 0; i < ro.size(); i++) + { + HMI_DEBUG("compositor::screen @ %p add layer %u", this->proxy.get(), ro[i]); + // Add the layer to screen render order at nearest z-position + ivi_wm_screen_add_layer(this->proxy.get(), ro[i]); + } +} + +/** + * controller + */ +controller::controller(struct wl_registry *r, uint32_t name, uint32_t version) + : wayland_proxy( + wl_registry_bind(r, name, &ivi_wm_interface, version)), + output_size{} +{ + ivi_wm_add_listener(this->proxy.get(), &listener, this); +} + +void controller::layer_create(uint32_t id, int32_t w, int32_t h) +{ + this->layers[id] = std::make_unique(id, w, h, this); +} + +void controller::surface_create(uint32_t id) +{ + this->surfaces[id] = std::make_unique(id, this); + + // TODO: If Clipping is necessary, this process should be modified. + { + // Set surface type:IVI_WM_SURFACE_TYPE_DESKTOP) + // for resizing wayland surface when switching from split to full surface. + ivi_wm_set_surface_type(this->proxy.get(), id, IVI_WM_SURFACE_TYPE_DESKTOP); + + // Set source reactangle even if we should not need to set it + // for enable setting for destination region. + this->surfaces[id]->set_source_rectangle(0, 0, this->output_size.w, this->output_size.h); + + // Flush display + this->display->flush(); + } +} + +void controller::create_screen(struct wl_output *output) +{ + // TODO: screen id is 0 (WM manages one screen for now) + this->screen = std::make_unique(0, this, output); +} + +void controller::get_surface_properties(uint32_t surface_id, int param) +{ + ivi_wm_surface_get(this->proxy.get(), surface_id, param); +} + +void controller::layer_created(uint32_t id) +{ + HMI_DEBUG("compositor::controller @ %p layer %u (%x)", this->proxy.get(), id, id); + if (this->layers.find(id) != this->layers.end()) + { + HMI_DEBUG("WindowManager has created layer %u (%x) already", id, id); + } + else + { + this->layers[id] = std::make_unique(id, this); + } +} + +void controller::layer_error_detected(uint32_t object_id, + uint32_t error_code, const char *error_text) +{ + HMI_DEBUG("compositor::controller @ %p error o %d c %d text %s", + this->proxy.get(), object_id, error_code, error_text); +} + +void controller::surface_visibility_changed(uint32_t id, int32_t visibility) +{ + HMI_DEBUG("compositor::surface %s @ %d v %i", __func__, id, + visibility); + this->sprops[id].visibility = visibility; + this->chooks->surface_visibility(id, visibility); +} + +void controller::surface_opacity_changed(uint32_t id, float opacity) +{ + HMI_DEBUG("compositor::surface %s @ %d o %f", + __func__, id, opacity); + this->sprops[id].opacity = opacity; +} + +void controller::surface_source_rectangle_changed(uint32_t id, int32_t x, + int32_t y, int32_t width, + int32_t height) +{ + HMI_DEBUG("compositor::surface %s @ %d x %i y %i w %i h %i", __func__, + id, x, y, width, height); + this->sprops[id].src_rect = rect{width, height, x, y}; +} + +void controller::surface_destination_rectangle_changed(uint32_t id, int32_t x, + int32_t y, int32_t width, + int32_t height) +{ + HMI_DEBUG("compositor::surface %s @ %d x %i y %i w %i h %i", __func__, + id, x, y, width, height); + this->sprops[id].dst_rect = rect{width, height, x, y}; + this->chooks->surface_destination_rectangle(id, x, y, width, height); +} + +void controller::surface_size_changed(uint32_t id, int32_t width, + int32_t height) +{ + HMI_DEBUG("compositor::surface %s @ %d w %i h %i", __func__, id, + width, height); + this->sprops[id].size = size{uint32_t(width), uint32_t(height)}; + this->surfaces[id]->set_source_rectangle(0, 0, width, height); +} + +void controller::surface_added_to_layer(uint32_t layer_id, uint32_t surface_id) +{ + HMI_DEBUG("compositor::surface %s @ %d l %u", + __func__, layer_id, surface_id); +} + +void controller::surface_stats_received(uint32_t surface_id, + uint32_t frame_count, uint32_t pid) +{ + HMI_DEBUG("compositor::surface %s @ %d f %u pid %u", + __func__, surface_id, frame_count, pid); + this->sprops[surface_id].pid = pid; +} + +void controller::surface_created(uint32_t id) +{ + HMI_DEBUG("compositor::controller @ %p surface %u (%x)", this->proxy.get(), id, + id); + if (this->surfaces.find(id) == this->surfaces.end()) + { + this->surfaces[id] = std::make_unique(id, this); + this->chooks->surface_created(id); + + // Set surface type:IVI_WM_SURFACE_TYPE_DESKTOP) + // for resizing wayland surface when switching from split to full surface. + ivi_wm_set_surface_type(this->proxy.get(), id, IVI_WM_SURFACE_TYPE_DESKTOP); + + // Flush display + this->display->flush(); + } +} + +void controller::surface_destroyed(uint32_t surface_id) +{ + HMI_DEBUG("compositor::surface %s @ %d", __func__, surface_id); + this->chooks->surface_removed(surface_id); + this->sprops.erase(surface_id); + this->surfaces.erase(surface_id); +} + +void controller::surface_error_detected(uint32_t object_id, + uint32_t error_code, const char *error_text) +{ + HMI_DEBUG("compositor::controller @ %p error o %d c %d text %s", + this->proxy.get(), object_id, error_code, error_text); +} + +void controller::layer_visibility_changed(uint32_t layer_id, int32_t visibility) +{ + HMI_DEBUG("compositor::layer %s @ %d v %i", __func__, layer_id, visibility); + this->lprops[layer_id].visibility = visibility; +} + +void controller::layer_opacity_changed(uint32_t layer_id, float opacity) +{ + HMI_DEBUG("compositor::layer %s @ %d o %f", __func__, layer_id, opacity); + this->lprops[layer_id].opacity = opacity; +} + +void controller::layer_source_rectangle_changed(uint32_t layer_id, + int32_t x, int32_t y, + int32_t width, int32_t height) +{ + HMI_DEBUG("compositor::layer %s @ %d x %i y %i w %i h %i", + __func__, layer_id, x, y, width, height); + this->lprops[layer_id].src_rect = rect{width, height, x, y}; +} + +void controller::layer_destination_rectangle_changed(uint32_t layer_id, + int32_t x, int32_t y, + int32_t width, int32_t height) +{ + HMI_DEBUG("compositor::layer %s @ %d x %i y %i w %i h %i", + __func__, layer_id, x, y, width, height); + this->lprops[layer_id].dst_rect = rect{width, height, x, y}; +} + +void controller::layer_destroyed(uint32_t layer_id) +{ + HMI_DEBUG("compositor::layer %s @ %d", __func__, layer_id); + this->lprops.erase(layer_id); + this->layers.erase(layer_id); +} + +void controller::add_proxy_to_sid_mapping(struct ivi_wm *p, + uint32_t id) +{ + HMI_DEBUG("Add surface proxy mapping for %p (%u)", p, id); + this->surface_proxy_to_id[uintptr_t(p)] = id; + this->sprops[id].id = id; +} + +void controller::remove_proxy_to_sid_mapping(struct ivi_wm *p) +{ + HMI_DEBUG("Remove surface proxy mapping for %p", p); + this->surface_proxy_to_id.erase(uintptr_t(p)); +} + +void controller::add_proxy_to_lid_mapping(struct ivi_wm *p, + uint32_t id) +{ + HMI_DEBUG("Add layer proxy mapping for %p (%u)", p, id); + this->layer_proxy_to_id[uintptr_t(p)] = id; + this->lprops[id].id = id; +} + +void controller::remove_proxy_to_lid_mapping(struct ivi_wm *p) +{ + HMI_DEBUG("Remove layer proxy mapping for %p", p); + this->layer_proxy_to_id.erase(uintptr_t(p)); +} + +void controller::add_proxy_to_id_mapping(struct wl_output *p, uint32_t id) +{ + HMI_DEBUG("Add screen proxy mapping for %p (%u)", p, id); + this->screen_proxy_to_id[uintptr_t(p)] = id; +} + +void controller::remove_proxy_to_id_mapping(struct wl_output *p) +{ + HMI_DEBUG("Remove screen proxy mapping for %p", p); + this->screen_proxy_to_id.erase(uintptr_t(p)); +} + +} // namespace compositor diff --git a/demo3/common/agl-service-windowmanager/src/wayland_ivi_wm.hpp b/demo3/common/agl-service-windowmanager/src/wayland_ivi_wm.hpp new file mode 100644 index 0000000..d8915a1 --- /dev/null +++ b/demo3/common/agl-service-windowmanager/src/wayland_ivi_wm.hpp @@ -0,0 +1,327 @@ +/* + * Copyright (c) 2017 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WM_WAYLAND_HPP +#define WM_WAYLAND_HPP + +#include "controller_hooks.hpp" +#include "ivi-wm-client-protocol.h" +#include "util.hpp" + +#include +#include +#include +#include + +/** + * @struct wayland_proxy + */ +template +struct wayland_proxy +{ + std::unique_ptr> proxy; + wayland_proxy(wayland_proxy const &) = delete; + wayland_proxy &operator=(wayland_proxy const &) = delete; + wayland_proxy(void *p) + : wayland_proxy(p, + reinterpret_cast(wl_proxy_destroy)) {} + wayland_proxy(void *p, std::function &&p_del) + : proxy(std::unique_ptr>( + static_cast(p), p_del)) {} +}; + +/** + * namespace wl + */ +namespace wl +{ + +/** + * @struct registry + */ +struct registry : public wayland_proxy +{ + typedef std::function binder; + std::unordered_map bindings; + + registry(registry const &) = delete; + registry &operator=(registry const &) = delete; + registry(struct wl_display *d); + + void add_global_handler(char const *iface, binder bind); + + // Events + void global_created(uint32_t name, char const *iface, uint32_t v); + void global_removed(uint32_t name); +}; + +/** + * @struct display + */ +struct display +{ + std::unique_ptr d; + struct registry r; + + display(display const &) = delete; + display &operator=(display const &) = delete; + display(); + bool ok() const; + void roundtrip(); + int dispatch(); + int dispatch_pending(); + int read_events(); + void flush(); + int get_fd() const; + int get_error(); + + // Lets just proxy this for the registry + inline void add_global_handler(char const *iface, registry::binder bind) + { + this->r.add_global_handler(iface, bind); + } +}; + +/** + * @struct output + */ +struct output : public wayland_proxy +{ + int width{}; + int height{}; + int physical_width{}; + int physical_height{}; + int refresh{}; + int transform{}; + + output(output const &) = delete; + output &operator=(output const &) = delete; + output(struct wl_registry *r, uint32_t name, uint32_t v); + + // Events + void geometry(int32_t x, int32_t y, int32_t pw, int32_t ph, int32_t subpel, + char const *make, char const *model, int32_t tx); + void mode(uint32_t flags, int32_t w, int32_t h, int32_t r); + void done(); + void scale(int32_t factor); +}; +} // namespace wl + +/** + * namespace compositor + */ +namespace compositor +{ + +struct size +{ + uint32_t w, h; +}; + +struct rect +{ + int32_t w, h; + int32_t x, y; +}; + +static const constexpr rect full_rect = rect{-1, -1, 0, 0}; + +inline bool operator==(struct rect a, struct rect b) +{ + return a.w == b.w && a.h == b.h && a.x == b.x && a.y == b.y; +} + +struct controller; + +struct controller_child +{ + struct controller *parent; + uint32_t id; + + controller_child(controller_child const &) = delete; + controller_child &operator=(controller_child const &) = delete; + controller_child(struct controller *c, uint32_t i) : parent(c), id(i) {} + virtual ~controller_child() {} +}; + +struct surface_properties +{ + uint32_t id; // let's just save an ID here too + struct rect dst_rect; + struct rect src_rect; + struct size size; + int32_t orientation; + int32_t visibility; + float opacity; + uint32_t pid; +}; + +/** + * @struct surface + */ +struct surface : public controller_child +{ + surface(surface const &) = delete; + surface &operator=(surface const &) = delete; + surface(uint32_t i, struct controller *c); + + // Requests + void set_visibility(uint32_t visibility); + void set_source_rectangle(int32_t x, int32_t y, + int32_t width, int32_t height); + void set_destination_rectangle(int32_t x, int32_t y, + int32_t width, int32_t height); +}; + +/** + * @struct layer + */ +struct layer : public controller_child +{ + layer(layer const &) = delete; + layer &operator=(layer const &) = delete; + layer(uint32_t i, struct controller *c); + layer(uint32_t i, int32_t w, int32_t h, struct controller *c); + + // Requests + void set_visibility(uint32_t visibility); + void set_destination_rectangle(int32_t x, int32_t y, + int32_t width, int32_t height); + void add_surface(uint32_t surface_id); + void remove_surface(uint32_t surface_id); +}; + +/** + * @struct screen + */ +struct screen : public wayland_proxy, + public controller_child +{ + screen(screen const &) = delete; + screen &operator=(screen const &) = delete; + screen(uint32_t i, struct controller *c, struct wl_output *o); + + void clear(); + void screen_created(struct screen *screen, uint32_t id); + void set_render_order(std::vector const &ro); +}; + +/** + * @struct controller + */ +struct controller : public wayland_proxy +{ + // This controller is still missing ivi-input + + typedef std::unordered_map proxy_to_id_map_type; + typedef std::unordered_map> + surface_map_type; + typedef std::unordered_map> + layer_map_type; + typedef std::unordered_map screen_map_type; + typedef std::unordered_map props_map; + + // HACK: + // The order of these member is mandatory, as when objects are destroyed + // they will call their parent (that's us right here!) and remove their + // proxy-to-id mapping. I.e. the *_proxy_to_id members need to be valid + // when the surfaces/layers/screens maps are destroyed. This sucks, but + // I cannot see a better solution w/o globals or some other horrible + // call-our-parent construct. + proxy_to_id_map_type surface_proxy_to_id; + proxy_to_id_map_type layer_proxy_to_id; + proxy_to_id_map_type screen_proxy_to_id; + + props_map sprops; + props_map lprops; + + surface_map_type surfaces; + layer_map_type layers; + screen_map_type screens; + + std::unique_ptr screen; + + size output_size; // Display size[pixel] + size physical_size; // Display size[mm] + + // Scale for conversion CSS PX -> DP(Device Pixel) + double scale; + + wm::controller_hooks *chooks; + + struct wl::display *display; + + void add_proxy_to_sid_mapping(struct ivi_wm *p, uint32_t id); + void remove_proxy_to_sid_mapping(struct ivi_wm *p); + + void add_proxy_to_lid_mapping(struct ivi_wm *p, uint32_t id); + void remove_proxy_to_lid_mapping(struct ivi_wm *p); + + void add_proxy_to_id_mapping(struct wl_output *p, uint32_t id); + void remove_proxy_to_id_mapping(struct wl_output *p); + + bool surface_exists(uint32_t id) const + { + return this->surfaces.find(id) != this->surfaces.end(); + } + + bool layer_exists(uint32_t id) const + { + return this->layers.find(id) != this->layers.end(); + } + + controller(struct wl_registry *r, uint32_t name, uint32_t version); + + // Requests + void commit_changes() const + { + ivi_wm_commit_changes(this->proxy.get()); + } + void layer_create(uint32_t id, int32_t w, int32_t h); + void surface_create(uint32_t id); + void create_screen(struct wl_output *output); + void get_surface_properties(uint32_t surface_id, int param = 0); + + // Events + void surface_visibility_changed(uint32_t id, int32_t visibility); + void surface_opacity_changed(uint32_t id, float opacity); + void surface_source_rectangle_changed(uint32_t id, int32_t x, int32_t y, + int32_t width, int32_t height); + void surface_destination_rectangle_changed(uint32_t id, int32_t x, int32_t y, + int32_t width, int32_t height); + void surface_created(uint32_t id); + void surface_destroyed(uint32_t surface_id); + void surface_error_detected(uint32_t object_id, + uint32_t error_code, char const *error_text); + void surface_size_changed(uint32_t id, int32_t width, int32_t height); + void surface_stats_received(uint32_t surface_id, + uint32_t frame_count, uint32_t pid); + void surface_added_to_layer(uint32_t layer_id, uint32_t surface_id); + + void layer_visibility_changed(uint32_t layer_id, int32_t visibility); + void layer_opacity_changed(uint32_t layer_id, float opacity); + void layer_source_rectangle_changed(uint32_t layer_id, int32_t x, int32_t y, + int32_t width, int32_t height); + void layer_destination_rectangle_changed(uint32_t layer_id, int32_t x, int32_t y, + int32_t width, int32_t height); + void layer_created(uint32_t id); + void layer_destroyed(uint32_t layer_id); + void layer_error_detected(uint32_t object_id, + uint32_t error_code, char const *error_text); +}; +} // namespace compositor + +#endif // !WM_WAYLAND_HPP diff --git a/demo3/common/agl-service-windowmanager/src/window_manager.cpp b/demo3/common/agl-service-windowmanager/src/window_manager.cpp new file mode 100644 index 0000000..f209f12 --- /dev/null +++ b/demo3/common/agl-service-windowmanager/src/window_manager.cpp @@ -0,0 +1,2326 @@ +/* + * Copyright (c) 2017 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include "window_manager.hpp" +#include "json_helper.hpp" +#include "applist.hpp" + +extern "C" +{ +#include +} + +using std::string; +using std::vector; + +namespace wm +{ + +static const uint64_t kTimeOut = 3ULL; /* 3s */ + +/* DrawingArea name used by "{layout}.{area}" */ +const char kNameLayoutNormal[] = "normal"; +const char kNameLayoutSplit[] = "split"; +const char kNameAreaFull[] = "full"; +const char kNameAreaMain[] = "main"; +const char kNameAreaSub[] = "sub"; + +/* Key for json obejct */ +const char kKeyDrawingName[] = "drawing_name"; +const char kKeyDrawingArea[] = "drawing_area"; +const char kKeyDrawingRect[] = "drawing_rect"; +const char kKeyX[] = "x"; +const char kKeyY[] = "y"; +const char kKeyWidth[] = "width"; +const char kKeyHeight[] = "height"; +const char kKeyWidthPixel[] = "width_pixel"; +const char kKeyHeightPixel[] = "height_pixel"; +const char kKeyWidthMm[] = "width_mm"; +const char kKeyHeightMm[] = "height_mm"; +const char kKeyScale[] = "scale"; +const char kKeyIds[] = "ids"; + +static const char kPathOldRolesConfigFile[] = "/etc/old_roles.json"; + +static sd_event_source *g_timer_ev_src = nullptr; +static AppList g_app_list; +static WindowManager *g_context; + +struct AfbClosure { +public: + AfbClosure(unsigned pid, unsigned ppid, unsigned surface) + : pid(pid), ppid(ppid), surface(surface) {} + ~AfbClosure() = default; + unsigned pid; + unsigned ppid; + unsigned surface; +}; + +namespace +{ + +static int processTimerHandler(sd_event_source *s, uint64_t usec, void *userdata) +{ + HMI_NOTICE("Time out occurs because the client replys endDraw slow, so revert the request"); + reinterpret_cast(userdata)->timerHandler(); + return 0; +} + +static void onStateTransitioned(vector actions) +{ + g_context->startTransitionWrapper(actions); +} + +static void onError() +{ + g_context->processError(WMError::LAYOUT_CHANGE_FAIL); +} + +static void onReceiveRemoteRequest(json_object *data) +{ + g_context->processForRemoteRequest(data); +} +} // namespace + +/** + * WindowManager Impl + */ +WindowManager::WindowManager() + : wmcon{}, + id_alloc{} +{ + const char *path = getenv("AFM_APP_INSTALL_DIR"); + if (!path) + { + HMI_ERROR("AFM_APP_INSTALL_DIR is not defined"); + } + string root = path; + + // TODO: ECU name should be decide by config file + // Get mode and decide ECU name + string ecu_name = this->wmcon.getEcuName(); + + this->lc = std::make_shared(root, ecu_name); + + HMI_DEBUG("Layer Controller initialized"); +} + +int WindowManager::init() +{ + LayerControlCallbacks lmcb; + lmcb.surfaceCreated = [&](unsigned pid, unsigned surface){ + this->surface_created(pid, surface); + }; + lmcb.surfaceDestroyed = [&](unsigned surface){ + this->surface_removed(surface); + }; + this->lc->init(lmcb); + + // TODO: application requests by old role, + // so create role map (old, new) + // Load old_roles config file + this->loadOldRolesConfigFile(); + + // Initialize LowCanClient + this->lcc.initialize(); + + // Store my context for calling callback from PolicyManager + g_context = this; + + // Initialize PMWrapper + this->pmw.initialize(this->wmcon.getEcuName()); + + // Register callback to PolicyManager + this->pmw.registerCallback(onStateTransitioned, onError); + + // Initialize WMConnection + this->wmcon.initialize(); + + // Register callback to WMConnection + this->wmcon.registerCallback(onReceiveRemoteRequest); + + // Make afb event + for (int i = Event_Val_Min; i <= Event_Val_Max; i++) + { + map_afb_event[kListEventName[i]] = afb_daemon_make_event(kListEventName[i]); + } + + const struct rect css_bg = this->lc->getAreaSize("fullscreen"); + Screen screen = this->lc->getScreenInfo(); + rectangle dp_bg(screen.width(), screen.height()); + + dp_bg.set_aspect(static_cast(css_bg.w) / css_bg.h); + dp_bg.fit(screen.width(), screen.height()); + dp_bg.center(screen.width(), screen.height()); + HMI_DEBUG("SCALING: CSS BG(%dx%d) -> DDP %dx%d,(%dx%d)", + css_bg.w, css_bg.h, dp_bg.left(), dp_bg.top(), dp_bg.width(), dp_bg.height()); + + double scale = static_cast(dp_bg.height()) / css_bg.h; + this->lc->setupArea(dp_bg, scale); + + return 0; //init_layers(); +} + +result WindowManager::api_request_surface(char const *appid, char const *drawing_name) +{ + // TODO: application requests by old role, + // so convert role old to new + const char *role = this->convertRoleOldToNew(drawing_name); + string l_name; + string s_appid = appid; + string s_role = role; + + if(!g_app_list.contains(s_appid)) + { + // auto lid = this->layers.get_layer_id(string(role)); + unsigned l_id = this->lc->getNewLayerID(s_role, &l_name); + if (l_id == 0) + { + /** + * register drawing_name as fallback and make it displayed. + */ + l_id = this->lc->getNewLayerID("fallback", &l_name); + HMI_DEBUG("%s is not registered in layers.json, then fallback as normal app", role); + if (l_id == 0) + { + return Err("Designated role does not match any role, fallback is disabled"); + } + } + + // TODO: remote layer size is fixed value + if ("Remote" == l_name) + { + this->lc->createNewRemoteLayer(l_id); + } + else + { + this->lc->createNewLayer(l_id); + } + + // add client into the db + g_app_list.addClient(s_appid, l_id, s_role); + } + + // generate surface ID for ivi-shell application + + auto rname = this->id_alloc.lookup(string(role)); + if (!rname) + { + // name does not exist yet, allocate surface id... + auto id = int(this->id_alloc.generate_id(role)); + this->tmp_surface2app[id] = {s_appid, 0}; + + // Work Around + HMI_NOTICE("WORK AROUND: add surface on request surface"); + auto client = g_app_list.lookUpClient(s_appid); + client->addSurface(id); + /////////////// + + // Set role map of (new, old) + this->rolenew2old[role] = string(drawing_name); + + return Ok(id); + } + + // Check currently registered drawing names if it is already there. + return Err("Surface already present"); +} + +char const *WindowManager::api_request_surface(char const *appid, char const *drawing_name, + char const *ivi_id) +{ + unsigned sid = std::stol(ivi_id); + + HMI_DEBUG("This API(requestSurfaceXDG) is for XDG Application using runXDG"); + /* + * IVI-shell doesn't send surface_size event via ivi-wm protocol + * if the application is using XDG surface. + * So WM has to set surface size with original size here + */ + WMError ret = this->lc->setXDGSurfaceOriginSize(sid); + if(ret != SUCCESS) + { + HMI_ERROR("%s", errorDescription(ret)); + HMI_WARNING("The main user of this API is runXDG"); + return "fail"; + } + + // TODO: application requests by old role, + // so convert role old to new + const char *role = this->convertRoleOldToNew(drawing_name); + string s_role = role; + string s_appid = appid; + string l_name; + + if(!g_app_list.contains(s_appid)) + { + // auto lid = this->layers.get_layer_id(string(role)); + unsigned l_id = this->lc->getNewLayerID(s_role, &l_name); + if (l_id == 0) + { + /** + * register drawing_name as fallback and make it displayed. + */ + l_id = this->lc->getNewLayerID("fallback", &l_name); + HMI_DEBUG("%s is not registered in layers.json, then fallback as normal app", role); + if (l_id == 0) + { + return "Designated role does not match any role, fallback is disabled"; + } + } + + // TODO: remote layer size is fixed value + if ("Remote" == l_name) + { + this->lc->createNewRemoteLayer(l_id); + } + else + { + this->lc->createNewLayer(l_id); + } + + // add client into the db + g_app_list.addClient(s_appid, l_id, s_role); + } + + auto rname = this->id_alloc.lookup(s_role); + if (rname) + { + return "Surface already present"; + } + + // register pair drawing_name and ivi_id + this->id_alloc.register_name_id(role, sid); + + auto client = g_app_list.lookUpClient(s_appid); + client->addSurface(sid); + + // Set role map of (new, old) + this->rolenew2old[role] = string(drawing_name); + + return nullptr; +} + +bool WindowManager::api_set_role(char const *appid, char const *drawing_name) +{ + bool ret = false; + + // TODO: application requests by old role, + // so convert role old to new + const char *role = this->convertRoleOldToNew(drawing_name); + string s_role = role; + string s_appid = appid; + string l_name; + + // Create WMClient + if(!g_app_list.contains(s_appid)) + { + // auto lid = this->layers.get_layer_id(string(role)); + unsigned l_id = this->lc->getNewLayerID(s_role, &l_name); + if (l_id == 0) + { + /** + * register drawing_name as fallback and make it displayed. + */ + l_id = this->lc->getNewLayerID("fallback", &l_name); + HMI_DEBUG("%s is not registered in layers.json, then fallback as normal app", role); + if (l_id == 0) + { + HMI_ERROR("Designated role does not match any role, fallback is disabled"); + return ret; + } + } + + // TODO: remote layer size is fixed value + if ("Remote" == l_name) + { + this->lc->createNewRemoteLayer(l_id); + } + else + { + this->lc->createNewLayer(l_id); + } + + // add client into the db + g_app_list.addClient(s_appid, l_id, s_role); + // Set role map of (new, old) + this->rolenew2old[role] = string(drawing_name); + } + + // for(auto itr = this->tmp_surface2app.begin(); + // itr != this->tmp_surface2app.end() ; ++itr) + // { + for(auto& x : this->tmp_surface2app) + { + if(x.second.appid == s_appid) + { + unsigned surface = x.first; + auto client = g_app_list.lookUpClient(s_appid); + client->addSurface(surface); + this->tmp_surface2app.erase(surface); + this->id_alloc.register_name_id(s_role, surface); + break; + } + } + +/* if(0 != pid){ + // search floating surfaceID from pid if pid is designated. + wm_err = g_app_list.popFloatingSurface(pid, &surface); + } + else{ + // get floating surface with appid. If WM queries appid from pid, + // WM can bind surface and role with appid(not implemented yet) + //wm_err = g_app_list.popFloatingSurface(id); + } + if(wm_err != WMError::SUCCESS){ + HMI_ERROR("No floating surface for app: %s", id.c_str()); + g_app_list.addFloatingClient(id, lid, role); + HMI_NOTICE("%s : Waiting for surface creation", id.c_str()); + return ret; + } + + ret = true; + if (g_app_list.contains(id)) + { + HMI_INFO("Add role: %s with surface: %d. Client %s has multi surfaces.", + role.c_str(), surface, id.c_str()); + auto client = g_app_list.lookUpClient(id); + client->appendRole(role); + } + else{ + HMI_INFO("Create new client: %s, surface: %d into layer: %d with role: %s", + id.c_str(), surface, lid, role.c_str()); + g_app_list.addClient(id, lid, role); + } */ + + // register pair drawing_name and ivi_id + + return true; +} + +void WindowManager::api_activate_surface(char const *appid, char const *drawing_name, + char const *drawing_area, const reply_func &reply) +{ + // TODO: application requests by old role, + // so convert role old to new + const char *c_role = this->convertRoleOldToNew(drawing_name); + + string id = appid; + string role = c_role; + string area = drawing_area; + + if(!g_app_list.contains(id)) + { + reply("app doesn't request 'requestSurface' or 'setRole' yet"); + return; + } + auto client = g_app_list.lookUpClient(id); + + // unsigned srfc = client->surfaceID(role); + // unsigned layer = client->layerID(); + + // g_app_list.removeFloatingSurface(client->surfaceID()); + // g_app_list.removeFloatingSurface(client); + + Task task = Task::TASK_ALLOCATE; + unsigned req_num = 0; + WMError ret = WMError::UNKNOWN; + + ret = this->setRequest(id, role, area, task, &req_num); + + //vector current_states = this->lc->getCurrentStates(); + // ret = this->setRequest(id, role, area, task, current_states, &req_num); + + if(ret != WMError::SUCCESS) + { + HMI_ERROR(errorDescription(ret)); + reply("Failed to set request"); + return; + } + + reply(nullptr); + if (req_num != g_app_list.currentRequestNumber()) + { + // Add request, then invoked after the previous task is finished + HMI_SEQ_DEBUG(req_num, "request is accepted"); + return; + } + + /* + * Do allocate tasks + */ + ret = this->checkPolicy(req_num); + + if (ret != WMError::SUCCESS) + { + //this->emit_error() + HMI_SEQ_ERROR(req_num, errorDescription(ret)); + g_app_list.removeRequest(req_num); + this->processNextRequest(); + } +} + +void WindowManager::api_activate_surface_for_slave( + char const *appid, char const *drawing_name, + char const *drawing_area, const reply_func &reply) +{ + // TODO: application requests by old role, + // so convert role old to new + const char *c_role = this->convertRoleOldToNew(drawing_name); + + string id = appid; + string role = c_role; + string area = drawing_area; + + if(!g_app_list.contains(id)) + { + // Request surface of app in slave to register app information + this->api_request_surface(appid, drawing_name); + + // Set role of app in slave to register app information + this->api_set_role(appid, drawing_name); + } + auto client = g_app_list.lookUpClient(id); + + // unsigned srfc = client->surfaceID(role); + // unsigned layer = client->layerID(); + + // g_app_list.removeFloatingSurface(client->surfaceID()); + // g_app_list.removeFloatingSurface(client); + + Task task = Task::TASK_ALLOCATE; + unsigned req_num = 0; + WMError ret = WMError::UNKNOWN; + + ret = this->setRequestForSlave(id, role, area, task, &req_num); + + //vector current_states = this->lc->getCurrentStates(); + // ret = this->setRequest(id, role, area, task, current_states, &req_num); + + if(ret != WMError::SUCCESS) + { + HMI_ERROR(errorDescription(ret)); + reply("Failed to set request"); + return; + } + + reply(nullptr); + if (req_num != g_app_list.currentRequestNumber()) + { + // Add request, then invoked after the previous task is finished + HMI_SEQ_DEBUG(req_num, "request is accepted"); + return; + } + + /* + * Do allocate tasks + */ + ret = this->checkPolicyForSlave(req_num); + + if (ret != WMError::SUCCESS) + { + //this->emit_error() + HMI_SEQ_ERROR(req_num, errorDescription(ret)); + g_app_list.removeRequest(req_num); + this->processNextRequest(); + } +} + +void WindowManager::api_activate_surface_to_master( + char const *appid, char const *drawing_name, + char const *drawing_area, const reply_func &reply) +{ + // TODO: application requests by old role, + // so convert role old to new + const char *c_role = this->convertRoleOldToNew(drawing_name); + + string id = appid; + string role = c_role; + string area = drawing_area; + + if(!g_app_list.contains(id)) + { + reply("app doesn't request 'requestSurface' or 'setRole' yet"); + return; + } + auto client = g_app_list.lookUpClient(id); + + // unsigned srfc = client->surfaceID(role); + // unsigned layer = client->layerID(); + + // g_app_list.removeFloatingSurface(client->surfaceID()); + // g_app_list.removeFloatingSurface(client); + + Task task = Task::TASK_ALLOCATE; + unsigned req_num = 0; + WMError ret = WMError::UNKNOWN; + + ret = this->setRequest(id, role, area, task, &req_num); + + //vector current_states = this->lc->getCurrentStates(); + // ret = this->setRequest(id, role, area, task, current_states, &req_num); + + if(ret != WMError::SUCCESS) + { + HMI_ERROR(errorDescription(ret)); + reply("Failed to set request"); + return; + } + + reply(nullptr); + if (req_num != g_app_list.currentRequestNumber()) + { + // Add request, then invoked after the previous task is finished + HMI_SEQ_DEBUG(req_num, "request is accepted"); + return; + } + + /* + * Do allocate tasks + */ + int i_ret = this->wmcon.sendRequest("activateWindow", appid, + drawing_name, drawing_area); + if (0 > i_ret) + { + //this->emit_error() + HMI_SEQ_ERROR(req_num, errorDescription(ret)); + g_app_list.removeRequest(req_num); + this->processNextRequest(); + } + + this->setTimer(); +} + +void WindowManager::api_deactivate_surface(char const *appid, char const *drawing_name, + const reply_func &reply) +{ + // TODO: application requests by old role, + // so convert role old to new + const char *c_role = this->convertRoleOldToNew(drawing_name); + + /* + * Check Phase + */ + string id = appid; + string role = c_role; + string area = ""; //drawing_area; + Task task = Task::TASK_RELEASE; + unsigned req_num = 0; + WMError ret = WMError::UNKNOWN; + + ret = this->setRequest(id, role, area, task, &req_num); + + if (ret != WMError::SUCCESS) + { + HMI_ERROR(errorDescription(ret)); + reply("Failed to set request"); + return; + } + + reply(nullptr); + if (req_num != g_app_list.currentRequestNumber()) + { + // Add request, then invoked after the previous task is finished + HMI_SEQ_DEBUG(req_num, "request is accepted"); + return; + } + + /* + * Do allocate tasks + */ + ret = this->checkPolicy(req_num); + + if (ret != WMError::SUCCESS) + { + //this->emit_error() + HMI_SEQ_ERROR(req_num, errorDescription(ret)); + g_app_list.removeRequest(req_num); + this->processNextRequest(); + } +} + +void WindowManager::api_deactivate_surface_for_slave(char const *appid, char const *drawing_name, + const reply_func &reply) +{ + // TODO: application requests by old role, + // so convert role old to new + const char *c_role = this->convertRoleOldToNew(drawing_name); + + /* + * Check Phase + */ + string id = appid; + string role = c_role; + string area = "";//drawing_area; + Task task = Task::TASK_RELEASE; + unsigned req_num = 0; + WMError ret = WMError::UNKNOWN; + + ret = this->setRequest(id, role, area, task, &req_num); + + if (ret != WMError::SUCCESS) + { + HMI_ERROR(errorDescription(ret)); + reply("Failed to set request"); + return; + } + + reply(nullptr); + if (req_num != g_app_list.currentRequestNumber()) + { + // Add request, then invoked after the previous task is finished + HMI_SEQ_DEBUG(req_num, "request is accepted"); + return; + } + + /* + * Do allocate tasks + */ + ret = this->checkPolicyForSlave(req_num); + + if (ret != WMError::SUCCESS) + { + //this->emit_error() + HMI_SEQ_ERROR(req_num, errorDescription(ret)); + g_app_list.removeRequest(req_num); + this->processNextRequest(); + } +} + +void WindowManager::api_deactivate_surface_to_master(char const *appid, char const *drawing_name, + const reply_func &reply) +{ + // TODO: application requests by old role, + // so convert role old to new + const char *c_role = this->convertRoleOldToNew(drawing_name); + + /* + * Check Phase + */ + string id = appid; + string role = c_role; + string area = "";//drawing_area; + Task task = Task::TASK_RELEASE; + unsigned req_num = 0; + WMError ret = WMError::UNKNOWN; + + ret = this->setRequest(id, role, area, task, &req_num); + + if (ret != WMError::SUCCESS) + { + HMI_ERROR(errorDescription(ret)); + reply("Failed to set request"); + return; + } + + reply(nullptr); + if (req_num != g_app_list.currentRequestNumber()) + { + // Add request, then invoked after the previous task is finished + HMI_SEQ_DEBUG(req_num, "request is accepted"); + return; + } + + /* + * Do allocate tasks + */ + int i_ret = this->wmcon.sendRequest("deactivateWindow", appid, + drawing_name, ""); + if (0 > i_ret) + { + //this->emit_error() + HMI_SEQ_ERROR(req_num, errorDescription(ret)); + g_app_list.removeRequest(req_num); + this->processNextRequest(); + } + + this->setTimer(); +} + +void WindowManager::api_enddraw(char const *appid, char const *drawing_name) +{ + // TODO: application requests by old role, + // so convert role old to new + const char *c_role = this->convertRoleOldToNew(drawing_name); + + string id = appid; + string role = c_role; + unsigned current_req = g_app_list.currentRequestNumber(); + bool result = g_app_list.setEndDrawFinished(current_req, id, role); + + if (!result) + { + HMI_ERROR("%s is not in transition state", id.c_str()); + return; + } + + if (g_app_list.endDrawFullfilled(current_req)) + { + // do task for endDraw + this->stopTimer(); + WMError ret = this->doEndDraw(current_req); + + if(ret != WMError::SUCCESS) + { + //this->emit_error(); + + // Undo state of PolicyManager + this->pmw.undoState(); + this->lc->undoUpdate(); + } + this->emitScreenUpdated(current_req); + HMI_SEQ_INFO(current_req, "Finish request status: %s", errorDescription(ret)); + + g_app_list.removeRequest(current_req); + + this->processNextRequest(); + } + else + { + HMI_SEQ_INFO(current_req, "Wait other App call endDraw"); + return; + } +} + +void WindowManager::api_enddraw_for_remote(char const *appid, char const *drawing_name) +{ + int ret = this->wmcon.sendRequest("endDraw", appid, drawing_name, ""); + if (0 > ret) + { + //this->emit_error() + + this->pmw.undoState(); + this->lc->undoUpdate(); + + unsigned current_req = g_app_list.currentRequestNumber(); + g_app_list.removeRequest(current_req); + this->processNextRequest(); + + return; + } + + this->api_enddraw(appid, drawing_name); +} + +bool WindowManager::api_client_set_render_order(char const* appid, const vector& render_order) +{ + bool ret = false; + string id = appid; + auto client = g_app_list.lookUpClient(id); + if(client) + { + client->setRenderOrder(render_order); + } + return ret; +} + +string WindowManager::api_client_attach_service_surface + (const char* appid, const char* dest, const char* service_surface) +{ + string uuid, s_dest = dest; + auto client = g_app_list.lookUpClient(s_dest); + if(!client) + { + HMI_ERROR("Failed to look up destination [%s]", dest); + return uuid; + } + uuid = client->attachTmpServiceSurface(appid, service_surface); + this->tmp_services.emplace_back(TmpService{appid, dest, service_surface, uuid}); + return uuid; +} + +result WindowManager::api_get_display_info() +{ + Screen screen = this->lc->getScreenInfo(); + + json_object *object = json_object_new_object(); + json_object_object_add(object, kKeyWidthPixel, json_object_new_int(screen.width())); + json_object_object_add(object, kKeyHeightPixel, json_object_new_int(screen.height())); + // TODO: set size + json_object_object_add(object, kKeyWidthMm, json_object_new_int(0)); + json_object_object_add(object, kKeyHeightMm, json_object_new_int(0)); + json_object_object_add(object, kKeyScale, json_object_new_double(this->lc->scale())); + + return Ok(object); +} + +result WindowManager::api_get_area_info(char const *drawing_name) +{ + HMI_DEBUG("called"); + + // TODO: application requests by old role, + // so convert role old to new + const char *role = this->convertRoleOldToNew(drawing_name); + + // Check drawing name, surface/layer id + auto const &surface_id = this->id_alloc.lookup(string(role)); + if (!surface_id) + { + return Err("Surface does not exist"); + } + + // Set area rectangle + rect area_info = this->area_info[*surface_id]; + json_object *object = json_object_new_object(); + json_object_object_add(object, kKeyX, json_object_new_int(area_info.x)); + json_object_object_add(object, kKeyY, json_object_new_int(area_info.y)); + json_object_object_add(object, kKeyWidth, json_object_new_int(area_info.w)); + json_object_object_add(object, kKeyHeight, json_object_new_int(area_info.h)); + + return Ok(object); +} + +result WindowManager::api_get_car_info(char const *label) +{ + json_object *j_in = nullptr; + json_object *j_out = nullptr; + + if (0 == strcmp("parking_brake_status", label)) + { + // Get parking brake status + json_bool val = (this->crr_car_info.parking_brake_stt) ? TRUE : FALSE; + j_in = json_object_new_boolean(val); + } + else if (0 == strcmp("accelerator.pedal.position", label)) + { + // Get accelerator pedal position + double val = this->crr_car_info.accel_pedal_pos; + j_in = json_object_new_double(val); + } + else if (0 == strcmp("car_state", label)) + { + // Get running state + const char* val = (this->crr_car_info.running_stt) ? "run" : "stop"; + j_in = json_object_new_string(val); + } + else if (0 == strcmp("lightstatus.brake", label)) { + // Get lightstatus brake status + json_bool val = (this->crr_car_info.lightstatus_brake_stt) ? TRUE : FALSE; + j_in = json_object_new_boolean(val); + } + else + { + return Err("Car info does not exist"); + } + + // Create output object + j_out = json_object_new_object(); + json_object_object_add(j_out, "value", j_in); + + return Ok(j_out); +} + +void WindowManager::send_event(char const *evname) +{ + HMI_DEBUG("%s: %s", __func__, evname); + + int ret = afb_event_push(this->map_afb_event[evname], nullptr); + if (ret != 0) + { + HMI_DEBUG("afb_event_push: %m"); + } +} + +void WindowManager::send_event(char const *evname, char const *label) +{ + HMI_DEBUG("%s: %s(%s)", __func__, evname, label); + + json_object *j = json_object_new_object(); + json_object_object_add(j, kKeyDrawingName, json_object_new_string(label)); + + int ret = afb_event_push(this->map_afb_event[evname], j); + if (ret != 0) + { + HMI_DEBUG("afb_event_push failed: %m"); + } +} + +void WindowManager::send_event(char const *evname, char const *label, char const *area, + int x, int y, int w, int h) +{ + HMI_DEBUG("%s: %s(%s, %s) x:%d y:%d w:%d h:%d", + __func__, evname, label, area, x, y, w, h); + + json_object *j_rect = json_object_new_object(); + json_object_object_add(j_rect, kKeyX, json_object_new_int(x)); + json_object_object_add(j_rect, kKeyY, json_object_new_int(y)); + json_object_object_add(j_rect, kKeyWidth, json_object_new_int(w)); + json_object_object_add(j_rect, kKeyHeight, json_object_new_int(h)); + + json_object *j = json_object_new_object(); + json_object_object_add(j, kKeyDrawingName, json_object_new_string(label)); + json_object_object_add(j, kKeyDrawingArea, json_object_new_string(area)); + json_object_object_add(j, kKeyDrawingRect, j_rect); + + int ret = afb_event_push(this->map_afb_event[evname], j); + if (ret != 0) + { + HMI_DEBUG("afb_event_push failed: %m"); + } +} + +string WindowManager::searchApp(unsigned pid, unsigned ppid, unsigned surface, json_object* resp) +{ + // retrieve appid from pid from application manager + string appid; + // check appid then add it to the client + HMI_INFO("Runners:%s", json_object_get_string(resp)); + int size = json_object_array_length(resp); + HMI_INFO("pid %d, ppid %d, surface %d",pid, ppid, surface); + for(int i = 0; i < size; i++) + { + json_object *j = json_object_array_get_idx(resp, i); + int runid = jh::getIntFromJson(j, "runid"); + const char* id = jh::getStringFromJson(j, "id"); + HMI_DEBUG("Appid %s, runid %d", id, runid); + if(id && (runid == ppid)) + { + string s_id = id; + s_id.erase(s_id.find('@')); + appid = s_id; + HMI_INFO("App found %s", appid.c_str()); + break; + } + } + if(appid.empty()) + { + HMI_WARNING("Failed to retrieve id"); + } + return appid; +} + +void WindowManager::storeSurface(const string& appid, unsigned ppid, unsigned surface) +{ + auto elem = std::find_if(this->tmp_services.begin(), this->tmp_services.end(), + [&appid](TmpService& ts){ + return (ts.dest == appid ); + }); + + this->lc->setXDGSurfaceOriginSize(surface); + if(elem != this->tmp_services.end()) + { + // attachApp + auto client = g_app_list.lookUpClient(elem->dest); + if(client == nullptr) + { + return; + } + HMI_INFO("Attach surface %d (service %s) to app %s", surface, elem->service.c_str(), elem->dest.c_str()); + client->attachServiceSurface(elem->service, surface); + } + else + { + // setRole + auto client = g_app_list.lookUpClient(appid); + if(client != nullptr) + { + client->addSurface(surface); + this->id_alloc.register_name_id(client->role(), surface); + } + else + { + // Store tmp surface and appid for application + // who requests setRole after creating shell surface + this->tmp_surface2app.emplace(surface, TmpClient{appid, ppid}); + } + } +} + +/** + * proxied events + */ +void WindowManager::surface_created(unsigned pid, unsigned surface_id) +{ + // requestSurface + if(this->tmp_surface2app.count(surface_id) != 0) + { + string appid = this->tmp_surface2app[surface_id].appid; + auto client = g_app_list.lookUpClient(appid); + if(client != nullptr) + { + WMError ret = client->addSurface(surface_id); + HMI_INFO("Add surface %d to \"%s\"", surface_id, appid.c_str()); + if(ret != WMError::SUCCESS) + { + HMI_ERROR("Failed to add surface to client %s", client->appID().c_str()); + } + } + this->tmp_surface2app.erase(surface_id); + } + else + { + HMI_NOTICE("Unknown surface %d", surface_id); + // retrieve ppid + std::ostringstream os; + os << pid ; + string path = "/proc/" + os.str() + "/stat"; + std::ifstream ifs(path.c_str()); + string str; + unsigned ppid = 0; + if(!ifs.fail() && std::getline(ifs, str)) + { + std::sscanf(str.data(), "%*d %*s %*c %d", &ppid); + HMI_INFO("Retrieve ppid %d", ppid); + } + else + { + HMI_ERROR("Failed to open /proc/%d/stat", pid); + HMI_ERROR("File system may be different"); + return; + } + struct AfbClosure* c = new struct AfbClosure(pid, ppid, surface_id); + // search pid from surfaceID + afb_service_call("afm-main", "runners", json_object_new_object(), + [](void* closure, int stat, json_object* resp){ + HMI_DEBUG("check %s", json_object_get_string(resp)); + struct AfbClosure* c = static_cast(closure); + HMI_DEBUG("check"); + if(stat != 0) + { + HMI_ERROR("Failed to call runners"); + } + else + { + json_object* j; + json_object_object_get_ex(resp, "response", &j); + string appid = g_context->searchApp(c->pid, c->ppid, c->surface, j); + if(!appid.empty()) + { + g_context->storeSurface(appid, c->ppid, c->surface); + } + } + json_object_put(resp); + delete c; + }, c); + } +} + +void WindowManager::surface_removed(unsigned surface_id) +{ + HMI_DEBUG("Delete surface_id %u", surface_id); + this->id_alloc.remove_id(surface_id); + // this->layers.remove_surface(surface_id); + g_app_list.removeSurface(surface_id); +} + +void WindowManager::removeClient(const string &appid) +{ + HMI_DEBUG("Remove clinet %s from list", appid.c_str()); + auto client = g_app_list.lookUpClient(appid); + this->lc->terminateApp(client); + g_app_list.removeClient(appid); +} + +void WindowManager::exceptionProcessForTransition() +{ + unsigned req_num = g_app_list.currentRequestNumber(); + HMI_SEQ_NOTICE(req_num, "Process exception handling for request. Remove current request %d", req_num); + g_app_list.removeRequest(req_num); + HMI_SEQ_NOTICE(g_app_list.currentRequestNumber(), "Process next request if exists"); + this->processNextRequest(); +} + +void WindowManager::analyzeReceivedEvent(const char *event, struct json_object *object) +{ + HMI_DEBUG("event:%s", event); + + // If receive low can signal + if (strstr(event, "low-can")) + { + // Analyze low can signal + const char *signal_name = this->lcc.analyzeCanSignal(object); + + // Create task for car state and input it to PolicyManager + Task task = this->convertCanSignalToCarStateTask(signal_name); + if (Task::TASK_INVALID != task) + { + this->inputCarStateTask(task); + } + } +} + +void WindowManager::timerHandler() +{ + unsigned req_num = g_app_list.currentRequestNumber(); + HMI_SEQ_DEBUG(req_num, "Timer expired remove Request"); + g_app_list.reqDump(); + g_app_list.removeRequest(req_num); + this->processNextRequest(); +} + +void WindowManager::startTransitionWrapper(vector &actions) +{ + WMError ret = WMError::UNKNOWN; + // req_num is guaranteed by Window Manager + unsigned req_num = g_app_list.currentRequestNumber(); + Task task = Task::TASK_INVALID; + + if (actions.empty()) + { + if (g_app_list.haveRequest()) + { + HMI_SEQ_DEBUG(req_num, "There is no WMAction for this request"); + goto proc_remove_request; + } + else + { + HMI_SEQ_DEBUG(req_num, "There is no request"); + return; + } + } + + // Check weather there is the no request task + // [The no request task] + // - TaskCarState::RESTRICTION_MODE_OFF + // - TaskCarState::RESTRICTION_MODE_ON + for (const auto &act : actions) + { + if (TaskCarState::RESTRICTION_MODE_OFF == act.car_state) + { + task = Task::TASK_RESTRICTION_MODE_OFF; + break; + } + else if (TaskCarState::RESTRICTION_MODE_ON == act.car_state) + { + task = Task::TASK_RESTRICTION_MODE_ON; + break; + } + } + + // If there is the request-less task, set request here + if (Task::TASK_INVALID != task) { + unsigned req_num; + ret = this->setRequest(task, &req_num); + + if(ret != WMError::SUCCESS) + { + goto error; + } + } + + for (auto &act : actions) + { + if ("" != act.role) + { + bool found; + auto const &surface_id = this->id_alloc.lookup(act.role.c_str()); + if(surface_id == nullopt) + { + HMI_SEQ_DEBUG(req_num, "There is not surface id for role:%s", act.role.c_str()); + continue; + } + + std::string appid = g_app_list.getAppID(*surface_id, &found); + if (!found) + { + if (TaskVisible::INVISIBLE == act.visible) + { + HMI_SEQ_DEBUG(req_num, "role:%s is killed, so do not set this action", act.role.c_str()); + continue; + } + else + { + HMI_SEQ_ERROR(req_num, "appid of role:%s which is visible is not found", act.role.c_str()); + ret = WMError::FAIL; + goto error; + } + } + auto client = g_app_list.lookUpClient(appid); + act.req_num = req_num; + act.client = client; + + // If Window Manager is master and this action is for slave, + // change TaskVisible + if (this->wmcon.isMasterMode()) + { + if (this->wmcon.isMasterArea(act.area.c_str())) + { + HMI_DEBUG("Set TaskVisible::REQ_REMOTE_VISIBLE"); + act.visible = TaskVisible::REQ_REMOTE_VISIBLE; + } + // TODO: Check whether role is tbtnavi to request remote invisible + else if (("tbtnavi" == act.role) && + (TaskVisible::INVISIBLE == act.visible)) + { + HMI_DEBUG("Set TaskVisible::REQ_REMOTE_INVISIBLE"); + act.visible = TaskVisible::REQ_REMOTE_INVISIBLE; + } + } + } + + ret = g_app_list.setAction(req_num, act); + if (ret != WMError::SUCCESS) + { + HMI_SEQ_ERROR(req_num, "Setting action is failed"); + goto error; + } + } + + HMI_SEQ_DEBUG(req_num, "Start transition."); + ret = this->startTransition(req_num); + if (ret != WMError::SUCCESS) + { + if (ret == WMError::NO_LAYOUT_CHANGE) + { + goto proc_remove_request; + } + else + { + HMI_SEQ_ERROR(req_num, "Transition state is failed"); + goto error; + } + } + + return; + +error: + //this->emit_error() + HMI_SEQ_ERROR(req_num, errorDescription(ret)); + this->pmw.undoState(); + +proc_remove_request: + g_app_list.removeRequest(req_num); + this->processNextRequest(); +} + +void WindowManager::processError(WMError error) +{ + unsigned req_num = g_app_list.currentRequestNumber(); + + //this->emit_error() + HMI_SEQ_ERROR(req_num, errorDescription(error)); + g_app_list.removeRequest(req_num); + this->processNextRequest(); +} + +void WindowManager::processForRemoteRequest(json_object *data) +{ + const char *req = jh::getStringFromJson(data, "req"); + const char *appid = jh::getStringFromJson(data, "appid"); + const char *drawing_name = jh::getStringFromJson(data, "drawing_name"); + const char *drawing_area = jh::getStringFromJson(data, "drawing_area"); + + if (!req || !drawing_name) + { + HMI_ERROR("Parse Error!!"); + return; + } + + if (this->wmcon.isMasterMode()) + { + if (!appid) + { + HMI_ERROR("Parse Error!!"); + return; + } + + auto reply = [](const char *errmsg) { + if (errmsg != nullptr) + { + HMI_ERROR(errmsg); + return; + } + }; + + if ("activateWindow" == std::string(req)) + { + if (!drawing_area) + { + HMI_ERROR("Parse Error!!"); + return; + } + + this->api_activate_surface_for_slave( + appid, drawing_name, drawing_area, reply); + } + else if ("deactivateWindow" == std::string(req)) + { + this->api_deactivate_surface_for_slave( + appid, drawing_name, reply); + } + else if ("endDraw" == std::string(req)) + { + this->api_enddraw(appid, drawing_name); + } + } + else + { + if ("syncDraw" == std::string(req)) + { + this->stopTimer(); + + if (!appid || !drawing_area) + { + HMI_ERROR("Parse Error!!"); + return; + } + + unsigned req_num = g_app_list.currentRequestNumber(); + auto client = g_app_list.lookUpClient(appid); + + // TODO: application requests by old role, + // so convert role old to new + const char *c_role = this->convertRoleOldToNew(drawing_name); + + // Create action + bool end_draw_finished = false; + WMAction act + { + req_num, + client, + string(c_role), + string(drawing_area), + TaskVisible::REMOTE_VISIBLE, + end_draw_finished, + TaskCarState::NO_TASK + }; + + // Set action + WMError ret = g_app_list.setAction(req_num, act); + if (ret != WMError::SUCCESS) + { + HMI_SEQ_ERROR(req_num, "Setting action is failed"); + return; + } + + this->emit_syncdraw(string(drawing_name), string(drawing_area)); + this->wmcon.startSyncDrawForRemote(appid); + this->setTimer(); + } + else if ("activated" == std::string(req)) + { + this->emit_visible(drawing_name); + this->emit_activated(drawing_name); + } + else if ("deactivated" == std::string(req)) + { + this->stopTimer(); + + if (!appid || !drawing_area) + { + HMI_ERROR("Parse Error!!"); + return; + } + + unsigned req_num = g_app_list.currentRequestNumber(); + auto client = g_app_list.lookUpClient(appid); + + // TODO: application requests by old role, + // so convert role old to new + const char *c_role = this->convertRoleOldToNew(drawing_name); + + // Create action + bool end_draw_finished = true; + WMAction act + { + req_num, + client, + string(c_role), + "", + TaskVisible::REMOTE_INVISIBLE, + end_draw_finished, + TaskCarState::NO_TASK + }; + + this->lc->visibilityChange(act); + this->lc->renderLayers(); + this->lc->renderLayersRemote(); + + this->emit_invisible(drawing_name); + this->emit_deactivated(drawing_name); + this->emitScreenUpdated(req_num); + + g_app_list.removeRequest(req_num); + this->processNextRequest(); + } + else if ("flushDraw" == std::string(req)) + { + this->emit_flushdraw(drawing_name); + } + } +} + +/* + ******* Private Functions ******* + */ + +void WindowManager::emit_activated(char const *label) +{ + this->send_event(kListEventName[Event_Active], label); +} + +void WindowManager::emit_deactivated(char const *label) +{ + this->send_event(kListEventName[Event_Inactive], label); +} + +void WindowManager::emit_syncdraw(char const *label, char const *area, int x, int y, int w, int h) +{ + this->send_event(kListEventName[Event_SyncDraw], label, area, x, y, w, h); +} + +void WindowManager::emit_syncdraw(const string &role, const string &area) +{ + rect rect = this->lc->getAreaSize(area); + this->send_event(kListEventName[Event_SyncDraw], + role.c_str(), area.c_str(), rect.x, rect.y, rect.w, rect.h); +} + +void WindowManager::emit_flushdraw(char const *label) +{ + this->send_event(kListEventName[Event_FlushDraw], label); +} + +void WindowManager::emit_visible(char const *label, bool is_visible) +{ + this->send_event(is_visible ? kListEventName[Event_Visible] : kListEventName[Event_Invisible], label); +} + +void WindowManager::emit_invisible(char const *label) +{ + return emit_visible(label, false); +} + +void WindowManager::emit_visible(char const *label) { return emit_visible(label, true); } + +void WindowManager::emitHeadlampOff() +{ + // Send HeadlampOff event for all application + this->send_event(kListEventName[Event_HeadlampOff]); +} + +void WindowManager::emitHeadlampOn() +{ + // Send HeadlampOn event for all application + this->send_event(kListEventName[Event_HeadlampOn]); +} + +void WindowManager::emitParkingBrakeOff() +{ + // Send ParkingBrakeOff event for all application + this->send_event(kListEventName[Event_ParkingBrakeOff]); +} + +void WindowManager::emitParkingBrakeOn() +{ + // Send ParkingBrakeOn event for all application + this->send_event(kListEventName[Event_ParkingBrakeOn]); +} + +void WindowManager::emitLightstatusBrakeOff() +{ + // Send LightstatusBrakeOff event for all application + this->send_event(kListEventName[Event_LightstatusBrakeOff]); +} + +void WindowManager::emitLightstatusBrakeOn() +{ + // Send LightstatusBrakeOn event for all application + this->send_event(kListEventName[Event_LightstatusBrakeOn]); +} + +void WindowManager::emitCarStop() +{ + // Send CarStop event for all application + this->send_event(kListEventName[Event_CarStop]); +} + +void WindowManager::emitCarRun() +{ + // Send CarRun event for all application + this->send_event(kListEventName[Event_CarRun]); +} + +WMError WindowManager::setRequest(const string& appid, const string &role, const string &area, + Task task, unsigned* req_num) +{ + if (!g_app_list.contains(appid)) + { + return WMError::NOT_REGISTERED; + } + + auto client = g_app_list.lookUpClient(appid); + + /* + * Queueing Phase + */ + unsigned current = g_app_list.currentRequestNumber(); + unsigned requested_num = g_app_list.getRequestNumber(appid); + if (requested_num != 0) + { + HMI_SEQ_INFO(requested_num, + "%s %s %s request is already queued", appid.c_str(), role.c_str(), area.c_str()); + return REQ_REJECTED; + } + + WMRequest req = WMRequest(appid, role, area, task); + unsigned new_req = g_app_list.addRequest(req); + *req_num = new_req; + g_app_list.reqDump(); + + HMI_SEQ_DEBUG(current, "%s start sequence with %s, %s", appid.c_str(), role.c_str(), area.c_str()); + + return WMError::SUCCESS; +} + +WMError WindowManager::setRequest(Task task, unsigned* req_num) +{ + /* + * Queueing Phase + */ + unsigned current = g_app_list.currentRequestNumber(); + + WMRequest req = WMRequest(task); + unsigned new_req = g_app_list.addRequest(req); + *req_num = new_req; + g_app_list.reqDump(); + + HMI_SEQ_DEBUG(current, "start sequence for task:%d", task); + + return WMError::SUCCESS; +} + +WMError WindowManager::setRequestForSlave(const string& appid, const string &role, const string &area, + Task task, unsigned* req_num) +{ + /* + * Queueing Phase + */ + unsigned current = g_app_list.currentRequestNumber(); + unsigned requested_num = g_app_list.getRequestNumber(appid); + if (requested_num != 0) + { + HMI_SEQ_INFO(requested_num, + "%s %s %s request is already queued", appid.c_str(), role.c_str(), area.c_str()); + return REQ_REJECTED; + } + + WMRequest req = WMRequest(appid, role, area, task); + unsigned new_req = g_app_list.addRequest(req); + *req_num = new_req; + g_app_list.reqDump(); + + HMI_SEQ_DEBUG(current, "%s start sequence with %s, %s", appid.c_str(), role.c_str(), area.c_str()); + + return WMError::SUCCESS; +} + +WMError WindowManager::checkPolicy(unsigned req_num) +{ + /* + * Check Policy + */ + // get current trigger + bool found = false; + WMError ret = WMError::LAYOUT_CHANGE_FAIL; + auto trigger = g_app_list.getRequest(req_num, &found); + if (!found) + { + ret = WMError::NO_ENTRY; + return ret; + } + string req_area = trigger.area; + + if (trigger.task == Task::TASK_ALLOCATE) + { + const char *msg = this->check_surface_exist(trigger.role.c_str()); + + if (msg) + { + HMI_SEQ_ERROR(req_num, msg); + return ret; + } + } + + // Input event data to PolicyManager + if (0 > this->pmw.setInputEventData(trigger.task, trigger.role, trigger.area)) + { + HMI_SEQ_ERROR(req_num, "Failed to set input event data to PolicyManager"); + return ret; + } + + // Execute state transition of PolicyManager + if (0 > this->pmw.executeStateTransition()) + { + HMI_SEQ_ERROR(req_num, "Failed to execute state transition of PolicyManager"); + return ret; + } + + ret = WMError::SUCCESS; + + g_app_list.reqDump(); + + return ret; +} + +WMError WindowManager::checkPolicyForSlave(unsigned req_num) +{ + /* + * Check Policy + */ + // get current trigger + bool found = false; + WMError ret = WMError::LAYOUT_CHANGE_FAIL; + auto trigger = g_app_list.getRequest(req_num, &found); + if (!found) + { + ret = WMError::NO_ENTRY; + return ret; + } + string req_area = trigger.area; + + // Input event data to PolicyManager + if (0 > this->pmw.setInputEventData(trigger.task, trigger.role, trigger.area)) + { + HMI_SEQ_ERROR(req_num, "Failed to set input event data to PolicyManager"); + return ret; + } + + // Execute state transition of PolicyManager + if (0 > this->pmw.executeStateTransition()) + { + HMI_SEQ_ERROR(req_num, "Failed to execute state transition of PolicyManager"); + return ret; + } + + ret = WMError::SUCCESS; + + g_app_list.reqDump(); + + return ret; +} + +WMError WindowManager::startTransition(unsigned req_num) +{ + bool sync_draw_happen = false; + bool found = false; + WMError ret = WMError::SUCCESS; + auto actions = g_app_list.getActions(req_num, &found); + if (!found) + { + ret = WMError::NO_ENTRY; + HMI_SEQ_ERROR(req_num, + "Window Manager bug :%s : Action is not set", errorDescription(ret)); + return ret; + } + + g_app_list.reqDump(); + for (const auto &action : actions) + { + // TODO: application requests by old role, + // so convert role new to old for emitting event + string old_role = this->rolenew2old[action.role]; + + if (action.visible == TaskVisible::VISIBLE) + { + sync_draw_happen = true; + + this->emit_syncdraw(old_role, action.area); + /* TODO: emit event for app not subscriber + if(g_app_list.contains(y.appid)) + g_app_list.lookUpClient(y.appid)->emit_syncdraw(y.role, y.area); */ + } + else if(action.visible == TaskVisible::REQ_REMOTE_VISIBLE) + { + // If this action is for slave, send to slave + this->wmcon.sendRequest("syncDraw", action.client->appID().c_str(), + old_role.c_str(), action.area.c_str()); + } + else if (action.car_state != TaskCarState::NO_TASK) + { + this->transitionCarState(action.car_state); + } + } + + if (sync_draw_happen) + { + this->setTimer(); + } + else + { + // deactivate only, no syncDraw + // Make it deactivate here + for (const auto &x : actions) + { + this->lc->visibilityChange(x); + string old_role = this->rolenew2old[x.role]; + + if (x.visible == TaskVisible::INVISIBLE) + { + emit_deactivated(old_role.c_str()); + } + else if (x.visible == TaskVisible::REQ_REMOTE_INVISIBLE) + { + // If this action is for slave, send to slave + int i_ret = this->wmcon.sendRequest("deactivated", x.client->appID().c_str(), + old_role.c_str(), ""); + if (0 > i_ret) + { + ret = WMError::FAIL; + } + } + + /* if (g_app_list.contains(x.appid)) + { + auto client = g_app_list.lookUpClient(x.appid); + //this->deactivate(client->surfaceID(x.role)); + } */ + } + this->lc->renderLayers(); + this->lc->renderLayersRemote(); + ret = WMError::NO_LAYOUT_CHANGE; + } + return ret; +} + +void WindowManager::transitionCarState(TaskCarState task) +{ + if (TaskCarState::PARKING_BRAKE_OFF == task) + { + this->crr_car_info.parking_brake_stt = false; + this->emitParkingBrakeOff(); + } + else if (TaskCarState::PARKING_BRAKE_ON == task) + { + this->crr_car_info.parking_brake_stt = true; + this->emitParkingBrakeOn(); + } + else if (TaskCarState::ACCEL_PEDAL_OFF == task) + { + this->crr_car_info.accel_pedal_stt = false; + } + else if (TaskCarState::ACCEL_PEDAL_ON == task) + { + this->crr_car_info.accel_pedal_stt = true; + } + else if (TaskCarState::HEDLAMP_OFF == task) + { + this->crr_car_info.headlamp_stt = false; + this->emitHeadlampOff(); + } + else if (TaskCarState::HEDLAMP_ON == task) + { + this->crr_car_info.headlamp_stt = true; + this->emitHeadlampOn(); + } + else if (TaskCarState::LIGHTSTATUS_BRAKE_OFF == task) + { + this->crr_car_info.lightstatus_brake_stt = false; + this->emitLightstatusBrakeOff(); + } + else if (TaskCarState::LIGHTSTATUS_BRAKE_ON == task) + { + this->crr_car_info.lightstatus_brake_stt = true; + this->emitLightstatusBrakeOn(); + } + else if (TaskCarState::CAR_STOP == task) + { + this->crr_car_info.running_stt = false; + this->emitCarStop(); + } + else if (TaskCarState::CAR_RUN == task) + { + this->crr_car_info.running_stt = true; + this->emitCarRun(); + } +} + +WMError WindowManager::doEndDraw(unsigned req_num) +{ + // get actions + bool found; + auto actions = g_app_list.getActions(req_num, &found); + WMError ret = WMError::SUCCESS; + if (!found) + { + ret = WMError::NO_ENTRY; + return ret; + } + + HMI_SEQ_INFO(req_num, "do endDraw"); + + // layout change and make it visible + for (const auto &act : actions) + { + if(act.visible != TaskVisible::NO_CHANGE) + { + // layout change + ret = this->lc->layoutChange(act); + if(ret != WMError::SUCCESS) + { + HMI_SEQ_WARNING(req_num, + "Failed to manipulate surfaces while state change : %s", errorDescription(ret)); + return ret; + } + ret = this->lc->visibilityChange(act); + + // Emit active/deactive event + string old_role = this->rolenew2old[act.role]; + if(act.visible == TaskVisible::VISIBLE) + { + emit_visible(old_role.c_str()); + emit_activated(old_role.c_str()); + } + else if(act.visible == TaskVisible::REQ_REMOTE_VISIBLE) + { + // If this action is for slave, send to slave + int i_ret = this->wmcon.sendRequest("activated", "", old_role.c_str(), ""); + if (0 > i_ret) + { + ret = WMError::FAIL; + } + } + else if(act.visible == TaskVisible::REQ_REMOTE_INVISIBLE) + { + // If this action is for slave, send to slave + int i_ret = this->wmcon.sendRequest("deactivated", "", old_role.c_str(), ""); + if (0 > i_ret) + { + ret = WMError::FAIL; + } + } + else if((act.visible == TaskVisible::REMOTE_VISIBLE) || + (act.visible == TaskVisible::REMOTE_INVISIBLE)) + { + // nop because emit active/deactive by event from remote + } + else + { + emit_invisible(old_role.c_str()); + emit_deactivated(old_role.c_str()); + } + + if (ret != WMError::SUCCESS) + { + HMI_SEQ_WARNING(req_num, + "Failed to manipulate surfaces while state change : %s", errorDescription(ret)); + return ret; + } + HMI_SEQ_DEBUG(req_num, "visible %s", act.role.c_str()); + } + } + this->lc->renderLayers(); + this->lc->renderLayersRemote(); + + HMI_SEQ_INFO(req_num, "emit flushDraw"); + + for(const auto &act_flush : actions) + { + // TODO: application requests by old role, + // so convert role new to old for emitting event + string old_role = this->rolenew2old[act_flush.role]; + + if(act_flush.visible == TaskVisible::VISIBLE) + { + this->emit_flushdraw(old_role.c_str()); + } + else if(act_flush.visible == TaskVisible::REQ_REMOTE_VISIBLE) + { + // If this action is for slave, send to slave + this->wmcon.sendRequest("flushDraw", "", old_role.c_str(), ""); + } + } + + return ret; +} + +void WindowManager::emitScreenUpdated(unsigned req_num) +{ + // Get visible apps + HMI_SEQ_DEBUG(req_num, "emit screen updated"); + bool found = false; + auto actions = g_app_list.getActions(req_num, &found); + + HMI_DEBUG("@@@@@"); + // create json object + json_object *j = json_object_new_object(); + json_object *jarray = json_object_new_array(); + + for(const auto& action: actions) + { + if((action.visible == TaskVisible::VISIBLE) || + (action.visible == TaskVisible::REMOTE_VISIBLE)) + { + json_object_array_add(jarray, json_object_new_string(action.client->appID().c_str())); + } + } + json_object_object_add(j, kKeyIds, jarray); + HMI_SEQ_INFO(req_num, "Visible app: %s", json_object_get_string(j)); + + int ret = afb_event_push( + this->map_afb_event[kListEventName[Event_ScreenUpdated]], j); + if (ret != 0) + { + HMI_DEBUG("afb_event_push failed: %m"); + } +} + +void WindowManager::setTimer() +{ + struct timespec ts; + if (clock_gettime(CLOCK_BOOTTIME, &ts) != 0) { + HMI_ERROR("Could't set time (clock_gettime() returns with error"); + return; + } + + HMI_SEQ_DEBUG(g_app_list.currentRequestNumber(), "Timer set activate"); + if (g_timer_ev_src == nullptr) + { + // firsttime set into sd_event + int ret = sd_event_add_time(afb_daemon_get_event_loop(), &g_timer_ev_src, + CLOCK_BOOTTIME, (uint64_t)(ts.tv_sec + kTimeOut) * 1000000ULL, 1, processTimerHandler, this); + if (ret < 0) + { + HMI_ERROR("Could't set timer"); + } + } + else + { + // update timer limitation after second time + sd_event_source_set_time(g_timer_ev_src, (uint64_t)(ts.tv_sec + kTimeOut) * 1000000ULL); + sd_event_source_set_enabled(g_timer_ev_src, SD_EVENT_ONESHOT); + } +} + +void WindowManager::stopTimer() +{ + unsigned req_num = g_app_list.currentRequestNumber(); + HMI_SEQ_DEBUG(req_num, "Timer stop"); + int rc = sd_event_source_set_enabled(g_timer_ev_src, SD_EVENT_OFF); + if (rc < 0) + { + HMI_SEQ_ERROR(req_num, "Timer stop failed"); + } +} + +void WindowManager::processNextRequest() +{ + g_app_list.next(); + g_app_list.reqDump(); + unsigned req_num = g_app_list.currentRequestNumber(); + if (g_app_list.haveRequest()) + { + HMI_SEQ_DEBUG(req_num, "Process next request"); + WMError rc = checkPolicy(req_num); + if (rc != WMError::SUCCESS) + { + HMI_SEQ_ERROR(req_num, errorDescription(rc)); + } + } + else + { + HMI_SEQ_DEBUG(req_num, "Nothing Request. Waiting Request"); + } +} + +const char* WindowManager::convertRoleOldToNew(char const *old_role) +{ + const char *new_role = nullptr; + + for (auto const &on : this->roleold2new) + { + std::regex regex = std::regex(on.first); + if (std::regex_match(old_role, regex)) + { + // role is old. So convert to new. + new_role = on.second.c_str(); + break; + } + } + + if (nullptr == new_role) + { + // role is new or fallback. + new_role = old_role; + } + + HMI_DEBUG("old:%s -> new:%s", old_role, new_role); + + return new_role; +} + +int WindowManager::loadOldRolesConfigFile() +{ + // Get afm application installed dir + char const *afm_app_install_dir = getenv("AFM_APP_INSTALL_DIR"); + HMI_DEBUG("afm_app_install_dir:%s", afm_app_install_dir); + + string file_name; + if (!afm_app_install_dir) + { + HMI_ERROR("AFM_APP_INSTALL_DIR is not defined"); + } + else + { + file_name = string(afm_app_install_dir) + string(kPathOldRolesConfigFile); + } + + // Load old_rolea config file + json_object* json_obj; + int ret = jh::inputJsonFilie(file_name.c_str(), &json_obj); + if (0 > ret) + { + HMI_ERROR("Could not open %s, so use default old_roles information", kPathOldRolesConfigFile); + json_obj = json_tokener_parse(kDefaultOldRolesConfig); + } + HMI_DEBUG("json_obj dump:%s", json_object_get_string(json_obj)); + + // Perse apps + json_object* json_cfg; + if (!json_object_object_get_ex(json_obj, "old_roles", &json_cfg)) + { + HMI_ERROR("Parse Error!!"); + return -1; + } + + int len = json_object_array_length(json_cfg); + HMI_DEBUG("json_cfg len:%d", len); + HMI_DEBUG("json_cfg dump:%s", json_object_get_string(json_cfg)); + + for (int i=0; iroleold2new[old_role] = string(new_role); + } + + // Check + for(auto itr = this->roleold2new.begin(); + itr != this->roleold2new.end(); ++itr) + { + HMI_DEBUG(">>> role old:%s new:%s", + itr->first.c_str(), itr->second.c_str()); + } + + // Release json_object + json_object_put(json_obj); + + return 0; +} + +Task WindowManager::convertCanSignalToCarStateTask(const char *signal_name) +{ + wm::LowCanClient *lcc = &(this->lcc); + Task task = Task::TASK_INVALID; + + // If car info is updated, set car state change event + if (strstr(signal_name, lcc->kSignalName[lcc->SignalNoParkingBrake])) + { + HMI_DEBUG("Parking Brake state is changed"); + + if (lcc->getCurrentParkingBrakeState()) + { + task = wm::Task::TASK_PARKING_BRAKE_ON; + } + else + { + task = wm::Task::TASK_PARKING_BRAKE_OFF; + } + } + else if (strstr(signal_name, lcc->kSignalName[lcc->SignalNoAccelPedalPos])) + { + // Update accel pedal position + this->crr_car_info.accel_pedal_pos = lcc->getCurrentAccelPedalPosition(); + + if (lcc->isChangedAccelPedalState()) + { + HMI_DEBUG("Accelerator Pedal state is changed"); + + if (lcc->getCurrentAccelPedalState()) + { + task = wm::Task::TASK_ACCEL_PEDAL_ON; + } + else + { + task = wm::Task::TASK_ACCEL_PEDAL_OFF; + } + } + } + else if (strstr(signal_name, lcc->kSignalName[lcc->SignalNoHeadlame])) + { + HMI_DEBUG("Headlamp state is changed"); + + if (lcc->getCurrentHeadlampState()) + { + task = wm::Task::TASK_HEDLAMP_ON; + } + else + { + task = wm::Task::TASK_HEDLAMP_OFF; + } + } + else if (strstr(signal_name, lcc->kSignalName[lcc->SignalNoLightstatusBrake])) + { + HMI_DEBUG("Lightstatus Brake state is changed"); + + if (lcc->getCurrentLightstatusBrakeState()) + { + task = wm::Task::TASK_LIGHTSTATUS_BRAKE_ON; + } + else + { + task = wm::Task::TASK_LIGHTSTATUS_BRAKE_OFF; + } + } + return task; +} + +void WindowManager::inputCarStateTask(Task task) +{ + unsigned req_num = 0; + WMError ret = WMError::UNKNOWN; + + ret = this->setRequest(task, &req_num); + + if(ret != WMError::SUCCESS) + { + HMI_ERROR(errorDescription(ret)); + return; + } + + if (req_num != g_app_list.currentRequestNumber()) + { + // Add request, then invoked after the previous task is finished + HMI_SEQ_DEBUG(req_num, "request is accepted"); + return; + } + + /* + * Do allocate tasks + */ + ret = this->checkPolicy(req_num); + + if (ret != WMError::SUCCESS) + { + //this->emit_error() + HMI_SEQ_ERROR(req_num, errorDescription(ret)); + g_app_list.removeRequest(req_num); + this->processNextRequest(); + } +} + +const char *WindowManager::check_surface_exist(const char *drawing_name) +{ + auto const &surface_id = this->id_alloc.lookup(string(drawing_name)); + if (!surface_id) + { + return "Surface does not exist"; + } + + /* if (!this->controller->surface_exists(*surface_id)) + { + return "Surface does not exist in controller!"; + } */ + + /* auto layer_id = this->layers.get_layer_id(*surface_id); + + if (!layer_id) + { + return "Surface is not on any layer!"; + } */ + + HMI_DEBUG("surface %d is detected", *surface_id); + return nullptr; +} + +const char* WindowManager::kDefaultOldRolesConfig = "{ \ + \"old_roles\": [ \ + { \ + \"name\": \"HomeScreen\", \ + \"new\": \"homescreen\" \ + }, \ + { \ + \"name\": \"Music\", \ + \"new\": \"music\" \ + }, \ + { \ + \"name\": \"MediaPlayer\", \ + \"new\": \"music\" \ + }, \ + { \ + \"name\": \"Video\", \ + \"new\": \"video\" \ + }, \ + { \ + \"name\": \"VideoPlayer\", \ + \"new\": \"video\" \ + }, \ + { \ + \"name\": \"WebBrowser\", \ + \"new\": \"browser\" \ + }, \ + { \ + \"name\": \"Radio\", \ + \"new\": \"radio\" \ + }, \ + { \ + \"name\": \"Phone\", \ + \"new\": \"phone\" \ + }, \ + { \ + \"name\": \"Navigation\", \ + \"new\": \"map\" \ + }, \ + { \ + \"name\": \"HVAC\", \ + \"new\": \"hvac\" \ + }, \ + { \ + \"name\": \"Settings\", \ + \"new\": \"settings\" \ + }, \ + { \ + \"name\": \"Dashboard\", \ + \"new\": \"dashboard\" \ + }, \ + { \ + \"name\": \"POI\", \ + \"new\": \"poi\" \ + }, \ + { \ + \"name\": \"Mixer\", \ + \"new\": \"mixer\" \ + }, \ + { \ + \"name\": \"Restriction\", \ + \"new\": \"restriction\" \ + }, \ + { \ + \"name\": \"^OnScreen.*\", \ + \"new\": \"on_screen\" \ + } \ + ] \ +}"; + +} // namespace wm diff --git a/demo3/common/agl-service-windowmanager/src/window_manager.hpp b/demo3/common/agl-service-windowmanager/src/window_manager.hpp new file mode 100644 index 0000000..c4ad0f5 --- /dev/null +++ b/demo3/common/agl-service-windowmanager/src/window_manager.hpp @@ -0,0 +1,346 @@ +/* + * Copyright (c) 2017 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINDOW_MANAGER_HPP +#define WINDOW_MANAGER_HPP + +#include +#include +#include +#include +#include "result.hpp" +#include "pm_wrapper.hpp" +#include "util.hpp" +#include "request.hpp" +#include "wm_error.hpp" +#include "wm_layer_control.hpp" +#include "wm_connection.hpp" +#include "low_can_client.hpp" +extern "C" +{ +#include +} + +struct json_object; + +namespace wm +{ + +using std::experimental::optional; + +/* DrawingArea name used by "{layout}.{area}" */ +extern const char kNameLayoutNormal[]; +extern const char kNameLayoutSplit[]; +extern const char kNameAreaFull[]; +extern const char kNameAreaMain[]; +extern const char kNameAreaSub[]; + +/* Key for json obejct */ +extern const char kKeyDrawingName[]; +extern const char kKeyDrawingArea[]; +extern const char kKeyDrawingRect[]; +extern const char kKeyX[]; +extern const char kKeyY[]; +extern const char kKeyWidth[]; +extern const char kKeyHeigh[]; +extern const char kKeyWidthPixel[]; +extern const char kKeyHeightPixel[]; +extern const char kKeyWidthMm[]; +extern const char kKeyHeightMm[]; +extern const char kKeyScale[]; +extern const char kKeyIds[]; + +struct id_allocator +{ + unsigned next = 1; + + // Surfaces that where requested but not yet created + std::unordered_map id2name; + std::unordered_map name2id; + + id_allocator(id_allocator const &) = delete; + id_allocator(id_allocator &&) = delete; + id_allocator &operator=(id_allocator const &); + id_allocator &operator=(id_allocator &&) = delete; + + // Insert and return a new ID + unsigned generate_id(std::string const &name) + { + unsigned sid = this->next++; + this->id2name[sid] = name; + this->name2id[name] = sid; + HMI_DEBUG("allocated new id %u with name %s", sid, name.c_str()); + return sid; + } + + // Insert a new ID which defined outside + void register_name_id(std::string const &name, unsigned sid) + { + this->id2name[sid] = name; + this->name2id[name] = sid; + HMI_DEBUG("register id %u with name %s", sid, name.c_str()); + return; + } + + // Lookup by ID or by name + optional lookup(std::string const &name) const + { + auto i = this->name2id.find(name); + return i == this->name2id.end() ? nullopt : optional(i->second); + } + + optional lookup(unsigned id) const + { + auto i = this->id2name.find(id); + return i == this->id2name.end() ? nullopt + : optional(i->second); + } + + // Remove a surface id and name + void remove_id(std::string const &name) + { + auto i = this->name2id.find(name); + if (i != this->name2id.end()) + { + this->id2name.erase(i->second); + this->name2id.erase(i); + } + } + + void remove_id(unsigned id) + { + auto i = this->id2name.find(id); + if (i != this->id2name.end()) + { + this->name2id.erase(i->second); + this->id2name.erase(i); + } + } +}; + +struct TmpClient +{ + std::string appid; + unsigned pid; +}; + +struct TmpService +{ + std::string appid; // Used to search who create service surface + std::string dest; // Used to attach service to destination application + std::string service;// The name of service surface + std::string uuid; // uuid + TmpService(const std::string& app, const std::string& dst, + const std::string& svc, const std::string& uuid) + : appid(app), dest(dst), service(svc), uuid(uuid) {} +}; + +struct CarInfo +{ + CarInfo() + : parking_brake_stt(true), + accel_pedal_stt(false), + accel_pedal_pos(0.0), + running_stt(false), + headlamp_stt(false), + lightstatus_brake_stt(true) + {}; + + bool parking_brake_stt; + bool accel_pedal_stt; + double accel_pedal_pos; + bool running_stt; + bool headlamp_stt; + bool lightstatus_brake_stt; +}; + +class WindowManager +{ + public: + typedef std::unordered_map rect_map; + typedef std::function reply_func; + + enum EventType + { + Event_Val_Min = 0, + + Event_Active = Event_Val_Min, + Event_Inactive, + + Event_Visible, + Event_Invisible, + + Event_SyncDraw, + Event_FlushDraw, + + Event_ScreenUpdated, + + Event_HeadlampOff, + Event_HeadlampOn, + + Event_ParkingBrakeOff, + Event_ParkingBrakeOn, + + Event_LightstatusBrakeOff, + Event_LightstatusBrakeOn, + + Event_CarStop, + Event_CarRun, + + Event_Error, + + Event_Val_Max = Event_Error, + }; + + explicit WindowManager(); + ~WindowManager() = default; + + WindowManager(WindowManager const &) = delete; + WindowManager &operator=(WindowManager const &) = delete; + WindowManager(WindowManager &&) = delete; + WindowManager &operator=(WindowManager &&) = delete; + + int init(); + + result api_request_surface(char const *appid, char const *role); + char const *api_request_surface(char const *appid, char const *role, char const *ivi_id); + bool api_set_role(char const *appid, char const *role); + void api_activate_surface(char const *appid, char const *role, char const *drawing_area, const reply_func &reply); + void api_activate_surface_for_slave(char const *appid, char const *drawing_name, + char const *drawing_area, const reply_func &reply); + void api_activate_surface_to_master(char const *appid, char const *drawing_name, + char const *drawing_area, const reply_func &reply); + void api_deactivate_surface(char const *appid, char const *role, const reply_func &reply); + void api_deactivate_surface_for_slave(char const *appid, char const *drawing_name, + const reply_func &reply); + void api_deactivate_surface_to_master(char const *appid, char const *drawing_name, + const reply_func &reply); + void api_enddraw(char const *appid, char const *role); + void api_enddraw_for_remote(char const *appid, char const *drawing_name); + bool api_client_set_render_order(const char *appid, const std::vector &render_order); + std::string api_client_attach_service_surface(const char* appid, const char* dest, const char* service_surface); + result api_get_display_info(); + result api_get_area_info(char const *role); + result api_get_car_info(char const *label); + void send_event(char const *evname); + void send_event(char const *evname, char const *label); + void send_event(char const *evname, char const *label, char const *area, int x, int y, int w, int h); + + // Events from the compositor we are interested in + void surface_created(unsigned pid, unsigned surface_id); + void surface_removed(unsigned surface_id); + + void removeClient(const std::string &appid); + void exceptionProcessForTransition(); + const char* convertRoleOldToNew(char const *role); + + void analyzeReceivedEvent(const char *event, struct json_object *object); + + // Do not use this function + void timerHandler(); + void startTransitionWrapper(std::vector &actions); + void processError(WMError error); + void processForRemoteRequest(json_object *data); + std::string searchApp(unsigned pid, unsigned ppid, unsigned surface, json_object* resp); + void storeSurface(const std::string& appid, unsigned ppid, unsigned surface); + + const std::vector kListEventName{ + "active", + "inactive", + "visible", + "invisible", + "syncDraw", + "flushDraw", + "screenUpdated", + "headlampOff", + "headlampOn", + "parkingBrakeOff", + "parkingBrakeOn", + "lightstatusBrakeOff", + "lightstatusBrakeOn", + "carStop", + "carRun", + "error"}; + std::map map_afb_event; + + WMConnection wmcon; + + private: + // WM Events to clients + void emit_activated(char const *label); + void emit_deactivated(char const *label); + void emit_syncdraw(char const *label, char const *area, int x, int y, int w, int h); + void emit_syncdraw(const std::string &role, const std::string &area); + void emit_flushdraw(char const *label); + void emit_visible(char const *label, bool is_visible); + void emit_invisible(char const *label); + void emit_visible(char const *label); + void emitHeadlampOff(); + void emitHeadlampOn(); + void emitParkingBrakeOff(); + void emitParkingBrakeOn(); + void emitLightstatusBrakeOff(); + void emitLightstatusBrakeOn(); + void emitCarStop(); + void emitCarRun(); + + WMError setRequest(const std::string &appid, const std::string &role, const std::string &area, + Task task, unsigned *req_num); + WMError setRequest(Task task, unsigned* req_num); + WMError setRequestForSlave(const std::string& appid, const std::string &role, + const std::string &area, Task task, unsigned* req_num); + WMError checkPolicy(unsigned req_num); + WMError checkPolicyForSlave(unsigned req_num); + WMError startTransition(unsigned req_num); + void transitionCarState(TaskCarState task); + + WMError doEndDraw(unsigned req_num); + void emitScreenUpdated(unsigned req_num); + + void setTimer(); + void stopTimer(); + void processNextRequest(); + + int loadOldRolesConfigFile(); + + Task convertCanSignalToCarStateTask(const char *signal_name); + void inputCarStateTask(Task task); + + const char *check_surface_exist(const char *role); + + private: + std::unordered_map roleold2new; + std::unordered_map rolenew2old; + std::shared_ptr lc; + + LowCanClient lcc; + CarInfo crr_car_info; + + PMWrapper pmw; + + // ID allocation and proxy methods for lookup + struct id_allocator id_alloc; + // Surface are info (x, y, w, h) + rect_map area_info; + // FOR CES DEMO + std::unordered_map tmp_surface2app; + std::vector tmp_services; + static const char* kDefaultOldRolesConfig; +}; + +} // namespace wm + +#endif // WINDOW_MANAGER_HPP diff --git a/demo3/common/agl-service-windowmanager/src/wm_client.cpp b/demo3/common/agl-service-windowmanager/src/wm_client.cpp new file mode 100644 index 0000000..f2ad7be --- /dev/null +++ b/demo3/common/agl-service-windowmanager/src/wm_client.cpp @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2017 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "wm_client.hpp" +#include "util.hpp" +#include +#include + + +#define INVALID_SURFACE_ID 0 + +using std::string; +using std::vector; + +namespace wm +{ + +static const vector kWMEvents = { + // Private event for applications + "syncDraw", "flushDraw", "visible", "invisible", "active", "inactive", "error"}; +static const vector kErrorDescription = { + "unknown-error"}; + +static const char kKeyDrawingName[] = "drawing_name"; +static const char kKeyrole[] = "role"; +static const char kKeyError[] = "error"; +static const char kKeyErrorDesc[] = "kErrorDescription"; + +WMClient::WMClient(const string &appid, unsigned layer, unsigned surface, const string &role) + : id(appid), layer(layer), + role2surface(0) +{ + role2surface[role] = surface; + for (auto x : kWMEvents) + { +#if GTEST_ENABLED + string ev = x; +#else + afb_event ev = afb_daemon_make_event(x.c_str()); +#endif + evname2afb_event[x] = ev; + } +} + +WMClient::WMClient(const string &appid, const string &role) + : id(appid), + layer(0), + role2surface(0), + evname2afb_event(0) +{ + role2surface[role] = INVALID_SURFACE_ID; + for (auto x : kWMEvents) + { +#if GTEST_ENABLED + string ev = x; +#else + afb_event ev = afb_daemon_make_event(x.c_str()); +#endif + evname2afb_event[x] = ev; + } +} + +WMClient::WMClient(const string &appid, unsigned layer, const string &role) + : id(appid), + layer(layer), + main_role(role), + role2surface(0), + evname2afb_event(0) +{ + role2surface[role] = INVALID_SURFACE_ID; + for (auto x : kWMEvents) + { +#if GTEST_ENABLED + string ev = x; +#else + afb_event ev = afb_daemon_make_event(x.c_str()); +#endif + evname2afb_event[x] = ev; + } +} + +string WMClient::appID() const +{ + return this->id; +} + +string WMClient::role() const +{ + return this->main_role; +} + +unsigned WMClient::layerID() const +{ + return this->layer; +} + +unsigned WMClient::surfaceID() const +{ + return this->surface; +} + +/** + * Add surface to the client + * + * This function add main surface to the client(ivi_layer). + * + * @param string[in] role + * @return WMError + */ +WMError WMClient::addSurface(unsigned surface) +{ + this->surface = surface; + ilmErrorTypes err = ilm_layerAddSurface(this->layer, surface); + + if(err == ILM_SUCCESS) + { + err = ilm_commitChanges(); + } + return (err == ILM_SUCCESS) ? WMError::SUCCESS : WMError::FAIL; +} + +bool WMClient::removeSurfaceIfExist(unsigned surface) +{ + bool ret = false; + if(surface == this->surface) + { + this->surface = INVALID_SURFACE_ID; + ret = true; + } + else + { + for(auto &x : this->service2surfaces) + { + if(x.second = surface) + { + ret = true; + string key = x.first; + this->service2surfaces.erase(key); + this->service2supplier.erase(key); + } + } + } + return ret; +} + +WMError WMClient::setRenderOrder(const vector &order) +{ + WMError ret = WMError::SUCCESS; + this->surface_render_order.clear(); + for(const auto& x : order) + { + unsigned s; // surface + if(x == this->role()) + { + s = this->surfaceID(); + } + else if(this->service2surfaces.count(x) != 0) + { + s = this->service2surfaces[x]; + } + else + { + ret = WMError::NOT_REGISTERED; + break; + } + this->surface_render_order.push_back(s); + } + if(ret == WMError::SUCCESS) + { + int count = 0; + t_ilm_layer* id_array = new t_ilm_surface[this->surface_render_order.size()]; + if(id_array == nullptr) + { + HMI_WARNING("short memory"); + ret = WMError::FAIL; + } + else + { + for(const auto& i : this->surface_render_order) + { + id_array[count] = i; + ++count; + } + ilm_layerSetRenderOrder(this->layerID(), + id_array, this->surface_render_order.size()); + delete id_array; + } + } + return ret; +} + +string WMClient::attachTmpServiceSurface(const string& supplier, const string& service_surface) +{ + string uuid; + uuid_t u; + char out[37]; // uuid is 36 characters + uuid_generate_random(u); + uuid_unparse(u, out); + uuid = out; + this->service2supplier.emplace(service_surface, supplier); + return uuid; +} + +WMError WMClient::attachServiceSurface(const string& service_surface, unsigned surface) +{ + WMError ret = WMError::NOT_REGISTERED; + if(this->service2supplier.count(service_surface) != 0) + { + this->service2surfaces.emplace(service_surface, surface); + ret = WMError::SUCCESS; + } + return ret; +} + +#if GTEST_ENABLED +bool WMClient::subscribe(afb_req req, const string &evname) +{ + if(evname != kKeyError){ + HMI_DEBUG("error is only enabeled for now"); + return false; + } + int ret = afb_req_subscribe(req, this->evname2afb_event[evname]); + if (ret) + { + HMI_DEBUG("Failed to subscribe %s", evname.c_str()); + return false; + } + return true; +} + +void WMClient::emitError(WM_CLIENT_ERROR_EVENT ev) +{ + if (!afb_event_is_valid(this->evname2afb_event[kKeyError])){ + HMI_ERROR("event err is not valid"); + return; + } + json_object *j = json_object_new_object(); + json_object_object_add(j, kKeyError, json_object_new_int(ev)); + json_object_object_add(j, kKeyErrorDesc, json_object_new_string(kErrorDescription[ev].c_str())); + HMI_DEBUG("error: %d, description:%s", ev, kErrorDescription[ev].c_str()); + + int ret = afb_event_push(this->evname2afb_event[kKeyError], j); + if (ret != 0) + { + HMI_DEBUG("afb_event_push failed: %m"); + } +} +#endif + +void WMClient::dumpInfo() +{ + DUMP("APPID : %s", id.c_str()); + DUMP(" LAYER : %d", layer); + DUMP(" ROLE : %s , SURFACE : %d", main_role.c_str(), surface); +} + +} // namespace wm \ No newline at end of file diff --git a/demo3/common/agl-service-windowmanager/src/wm_client.hpp b/demo3/common/agl-service-windowmanager/src/wm_client.hpp new file mode 100644 index 0000000..fc171f4 --- /dev/null +++ b/demo3/common/agl-service-windowmanager/src/wm_client.hpp @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2017 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINDOWMANAGER_CLIENT_HPP +#define WINDOWMANAGER_CLIENT_HPP + +#include +#include +#include +#include "wm_error.hpp" + +extern "C" +{ +#define AFB_BINDING_VERSION 2 +#include +} + +namespace wm +{ + +enum WM_CLIENT_ERROR_EVENT +{ + UNKNOWN_ERROR +}; + +class WMClient +{ + public: + WMClient(); + WMClient(const std::string &appid, unsigned layer, + unsigned surface, const std::string &role); + WMClient(const std::string &appid, const std::string &role); + WMClient(const std::string &appid, unsigned layer, const std::string &role); + WMClient(const std::string &appid, unsigned layer, + const std::string& layer_name, unsigned surface, const std::string &role); + ~WMClient() = default; + + std::string appID() const; + std::string role() const; + unsigned layerID() const; + unsigned surfaceID() const; + // void setRole(const std::string& role); + // void appendRole(const std::string& role); + WMError addSurface(unsigned surface); + bool removeSurfaceIfExist(unsigned surface); + // bool removeRole(const std::string& role); + std::vector renderOrder() const; + WMError setRenderOrder(const std::vector& order); + std::string attachTmpServiceSurface(const std::string& from, const std::string& service_surface); + WMError attachServiceSurface(const std::string& service_surface, unsigned surface); + +#if GTEST_ENABLED + bool subscribe(afb_req req, const std::string &event_name); + void emitError(WM_CLIENT_ERROR_EVENT ev); +#endif + + void dumpInfo(); + + private: + std::string id; + unsigned layer; + std::string main_role; + std::string area; + unsigned surface; // currently, main application has only one surface. + //std::vector role_list; + std::vector surface_render_order; + std::unordered_map service2surfaces; + std::unordered_map role2surface; + std::unordered_map service2supplier; +#if GTEST_ENABLED + // This is for unit test. afb_make_event occurs sig11 if call not in afb-binding + std::unordered_map evname2afb_event; +#else + std::unordered_map evname2afb_event; +#endif +}; +} // namespace wm + +#endif \ No newline at end of file diff --git a/demo3/common/agl-service-windowmanager/src/wm_connection.cpp b/demo3/common/agl-service-windowmanager/src/wm_connection.cpp new file mode 100644 index 0000000..10ecc3b --- /dev/null +++ b/demo3/common/agl-service-windowmanager/src/wm_connection.cpp @@ -0,0 +1,457 @@ +/* + * Copyright (c) 2017 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wm_connection.hpp" +#include +#include +#include +#include +#include +#include +#include "json_helper.hpp" +#include "util.hpp" + +extern "C" +{ +#include +#include +} + + +/** + * namespace wm + */ +namespace wm +{ + + +namespace +{ + +static const char kPathConnectionConfigFile[] = "/etc/connection.json"; +static const char kDefaultIpAddr[] = "192.168.10.10"; +static const int kDefaultPort = 4000; + +static int onIoEventReceive(sd_event_source *src, int fd, uint32_t revents, void * data) +{ + WMConnection *p_wmcon = (WMConnection*)data; + + json_object *j_out; + int ret = p_wmcon->receive(&j_out); + if (0 > ret) + { + return 0; + } + + const char* rq = jh::getStringFromJson(j_out, "req"); + const char* id = jh::getStringFromJson(j_out, "appid"); + const char* dn = jh::getStringFromJson(j_out, "drawing_name"); + const char* da = jh::getStringFromJson(j_out, "drawing_area"); + + HMI_DEBUG("req:%s appid:%s, drawing_name:%s, drawing_area:%s", rq, id, dn, da); + + // Callback + p_wmcon->callOnReceivedHandler(j_out); + + return 0; +} + +static int onIoEventAccept(sd_event_source *src, int fd, uint32_t revents, void * data) +{ + struct sockaddr_in addr; + + WMConnection *p_wmcon = (WMConnection*)data; + + // Accept connection + socklen_t len = sizeof(addr); + int my_socket = p_wmcon->getMySocket(); + int connected_socket = accept(my_socket, (struct sockaddr *)&addr, &len); + if (0 > connected_socket) + { + HMI_ERROR("Failed to accept connection (%s)", strerror(errno)); + return -1; + } + + // Store connected socket + p_wmcon->setConnectedSocket(connected_socket); + + // Register callback to receive + int ret = sd_event_add_io(afb_daemon_get_event_loop(), nullptr, + connected_socket, EPOLLIN, + onIoEventReceive, p_wmcon); + if (0 > ret) + { + HMI_ERROR("Failed to add I/O event receive(%s)", strerror(-ret)); + return -1; + } + + return 0; +} + +} // namespace + +WMConnection::WMConnection() +{ + // Load connection config file + this->loadConnectionConfigFile(); + + // TODO: ECU name should be decide by config file + this->ecu_name = this->mode; +} + +int WMConnection::initialize() +{ + int ret; + + // Initialize for Master/Slave + if (this->isMasterMode()) + { + ret = this->initializeMaster(); + } + else + { + ret = this->initializeSlave(); + } + + return ret; +} + +void WMConnection::registerCallback(ReceivedHandler on_received) +{ + this->onReceived = on_received; +} + +int WMConnection::sendRequest(char const *req, char const *appid, + char const *drawing_name, char const *drawing_area) +{ + int ret; + json_object *j_obj = json_object_new_object(); + json_object_object_add(j_obj, "req", json_object_new_string(req)); + json_object_object_add(j_obj, "appid", json_object_new_string(appid)); + json_object_object_add(j_obj, "drawing_name", json_object_new_string(drawing_name)); + json_object_object_add(j_obj, "drawing_area", json_object_new_string(drawing_area)); + + ret = this->send(j_obj); + + json_object_put(j_obj); + + return ret; +} + +int WMConnection::send(struct json_object* j_in) +{ + // Convert json_object to string to send + const char *buf = json_object_to_json_string(j_in); + if (nullptr == buf) + { + HMI_ERROR("Failed to convert json_object to string"); + return -1; + } + + int len = strlen(buf); + + HMI_DEBUG("Send data(len:%d): %s", len, buf); + + int n = write(this->connected_socket, buf, len); + if(0 > n) + { + HMI_ERROR("Failed to send data (%s)", strerror(errno)); + return -1; + } + + return 0; +} + +bool WMConnection::isMasterMode() +{ + if ("master" == this->mode) + { + return true; + } + else + { + return false; + } +} + +bool WMConnection::isMasterArea(const char* area) +{ + if (nullptr == area) + { + return false; + } + + std::string str_area = std::string(area); + if ("" == str_area) + { + return false; + } + + std::vector elements; + elements = parseString(str_area, '.'); + + if ("master" == elements[0]) + { + return true; + } + else + { + return false; + } +} + +bool WMConnection::isConnecting() +{ + return (0 > this->connected_socket) ? false : true; +} + +std::string WMConnection::parseMasterArea(const char* area) +{ + std::string ret_area = ""; + std::vector elements; + elements = parseString(std::string(area), '.'); + + if ("master" != elements[0]) + { + return std::string(area); + } + + for (auto itr = (elements.begin() + 1); itr != elements.end(); ++itr) + { + ret_area += *itr; + + if ((elements.end() - 1) != itr) + { + ret_area += "."; + } + } + return ret_area; +} + +bool WMConnection::isSyncDrawingForRemote(const char* appid) +{ + if (std::string(appid) == this->syndDrawingAppId) + { + return true; + } + else + { + return false; + } +} + +void WMConnection::startSyncDrawForRemote(const char* appid) +{ + this->syndDrawingAppId = std::string(appid); +} + +void WMConnection::finishSyncDrawForRemote(const char* appid) +{ + if (std::string(appid) == this->syndDrawingAppId) + { + this->syndDrawingAppId = ""; + } +} + +int WMConnection::getMySocket() +{ + return this->my_socket; +} + +int WMConnection::getConnectedSocket() +{ + return this->connected_socket; +} + +void WMConnection::setConnectedSocket(int connected_socket) +{ + this->connected_socket = connected_socket; +} + +std::string WMConnection::getEcuName() +{ + return this->ecu_name; +} + +void WMConnection::callOnReceivedHandler(json_object *j_out) +{ + this->onReceived(j_out); +} + +int WMConnection::initializeMaster() +{ + int ret = 0; + struct sockaddr_in addr; + + // Create socket + this->my_socket = socket(AF_INET, SOCK_STREAM, 0); + if (0 > this->my_socket) + { + HMI_ERROR("Failed to create socket (%s)", strerror(errno)); + return -1; + } + + // Bind socket + addr.sin_family = AF_INET; + addr.sin_port = htons(this->port); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + + ret = bind(this->my_socket, (struct sockaddr *)&addr, sizeof(addr)); + if (0 > ret) + { + HMI_ERROR("Failed to bind socket (%s)", strerror(errno)); + return -1; + } + + // Listen connection + ret = listen(this->my_socket, 1); + if (0 > ret) + { + HMI_ERROR("Failed to listen connection (%s)", strerror(errno)); + return -1; + } + + // Register callback to accept connection + ret = sd_event_add_io(afb_daemon_get_event_loop(), nullptr, + this->my_socket, EPOLLIN, + onIoEventAccept, this); + if (0 > ret) + { + HMI_ERROR("Failed to add I/O event accept(%s)", strerror(-ret)); + return -1; + } + + return ret; +} + +int WMConnection::initializeSlave() +{ + // Create socket + this->my_socket = socket(AF_INET, SOCK_STREAM, 0); + if (0 > this->my_socket) + { + HMI_ERROR("Failed to create socket (%s)", strerror(errno)); + return -1; + } + + return 0; +} + +int WMConnection::connectToMaster() +{ + int ret = 0; + struct sockaddr_in addr; + + // Connect to master + addr.sin_family = AF_INET; + addr.sin_port = htons(this->port); + addr.sin_addr.s_addr = inet_addr(this->ip.c_str()); + + ret = connect(this->my_socket, (struct sockaddr *)&addr, sizeof(addr)); + if (0 > ret) + { + HMI_ERROR("Failed to connect to master (%s)", strerror(errno)); + return ret; + } + + HMI_DEBUG("Connected to master"); + + // Store connected socket + this->connected_socket = this->my_socket; + + // Register callback to receive + ret = sd_event_add_io(afb_daemon_get_event_loop(), nullptr, + this->connected_socket, EPOLLIN, + onIoEventReceive, this); + if (0 > ret) + { + HMI_ERROR("Failed to add I/O event receive(%s)", strerror(-ret)); + return -1; + } + + return ret; +} + +int WMConnection::receive(struct json_object** j_out) +{ + char buf[1024]; + int n; + + n = read(this->connected_socket, buf, sizeof(buf)); + if(0 > n) + { + HMI_ERROR("Failed to receive data (%s)", strerror(errno)); + return -1; + } + + HMI_DEBUG("Received data length: %d", n); + HMI_DEBUG("Received data: %s", buf); + + // Parse received data + struct json_tokener *tokener = json_tokener_new(); + *j_out = json_tokener_parse_ex(tokener, buf, n); + if (nullptr == *j_out) + { + HMI_DEBUG("Failed to parse received data"); + return -1; + } + + return 0; +} + +int WMConnection::loadConnectionConfigFile() +{ + // Get afm application installed dir + char const *afm_app_install_dir = getenv("AFM_APP_INSTALL_DIR"); + if (!afm_app_install_dir) + { + HMI_ERROR("AFM_APP_INSTALL_DIR is not defined"); + } + std::string path = std::string(afm_app_install_dir) + std::string(kPathConnectionConfigFile); + + // Load connection config file + json_object* json_obj; + int ret = jh::inputJsonFilie(path.c_str(), &json_obj); + if (0 > ret) + { + HMI_ERROR("Could not open %s, so use default mode \"slave\"", kPathConnectionConfigFile); + this->mode = "slave"; + this->ip = kDefaultIpAddr; + this->port = kDefaultPort; + return 0; + } + HMI_DEBUG("json_obj dump:%s", json_object_get_string(json_obj)); + + const char* mode = jh::getStringFromJson(json_obj, "mode"); + this->mode = (nullptr != mode) ? mode : "slave"; + + const char* ip = jh::getStringFromJson(json_obj, "master_ip"); + this->ip = (nullptr != ip) ? ip : kDefaultIpAddr; + + int port = jh::getIntFromJson(json_obj, "master_port"); + this->port = (0 != port) ? port : kDefaultPort; + + // Check + HMI_DEBUG("mode:%s master_ip:%s master_port:%d", mode, ip, port); + + // Release json_object + json_object_put(json_obj); + + return 0; +} + + +} // namespace wm diff --git a/demo3/common/agl-service-windowmanager/src/wm_connection.hpp b/demo3/common/agl-service-windowmanager/src/wm_connection.hpp new file mode 100644 index 0000000..9d3180f --- /dev/null +++ b/demo3/common/agl-service-windowmanager/src/wm_connection.hpp @@ -0,0 +1,64 @@ +/* + * Insert Copyright if needed. + */ + +#ifndef WM_CONNECTION_HPP +#define WM_CONNECTION_HPP + +#include + +struct json_object; + +namespace wm +{ + +class WMConnection +{ + public: + WMConnection(); + ~WMConnection() = default; + + using ReceivedHandler = std::function; + + int initialize(); + void registerCallback(ReceivedHandler on_received); + int sendRequest(char const *req, char const *appid, + char const *drawing_name, char const *drawing_area); + bool isMasterMode(); + bool isMasterArea(const char* area); + bool isConnecting(); + std::string parseMasterArea(const char* area); + bool isSyncDrawingForRemote(const char* role); + void startSyncDrawForRemote(const char* role); + void finishSyncDrawForRemote(const char* role); + int getMySocket(); + int getConnectedSocket(); + void setConnectedSocket(int connected_socket); + std::string getEcuName(); + void callOnReceivedHandler(json_object *j_out); + int connectToMaster(); + + int receive(json_object** j_out); + + private: + std::string mode; + std::string ip; + int port; + int my_socket = -1; + int connected_socket = -1; + ReceivedHandler onReceived; + std::string syndDrawingAppId; + + std::string ecu_name; + + int initializeMaster(); + int initializeSlave(); + int loadConnectionConfigFile(); + + int send(json_object* j_in); +}; + +} // namespace wm + +#endif // WM_CONNECTION_HPP + diff --git a/demo3/common/agl-service-windowmanager/src/wm_error.cpp b/demo3/common/agl-service-windowmanager/src/wm_error.cpp new file mode 100644 index 0000000..694a7d0 --- /dev/null +++ b/demo3/common/agl-service-windowmanager/src/wm_error.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2017 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "wm_error.hpp" + +namespace wm { + +const char *errorDescription(WMError enum_error_number) +{ + switch (enum_error_number){ + case SUCCESS: + return "Success"; + case FAIL: + return "Request failed"; + case REQ_REJECTED: + return "Request is rejected, due to the policy rejection of the request."; + case REQ_DROPPED: + return "Request is dropped, because the high priority request is done"; + case NOT_REGISTERED: + return "Not registered"; + case TIMEOUT_EXPIRED: + return "Request is dropped, due to time out expiring"; + case LAYOUT_CHANGE_FAIL: + return "Layout change fails, due to some reasons"; + case NO_ENTRY: + return "No element"; + case NO_LAYOUT_CHANGE: + return "No layout change(deactivate only)"; + default: + return "Unknown error number. Window manager bug."; + } +} + +} \ No newline at end of file diff --git a/demo3/common/agl-service-windowmanager/src/wm_error.hpp b/demo3/common/agl-service-windowmanager/src/wm_error.hpp new file mode 100644 index 0000000..c4c61b4 --- /dev/null +++ b/demo3/common/agl-service-windowmanager/src/wm_error.hpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2017 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINDOW_MANAGER_ERROR +#define WINDOW_MANAGER_ERROR + +namespace wm { + +typedef enum WINDOWMANAGER_ERROR +{ + SUCCESS = 0, + FAIL, + REQ_REJECTED, + REQ_DROPPED, + TIMEOUT_EXPIRED, + NOT_REGISTERED, + LAYOUT_CHANGE_FAIL, + NO_ENTRY, + NO_LAYOUT_CHANGE, + UNKNOWN, + ERR_MAX = UNKNOWN +} +WMError; + +const char *errorDescription(WMError enum_error_number); + +} +#endif // WINDOW_MANAGER_ERROR \ No newline at end of file diff --git a/demo3/common/agl-service-windowmanager/src/wm_layer.cpp b/demo3/common/agl-service-windowmanager/src/wm_layer.cpp new file mode 100644 index 0000000..d3e7073 --- /dev/null +++ b/demo3/common/agl-service-windowmanager/src/wm_layer.cpp @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2017 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include "wm_client.hpp" +#include "wm_layer.hpp" +#include "json_helper.hpp" +#include "util.hpp" + +using std::string; +using std::vector; +using std::unordered_map; + +#define BG_LAYER_NAME "BackGroundLayer" + +namespace wm +{ + +LayerState::LayerState() + : render_order(), + area2appid() +{} + + +void LayerState::attachIdToArea(const string& area, const WMClient& client) +{ + this->area2appid[area] = client.appID(); + this->render_order.push_back(client.layerID()); +} + +const unordered_map LayerState::popCurrentState() +{ + unordered_map tmp = this->area2appid; + this->area2appid.clear(); + this->render_order.clear(); + return tmp; +} + +const unordered_map LayerState::getCurrentState() +{ + return this->area2appid; +} + +const vector LayerState::getIviIdList() +{ + return this->render_order; +} + +void LayerState::addLayer(unsigned layer) +{ + auto result = std::find(this->render_order.begin(), this->render_order.end(), layer); + if(result == this->render_order.end()) + this->render_order.push_back(layer); +} + +void LayerState::removeLayer(unsigned layer) +{ + auto fwd_itr = std::remove_if( + this->render_order.begin(), this->render_order.end(), + [layer](unsigned elm) { + if(elm == layer) + HMI_DEBUG("remove layer %d", elm); + return elm == layer; + } + ); + this->render_order.erase(fwd_itr, this->render_order.end()); +} + +void LayerState::setArea(const string& app, const string& area) +{ + this->area2appid[area] = app; +} + +void LayerState::dump() +{ + std::string ids, apps; + for(const auto& ro : this->render_order) + { + ids += std::to_string(ro); + ids += ","; + } + for(const auto& area : this->area2appid) + { + apps += area.first; + apps += ":"; + apps += area.second; + apps += ","; + } + DUMP(" render order : %s", ids.c_str()); + DUMP(" area, app : %s", apps.c_str()); +} + +WMLayer::WMLayer(json_object* j, unsigned uuid) : tmp_state(), state(), uuid(uuid) +{ + this->name = jh::getStringFromJson(j, "name"); + this->role_list = jh::getStringFromJson(j, "role"); + const char* type = jh::getStringFromJson(j, "type"); + this->id_begin = static_cast(jh::getIntFromJson(j, "id_range_begin")); + this->id_end = static_cast(jh::getIntFromJson(j, "id_range_end")); + + if (name.size() == 0 || !type) + { + HMI_ERROR("Parse Error!!"); + exit(1); + } + if(this->id_begin > this->id_end) + { + HMI_ERROR("INVALID"); + exit(1); + } + string str_type = type; + this->type = (str_type == "tile") ? MANAGEMENT_TYPE::TILE : MANAGEMENT_TYPE::STACK; +} + +unsigned WMLayer::getNewLayerID(const string& role) +{ + unsigned ret = 0; + if(this->name == BG_LAYER_NAME) + return ret; + + // generate new layer id; + if(this->hasRole(role)) + { + if(this->id_list.size() == 0) + { + ret = this->idBegin(); + this->id_list.push_back(ret); + } + else + { + ret = this->id_list.back() + 1; + } + HMI_INFO("Generate new id: %d", ret); + } + else + { + return ret; + } + + size_t count = std::count(id_list.begin(), id_list.end(), ret); + if( (ret > this->idEnd()) || (count > 1)) + { + HMI_NOTICE("id %d is not available then generate new id", ret); + ret = 0; // reset + for(unsigned i = this->idBegin(); i < this->idEnd(); i++) + { + auto ret_found = std::find(id_list.begin(), id_list.end(), i); + if(ret_found == id_list.cend()) + { + HMI_INFO("set new id: %d", i); + ret = i; + break; + } + } + } + + if(ret != 0) + { + id_list.push_back(ret); + } + else + { + HMI_ERROR("failed to get New ID"); + } + return ret; +} + +const string& WMLayer::layerName() +{ + return this->name; +} + +WMError WMLayer::setLayerState(const LayerState& l) +{ + this->tmp_state = l; + return WMError::SUCCESS; +} + +void WMLayer::addLayerToState(unsigned layer) +{ + this->tmp_state.addLayer(layer); +} + +void WMLayer::removeLayerFromState(unsigned layer) +{ + this->tmp_state.removeLayer(layer); +} + +void WMLayer::setAreaToState(const string& app, const string& area) +{ + this->tmp_state.setArea(app, area); +} + +void WMLayer::appendArea(const string& area) +{ + this->area_list.push_back(area); +} + +void WMLayer::terminateApp(unsigned id) +{ + auto fwd_itr = std::remove_if(this->id_list.begin(), this->id_list.end(), + [id](unsigned elm) { + return elm == id; + }); + this->id_list.erase(fwd_itr, this->id_list.end()); + this->tmp_state.removeLayer(id); + this->state.removeLayer(id); + ilm_layerRemove(id); +} + +bool WMLayer::hasLayerID(unsigned id) +{ + bool ret = (id >= this->idBegin() && id <= this->idEnd()); + if(!ret) + return ret; + auto itr = std::find(this->id_list.begin(), this->id_list.end(), id); + return (itr != this->id_list.end()) ? true : false; +} + +bool WMLayer::hasRole(const string& role) +{ + auto re = std::regex(this->role_list); + if (std::regex_match(role, re)) + { + HMI_DEBUG("role %s matches layer %s", role.c_str(), this->name.c_str()); + return true; + } + return false; +} + +void WMLayer::update() +{ + this->state = this->tmp_state; +} + +void WMLayer::undo() +{ + this->tmp_state = this->state; +} + +void WMLayer::dump() +{ + DUMP("===== wm layer status ====="); + DUMP("Layer :%s", this->name.c_str()); + this->tmp_state.dump(); + this->state.dump(); + DUMP("===== wm layer status end ====="); + +} + +/* void WMLayer::undo() +{ + this->tmp_state = this->state; +} + */ +} // namespace wm diff --git a/demo3/common/agl-service-windowmanager/src/wm_layer.hpp b/demo3/common/agl-service-windowmanager/src/wm_layer.hpp new file mode 100644 index 0000000..a6a359e --- /dev/null +++ b/demo3/common/agl-service-windowmanager/src/wm_layer.hpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2017 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WM_LAYER_HPP +#define WM_LAYER_HPP + +#include +#include +#include +#include +#include "wm_error.hpp" + +struct json_object; + +namespace wm +{ + +class WMClient; +class LayerState +{ + public: + LayerState(); + ~LayerState() = default; + void attachIdToArea(const std::string& area, const WMClient&); + const std::unordered_map popCurrentState(); + const std::unordered_map getCurrentState(); + const std::vector getIviIdList(); + void addLayer(unsigned layer); + void removeLayer(unsigned layer); + void setArea(const std::string& app, const std::string& area); + + // Debug + void dump(); + + private: + std::vector render_order; + std::unordered_map area2appid; +}; + +class WMLayer +{ + public: + enum MANAGEMENT_TYPE + { + TILE, + STACK + }; + + explicit WMLayer(json_object* j, unsigned uuid); + ~WMLayer() = default; + + // Status & Setting API + unsigned getNewLayerID(const std::string& role); + unsigned idBegin() { return this->id_begin; } + unsigned idEnd() { return this->id_end; } + unsigned getUuid() { return this->uuid; } + const std::string& layerName(); + MANAGEMENT_TYPE layerType() { return this->type; } + void appendArea(const std::string& area); + LayerState& getLayerState() { return tmp_state; } + WMError setLayerState(const LayerState& l); + bool hasLayerID(unsigned id); + bool hasRole(const std::string& role); + + // Manipulation + void addLayerToState(unsigned layer); + void removeLayerFromState(unsigned layer); + void setAreaToState(const std::string& app, const std::string& area); + void terminateApp(unsigned layer); + void update(); + void undo(); + + // Debug + void dump(); + + private: + LayerState tmp_state; + LayerState state; + unsigned uuid; + std::string name = ""; // Layer name + MANAGEMENT_TYPE type; + std::string role_list; + std::vector area_list; + std::vector id_list; + unsigned id_begin; + unsigned id_end; +}; + +} // namespace wm + +#endif // WM_LAYER_HPP diff --git a/demo3/common/agl-service-windowmanager/src/wm_layer_control.cpp b/demo3/common/agl-service-windowmanager/src/wm_layer_control.cpp new file mode 100644 index 0000000..80dc0c9 --- /dev/null +++ b/demo3/common/agl-service-windowmanager/src/wm_layer_control.cpp @@ -0,0 +1,911 @@ +/* + * Copyright (c) 2017 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include "wm_layer_control.hpp" +#include "wm_layer.hpp" +#include "wm_client.hpp" +#include "request.hpp" +#include "json_helper.hpp" + +#define LC_AREA_PATH "/etc/areas.json" +#define LC_LAYER_SETTING_PATH "/etc/layers_setting.json" +#define LC_DEFAULT_AREA "fullscreen" +#define BACK_GROUND_LAYER "BackGroundLayer" + +using std::string; +using std::vector; +using std::shared_ptr; + +namespace wm { + +LayerControl* g_lc_ctxt; + +static void createCallback_static(ilmObjectType object, + t_ilm_uint id, + t_ilm_bool created, + void* data) +{ + static_cast(data)->dispatchCreateEvent(object, id, created); +} + +static void surfaceCallback_static(t_ilm_surface surface, + struct ilmSurfaceProperties* surface_prop, + t_ilm_notification_mask mask) +{ + g_lc_ctxt->dispatchSurfacePropChangeEvent(surface, surface_prop, mask); +} + +static void layerCallback_static(t_ilm_layer layer, + struct ilmLayerProperties* layer_prop, + t_ilm_notification_mask mask) +{ + g_lc_ctxt->dispatchLayerPropChangeEvent(layer, layer_prop, mask); +} + +LayerControl::LayerControl(const std::string& root, const std::string& ecu_name) +{ + string area_path = root + LC_AREA_PATH; + string layer_path= root + LC_LAYER_SETTING_PATH; + // load layers.setting.json + WMError ret = this->loadLayerSetting(layer_path); + assert(ret == WMError::SUCCESS); + // load areas.json + ret = this->loadAreasConfigFile(area_path, ecu_name); + assert(ret == WMError::SUCCESS); +} + +WMError LayerControl::init(const LayerControlCallbacks& cb) +{ + HMI_DEBUG("Initialize of ilm library and display"); + t_ilm_uint num = 0; + t_ilm_uint *ids; + int cnt = 0; + ilmErrorTypes rc = ilm_init(); + + while (rc != ILM_SUCCESS) + { + cnt++; + if (20 <= cnt) + { + HMI_ERROR("Could not connect to compositor"); + goto lc_init_error; + } + HMI_ERROR("Wait to start weston ..."); + sleep(1); + rc = ilm_init(); + } + if(rc != ILM_SUCCESS) goto lc_init_error; + + // Get current screen setting + rc = ilm_getScreenIDs(&num, &ids); + + if(rc != ILM_SUCCESS) goto lc_init_error; + + for(unsigned i = 0; i < num; i++) + { + HMI_INFO("get screen: %d", ids[i]); + } + // Currently, 0 is only available + this->screenID = ids[0]; + + if (1 < num) + { + // TODO: set remote screen id + HMI_INFO("There is remote screen (id:%d)", ids[1]); + this->remoteScreenID = ids[1]; + } + else + { + HMI_INFO("There is no remote screen"); + this->remoteScreenID = -1; + } + + rc = ilm_getPropertiesOfScreen(this->screenID, &this->screen_prop); + + if(rc != ILM_SUCCESS) goto lc_init_error; + + // Register Callback to Window Manager and from ILM + this->cb = cb; + ilm_registerNotification(createCallback_static, this); + + return WMError::SUCCESS; + +lc_init_error: + HMI_ERROR("Failed to initialize. Terminate WM"); + + return WMError::FAIL; +} + +void LayerControl::createNewLayer(unsigned id) +{ + HMI_INFO("create new ID :%d", id); + struct rect rct = this->area2size[LC_DEFAULT_AREA]; + ilm_layerCreateWithDimension(&id, rct.w, rct.h); + //ilm_layerSetSourceRectangle(id, rct.x, rct.y, rct.w, rct.h); + ilm_layerSetDestinationRectangle(id, this->offset_x, this->offset_y, rct.w, rct.h); + ilm_layerSetOpacity(id, 1.0); + ilm_layerSetVisibility(id, ILM_FALSE); + ilm_commitChanges(); + auto wm_layer = getWMLayer(id); + wm_layer->addLayerToState(id); + this->renderLayers(); + this->renderLayersRemote(); +} + +void LayerControl::createNewRemoteLayer(unsigned id) +{ + HMI_INFO("create new ID :%d (For remote layer)", id); + struct rect rct = {640, 720, 0, 0}; + ilm_layerCreateWithDimension(&id, rct.w, rct.h); + //ilm_layerSetSourceRectangle(id, rct.x, rct.y, rct.w, rct.h); + ilm_layerSetDestinationRectangle(id, this->offset_x, this->offset_y, rct.w, rct.h); + ilm_layerSetOpacity(id, 1.0); + ilm_layerSetVisibility(id, ILM_FALSE); + ilm_commitChanges(); + auto wm_layer = getWMLayer(id); + wm_layer->addLayerToState(id); + this->renderLayers(); + this->renderLayersRemote(); +} + +unsigned LayerControl::getNewLayerID(const string& role, string* layer_name) +{ + unsigned ret = 0; + for(const auto& l: this->wm_layers) + { + ret = l->getNewLayerID(role); + if(ret != 0) + { + *layer_name = l->layerName(); + unsigned uid = l->getUuid(); + this->lid2wmlid[ret] = uid; + break; + } + } + return ret; +} + +shared_ptr LayerControl::getWMLayer(unsigned layer) +{ + unsigned uuid = this->lid2wmlid[layer]; + return this->wm_layers[uuid]; +} + +std::shared_ptr LayerControl::getWMLayer(std::string layer_name) +{ + for(auto &l : this->wm_layers) + { + if(l->layerName() == layer_name) + { + return l; + } + } + return nullptr; +} + +struct rect LayerControl::getAreaSize(const std::string& area) +{ + return area2size[area]; +} + +void LayerControl::setupArea(const rectangle& base_rct, double scaling) +{ + this->scaling = scaling; + this->offset_x = base_rct.left(); + this->offset_y = base_rct.top(); + + for (auto &i : this->area2size) + { + i.second.x = static_cast(scaling * i.second.x + 0.5); + i.second.y = static_cast(scaling * i.second.y + 0.5); + i.second.w = static_cast(scaling * i.second.w + 0.5); + i.second.h = static_cast(scaling * i.second.h + 0.5); + + HMI_DEBUG("area:%s size(after) : x:%d y:%d w:%d h:%d", + i.first.c_str(), i.second.x, i.second.y, i.second.w, i.second.h); + } +} + +Screen LayerControl::getScreenInfo() +{ + return Screen(this->screen_prop.screenWidth, this->screen_prop.screenHeight); +} + +double LayerControl::scale() +{ + return this->scaling; +} + +WMError LayerControl::updateLayer(LayerState& layer_state) +{ + return WMError::SUCCESS; +} + +WMError LayerControl::renderLayers() +{ + HMI_INFO("Commit change"); + WMError rc = WMError::SUCCESS; + + // Check the number of layers + vector ivi_l_ids; + for(auto& l : this->wm_layers) + { + auto state = l->getLayerState(); + HMI_DEBUG("layer %s", l->layerName().c_str()); + for(const auto& id : state.getIviIdList()) + { + HMI_DEBUG("Add %d", id); + ivi_l_ids.push_back(id); + } + } + + // Create render order + t_ilm_layer* id_array = new t_ilm_layer[ivi_l_ids.size()]; + if(id_array == nullptr) + { + HMI_WARNING("short memory"); + this->undoUpdate(); + return WMError::FAIL; + } + int count = 0; + for(const auto& i : ivi_l_ids) + { + id_array[count] = i; + ++count; + } + + // Display + ilmErrorTypes ret = ilm_displaySetRenderOrder(this->screenID, id_array, ivi_l_ids.size()); + if(ret != ILM_SUCCESS) + { + this->undoUpdate(); + rc = WMError::FAIL; + } + else + { + for(auto& l : this->wm_layers) + { + l->update(); + } + } + ilm_commitChanges(); + delete id_array; + return rc; +} + +WMError LayerControl::renderLayersRemote() +{ + HMI_INFO("Commit change"); + WMError rc = WMError::SUCCESS; + + if (0 > this->remoteScreenID) + { + return rc; + } + + // Check the number of layers + vector ivi_l_ids; + for(auto& l : this->wm_remote_layers) + { + auto state = l->getLayerState(); + HMI_DEBUG("layer %s", l->layerName().c_str()); + for(const auto& id : state.getIviIdList()) + { + HMI_DEBUG("Add %d", id); + ivi_l_ids.push_back(id); + } + } + + if (0 == ivi_l_ids.size()) + { + ilm_displaySetRenderOrder(this->remoteScreenID, nullptr, 0); + return rc; + } + + // Create render order + t_ilm_layer* id_array = new t_ilm_layer[ivi_l_ids.size()]; + if(id_array == nullptr) + { + HMI_WARNING("short memory"); + this->undoUpdate(); + return WMError::FAIL; + } + int count = 0; + for(const auto& i : ivi_l_ids) + { + id_array[count] = i; + ++count; + } + + // Display + ilmErrorTypes ret = ilm_displaySetRenderOrder(this->remoteScreenID, id_array, ivi_l_ids.size()); + if(ret != ILM_SUCCESS) + { + this->undoUpdate(); + rc = WMError::FAIL; + } + else + { + for(auto& l : this->wm_remote_layers) + { + l->update(); + } + } + ilm_commitChanges(); + delete id_array; + return rc; +} + +WMError LayerControl::setXDGSurfaceOriginSize(unsigned surface) +{ + WMError ret = WMError::NOT_REGISTERED; + ilmSurfaceProperties prop; + ilmErrorTypes rc = ilm_getPropertiesOfSurface(surface, &prop); + if(rc == ILM_SUCCESS) + { + HMI_INFO("xdg surface info %d, %d", prop.origSourceWidth, prop.origSourceHeight); + ilm_surfaceSetSourceRectangle(surface, 0, 0, prop.origSourceWidth, prop.origSourceHeight); + ret = WMError::SUCCESS; + } + return ret; +} + + +void LayerControl::undoUpdate() +{ + for(auto& l : this->wm_layers) + { + l->undo(); + } +} + +WMError LayerControl::loadLayerSetting(const string &path) +{ + HMI_DEBUG("loading WMLayer(Application Containers) Setting from %s", path); + + json_object *json_obj, *json_cfg; + int ret = jh::inputJsonFilie(path.c_str(), &json_obj); + if (0 > ret) + { + HMI_ERROR("Could not open %s", path.c_str()); + return WMError::FAIL; + } + HMI_INFO("json_obj dump:%s", json_object_get_string(json_obj)); + + if (!json_object_object_get_ex(json_obj, "mappings", &json_cfg)) + { + HMI_ERROR("Parse Error!!"); + return WMError::FAIL; + } + + int len = json_object_array_length(json_cfg); + HMI_DEBUG("json_cfg len:%d", len); + + for (int i = 0; i < len; i++) + { + json_object *json_tmp = json_object_array_get_idx(json_cfg, i); + HMI_DEBUG("> json_tmp dump:%s", json_object_get_string(json_tmp)); + + this->wm_layers.emplace_back(std::make_shared(json_tmp, i)); + } + json_object_put(json_obj); + + return WMError::SUCCESS; +} + +WMError LayerControl::loadAreasConfigFile(const std::string& path, const std::string& ecu_name) +{ + // Load areas config file + json_object *json_obj; + int ret = jh::inputJsonFilie(path.c_str(), &json_obj); + if (0 > ret) + { + HMI_ERROR("Could not open %s", path.c_str()); + return WMError::FAIL; + } + HMI_INFO("json_obj dump:%s", json_object_get_string(json_obj)); + + // Parse ecus + json_object *json_cfg; + if (!json_object_object_get_ex(json_obj, "ecus", &json_cfg)) + { + HMI_ERROR("Parse Error!!"); + return WMError::FAIL; + } + + int num_ecu = json_object_array_length(json_cfg); + HMI_DEBUG("json_cfg(ecus) len:%d", num_ecu); + + const char* c_ecu_name; + json_object *json_ecu; + for (int i = 0; i < num_ecu; i++) + { + json_ecu= json_object_array_get_idx(json_cfg, i); + + c_ecu_name = jh::getStringFromJson(json_ecu, "name"); + if (nullptr == c_ecu_name) + { + HMI_ERROR("Parse Error!!"); + return WMError::FAIL; + } + + if (ecu_name == string(c_ecu_name)) + { + break; + } + else + { + json_ecu = nullptr; + } + } + + if (!json_ecu) + { + HMI_ERROR("Areas for ecu:%s is NOT exist!!", ecu_name.c_str()); + return WMError::FAIL; + } + + // Parse screens + if (!json_object_object_get_ex(json_ecu, "screens", &json_cfg)) + { + HMI_ERROR("Parse Error!!"); + return WMError::FAIL; + } + + int num_screen = json_object_array_length(json_cfg); + HMI_DEBUG("json_cfg(screens) len:%d", num_screen); + + int screen_id; + json_object *json_screen; + for (int i = 0; i < num_screen; i++) + { + json_screen = json_object_array_get_idx(json_cfg, i); + HMI_INFO("json_cfg dump:%s", json_object_get_string(json_cfg)); + + // TODO: Currently only one display is connected to a ECU. + + screen_id = jh::getIntFromJson(json_screen, "id"); + + // Parse areas + json_object *json_tmp; + if (!json_object_object_get_ex(json_screen, "areas", &json_tmp)) + { + HMI_ERROR("Parse Error!!"); + return WMError::FAIL; + } + + int num_area = json_object_array_length(json_tmp); + HMI_DEBUG("json_tmp(areas) len:%d", num_area); + + const char *area; + for (int j = 0; j < num_area; j++) + { + json_object *json_area = json_object_array_get_idx(json_tmp, j); + + area = jh::getStringFromJson(json_area, "name"); + if (nullptr == area) + { + HMI_ERROR("Parse Error!!"); + return WMError::FAIL; + } + HMI_DEBUG("> area:%s", area); + + json_object *json_rect; + if (!json_object_object_get_ex(json_area, "rect", &json_rect)) + { + HMI_ERROR("Parse Error!!"); + return WMError::FAIL; + } + + struct rect area_size; + area_size.x = jh::getIntFromJson(json_rect, "x"); + area_size.y = jh::getIntFromJson(json_rect, "y"); + area_size.w = jh::getIntFromJson(json_rect, "w"); + area_size.h = jh::getIntFromJson(json_rect, "h"); + + this->area2size[area] = area_size; + } + + // Check + for (const auto& itr : this->area2size) + { + HMI_DEBUG("area:%s x:%d y:%d w:%d h:%d", + itr.first.c_str(), itr.second.x, itr.second.y, + itr.second.w, itr.second.h); + } + } + + // Release json_object + json_object_put(json_obj); + + return WMError::SUCCESS; +} + +WMError LayerControl::layoutChange(const WMAction& action) +{ + if ((action.visible == TaskVisible::INVISIBLE) || + (action.visible == TaskVisible::REQ_REMOTE_VISIBLE) || + (action.visible == TaskVisible::REQ_REMOTE_INVISIBLE)) + { + // Visibility is not change -> no redraw is required + return WMError::SUCCESS; + } + if(action.client == nullptr) + { + HMI_SEQ_ERROR(action.req_num, "client may vanish"); + return WMError::NOT_REGISTERED; + } + unsigned layer = action.client->layerID(); + unsigned surface = action.client->surfaceID(); + + auto rect = this->getAreaSize(action.area); + HMI_DEBUG("Set layout %d, %d, %d, %d",rect.x, rect.y, rect.w, rect.h); + + // TO BE FIXED: + // Sometimes, ivi_wm_surface_size signal doesn't reach window manager, + // then, Window Manager can't set source size. + // This fixes it but it takes about 200ns(on R-Car M3) wastefully + ilmSurfaceProperties sp; + ilm_getPropertiesOfSurface(surface, &sp); + if(sp.origSourceHeight != sp.sourceHeight) { + HMI_SEQ_NOTICE(action.req_num, "WORK AROUND: set source size w:%d h%d", sp.origSourceWidth, sp.origSourceHeight); + ilm_surfaceSetSourceRectangle(surface, 0, 0, sp.origSourceWidth, sp.origSourceHeight); + ilm_commitChanges(); + } + + ilm_surfaceSetDestinationRectangle(surface, rect.x, rect.y, rect.w, rect.h); + ilm_commitChanges(); + for(auto &wm_layer: this->wm_layers) + { + // Store the state who is assigned to the area + if(wm_layer->hasLayerID(layer)) + { + wm_layer->setAreaToState(action.client->appID(), action.area); + /* TODO: manipulate state directly + LayerState ls = wm_layer->getLayerState(); + ls.setArea(action.client->appID(), action.area); + wm_layer->dump(); */ + } + } + + return WMError::SUCCESS; +} + +WMError LayerControl::visibilityChange(const WMAction& action) +{ + WMError ret = WMError::FAIL; + if(action.client == nullptr) + { + HMI_SEQ_ERROR(action.req_num, "client may vanish"); + return WMError::NOT_REGISTERED; + } + + if (action.visible == TaskVisible::VISIBLE) + { + ret = this->makeVisible(action.client); + } + else if (action.visible == TaskVisible::INVISIBLE) + { + ret = this->makeInvisible(action.client); + } + else if (action.visible == TaskVisible::REMOTE_VISIBLE) + { + ret = this->makeRemoteVisible(action.client); + } + else if (action.visible == TaskVisible::REMOTE_INVISIBLE) + { + ret = this->makeRemoteInvisible(action.client); + } + else // TaskVisible::REQ_REMOTE_VISIBLE || TaskVisible::REQ_REMOTE_INVISIBLE + { + // Visibility is not change + ret = WMError::SUCCESS; + } + + return ret; +} + +void LayerControl::terminateApp(const shared_ptr client) +{ + for(auto& l : this->wm_layers) + { + l->terminateApp(client->layerID()); + } +} + +void LayerControl::dispatchCreateEvent(ilmObjectType object, unsigned id, bool created) +{ + if (ILM_SURFACE == object) + { + if (created) + { + ilmSurfaceProperties sp; + ilmErrorTypes rc; + rc = ilm_getPropertiesOfSurface(id, &sp); + if(rc != ILM_SUCCESS) + { + HMI_ERROR("Failed to get surface %d property due to %d", id, ilm_getError()); + return; + } + this->cb.surfaceCreated(sp.creatorPid, id); + ilm_surfaceSetSourceRectangle(id, 0, 0, sp.origSourceWidth, sp.origSourceHeight); + ilm_surfaceAddNotification(id, surfaceCallback_static); + ilm_surfaceSetVisibility(id, ILM_TRUE); + ilm_surfaceSetType(id, ILM_SURFACETYPE_DESKTOP); + } + else + { + this->cb.surfaceDestroyed(id); + } + } + if (ILM_LAYER == object) + { + if(created) + { + ilm_layerAddNotification(id, layerCallback_static); + } + else + { + // Ignore here. Nothing to do currently. + // Process of application dead is handled by Window Manager + // from binder notification + } + } +} + +void LayerControl::dispatchSurfacePropChangeEvent(unsigned id, + struct ilmSurfaceProperties* sprop, + t_ilm_notification_mask mask) +{ + /* + ILM_NOTIFICATION_CONTENT_AVAILABLE & ILM_NOTIFICATION_CONTENT_REMOVED + are not handled here, handled in create/destroy event + */ + if (ILM_NOTIFICATION_VISIBILITY & mask) + { + HMI_DEBUG("surface %d turns visibility %d", id, sprop->visibility); + } + if (ILM_NOTIFICATION_OPACITY & mask) + { + HMI_DEBUG("surface %d turns opacity %f", id, sprop->opacity); + } + if (ILM_NOTIFICATION_SOURCE_RECT & mask) + { + HMI_DEBUG("surface %d source rect changes", id); + } + if (ILM_NOTIFICATION_DEST_RECT & mask) + { + HMI_DEBUG("surface %d dest rect changes", id); + } + if (ILM_NOTIFICATION_CONFIGURED & mask) + { + HMI_DEBUG("surface %d size %d, %d, %d, %d", id, + sprop->sourceX, sprop->sourceY, sprop->origSourceWidth, sprop->origSourceHeight); + ilm_surfaceSetSourceRectangle(id, 0, 0, sprop->origSourceWidth, sprop->origSourceHeight); + } +} + +void LayerControl::dispatchLayerPropChangeEvent(unsigned id, + struct ilmLayerProperties* lprop, + t_ilm_notification_mask mask) +{ + if (ILM_NOTIFICATION_VISIBILITY & mask) + { + HMI_DEBUG("layer %d turns visibility %d", id, lprop->visibility); + } + if (ILM_NOTIFICATION_OPACITY & mask) + { + HMI_DEBUG("layer %d turns opacity %f", id, lprop->opacity); + } + if (ILM_NOTIFICATION_SOURCE_RECT & mask) + { + HMI_DEBUG("layer %d source rect changes", id); + } + if (ILM_NOTIFICATION_DEST_RECT & mask) + { + HMI_DEBUG("layer %d dest rect changes", id); + } +} + +WMError LayerControl::makeVisible(const shared_ptr client) +{ + WMError ret = WMError::SUCCESS; + // Don't check here the client is not nullptr + unsigned layer = client->layerID(); + + this->moveForeGround(client); + + ilm_layerSetVisibility(layer, ILM_TRUE); + + /* for(auto& wm_layer : this->wm_layers) + { + if(wm_layer->hasLayerID(layer)) + { + LayerState ls = wm_layer->getLayerState(); + ls.addLayer(layer);; + } + } */ + + // Move foreground from back ground layer + /* for(auto& wm_layer : this->wm_layers) + { + if(wm_layer->layerName() == "BackGroundLayer") + { + if(wm_layer->hasRole(client->role())) + { + LayerState ls = wm_layer->getLayerState(); + ls.removeLayer(layer); + } + break; + } + } */ + + return ret; +} + +WMError LayerControl::makeInvisible(const shared_ptr client) +{ + WMError ret = WMError::SUCCESS; + unsigned layer = client->layerID(); // Don't check here the client is not nullptr + + bool mv_ok = this->moveBackGround(client); + + if(!mv_ok) + { + HMI_INFO("make invisible client %s", client->appID().c_str()); + ilm_layerSetVisibility(layer, ILM_FALSE); + } + + //ilm_layerSetDestinationRectangle(layer, 0, 0, 0, 0); + + /* for(auto& wm_layer : this->wm_layers) + { + if(wm_layer->hasLayerID(layer)) + { + LayerState ls = wm_layer->getLayerState(); + ls.removeLayer(layer);; + } + } */ + + + + return ret; +} + +WMError LayerControl::makeRemoteVisible(const shared_ptr client) +{ + WMError ret = WMError::SUCCESS; + unsigned layer = client->layerID(); // Don't check here the client is not nullptr + + if (0 > this->remoteScreenID) + { + return ret; + } + + // TODO: Currently there is only one remote screen. + for (auto itr = this->wm_layers.begin(); itr != this->wm_layers.end(); ++itr) + { + if((*itr)->hasLayerID(layer)) + { + HMI_DEBUG("Add layer:%d to remote screen:%d", layer, this->remoteScreenID); + this->wm_remote_layers.push_back(*itr); + this->wm_layers.erase(itr); + } + + if (this->wm_layers.end() == itr) + { + HMI_DEBUG("iteretor indicates end of vector of wm_layers"); + break; + } + } + + ilm_layerSetVisibility(layer, ILM_TRUE); + + return ret; +} + +WMError LayerControl::makeRemoteInvisible(const shared_ptr client) +{ + WMError ret = WMError::SUCCESS; + unsigned layer = client->layerID(); // Don't check here the client is not nullptr + + if (0 > this->remoteScreenID) + { + return ret; + } + + // TODO: Currently there is only one remote screen. + for (auto itr = this->wm_remote_layers.begin(); + itr != this->wm_remote_layers.end(); ++itr) + { + if((*itr)->hasLayerID(layer)) + { + HMI_DEBUG("Remove layer:%d from remote screen:%d", layer, this->remoteScreenID); + this->wm_layers.push_back(*itr); + this->wm_remote_layers.erase(itr); + } + + if (this->wm_remote_layers.end() == itr) + { + HMI_DEBUG("iteretor indicates end of vector of wm_remote_layers"); + break; + } + } + + ilm_layerSetVisibility(layer, ILM_FALSE); + + return ret; +} + +bool LayerControl::moveBackGround(const shared_ptr client) +{ + bool ret = false; + + // Move background from foreground layer + auto bg = this->getWMLayer(BACK_GROUND_LAYER); + if(bg != nullptr) + { + HMI_DEBUG("client %s role %s", client->appID().c_str(), client->role().c_str()); + unsigned layer = client->layerID(); + if(bg->hasRole(client->role())) + { + HMI_INFO("%s go to background", client->appID().c_str()); + bg->addLayerToState(layer); + auto wm_layer = this->getWMLayer(layer); + wm_layer->removeLayerFromState(layer); + /* TODO: manipulate state directly + LayerState bg_ls = bg->getLayerState(); + bg_ls.addLayer(layer); + LayerState ls = wm_layer->getLayerState(); + ls.removeLayer(layer); */ + bg->dump(); + wm_layer->dump(); + ret = true; + } + } + return ret; +} + +bool LayerControl::moveForeGround(const shared_ptr client) +{ + bool ret = false; + + // Move foreground from foreground layer + auto bg = this->getWMLayer(BACK_GROUND_LAYER); + if(bg != nullptr) + { + if(bg->hasRole(client->role())) + { + unsigned layer = client->layerID(); + HMI_INFO("%s go to foreground", client->appID().c_str()); + bg->removeLayerFromState(layer); + auto wm_layer = this->getWMLayer(layer); + wm_layer->addLayerToState(layer); + /* TODO: manipulate state directly + LayerState bg_ls = bg->getLayerState(); + bg_ls.removeLayer(layer); + LayerState ls = wm_layer->getLayerState(); + ls.addLayer(layer); */ + bg->dump(); + wm_layer->dump(); + ret = true; + } + } + return ret; +} + +} // namespace wm diff --git a/demo3/common/agl-service-windowmanager/src/wm_layer_control.hpp b/demo3/common/agl-service-windowmanager/src/wm_layer_control.hpp new file mode 100644 index 0000000..fa7c7cb --- /dev/null +++ b/demo3/common/agl-service-windowmanager/src/wm_layer_control.hpp @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2017 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include "wm_error.hpp" +#include "util.hpp" + +namespace wm { + +class Screen { + public: + Screen(unsigned w, unsigned h) : _width(w), _height(h){} + unsigned width() { return _width; } + unsigned height() { return _height; } + private: + unsigned _width; + unsigned _height; + unsigned _pysical_width = 0; + unsigned _pysical_height = 0; +}; + +class LayerControlCallbacks { + public: + LayerControlCallbacks() {}; + ~LayerControlCallbacks() = default; + LayerControlCallbacks(const LayerControlCallbacks &obj) = default; + + // callback functions + std::function surfaceCreated; + std::function surfaceDestroyed; + /* std::function surfaceDestroyed; + std::function layerCreated; + std::function layerDestroyed; */ +}; + +class WMLayer; +class LayerState; +class WMAction; +class WMClient; + +class LayerControl +{ + public: + explicit LayerControl(const std::string& root, const std::string& ecu_name); + ~LayerControl() = default; + WMError init(const LayerControlCallbacks& cb); + void createNewLayer(unsigned id); + void createNewRemoteLayer(unsigned id); + unsigned getNewLayerID(const std::string& role, std::string* layer_name); + std::shared_ptr getWMLayer(unsigned layer); + std::shared_ptr getWMLayer(std::string layer_name); + struct rect getAreaSize(const std::string& area); + void setupArea(const rectangle& base_rct, double scaling); + Screen getScreenInfo(); + double scale(); + // void setRenderOrder(const std::vector layer_render_order); + // std::vector getAllRenderOrder(); + // std::vector>& getAllLayers(); + // std::vector getRenderOrder(const std::string& layer_name); + WMError updateLayer(LayerState& layer_state); + WMError renderLayers(); + WMError renderLayersRemote(); + WMError setXDGSurfaceOriginSize(unsigned surface); + // WMError renderWMLayers(); + void undoUpdate(); + WMError layoutChange(const WMAction& action); + WMError visibilityChange(const WMAction &action); + void terminateApp(const std::shared_ptr client); + + // Don't use this function. + void dispatchCreateEvent(ilmObjectType object, unsigned id, bool created); + void dispatchSurfacePropChangeEvent(unsigned id, struct ilmSurfaceProperties*, t_ilm_notification_mask); + void dispatchLayerPropChangeEvent(unsigned id, struct ilmLayerProperties*, t_ilm_notification_mask); + + private: + WMError makeVisible(const std::shared_ptr client); + WMError makeInvisible(const std::shared_ptr client); + bool moveForeGround(const std::shared_ptr client); + bool moveBackGround(const std::shared_ptr client); + WMError loadLayerSetting(const std::string& path); + WMError loadAreasConfigFile(const std::string& path, const std::string& ecu_name); + + // For Remote + WMError makeRemoteVisible(const std::shared_ptr client); + WMError makeRemoteInvisible(const std::shared_ptr client); + + std::vector> wm_layers; + std::vector> wm_remote_layers; + std::unordered_map lid2wmlid; + std::unordered_map area2size; + unsigned screenID; + signed remoteScreenID; + struct ilmScreenProperties screen_prop; + double scaling; + int offset_x; + int offset_y; + LayerControlCallbacks cb; +}; + +} // namespace wm -- cgit 1.2.3-korg