aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYuta Doi <yuta-d@witz-inc.co.jp>2018-04-27 19:01:36 +0900
committerYuta Doi <yuta-d@witz-inc.co.jp>2018-04-27 19:01:36 +0900
commit55be85ed4bdfea6fc037d781b8cd8f58487718d1 (patch)
tree279a88e26c74aee58f11e2a5d20348569ce34667
parentd50188f726b15a0ae2777bf2d91ee88836feeac5 (diff)
Add PolicyManager, related classes and some config files
- PolicyManager Decide next layout by using occured event and current state based on policy table. This PolicyManger is reference and the OEMs can replace it. - LayoutManager Change the current layout to the layout which decided by PolicyManager. NOTE: The functions of this class had been included in App class. The part of function of this class remain there yet. - LowCanClient Receive the CAN signal from low level CAN service. - app.db Define the applications name and its role. This file will be deleted when the names and roles can be given by other module. - layout.cb Define the layouts and areas which are included by the layout. - role.db Define the roles of the applications. Change-Id: I2f84bdf5e68355e022f516cee9a1db88efe58825 Signed-off-by: Yuta Doi <yuta-d@witz-inc.co.jp>
-rw-r--r--CMakeLists.txt3
-rw-r--r--layers.json2
-rw-r--r--package/root/config.xml1
-rw-r--r--src/CMakeLists.txt24
-rw-r--r--src/app.cpp902
-rw-r--r--src/app.hpp48
-rw-r--r--src/db/app.db68
-rw-r--r--src/json_helper.cpp54
-rw-r--r--src/json_helper.hpp7
-rw-r--r--src/layout.cpp17
-rw-r--r--src/layout.hpp42
-rw-r--r--src/layout_manager/db/layout.db158
-rw-r--r--src/layout_manager/layout.cpp570
-rw-r--r--src/layout_manager/layout.hpp84
-rw-r--r--src/low_can_client.cpp193
-rw-r--r--src/low_can_client.hpp72
-rw-r--r--src/main.cpp79
-rw-r--r--src/policy_manager/CMakeLists.txt65
-rw-r--r--src/policy_manager/db/role.db44
-rw-r--r--src/policy_manager/policy_manager.cpp497
-rw-r--r--src/policy_manager/policy_manager.hpp67
-rw-r--r--src/policy_manager/zipc/category.db32
-rw-r--r--src/policy_manager/zipc/dummy_stm.c212
-rw-r--r--src/policy_manager/zipc/dummy_stm.h127
24 files changed, 2922 insertions, 446 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4ed6d6d..4762e0d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -79,4 +79,7 @@ set(SANITIZER_MODE "none" CACHE STRING "Build using a specific sanitizer (e.g. '
set(LINK_LIBCXX OFF CACHE BOOL "Link against LLVMs libc++")
+set(PLUGIN_PM policy_manager)
+add_subdirectory(src/${PLUGIN_PM})
+
add_subdirectory(src)
diff --git a/layers.json b/layers.json
index cf7ed34..1d2390d 100644
--- a/layers.json
+++ b/layers.json
@@ -22,7 +22,7 @@
"comment": "Single layer map for the HomeScreen"
},
{
- "role": "Music|Video|WebBrowser|MediaPlayer|Radio|Phone|Navigation|HVAC|Settings|Dashboard|POI|Mixer|Fallback",
+ "role": "Music|Video|WebBrowser|MediaPlayer|Radio|Phone|Navigation|HVAC|Settings|Dashboard|POI|Mixer|Splitable1|Splitable2|Fallback",
"name": "apps",
"layer_id": 1001,
"area": { "type": "rect", "rect": { "x": 0, "y": 218, "width": -1, "height": -433 } },
diff --git a/package/root/config.xml b/package/root/config.xml
index 6392dc3..d375905 100644
--- a/package/root/config.xml
+++ b/package/root/config.xml
@@ -14,6 +14,7 @@
<param name="windowmanager" value="ws" />
</feature>
<feature name="urn:AGL:widget:required-api">
+ <param name="low-can" value="ws" />
<param name="lib/windowmanager-service.so" value="local" />
</feature>
</widget>
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index cc3efc3..a632b9c 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -25,14 +25,17 @@ set(CMAKE_SHARED_MODULE_PREFIX "")
set(TARGETS_WM windowmanager-service)
+# Set use STM name
+set(USE_STM_NAME zipc)
+
add_library(${TARGETS_WM} MODULE
main.cpp
wayland_ivi_wm.cpp
wayland_ivi_wm.hpp
util.cpp
util.hpp
- layout.cpp
- layout.hpp
+ layout_manager/layout.cpp
+ layout_manager/layout.hpp
${IVI_CON_PROTO}
json_helper.cpp
json_helper.hpp
@@ -43,20 +46,28 @@ add_library(${TARGETS_WM} MODULE
controller_hooks.hpp
config.cpp
config.hpp
- policy.hpp)
+ low_can_client.cpp
+ low_can_client.hpp
+)
target_include_directories(${TARGETS_WM}
PRIVATE
${AFB_INCLUDE_DIRS}
${SD_INCLUDE_DIRS}
../include
- ../src)
+ ../src
+ ../src/layout_manager
+ ../src/${PLUGIN_PM}
+ ../src/${PLUGIN_PM}/${USE_STM_NAME}
+)
target_link_libraries(${TARGETS_WM}
PRIVATE
${AFB_LIBRARIES}
${WLC_LIBRARIES}
- ${SD_LIBRARIES})
+ ${SD_LIBRARIES}
+ ${PLUGIN_PM}
+)
target_compile_definitions(${TARGETS_WM}
PRIVATE
@@ -116,6 +127,9 @@ add_custom_command(TARGET ${TARGETS_WM} POST_BUILD
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.json ${PROJECT_BINARY_DIR}/package/root/etc
+ COMMAND cp -f ${PROJECT_SOURCE_DIR}/src/layout_manager/db/layout.db ${PROJECT_BINARY_DIR}/package/root/etc
+ COMMAND cp -f ${PROJECT_SOURCE_DIR}/src/${PLUGIN_PM}/db/role.db ${PROJECT_BINARY_DIR}/package/root/etc
+ COMMAND cp -f ${PROJECT_SOURCE_DIR}/src/db/app.db ${PROJECT_BINARY_DIR}/package/root/etc
)
add_custom_target(package DEPENDS ${PROJECT_BINARY_DIR}/package/root
diff --git a/src/app.cpp b/src/app.cpp
index 937da6a..20f5b56 100644
--- a/src/app.cpp
+++ b/src/app.cpp
@@ -34,6 +34,7 @@
#include <json.hpp>
#include <regex>
#include <thread>
+#include <string>
namespace wm {
@@ -130,6 +131,17 @@ int App::init() {
return -1;
}
+#if 1 // @@@@@
+ // Load app.db
+ this->loadAppDb();
+#endif
+
+ // Initialize PolicyManager
+ this->pm_.initialize();
+
+ // Initialize LayoutManager
+ this->lm_.initialize();
+
// 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]);
@@ -245,328 +257,177 @@ int App::init_layers() {
return 0;
}
-void App::surface_set_layout(int surface_id, optional<int> sub_surface_id) {
- if (!this->controller->surface_exists(surface_id)) {
- HMI_ERROR("wm", "Surface %d does not exist", surface_id);
- return;
- }
-
- auto o_layer_id = this->layers.get_layer_id(surface_id);
-
- if (!o_layer_id) {
- HMI_ERROR("wm", "Surface %d is not associated with any layer!", surface_id);
- return;
- }
-
- uint32_t layer_id = *o_layer_id;
-
- auto const &layer = this->layers.get_layer(layer_id);
- auto rect = layer.value().rect;
- auto &s = this->controller->surfaces[surface_id];
-
- int x = rect.x;
- int y = rect.y;
- int w = rect.w;
- int h = rect.h;
-
- // less-than-0 values refer to MAX + 1 - $VALUE
- // e.g. MAX is either screen width or height
- if (w < 0) {
- w = this->controller->output_size.w + 1 + w;
- }
- if (h < 0) {
- h = this->controller->output_size.h + 1 + h;
- }
-
- if (sub_surface_id) {
- if (o_layer_id != this->layers.get_layer_id(*sub_surface_id)) {
- HMI_ERROR("wm",
- "surface_set_layout: layers of surfaces (%d and %d) don't match!",
- surface_id, *sub_surface_id);
- return;
- }
-
- int x_off = 0;
- int y_off = 0;
-
- // split along major axis
- if (w > h) {
- w /= 2;
- x_off = w;
- } else {
- h /= 2;
- y_off = h;
- }
-
- auto &ss = this->controller->surfaces[*sub_surface_id];
-
- HMI_DEBUG("wm", "surface_set_layout for sub surface %u on layer %u",
- *sub_surface_id, layer_id);
-
- // set destination to the display rectangle
- ss->set_destination_rectangle(x + x_off, y + y_off, w, h);
-
- this->area_info[*sub_surface_id].x = x;
- this->area_info[*sub_surface_id].y = y;
- this->area_info[*sub_surface_id].w = w;
- this->area_info[*sub_surface_id].h = h;
- }
-
- HMI_DEBUG("wm", "surface_set_layout for surface %u on layer %u", surface_id,
- layer_id);
-
- // set destination to the display rectangle
- s->set_destination_rectangle(x, y, w, h);
-
- // update area information
- this->area_info[surface_id].x = x;
- this->area_info[surface_id].y = y;
- this->area_info[surface_id].w = w;
- this->area_info[surface_id].h = h;
-
- HMI_DEBUG("wm", "Surface %u now on layer %u with rect { %d, %d, %d, %d }",
- surface_id, layer_id, x, y, w, h);
-}
-
void App::layout_commit() {
this->controller->commit_changes();
this->display->flush();
}
-void App::api_activate_surface(char const *drawing_name, char const *drawing_area, const reply_func &reply) {
- ST();
-
- auto const &surface_id = this->lookup_id(drawing_name);
-
- if (!surface_id) {
- reply("Surface does not exist");
- return;
- }
-
- if (!this->controller->surface_exists(*surface_id)) {
- reply("Surface does not exist in controller!");
- return;
- }
-
- auto layer_id = this->layers.get_layer_id(*surface_id);
-
- if (!layer_id) {
- reply("Surface is not on any layer!");
- return;
- }
-
- auto o_state = *this->layers.get_layout_state(*surface_id);
-
- if (o_state == nullptr) {
- reply("Could not find layer for surface");
- return;
- }
-
- HMI_DEBUG("wm", "surface %d is detected", *surface_id);
- reply(nullptr);
-
- struct LayoutState &state = *o_state;
-
- // disable layers that are above our current layer
- for (auto const &l : this->layers.mapping) {
- if (l.second.layer_id <= *layer_id) {
- continue;
- }
-
- bool flush = false;
- if (l.second.state.main != -1) {
- this->deactivate(l.second.state.main);
- l.second.state.main = -1;
- flush = true;
- }
-
- if (l.second.state.sub != -1) {
- this->deactivate(l.second.state.sub);
- l.second.state.sub = -1;
- flush = true;
- }
-
- if (flush) {
- this->layout_commit();
- }
- }
-
- auto layer = this->layers.get_layer(*layer_id);
-
- if (state.main == -1) {
- this->try_layout(
- state, LayoutState{*surface_id}, [&] (LayoutState const &nl) {
- HMI_DEBUG("wm", "Layout: %s", kNameLayoutNormal);
- this->surface_set_layout(*surface_id);
- state = nl;
-
- // Commit for configuraton
- this->layout_commit();
-
- std::string str_area = std::string(kNameLayoutNormal) + "." + std::string(kNameAreaFull);
- compositor::rect area_rect = this->area_info[*surface_id];
- this->emit_syncdraw(drawing_name, str_area.c_str(),
- area_rect.x, area_rect.y, area_rect.w, area_rect.h);
- this->enqueue_flushdraw(state.main);
- });
- } else {
- if (0 == strcmp(drawing_name, "HomeScreen")) {
- this->try_layout(
- state, LayoutState{*surface_id}, [&] (LayoutState const &nl) {
- HMI_DEBUG("wm", "Layout: %s", kNameLayoutNormal);
- std::string str_area = std::string(kNameLayoutNormal) + "." + std::string(kNameAreaFull);
- compositor::rect area_rect = this->area_info[*surface_id];
- this->emit_syncdraw(drawing_name, str_area.c_str(),
- area_rect.x, area_rect.y, area_rect.w, area_rect.h);
- this->enqueue_flushdraw(state.main);
- });
- } else {
- bool can_split = this->can_split(state, *surface_id);
-
- if (can_split) {
- this->try_layout(
- state,
- LayoutState{state.main, *surface_id},
- [&] (LayoutState const &nl) {
- HMI_DEBUG("wm", "Layout: %s", kNameLayoutSplit);
- std::string main =
- std::move(*this->lookup_name(state.main));
-
- this->surface_set_layout(state.main, surface_id);
- if (state.sub != *surface_id) {
- if (state.sub != -1) {
- this->deactivate(state.sub);
- }
- }
- state = nl;
-
- // Commit for configuration and visibility(0)
- this->layout_commit();
-
- std::string str_area_main = std::string(kNameLayoutSplit) + "." + std::string(kNameAreaMain);
- std::string str_area_sub = std::string(kNameLayoutSplit) + "." + std::string(kNameAreaSub);
- compositor::rect area_rect_main = this->area_info[state.main];
- compositor::rect area_rect_sub = this->area_info[*surface_id];
- this->emit_syncdraw(main.c_str(), str_area_main.c_str(),
- area_rect_main.x, area_rect_main.y,
- area_rect_main.w, area_rect_main.h);
- this->emit_syncdraw(drawing_name, str_area_sub.c_str(),
- area_rect_sub.x, area_rect_sub.y,
- area_rect_sub.w, area_rect_sub.h);
- this->enqueue_flushdraw(state.main);
- this->enqueue_flushdraw(state.sub);
- });
- } else {
- this->try_layout(
- state, LayoutState{*surface_id}, [&] (LayoutState const &nl) {
- HMI_DEBUG("wm", "Layout: %s", kNameLayoutNormal);
-
- this->surface_set_layout(*surface_id);
- if (state.main != *surface_id) {
- this->deactivate(state.main);
- }
- if (state.sub != -1) {
- if (state.sub != *surface_id) {
- this->deactivate(state.sub);
- }
- }
- state = nl;
-
- // Commit for configuraton and visibility(0)
- this->layout_commit();
-
- std::string str_area = std::string(kNameLayoutNormal) + "." + std::string(kNameAreaFull);
- compositor::rect area_rect = this->area_info[*surface_id];
- this->emit_syncdraw(drawing_name, str_area.c_str(),
- area_rect.x, area_rect.y, area_rect.w, area_rect.h);
- this->enqueue_flushdraw(state.main);
- });
- }
- }
- }
-}
-
-void App::api_deactivate_surface(char const *drawing_name, const reply_func &reply) {
- ST();
- auto const &surface_id = this->lookup_id(drawing_name);
-
- if (!surface_id) {
- reply ("Surface does not exist");
- return;
- }
-
- if (*surface_id == this->layers.main_surface) {
- reply("Cannot deactivate main_surface");
- return;
- }
-
- auto o_state = *this->layers.get_layout_state(*surface_id);
-
- if (o_state == nullptr) {
- reply("Could not find layer for surface");
- return;
- }
-
- struct LayoutState &state = *o_state;
-
- if (state.main == -1) {
- reply("No surface active");
- return;
- }
-
- // Check against main_surface, main_surface_name is the configuration item.
- if (*surface_id == this->layers.main_surface) {
- HMI_DEBUG("wm", "Refusing to deactivate main_surface %d", *surface_id);
- reply(nullptr);
- return;
- }
- if((state.main == *surface_id) && (state.sub == *surface_id)){
- reply("Surface is not active");
- return;
- }
- reply(nullptr);
-
- if (state.main == *surface_id) {
- if (state.sub != -1) {
- this->try_layout(
- state, LayoutState{state.sub, -1}, [&] (LayoutState const &nl) {
- std::string sub = std::move(*this->lookup_name(state.sub));
-
- this->deactivate(*surface_id);
- this->surface_set_layout(state.sub);
- state = nl;
-
- this->layout_commit();
- std::string str_area = std::string(kNameLayoutNormal) + "." + std::string(kNameAreaFull);
- compositor::rect area_rect = this->area_info[state.sub];
- this->emit_syncdraw(sub.c_str(), str_area.c_str(),
- area_rect.x, area_rect.y, area_rect.w, area_rect.h);
- this->enqueue_flushdraw(state.sub);
- });
- } else {
- this->try_layout(state, LayoutState{-1, -1}, [&] (LayoutState const &nl) {
- this->deactivate(*surface_id);
- state = nl;
- this->layout_commit();
- });
- }
- } else if (state.sub == *surface_id) {
- this->try_layout(
- state, LayoutState{state.main, -1}, [&] (LayoutState const &nl) {
- std::string main = std::move(*this->lookup_name(state.main));
-
- this->deactivate(*surface_id);
- this->surface_set_layout(state.main);
- state = nl;
-
- this->layout_commit();
- std::string str_area = std::string(kNameLayoutNormal) + "." + std::string(kNameAreaFull);
- compositor::rect area_rect = this->area_info[state.main];
- this->emit_syncdraw(main.c_str(), str_area.c_str(),
- area_rect.x, area_rect.y, area_rect.w, area_rect.h);
- this->enqueue_flushdraw(state.main);
- });
- }
+void App::allocateWindowResource(char const *event, char const *drawing_name,
+ char const *drawing_area, char const *role,
+ const reply_func &reply) {
+ const char* new_role = nullptr;
+ const char* new_area = nullptr;
+
+ if (0 == strcmp("activate", event)) {
+ // TODO:
+ // This process will be removed
+ // because the applications will specify role instead of drawing_name
+ {
+ if ((nullptr == role) || (0 == strcmp("", role))) {
+ HMI_DEBUG("wm", "Role is not specified, so get by using app name");
+ new_role = this->app2role_[drawing_name].c_str();
+ }
+ else {
+ new_role = role;
+ }
+ HMI_DEBUG("wm", "drawing_name:%s, new_role: %s", drawing_name, new_role);
+ }
+
+ // TODO:
+ // This process will be removed
+ // because the area "normal.full" and "normalfull" will be prohibited
+ {
+ if (nullptr == drawing_area) {
+ new_area = "normal";
+ }
+ else if (0 == strcmp("normal.full", drawing_area)) {
+ new_area = "normal";
+ }
+ else if (0 == strcmp("homescreen", new_role)) {
+ // Now homescreen specifies "normalfull"
+ new_area = "full";
+ }
+ else {
+ new_area = "normal";
+ }
+ HMI_DEBUG("wm", "drawing_area:%s, new_area: %s", drawing_area, new_area);
+ }
+ }
+ else if (0 == strcmp("deactivate", event)) {
+ // TODO:
+ // This process will be removed
+ // because the applications will specify role instead of drawing_name
+ {
+ if ((nullptr == role) || (0 == strcmp("", role))) {
+ HMI_DEBUG("wm", "Role is not specified, so get by using app name");
+ new_role = this->app2role_[drawing_name].c_str();
+ }
+ else {
+ new_role = role;
+ }
+ HMI_DEBUG("wm", "drawing_name:%s, new_role: %s", drawing_name, new_role);
+ }
+ new_area = "";
+ }
+
+ // TODO:
+ // Check role
+
+ // TODO:
+ // If event is "activate" and area is not specifid,
+ // get default value by using role
+
+ // Check Policy
+ json_object* json_in = json_object_new_object();
+ json_object* json_out = json_object_new_object();
+ json_object_object_add(json_in, "event", json_object_new_string(event));
+
+ if (nullptr != new_role) {
+ json_object_object_add(json_in, "role", json_object_new_string(new_role));
+ }
+ if (nullptr != new_area) {
+ json_object_object_add(json_in, "area", json_object_new_string(new_area));
+ }
+
+ int ret = this->pm_.checkPolicy(json_in, &json_out);
+ if (0 > ret) {
+ reply("Error checkPolicy()");
+ return;
+ }
+ else {
+ HMI_DEBUG("wm", "result: %s", json_object_get_string(json_out));
+ }
+
+ // Release json_object
+ json_object_put(json_in);
+
+ // Cat state
+ json_object* json_car;
+ if (!json_object_object_get_ex(json_out, "car", &json_car)) {
+ reply("Not found key \"car\"");
+ return;
+ }
+
+ json_bool is_changed;
+ is_changed = jh::getBoolFromJson(json_car, "is_changed");
+ if (is_changed) {
+ // Update car state
+ std::string car_state = jh::getStringFromJson(json_car, "state");
+ HMI_DEBUG("wm", "car_state: %s", car_state.c_str());
+
+ // Emit car event
+ if ("car_stop" == car_state) {
+ this->emitCarStop();
+ }
+ else if ("car_run" == car_state) {
+ this->emitCarRun();
+ }
+ else {
+ reply("Unknown car state");
+ return;
+ }
+ }
+
+ // Lamp state
+ json_object* json_lamp;
+ if (!json_object_object_get_ex(json_out, "lamp", &json_lamp)) {
+ reply("Not found key \"lamp\"");
+ return;
+ }
+
+ is_changed = jh::getBoolFromJson(json_lamp, "is_changed");
+ if (is_changed) {
+ // Update car state
+ std::string lamp_state = jh::getStringFromJson(json_lamp, "state");
+ HMI_DEBUG("wm", "lamp_state: %s", lamp_state.c_str());
+
+ // Emit lamp event
+ if ("lamp_off" == lamp_state) {
+ this->emitHeadlampOff();
+ }
+ else if ("lamp_on" == lamp_state) {
+ this->emitHeadlampOn();
+ }
+ else {
+ reply("Unknown lamp state");
+ return;
+ }
+ }
+
+ // Get category
+ const char* category = nullptr;
+ std::string str_category;
+ if (nullptr != new_role) {
+ str_category = this->pm_.roleToCategory(new_role);
+ category = str_category.c_str();
+ HMI_DEBUG("wm", "role:%s category:%s", new_role, category);
+ }
+
+ // Update layout
+ if (this->lm_.updateLayout(json_out, new_role, category)) {
+ HMI_DEBUG("wm", "Layer is changed!!");
+
+ // Allocate surface
+ this->allocateSurface();
+ }
+ else {
+ HMI_DEBUG("wm", "All layer is NOT changed!!");
+ }
+
+ // Release json_object
+ json_object_put(json_out);
+
+ return;
}
void App::enqueue_flushdraw(int surface_id) {
@@ -603,6 +464,15 @@ void App::api_enddraw(char const *drawing_name) {
void App::api_ping() { this->dispatch_pending_events(); }
+void App::send_event(char const *evname){
+ HMI_DEBUG("wm", "%s: %s", __func__, evname);
+
+ int ret = afb_event_push(this->map_afb_event[evname], nullptr);
+ if (ret != 0) {
+ HMI_DEBUG("wm", "afb_event_push failed: %m");
+ }
+}
+
void App::send_event(char const *evname, char const *label){
HMI_DEBUG("wm", "%s: %s(%s)", __func__, evname, label);
@@ -671,7 +541,9 @@ void App::surface_removed(uint32_t surface_id) {
} else {
auto drawing_name = this->lookup_name(surface_id);
if (drawing_name) {
- this->api_deactivate_surface(drawing_name->c_str(), [](const char*){});
+ this->allocateWindowResource("deactivate", drawing_name->c_str(),
+ nullptr, nullptr,
+ [](const char*){});
}
}
@@ -705,6 +577,26 @@ void App::emit_invisible(char const *label) {
void App::emit_visible(char const *label) { return emit_visible(label, true); }
+void App::emitHeadlampOff() {
+ // Send HeadlampOff event for all application
+ this->send_event(kListEventName[Event_HeadlampOff]);
+}
+
+void App::emitHeadlampOn() {
+ // Send HeadlampOn event for all application
+ this->send_event(kListEventName[Event_HeadlampOn]);
+}
+
+void App::emitCarStop() {
+ // Send CarStop event for all application
+ this->send_event(kListEventName[Event_CarStop]);
+}
+
+void App::emitCarRun() {
+ // Send CarRun event for all application
+ this->send_event(kListEventName[Event_CarRun]);
+}
+
result<int> App::api_request_surface(char const *drawing_name) {
auto lid = this->layers.get_layer_id(std::string(drawing_name));
if (!lid) {
@@ -731,6 +623,20 @@ result<int> App::api_request_surface(char const *drawing_name) {
HMI_DEBUG("wm", "Set main_surface id to %u", id);
}
+#if 0 // @@@@@
+ // TODO:
+ // This process will be implemented in SystemManager
+ {
+ // Generate app id
+ auto id = int(this->app_id_alloc_.generate_id(drawing_name));
+ this->appname2appid_[drawing_name] = id;
+ }
+#endif
+
+ // Set map of (role, surface_id)
+ std::string role = this->app2role_[std::string(drawing_name)];
+ this->role2surfaceid_[role] = id;
+
return Ok<int>(id);
}
@@ -910,56 +816,24 @@ void App::deactivate(int id) {
}
}
-void App::deactivate_main_surface() {
- this->layers.main_surface = -1;
- this->api_deactivate_surface(this->layers.main_surface_name.c_str(), [](const char*){});
-}
-
-bool App::can_split(struct LayoutState const &state, int new_id) {
- if (state.main != -1 && state.main != new_id) {
- auto new_id_layer = this->layers.get_layer_id(new_id).value();
- auto current_id_layer = this->layers.get_layer_id(state.main).value();
-
- // surfaces are on separate layers, don't bother.
- if (new_id_layer != current_id_layer) {
- return false;
- }
-
- std::string const &new_id_str = this->lookup_name(new_id).value();
- std::string const &cur_id_str = this->lookup_name(state.main).value();
-
- auto const &layer = this->layers.get_layer(new_id_layer);
-
- HMI_DEBUG("wm", "layer info name: %s", layer->name.c_str());
-
- if (layer->layouts.empty()) {
- return false;
- }
-
- for (auto i = layer->layouts.cbegin(); i != layer->layouts.cend(); i++) {
- HMI_DEBUG("wm", "%d main_match '%s'", new_id_layer, i->main_match.c_str());
- auto rem = std::regex(i->main_match);
- if (std::regex_match(cur_id_str, rem)) {
- // build the second one only if the first already matched
- HMI_DEBUG("wm", "%d sub_match '%s'", new_id_layer, i->sub_match.c_str());
- auto res = std::regex(i->sub_match);
- if (std::regex_match(new_id_str, res)) {
- HMI_DEBUG("wm", "layout matched!");
- return true;
- }
- }
- }
- }
+void App::deactivate(std::string role) {
+ std::string app = this->roleToApp(role);
+ auto const &id = this->lookup_id(app.c_str());
+ if (!id) {
+ HMI_ERROR("wm", "Surface does not exist");
+ return;
+ }
+ HMI_DEBUG("wm", "Deactivate role:%s (app:%s)",
+ role.c_str(), app.c_str());
- return false;
+ this->deactivate(*id);
}
-void App::try_layout(struct LayoutState & /*state*/,
- struct LayoutState const &new_layout,
- std::function<void(LayoutState const &nl)> apply) {
- if (this->policy.layout_is_valid(new_layout)) {
- apply(new_layout);
- }
+void App::deactivate_main_surface() {
+ this->layers.main_surface = -1;
+ this->allocateWindowResource("deactivate", this->layers.main_surface_name.c_str(),
+ nullptr, nullptr,
+ [](const char*){});
}
/**
@@ -982,4 +856,302 @@ void controller_hooks::surface_destination_rectangle(uint32_t /*surface_id*/,
uint32_t /*w*/,
uint32_t /*h*/) {}
+int App::allocateSurface() {
+ HMI_DEBUG("wm", "Call");
+
+ // Get current/previous layers
+ LayoutManager::TypeLayers crr_layers = this->lm_.getCurrentLayers();
+ LayoutManager::TypeLayers prv_layers = this->lm_.getPreviousLayers();
+
+ // Update resource of all layers
+ for (auto itr_layers = crr_layers.begin();
+ itr_layers != crr_layers.end(); ++itr_layers) {
+ // Get layer
+ std::string layer = itr_layers->first;
+ HMI_DEBUG("wm", "Update resource in %s layer", layer.c_str());
+
+ // If layout is changed, update resouce
+ if (this->lm_.isLayoutChanged(layer.c_str())) {
+ // Get current/previous layout
+ LayoutManager::TypeLayouts crr_layout = itr_layers->second;
+ LayoutManager::TypeLayouts prv_layout = prv_layers[layer];
+
+ // Get current/previous layout name
+ std::string crr_layout_name = crr_layout.begin()->first;
+ std::string prv_layout_name = prv_layout.begin()->first;
+ HMI_DEBUG("wm", "layout name crr:%s prv:%s",
+ crr_layout_name.c_str(), prv_layout_name.c_str());
+
+ // Get current/previous ares
+ LayoutManager::TypeAreas crr_areas = crr_layout[crr_layout_name];
+ LayoutManager::TypeAreas prv_areas = prv_layout[prv_layout_name];
+
+ // Create previous displayed role list
+ std::string prv_area_name;
+ std::vector<std::string> prv_role_list;
+ for (auto itr_areas = prv_areas.begin();
+ itr_areas != prv_areas.end(); ++itr_areas) {
+ prv_area_name = itr_areas->first;
+ prv_role_list.push_back(prv_areas[prv_area_name]["role"]);
+ HMI_DEBUG("wm", "previous displayed role:%s",
+ prv_areas[prv_area_name]["role"].c_str());
+ }
+
+ // Allocate surface for each area
+ std::string crr_area_name;
+ std::string crr_role_name;
+ LayoutManager::TypeRolCtg crr_rol_ctg;
+ for (auto itr_areas = crr_areas.begin();
+ itr_areas != crr_areas.end(); ++itr_areas) {
+ crr_area_name = itr_areas->first;
+ crr_rol_ctg = itr_areas->second;
+
+ // Get role of current area
+ if ("category" == crr_rol_ctg.begin()->first) {
+ // If current area have category
+ // Get category name
+ std::string crr_ctg = crr_rol_ctg.begin()->second;
+
+ // Serch relevant role fron previous displayed role list
+ for (auto itr_role = prv_role_list.begin();
+ itr_role != prv_role_list.end(); ++itr_role) {
+ std::string prv_ctg = this->pm_.roleToCategory((*itr_role).c_str());
+ if (crr_ctg == prv_ctg) {
+ // First discovered role is set to current role
+ crr_role_name = *itr_role;
+
+ // Delete used role for other areas
+ // which have same category
+ prv_role_list.erase(itr_role);
+
+ break;
+ }
+ }
+ }
+ else {
+ crr_role_name = itr_areas->second["role"];
+ }
+ HMI_DEBUG("wm", "Allocate surface for area:%s role:%s",
+ crr_area_name.c_str(), crr_role_name.c_str());
+
+ // Deactivate non-displayed role
+ std::string prv_role_name;
+ if (crr_layout_name == prv_layout_name) {
+ HMI_DEBUG("wm", "Current layout is same with previous");
+
+ // Deactivate previous role in same area
+ // if it is different with current
+ prv_role_name = prv_areas[crr_area_name]["role"];
+ if (crr_role_name != prv_role_name) {
+ this->deactivate(prv_role_name);
+ }
+ }
+ else {
+ HMI_DEBUG("wm", "Current layout is different with previous");
+
+ if ("none" != prv_layout_name) {
+ // Deactivate previous role in all area in previous layout
+ // if it is different with current role
+ for(auto itr = prv_areas.begin(); itr != prv_areas.end(); ++itr) {
+ prv_role_name = itr->second["role"].c_str();
+ if (crr_role_name != prv_role_name) {
+ this->deactivate(prv_role_name);
+ }
+ }
+ }
+ }
+
+ // Set surface for displayed role
+ if ("none" != crr_layout_name) {
+ // If current layout is not "none",
+ // set surface for current role
+ this->setSurfaceSize(crr_role_name.c_str(), crr_area_name.c_str());
+
+ // TODO:
+ // This API is workaround.
+ // Resource manager should manage each resource infomations
+ // according to architecture document.
+ this->lm_.updateArea(layer.c_str(), crr_role_name.c_str(), crr_area_name.c_str());
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+void App::setSurfaceSize(const char* role, const char* area) {
+ HMI_DEBUG("wm", "role:%s area:%s", role, area);
+
+ // Get size of current area
+ compositor::rect size = this->lm_.getAreaSize(area);
+
+ // Set destination to the display rectangle
+ int surface_id = this->role2surfaceid_[role];
+ auto &s = this->controller->surfaces[surface_id];
+ s->set_destination_rectangle(size.x, size.y, size.w, size.h);
+ this->layout_commit();
+
+ // Update area information
+ this->area_info[surface_id].x = size.x;
+ this->area_info[surface_id].y = size.y;
+ this->area_info[surface_id].w = size.w;
+ this->area_info[surface_id].h = size.h;
+ HMI_DEBUG("wm", "Surface rect { %d, %d, %d, %d }",
+ size.x, size.y, size.w, size.h);
+
+ // Emit syncDraw event
+ const char* app = this->roleToApp(role).c_str();
+ this->emit_syncdraw(app, area,
+ size.x, size.y, size.w, size.h);
+
+ // Enqueue flushDraw event
+ this->enqueue_flushdraw(surface_id);
+}
+
+std::string App::roleToApp(std::string role) {
+ HMI_DEBUG("wm", "Call");
+
+ for (auto itr = this->app2role_.begin();
+ itr != this->app2role_.end(); itr++) {
+ if (role == itr->second) {
+ return itr->first;
+ }
+ }
+ return std::string("none");
+}
+
+extern const char* kDefaultAppDb;
+int App::loadAppDb() {
+ HMI_DEBUG("wm", "Call");
+
+ // Get afm application installed dir
+ char const *afm_app_install_dir = getenv("AFM_APP_INSTALL_DIR");
+ HMI_DEBUG("wm", "afm_app_install_dir:%s", afm_app_install_dir);
+
+ std::string file_name;
+ if (!afm_app_install_dir) {
+ HMI_ERROR("wm", "AFM_APP_INSTALL_DIR is not defined");
+ }
+ else {
+ file_name = std::string(afm_app_install_dir) + std::string("/etc/app.db");
+ }
+
+ // Load app.db
+ HMI_DEBUG("wm", "file_name:%s", file_name.c_str());
+ json_object* json_obj = json_object_from_file(file_name.c_str());
+ if (nullptr == json_obj) {
+ HMI_ERROR("wm", "Could not open app.db, so use default role information");
+ json_obj = json_tokener_parse(kDefaultAppDb);
+ }
+ HMI_DEBUG("wm", "json_obj dump:%s", json_object_get_string(json_obj));
+
+ // Perse apps
+ HMI_DEBUG("wm", "Perse apps");
+ json_object* json_cfg;
+ if (!json_object_object_get_ex(json_obj, "apps", &json_cfg)) {
+ HMI_ERROR("wm", "Parse Error!!");
+ return -1;
+ }
+
+ int len = json_object_array_length(json_cfg);
+ HMI_DEBUG("wm", "json_cfg len:%d", len);
+ HMI_DEBUG("wm", "json_cfg dump:%s", json_object_get_string(json_cfg));
+
+ for (int i=0; i<len; i++) {
+ json_object* json_tmp = json_object_array_get_idx(json_cfg, i);
+ HMI_DEBUG("wm", "> json_tmp dump:%s", json_object_get_string(json_tmp));
+
+ const char* app = jh::getStringFromJson(json_tmp, "name");
+ if (nullptr == app) {
+ HMI_ERROR("wm", "Parse Error!!");
+ return -1;
+ }
+ HMI_DEBUG("wm", "> app:%s", app);
+
+ const char* role = jh::getStringFromJson(json_tmp, "role");
+ if (nullptr == role) {
+ HMI_ERROR("wm", "Parse Error!!");
+ return -1;
+ }
+ HMI_DEBUG("wm", "> role:%s", role);
+
+ this->app2role_[app] = std::string(role);
+ }
+
+ // Check
+ for(auto itr = this->app2role_.begin();
+ itr != this->app2role_.end(); ++itr) {
+ HMI_DEBUG("wm", "app:%s role:%s",
+ itr->first.c_str(), itr->second.c_str());
+ }
+
+ // Release json_object
+ json_object_put(json_obj);
+
+ return 0;
+}
+
+
+const char* kDefaultAppDb = "{ \
+ \"apps\": [ \
+ { \
+ \"name\": \"HomeScreen\", \
+ \"role\": \"homescreen\" \
+ }, \
+ { \
+ \"name\": \"Music\", \
+ \"role\": \"music\" \
+ }, \
+ { \
+ \"name\": \"MediaPlayer\", \
+ \"role\": \"music\" \
+ }, \
+ { \
+ \"name\": \"Video\", \
+ \"role\": \"video\" \
+ }, \
+ { \
+ \"name\": \"VideoPlayer\", \
+ \"role\": \"video\" \
+ }, \
+ { \
+ \"name\": \"WebBrowser\", \
+ \"role\": \"browser\" \
+ }, \
+ { \
+ \"name\": \"Radio\", \
+ \"role\": \"radio\" \
+ }, \
+ { \
+ \"name\": \"Phone\", \
+ \"role\": \"phone\" \
+ }, \
+ { \
+ \"name\": \"Navigation\", \
+ \"role\": \"map\" \
+ }, \
+ { \
+ \"name\": \"HVAC\", \
+ \"role\": \"hvac\" \
+ }, \
+ { \
+ \"name\": \"Settings\", \
+ \"role\": \"settings\" \
+ }, \
+ { \
+ \"name\": \"Dashboard\", \
+ \"role\": \"dashboard\" \
+ }, \
+ { \
+ \"name\": \"POI\", \
+ \"role\": \"poi\" \
+ }, \
+ { \
+ \"name\": \"Mixer\", \
+ \"role\": \"mixer\" \
+ } \
+ ] \
+}";
+
+
} // namespace wm
diff --git a/src/app.hpp b/src/app.hpp
index d1393c0..9ab1280 100644
--- a/src/app.hpp
+++ b/src/app.hpp
@@ -31,6 +31,7 @@
#include "policy.hpp"
#include "result.hpp"
#include "wayland_ivi_wm.hpp"
+#include "policy_manager.hpp"
#include "hmi-debug.h"
namespace wl {
@@ -144,7 +145,13 @@ struct App {
Event_SyncDraw,
Event_FlushDraw,
- Event_Val_Max = Event_FlushDraw,
+ Event_HeadlampOff,
+ Event_HeadlampOn,
+
+ Event_CarStop,
+ Event_CarRun,
+
+ Event_Val_Max = Event_CarRun,
};
const std::vector<const char *> kListEventName{
@@ -153,7 +160,11 @@ struct App {
"visible",
"invisible",
"syncdraw",
- "flushdraw"
+ "flushdraw",
+ "headlamp_off",
+ "headlamp_on",
+ "car_stop",
+ "car_run",
};
struct controller_hooks chooks;
@@ -203,12 +214,14 @@ struct App {
result<int> api_request_surface(char const *drawing_name);
char const *api_request_surface(char const *drawing_name, char const *ivi_id);
- void api_activate_surface(char const *drawing_name, char const *drawing_area, const reply_func &reply);
- void api_deactivate_surface(char const *drawing_name, const reply_func &reply);
+ void allocateWindowResource(char const *event, char const *drawing_name,
+ char const *role, char const *drawing_area,
+ const reply_func &reply);
void api_enddraw(char const *drawing_name);
result<json_object *> api_get_display_info();
result<json_object *> api_get_area_info(char const *drawing_name);
void api_ping();
+ 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);
@@ -217,6 +230,23 @@ struct App {
void surface_removed(uint32_t surface_id);
private:
+#if 1 // @@@@@
+ PolicyManager pm_;
+ LayoutManager lm_;
+ std::unordered_map<std::string, int> role2surfaceid_;
+ std::unordered_map<std::string, std::string> app2role_;
+ std::unordered_map<int, int> appid2role_;
+
+ int allocateSurface();
+ void setSurfaceSize(const char* role, const char* area);
+ std::string roleToApp(std::string role);
+ int loadAppDb();
+
+#if 0
+ struct id_allocator app_id_alloc_;
+ std::unordered_map<std::string, int> appname2appid_;
+#endif
+#endif
optional<int> lookup_id(char const *name);
optional<std::string> lookup_name(int id);
@@ -227,7 +257,6 @@ private:
int init_layers();
- void surface_set_layout(int surface_id, optional<int> sub_surface_id = nullopt);
void layout_commit();
// TMC WM Events to clients
@@ -238,15 +267,16 @@ private:
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 emitCarStop();
+ void emitCarRun();
void activate(int id);
void deactivate(int id);
+ void deactivate(std::string role);
void deactivate_main_surface();
- bool can_split(struct LayoutState const &state, int new_id);
- void try_layout(struct LayoutState &state,
- struct LayoutState const &new_layout,
- std::function<void(LayoutState const &nl)> apply);
};
} // namespace wm
diff --git a/src/db/app.db b/src/db/app.db
new file mode 100644
index 0000000..32155ac
--- /dev/null
+++ b/src/db/app.db
@@ -0,0 +1,68 @@
+{
+ "apps": [
+ {
+ "name": "HomeScreen",
+ "role": "homescreen"
+ },
+ {
+ "name": "Music",
+ "role": "music"
+ },
+ {
+ "name": "MediaPlayer",
+ "role": "music"
+ },
+ {
+ "name": "Video",
+ "role": "video"
+ },
+ {
+ "name": "VideoPlayer",
+ "role": "video"
+ },
+ {
+ "name": "WebBrowser",
+ "role": "browser"
+ },
+ {
+ "name": "Radio",
+ "role": "radio"
+ },
+ {
+ "name": "Phone",
+ "role": "phone"
+ },
+ {
+ "name": "Navigation",
+ "role": "map"
+ },
+ {
+ "name": "HVAC",
+ "role": "hvac"
+ },
+ {
+ "name": "Settings",
+ "role": "settings"
+ },
+ {
+ "name": "Dashboard",
+ "role": "dashboard"
+ },
+ {
+ "name": "POI",
+ "role": "poi"
+ },
+ {
+ "name": "Mixer",
+ "role": "mixer"
+ },
+ {
+ "name": "Splitable1",
+ "role": "splitable1"
+ },
+ {
+ "name": "Splitable2",
+ "role": "splitable2"
+ }
+ ]
+} \ No newline at end of file
diff --git a/src/json_helper.cpp b/src/json_helper.cpp
index 193a187..ac3d2b0 100644
--- a/src/json_helper.cpp
+++ b/src/json_helper.cpp
@@ -14,9 +14,12 @@
* limitations under the License.
*/
-#include "json_helper.hpp"
+#include <json-c/json.h>
#include <json.h>
+#include "json_helper.hpp"
+#include "hmi-debug.h"
+
json_object *to_json(compositor::surface_properties const &s) {
// auto j = json::object({
@@ -100,3 +103,52 @@ json_object *to_json(std::vector<uint32_t> const &v) {
}
return a;
}
+
+namespace jh {
+
+const char* getStringFromJson(json_object* obj, const char* key) {
+ if ((nullptr == obj) || (nullptr == key)) {
+ HMI_ERROR("wm:jh", "Argument is nullptr!!!");
+ return nullptr;
+ }
+
+ json_object* tmp;
+ if (!json_object_object_get_ex(obj, key, &tmp)) {
+ HMI_DEBUG("wm:jh", "Not found key \"%s\"", key);
+ return nullptr;
+ }
+
+ return json_object_get_string(tmp);
+}
+
+int getIntFromJson(json_object* obj, const char* key) {
+ if ((nullptr == obj) || (nullptr == key)) {
+ HMI_ERROR("wm", "Argument is nullptr!!!");
+ return 0;
+ }
+
+ json_object* tmp;
+ if (!json_object_object_get_ex(obj, key, &tmp)) {
+ HMI_DEBUG("wm", "Not found key \"%s\"", key);
+ return 0;
+ }
+
+ return json_object_get_int(tmp);
+}
+
+json_bool getBoolFromJson(json_object* obj, const char* key) {
+ if ((nullptr == obj) || (nullptr == key)) {
+ HMI_ERROR("wm", "Argument is nullptr!!!");
+ return 0;
+ }
+
+ json_object* tmp;
+ if (!json_object_object_get_ex(obj, key, &tmp)) {
+ HMI_DEBUG("wm", "Not found key \"%s\"", key);
+ return 0;
+ }
+
+ return json_object_get_boolean(tmp);
+}
+
+} // namespace jh
diff --git a/src/json_helper.hpp b/src/json_helper.hpp
index 78e03f7..c2087cf 100644
--- a/src/json_helper.hpp
+++ b/src/json_helper.hpp
@@ -19,12 +19,19 @@
#include "result.hpp"
#include "wayland_ivi_wm.hpp"
+#include <json-c/json.h>
#include <json.hpp>
+
struct json_object;
json_object *to_json(compositor::screen const *s);
json_object *to_json(compositor::controller::props_map const &s);
json_object *to_json(std::vector<uint32_t> const &v);
+namespace jh {
+const char* getStringFromJson(json_object* obj, const char* key);
+int getIntFromJson(json_object* obj, const char* key);
+json_bool getBoolFromJson(json_object* obj, const char* key);
+} // namespace jh
#endif // TMCAGLWM_JSON_HELPER_HPP
diff --git a/src/layout.cpp b/src/layout.cpp
deleted file mode 100644
index fbf2baa..0000000
--- a/src/layout.cpp
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * 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 "layout.hpp"
diff --git a/src/layout.hpp b/src/layout.hpp
deleted file mode 100644
index d5fd00c..0000000
--- a/src/layout.hpp
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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_LAYOUT_HPP
-#define TMCAGLWM_LAYOUT_HPP
-
-#include <cstdint>
-#include <string>
-
-#include "result.hpp"
-
-namespace wm {
-
-struct LayoutState {
- int main{-1};
- int sub{-1};
-
- bool operator==(const LayoutState &b) const {
- return main == b.main && sub == b.sub;
- }
-
- bool operator!=(const LayoutState &b) const {
- return !(*this == b);
- }
-};
-
-} // namespace wm
-
-#endif // TMCAGLWM_LAYOUT_HPP
diff --git a/src/layout_manager/db/layout.db b/src/layout_manager/db/layout.db
new file mode 100644
index 0000000..c7cefd8
--- /dev/null
+++ b/src/layout_manager/db/layout.db
@@ -0,0 +1,158 @@
+{
+ "layouts": [
+ {
+ "name": "pu",
+ "layer": "on_screen",
+ "areas": [
+ {
+ "name": "pop_up",
+ "role": "incomming_call"
+ }
+ ]
+ },
+ {
+ "name": "sa",
+ "layer": "on_screen",
+ "areas": [
+ {
+ "name": "system_alert",
+ "role": "system_alert"
+ }
+ ]
+ },
+ {
+ "name": "m1",
+ "layer": "apps",
+ "areas": [
+ {
+ "name": "normal",
+ "role": "map"
+ }
+ ]
+ },
+ {
+ "name": "m2",
+ "layer": "apps",
+ "areas": [
+ {
+ "name": "split.main",
+ "role": "map"
+ },
+ {
+ "name": "split.sub",
+ "category": "splitable"
+ }
+ ]
+ },
+ {
+ "name": "mf",
+ "layer": "apps",
+ "areas": [
+ {
+ "name": "full",
+ "role": "map"
+ }
+ ]
+ },
+ {
+ "name": "s1",
+ "layer": "apps",
+ "areas": [
+ {
+ "name": "normal",
+ "category": "splitable"
+ }
+ ]
+ },
+ {
+ "name": "s2",
+ "layer": "apps",
+ "areas": [
+ {
+ "name": "split.main",
+ "category": "splitable"
+ },
+ {
+ "name": "split.sub",
+ "category": "splitable"
+ }
+ ]
+ },
+ {
+ "name": "g",
+ "layer": "apps",
+ "areas": [
+ {
+ "name": "normal",
+ "category": "general"
+ }
+ ]
+ },
+ {
+ "name": "hs",
+ "layer": "homescreen",
+ "areas": [
+ {
+ "name": "full",
+ "role": "homescreen"
+ }
+ ]
+ }
+ ],
+ "areas": [
+ {
+ "name": "normal",
+ "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": "full",
+ "rect": {
+ "x": 0,
+ "y": 0,
+ "w": 1080,
+ "h": 1920
+ }
+ },
+ {
+ "name": "pop_up",
+ "rect": {
+ "x": 0,
+ "y": 640,
+ "w": 1080,
+ "h": 640
+ }
+ },
+ {
+ "name": "system_alert",
+ "rect": {
+ "x": 0,
+ "y": 640,
+ "w": 1080,
+ "h": 640
+ }
+ }
+ ]
+}
diff --git a/src/layout_manager/layout.cpp b/src/layout_manager/layout.cpp
new file mode 100644
index 0000000..dc73cbf
--- /dev/null
+++ b/src/layout_manager/layout.cpp
@@ -0,0 +1,570 @@
+/*
+ * 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-c/json.h>
+#include "layout.hpp"
+#include "json_helper.hpp"
+#include "hmi-debug.h"
+
+
+LayoutManager::LayoutManager() {
+ HMI_DEBUG("wm:lm", "Call");
+}
+
+int LayoutManager::initialize() {
+ HMI_DEBUG("wm:lm", "Call");
+
+ int ret = 0;
+
+ // Load layout.db
+ ret = this->loadLayoutDb();
+ if (0 > ret) {
+ HMI_ERROR("wm:lm", "Load layout.db Error!!");
+ return ret;
+ }
+
+ TypeLayouts layout;
+ TypeAreas area;
+ TypeRolCtg rol_ctg;
+
+ rol_ctg["none"] = "none";
+ area["none"] = rol_ctg;
+ layout["none"] = area;
+
+ this->prv_layers_["on_screen"] = layout;
+ this->prv_layers_["apps"] = layout;
+ this->prv_layers_["homescreen"] = layout;
+
+ this->crr_layers_["on_screen"] = layout;
+ this->crr_layers_["apps"] = layout;
+ this->crr_layers_["homescreen"] = layout;
+
+ this->prv_layers_car_stop_["on_screen"] = layout;
+ this->prv_layers_car_stop_["apps"] = layout;
+ this->prv_layers_car_stop_["homescreen"] = layout;
+
+ return ret;
+}
+
+bool LayoutManager::updateLayout(json_object* obj,
+ const char* new_role, const char* category) {
+ HMI_DEBUG("wm:lm", "Call");
+
+ bool ret = false;
+
+ // Check car state change
+ json_object* json_car;
+ if (!json_object_object_get_ex(obj, "car", &json_car)) {
+ HMI_ERROR("wm:lm", "Parse Error!!");
+ return -1;
+ }
+
+ json_bool is_car_state_changed;
+ std::string car_state = "";
+ is_car_state_changed = jh::getBoolFromJson(json_car, "is_changed");
+ if (is_car_state_changed) {
+ // If car state is changed, get car state
+ car_state = jh::getStringFromJson(json_car, "state");
+ }
+
+ // Update layout of all layers
+ json_object* json_layers;
+ if (!json_object_object_get_ex(obj, "layers", &json_layers)) {
+ HMI_ERROR("wm:lm", "Parse Error!!");
+ return -1;
+ }
+
+ int len = json_object_array_length(json_layers);
+ HMI_DEBUG("wm:lm", "json_layers len:%d", len);
+ HMI_DEBUG("wm:lm", "json_layers dump:%s", json_object_get_string(json_layers));
+
+ for (int i=0; i<len; i++) {
+ json_object* json_tmp = json_object_array_get_idx(json_layers, i);
+
+ // Get layer name and json_object
+ const char* layer;
+ json_object* json_layer;
+ json_object_object_foreach(json_tmp, key, val) {
+ layer = key;
+ json_layer = val;
+ HMI_DEBUG("wm:lm", "Update %s layer state", layer);
+ }
+
+ // Store previous state
+ this->prv_layers_[layer] = this->crr_layers_[layer];
+ std::string prv_layout_name = this->prv_layers_[layer].begin()->first;
+
+ // If car state is changed car_stop -> car_run,
+ // store current state for state of car stop
+ if ((is_car_state_changed) && ("car_run" == car_state)) {
+ HMI_DEBUG("wm:lm", "Store current state for state of car stop");
+ this->prv_layers_car_stop_[layer] = this->crr_layers_[layer];
+ }
+
+ json_object* json_is_changed;
+ if (!json_object_object_get_ex(json_layer, "is_changed", &json_is_changed)) {
+ HMI_ERROR("wm:lm", "Not found key \"is_changed\"");
+ return false;
+ }
+
+ // If layer state is changed
+ if (json_object_get_boolean(json_is_changed)) {
+ // Set layout changed flag
+ this->is_layout_changed_[layer] = true;
+
+ json_object* json_state;
+ if (!json_object_object_get_ex(json_layer, "state", &json_state)) {
+ HMI_ERROR("wm:lm", "Not found key \"state\"");
+ return false;
+ }
+
+ const char* crr_layout_name = json_object_get_string(json_state);
+ HMI_DEBUG("wm:lm", "crr state: %s", crr_layout_name);
+
+ TypeLayouts crr_layout;
+ if ((is_car_state_changed) && ("car_stop" == car_state)) {
+ // If car state is changed car_run -> car_stop,
+ // restore state of car stop
+ HMI_DEBUG("wm:lm", "Restore state of car stop");
+ crr_layout = this->prv_layers_car_stop_[layer];
+ }
+ else if ("none" == std::string(crr_layout_name)) {
+ // If current layout is "none",
+ // current areas is set with "none"
+ TypeAreas area;
+ TypeRolCtg rol_ctg;
+ rol_ctg["none"] = "none";
+ area["none"] = rol_ctg;
+ crr_layout["none"] = area;
+ }
+ else {
+ if (std::string(crr_layout_name) == prv_layout_name) {
+ // If previous layout is same with current,
+ // previous areas are copied to current
+ crr_layout[crr_layout_name] = this->prv_layers_[layer][crr_layout_name];
+ }
+ else {
+ // If previous layout is NOT same with current,
+ // current areas is set with default value
+ crr_layout[crr_layout_name] = this->layout_define_[crr_layout_name];
+ }
+
+ if (is_car_state_changed) {
+ // Updating role is not necessary
+ // because new_role is not specified when car state is changed
+ }
+ else {
+ // Get new_area for new role
+ std::string new_area = this->getAreaName(this->layout_define_[crr_layout_name],
+ new_role, category);
+
+ // Update role in new area
+ TypeRolCtg crr_role;
+ crr_role["role"] = std::string(new_role);
+ crr_layout[crr_layout_name][new_area] = crr_role;
+ }
+ }
+
+ // Update layer state
+ this->crr_layers_[layer] = crr_layout;
+
+ // Check
+ for (auto itr_layout = this->crr_layers_[layer].begin();
+ itr_layout != this->crr_layers_[layer].end(); ++itr_layout) {
+ for (auto itr_area = itr_layout->second.begin();
+ itr_area != itr_layout->second.end(); ++itr_area) {
+ for (auto itr_role = itr_area->second.begin();
+ itr_role != itr_area->second.end(); ++itr_role) {
+ HMI_DEBUG("wm:lm", "layout:%s, area:%s, rol_ctg:%s, name:%s",
+ itr_layout->first.c_str(), itr_area->first.c_str(),
+ itr_role->first.c_str(), itr_role->second.c_str());
+ }
+ }
+ }
+
+ ret = true;
+ }
+ else {
+ // Clear layout changed flag
+ this->is_layout_changed_[layer] = false;
+ }
+ }
+ return ret;
+}
+
+// TODO: This API is for workaround, so this will be removed
+void LayoutManager::updateArea(const char* layer, const char* role, const char* area) {
+ this->crr_layers_[layer].begin()->second[area]["role"] = std::string(role);
+}
+
+LayoutManager::TypeLayers LayoutManager::getCurrentLayers() {
+ return this->crr_layers_;
+}
+
+LayoutManager::TypeLayers LayoutManager::getPreviousLayers() {
+ return this->prv_layers_;
+}
+
+compositor::rect LayoutManager::getAreaSize(const char* area) {
+ return this->area2size_[area];
+}
+
+std::string LayoutManager::getAreaName(TypeAreas areas, const char* role, const char* category) {
+ for (auto itr_area = areas.begin(); itr_area != areas.end(); ++itr_area) {
+ std::string area_name = itr_area->first;
+ TypeRolCtg rol_ctg = itr_area->second;
+
+ if ("role" == rol_ctg.begin()->first) {
+ if (std::string(role) == rol_ctg.begin()->second) {
+ return area_name;
+ }
+ }
+ else if ("category" == rol_ctg.begin()->first) {
+ if (std::string(category) == rol_ctg.begin()->second) {
+ return area_name;
+ }
+ }
+ else {
+ return std::string("none");
+ }
+ }
+ return std::string("none");
+}
+
+
+bool LayoutManager::isLayoutChanged(const char* layer) {
+ return this->is_layout_changed_[layer];
+}
+
+extern const char* kDefaultLayoutDb;
+int LayoutManager::loadLayoutDb() {
+ 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/layout.db");
+ }
+
+ // Load layout.db
+ HMI_DEBUG("wm:lm", "file_name:%s", file_name.c_str());
+ json_object* json_obj = json_object_from_file(file_name.c_str());
+ if (nullptr == json_obj) {
+ HMI_ERROR("wm:lm", "Could not open layout.db, so use default role information");
+ json_obj = json_tokener_parse(kDefaultLayoutDb);
+ }
+ HMI_DEBUG("wm:lm", "json_obj dump:%s", json_object_get_string(json_obj));
+
+ // Perse layouts
+ HMI_DEBUG("wm:lm", "Perse layouts");
+ json_object* json_cfg;
+ if (!json_object_object_get_ex(json_obj, "layouts", &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* layout;
+ const char* role;
+ const char* category;
+ for (int i=0; i<len; i++) {
+ json_object* json_tmp = json_object_array_get_idx(json_cfg, i);
+
+ layout = jh::getStringFromJson(json_tmp, "name");
+ if (nullptr == layout) {
+ HMI_ERROR("wm:lm", "Parse Error!!");
+ return -1;
+ }
+ HMI_DEBUG("wm:lm", "> layout:%s", layout);
+
+ json_object* json_area_array;
+ if (!json_object_object_get_ex(json_tmp, "areas", &json_area_array)) {
+ HMI_ERROR("wm:lm", "Parse Error!!");
+ return -1;
+ }
+
+ int len_area = json_object_array_length(json_area_array);
+ HMI_DEBUG("wm:lm", "json_area_array len:%d", len_area);
+ HMI_DEBUG("wm:lm", "json_area_array dump:%s", json_object_get_string(json_area_array));
+
+ TypeAreas areas;
+ for (int j=0; j<len_area; j++) {
+ json_object* json_area = json_object_array_get_idx(json_area_array, j);
+
+ const char* area = jh::getStringFromJson(json_area, "name");
+ if (nullptr == area) {
+ HMI_ERROR("wm:lm", "Parse Error!!");
+ return -1;
+ }
+ HMI_DEBUG("wm:lm", ">> area:%s", area);
+
+ TypeRolCtg rol_ctg_name;
+ role = jh::getStringFromJson(json_area, "role");
+ if (nullptr == role) {
+ category = jh::getStringFromJson(json_area, "category");
+ if (nullptr == category) {
+ HMI_ERROR("wm:lm", "Parse Error!!");
+ return -1;
+ }
+ rol_ctg_name["category"] = std::string(category);
+ HMI_DEBUG("wm:lm", ">>> category:%s", category);
+ }
+ else {
+ rol_ctg_name["role"] = std::string(role);
+ HMI_DEBUG("wm:lm", ">>> role:%s", role);
+ }
+
+ areas[area] = rol_ctg_name;
+ }
+
+ this->layout_define_[layout] = areas;
+ }
+
+ // Check
+ for(auto itr_layout = this->layout_define_.begin();
+ itr_layout != this->layout_define_.end(); ++itr_layout) {
+ for (auto itr_area = itr_layout->second.begin();
+ itr_area != itr_layout->second.end(); ++itr_area) {
+ for (auto itr_role = itr_area->second.begin();
+ itr_role != itr_area->second.end(); ++itr_role) {
+ HMI_DEBUG("wm:lm", "layout:%s, area:%s, rol_ctg:%s, name:%s",
+ itr_layout->first.c_str(), itr_area->first.c_str(),
+ itr_role->first.c_str(), itr_role->second.c_str());
+ }
+ }
+ }
+
+ // Perse areas
+ HMI_DEBUG("wm:lm", "Perse areas");
+ if (!json_object_object_get_ex(json_obj, "areas", &json_cfg)) {
+ HMI_ERROR("wm:lm", "Parse Error!!");
+ return -1;
+ }
+
+ 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* kDefaultLayoutDb = "{ \
+ \"layouts\": [ \
+ { \
+ \"name\": \"pu\", \
+ \"layer\": \"on_screen\", \
+ \"areas\": [ \
+ { \
+ \"name\": \"pop_up\", \
+ \"role\": \"incomming_call\" \
+ } \
+ ] \
+ }, \
+ { \
+ \"name\": \"sa\", \
+ \"layer\": \"on_screen\", \
+ \"areas\": [ \
+ { \
+ \"name\": \"system_alert\", \
+ \"role\": \"system_alert\" \
+ } \
+ ] \
+ }, \
+ { \
+ \"name\": \"m1\", \
+ \"layer\": \"apps\", \
+ \"areas\": [ \
+ { \
+ \"name\": \"normal\", \
+ \"role\": \"map\" \
+ } \
+ ] \
+ }, \
+ { \
+ \"name\": \"m2\", \
+ \"layer\": \"apps\", \
+ \"areas\": [ \
+ { \
+ \"name\": \"split.main\", \
+ \"role\": \"map\" \
+ }, \
+ { \
+ \"name\": \"split.sub\", \
+ \"category\": \"hvac\" \
+ } \
+ ] \
+ }, \
+ { \
+ \"name\": \"mf\", \
+ \"layer\": \"apps\", \
+ \"areas\": [ \
+ { \
+ \"name\": \"full\", \
+ \"role\": \"map\" \
+ } \
+ ] \
+ }, \
+ { \
+ \"name\": \"s1\", \
+ \"layer\": \"apps\", \
+ \"areas\": [ \
+ { \
+ \"name\": \"normal\", \
+ \"category\": \"splitable\" \
+ } \
+ ] \
+ }, \
+ { \
+ \"name\": \"s2\", \
+ \"layer\": \"apps\", \
+ \"areas\": [ \
+ { \
+ \"name\": \"split.main\", \
+ \"category\": \"splitable\" \
+ }, \
+ { \
+ \"name\": \"split.sub\", \
+ \"category\": \"splitable\" \
+ } \
+ ] \
+ }, \
+ { \
+ \"name\": \"g\", \
+ \"layer\": \"apps\", \
+ \"areas\": [ \
+ { \
+ \"name\": \"normal\", \
+ \"category\": \"general\" \
+ } \
+ ] \
+ }, \
+ { \
+ \"name\": \"hs\", \
+ \"layer\": \"homescreen\", \
+ \"areas\": [ \
+ { \
+ \"name\": \"full\", \
+ \"role\": \"homescreen\" \
+ } \
+ ] \
+ } \
+ ], \
+ \"areas\": [ \
+ { \
+ \"name\": \"normal\", \
+ \"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\": \"full\", \
+ \"rect\": { \
+ \"x\": 0, \
+ \"y\": 0, \
+ \"w\": 1080, \
+ \"h\": 1920 \
+ } \
+ }, \
+ { \
+ \"name\": \"pop_up\", \
+ \"rect\": { \
+ \"x\": 0, \
+ \"y\": 640, \
+ \"w\": 1080, \
+ \"h\": 640 \
+ } \
+ }, \
+ { \
+ \"name\": \"system_alert\", \
+ \"rect\": { \
+ \"x\": 0, \
+ \"y\": 640, \
+ \"w\": 1080, \
+ \"h\": 640 \
+ } \
+ } \
+ ] \
+}";
diff --git a/src/layout_manager/layout.hpp b/src/layout_manager/layout.hpp
new file mode 100644
index 0000000..bfa4a6c
--- /dev/null
+++ b/src/layout_manager/layout.hpp
@@ -0,0 +1,84 @@
+/*
+ * 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_LAYOUT_HPP
+#define TMCAGLWM_LAYOUT_HPP
+
+#include <cstdint>
+#include <string>
+#include <map>
+#include "result.hpp"
+#include "wayland_ivi_wm.hpp"
+
+namespace wm {
+
+struct LayoutState {
+ int main{-1};
+ int sub{-1};
+
+ bool operator==(const LayoutState &b) const {
+ return main == b.main && sub == b.sub;
+ }
+
+ bool operator!=(const LayoutState &b) const {
+ return !(*this == b);
+ }
+};
+
+} // namespace wm
+
+class LayoutManager {
+public:
+ explicit LayoutManager();
+ ~LayoutManager() = default;
+
+ typedef std::unordered_map<std::string, std::string> TypeRolCtg;
+ typedef std::unordered_map<std::string, TypeRolCtg> TypeAreas;
+ typedef std::unordered_map<std::string, TypeAreas> TypeLayouts;
+ typedef std::unordered_map<std::string, TypeLayouts> TypeLayers;
+
+ int initialize();
+ bool updateLayout(json_object* obj, const char* new_role, const char* new_area);
+ TypeLayers getCurrentLayers();
+ TypeLayers getPreviousLayers();
+ compositor::rect getAreaSize(const char* area);
+ bool isLayoutChanged(const char* layer);
+
+ void updateArea(const char* layer, const char* role, const char* area);
+
+private:
+ // Disable copy and move
+ LayoutManager(LayoutManager const &) = delete;
+ LayoutManager &operator=(LayoutManager const &) = delete;
+ LayoutManager(LayoutManager &&) = delete;
+ LayoutManager &operator=(LayoutManager &&) = delete;
+
+ TypeLayouts layout_define_;
+ std::unordered_map<std::string, compositor::rect> area2size_;
+
+ TypeLayers crr_layers_, prv_layers_;
+ TypeLayers prv_layers_car_stop_;
+
+ std::unordered_map<std::string, bool> is_layout_changed_;
+
+ std::string getAreaName(TypeAreas area, const char* role, const char* category);
+ int loadLayoutDb();
+ std::string role2App(std::string role, void* ptr);
+};
+
+
+
+#endif // TMCAGLWM_LAYOUT_HPP
diff --git a/src/low_can_client.cpp b/src/low_can_client.cpp
new file mode 100644
index 0000000..23a39e2
--- /dev/null
+++ b/src/low_can_client.cpp
@@ -0,0 +1,193 @@
+/*
+ * 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 "json_helper.hpp"
+#include "low_can_client.hpp"
+#include "hmi-debug.h"
+
+extern "C" {
+#include <afb/afb-binding.h>
+}
+
+
+namespace wm {
+
+LowCanClient::LowCanClient() :
+ vehicle_speed_(0),
+ trans_gear_pos_(0),
+ park_brake_status_(TRUE),
+ headlamp_status_(FALSE),
+ prv_car_state_("car_stop"),
+ crr_car_state_("car_stop"),
+ prv_lamp_state_("lamp_off"),
+ crr_lamp_state_("lamp_off"),
+ is_changed_car_state_(false),
+ is_changed_lamp_state_(false)
+{
+ HMI_DEBUG("wm:lcc", "Call");
+}
+
+void LowCanClient::initialize() {
+ HMI_DEBUG("wm:lcc", "Call");
+
+ int ret;
+
+ // Require API "low-can"
+ ret = afb_daemon_require_api_v2("low-can", 1);
+ if (0 > ret) {
+ HMI_INFO("wm:lcc", "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" }
+ for (int i=0; i<this->kNumEvent_; i++) {
+ json_object *json_obj = json_object_new_object();
+ json_object_object_add(json_obj, "event", json_object_new_string(this->kEventName_[i]));
+ HMI_DEBUG("wm:lcc", "subscribe message:%s", json_object_get_string(json_obj));
+
+ json_object *json_result = json_object_new_object();
+ ret = afb_service_call_sync("low-can", "subscribe", json_obj, &json_result);
+ if (0 > ret) {
+ HMI_INFO("wm:lcc", "Could not subscribe to \"low-can\" :%d", ret);
+ return;
+ }
+ HMI_DEBUG("wm:lcc", "subscribe result:%s", json_object_get_string(json_result));
+ }
+
+ return;
+}
+
+void LowCanClient::analyzeCanSignal(struct json_object *object) {
+ HMI_DEBUG("wm:lcc", "object:%s", json_object_get_string(object));
+
+ const char* name = jh::getStringFromJson(object, "name");
+ HMI_DEBUG("wm:lcc", "CAN signal name:%s", name);
+
+ if (strstr(name, "vehicle.speed")) {
+ HMI_DEBUG("wm:lcc", "Receive vehicle speed");
+ // Update vehicle speed
+ int speed = jh::getIntFromJson(object, "value");
+ if (this->vehicle_speed_ != speed) {
+ this->vehicle_speed_ = speed;
+ HMI_DEBUG("wm:lcc", "Update vehicle speed:%d", this->vehicle_speed_);
+ }
+ }
+ else if (strstr(name, "transmission_gear_position")) {
+ HMI_DEBUG("wm:lcc", "Receive transmission gear position");
+ // Update transmission gear position
+ int gear_pos = jh::getIntFromJson(object, "value");
+ if (this->trans_gear_pos_ != gear_pos) {
+ this->trans_gear_pos_ = gear_pos;
+ HMI_DEBUG("wm:lcc", "Update transmission gear position:%d", this->trans_gear_pos_);
+ }
+ }
+ else if (strstr(name, "parking_brake_status")) {
+ HMI_DEBUG("wm:lcc", "Receive parking brake status");
+ // Update parking gear status
+ json_bool park_brake = jh::getBoolFromJson(object, "value");
+ if (this->park_brake_status_ != park_brake) {
+ this->park_brake_status_ = park_brake;
+ HMI_DEBUG("wm:lcc", "Update parking brake status:%d", this->park_brake_status_);
+ }
+ }
+ else if (strstr(name, "headlamp_status")) {
+ HMI_DEBUG("wm:lcc", "Receive headlamp status");
+ // Update headlamp status
+ json_bool headlamp = jh::getBoolFromJson(object, "value");
+ if (this->headlamp_status_ != headlamp) {
+ this->headlamp_status_ = headlamp;
+ HMI_DEBUG("wm:lcc", "Update headlamp status:%d", this->headlamp_status_);
+ }
+ }
+
+ // Update car state
+ if ((0 == this->vehicle_speed_) || (true == this->park_brake_status_)) {
+ this->crr_car_state_ = "car_stop";
+ }
+ else {
+ this->crr_car_state_ = "car_run";
+ }
+ HMI_DEBUG("wm:lcc", "Current car state:%s", this->crr_car_state_.c_str());
+
+ // Update lamp state
+ if (true == this->headlamp_status_) {
+ this->crr_lamp_state_ = "lamp_on";
+ }
+ else {
+ this->crr_lamp_state_ = "lamp_off";
+ }
+ HMI_DEBUG("wm:lcc", "Current lamp state:%s", this->crr_lamp_state_.c_str());
+
+ // If car state is changed,
+ // backup current state for previous state and set flag
+ if (this->prv_car_state_ != this->crr_car_state_) {
+ HMI_DEBUG("wm:lcc", "Car state is changed: %s -> %s",
+ this->prv_car_state_.c_str(), this->crr_car_state_.c_str());
+ this->prv_car_state_ = this->crr_car_state_;
+ this->is_changed_car_state_ = true;
+ }
+
+ // If lamp state is changed,
+ // backup current state for previous state and set flag
+ if (this->prv_lamp_state_ != this->crr_lamp_state_) {
+ HMI_DEBUG("wm:lcc", "Lamp state is changed: %s -> %s",
+ this->prv_lamp_state_.c_str(), this->crr_lamp_state_.c_str());
+ this->prv_lamp_state_ = this->crr_lamp_state_;
+ this->is_changed_lamp_state_ = true;
+ }
+}
+
+bool LowCanClient::isChangedCarState() {
+ HMI_DEBUG("wm:lcc", "Call");
+
+ // Return changed flag
+ return this->is_changed_car_state_;
+}
+
+bool LowCanClient::isChangedLampState() {
+ HMI_DEBUG("wm:lcc", "Call");
+
+ // Return changed flag
+ return this->is_changed_lamp_state_;
+}
+
+const char* LowCanClient::getCurrentCarState() {
+ HMI_DEBUG("wm:lcc", "Call");
+
+ // Clear changed flag
+ this->is_changed_car_state_ = false;
+
+ // Return current car state
+ return this->crr_car_state_.c_str();
+}
+
+const char* LowCanClient::getCurrentLampState() {
+ HMI_DEBUG("wm:lcc", "Call");
+
+ // Clear changed flag
+ this->is_changed_lamp_state_ = false;
+
+ // Return current lamp state
+ return this->crr_lamp_state_.c_str();
+}
+
+
+} // namespace wm
diff --git a/src/low_can_client.hpp b/src/low_can_client.hpp
new file mode 100644
index 0000000..5119c23
--- /dev/null
+++ b/src/low_can_client.hpp
@@ -0,0 +1,72 @@
+/*
+ * 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 <vector>
+#include <json-c/json.h>
+
+
+namespace wm {
+
+class LowCanClient {
+
+public:
+ explicit LowCanClient();
+ ~LowCanClient() = default;
+
+ void initialize();
+ void analyzeCanSignal(struct json_object *object);
+ bool isChangedCarState();
+ bool isChangedLampState();
+ const char* getCurrentCarState();
+ const char* getCurrentLampState();
+
+private:
+ // Disable copy and move
+ LowCanClient(LowCanClient const &) = delete;
+ LowCanClient &operator=(LowCanClient const &) = delete;
+ LowCanClient(LowCanClient &&) = delete;
+ LowCanClient &operator=(LowCanClient &&) = delete;
+
+ const int kNumEvent_ = 4;
+ const std::vector<const char*> kEventName_{
+ "vehicle.speed",
+ "transmission_gear_position",
+ "headlamp_status",
+ "parking_brake_status"
+ };
+
+ int vehicle_speed_;
+ int trans_gear_pos_;
+ json_bool park_brake_status_;
+ json_bool headlamp_status_;
+
+ std::string prv_car_state_;
+ std::string crr_car_state_;
+ std::string prv_lamp_state_;
+ std::string crr_lamp_state_;
+
+ bool is_changed_car_state_;
+ bool is_changed_lamp_state_;
+};
+
+} // namespace wm
+
+
+#endif // TMCAGLWM_LOW_CAN_CLIENT_HPP
diff --git a/src/main.cpp b/src/main.cpp
index 2f813a3..067a006 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -24,6 +24,7 @@
#include "json_helper.hpp"
#include "util.hpp"
#include "wayland_ivi_wm.hpp"
+#include "low_can_client.hpp"
extern "C" {
#include <afb/afb-binding.h>
@@ -39,9 +40,10 @@ typedef struct wmClientCtxt{
struct afb_instance {
std::unique_ptr<wl::display> display;
+ wm::LowCanClient lcc_;
wm::App app;
- afb_instance() : display{new wl::display}, app{this->display.get()} {}
+ afb_instance() : display{new wl::display}, lcc_{}, app{this->display.get()} {}
int init();
};
@@ -50,6 +52,10 @@ struct afb_instance *g_afb_instance;
std::mutex binding_m;
int afb_instance::init() {
+ // Initialize LowCanClient class
+ this->lcc_.initialize();
+
+ // Initialize App class
return this->app.init();
}
@@ -298,8 +304,14 @@ void windowmanager_activatesurface(afb_req req) noexcept {
return;
}
- g_afb_instance->app.api_activate_surface(a_drawing_name, a_drawing_area,
- [&req](const char* errmsg){
+ const char* a_role = afb_req_value(req, "role");
+ if(!a_role){
+ a_role = "";
+ }
+
+ g_afb_instance->app.allocateWindowResource("activate", a_drawing_name,
+ a_drawing_area, a_role,
+ [&req](const char* errmsg){
if (errmsg != nullptr) {
HMI_ERROR("wm", errmsg);
afb_req_fail(req, "failed", errmsg);
@@ -332,8 +344,14 @@ void windowmanager_deactivatesurface(afb_req req) noexcept {
return;
}
- g_afb_instance->app.api_deactivate_surface(a_drawing_name,
- [&req](const char* errmsg){
+ const char* a_role = afb_req_value(req, "role");
+ if(!a_role){
+ a_role = "";
+ }
+
+ g_afb_instance->app.allocateWindowResource("deactivate", a_drawing_name,
+ nullptr, a_role,
+ [&req](const char* errmsg){
if (errmsg != nullptr) {
HMI_ERROR("wm", errmsg);
afb_req_fail(req, "failed", errmsg);
@@ -452,6 +470,12 @@ void windowmanager_wm_subscribe(afb_req req) noexcept {
return;
}
int event_type = json_object_get_int(j);
+ if ((wm::App::Event_Val_Min > event_type)
+ || (wm::App::Event_Val_Max < event_type)) {
+ afb_req_fail(req, "failed", "Invalid EventType");
+ return;
+ }
+
const char *event_name = g_afb_instance->app.kListEventName[event_type];
struct afb_event event = g_afb_instance->app.map_afb_event[event_name];
int ret = afb_req_subscribe(req, event);
@@ -635,5 +659,48 @@ const struct afb_verb_v2 windowmanager_verbs[] = {
{}
};
+void on_event(const char *event, struct json_object *object){
+ HMI_DEBUG("wm", "event:%s", event);
+
+ // If receive low can signal
+ if (strstr(event, "low-can")) {
+ // Analyze low can signal
+ g_afb_instance->lcc_.analyzeCanSignal(object);
+
+ if (g_afb_instance->lcc_.isChangedCarState()) {
+ // If car state is changed
+ HMI_DEBUG("wm", "Car state is changed");
+
+ // Get car state
+ const char* car_state = g_afb_instance->lcc_.getCurrentCarState();
+
+ // Allocate window resource
+ g_afb_instance->app.allocateWindowResource(car_state, nullptr,
+ nullptr, nullptr,
+ [](const char* errmsg){
+ if (errmsg != nullptr) {
+ HMI_ERROR("wm", errmsg);
+ }
+ });
+ }
+ else if (g_afb_instance->lcc_.isChangedLampState()) {
+ // If lamp state is changed
+ HMI_DEBUG("wm", "Lamp state is changed");
+
+ // Get lamp state
+ const char* lamp_state = g_afb_instance->lcc_.getCurrentLampState();
+
+ // Allocate window resource
+ g_afb_instance->app.allocateWindowResource(lamp_state, nullptr,
+ nullptr, nullptr,
+ [](const char* errmsg){
+ if (errmsg != nullptr) {
+ HMI_ERROR("wm", errmsg);
+ }
+ });
+ }
+ }
+}
+
extern "C" const struct afb_binding_v2 afbBindingV2 = {
- "windowmanager", nullptr, nullptr, windowmanager_verbs, nullptr, binding_init, nullptr, 0};
+ "windowmanager", nullptr, nullptr, windowmanager_verbs, nullptr, binding_init, on_event, 0};
diff --git a/src/policy_manager/CMakeLists.txt b/src/policy_manager/CMakeLists.txt
new file mode 100644
index 0000000..ca31e5f
--- /dev/null
+++ b/src/policy_manager/CMakeLists.txt
@@ -0,0 +1,65 @@
+#
+# 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)
+
+# We do not want a prefix for our module
+set(CMAKE_SHARED_MODULE_PREFIX "")
+
+set(TARGETS_PM lib${PLUGIN_PM})
+
+# Set use STM name
+set(USE_STM_NAME zipc)
+
+add_library(${TARGETS_PM} MODULE
+ policy_manager.cpp
+ policy_manager.hpp
+ ${USE_STM_NAME}/dummy_stm.c
+)
+
+target_include_directories(${TARGETS_PM}
+ PRIVATE
+ ../../include
+ ../
+ ./
+ ./${USE_STM_NAME}
+)
+
+target_compile_definitions(${TARGETS_PM}
+ PRIVATE
+ _GNU_SOURCE
+)
+
+target_compile_options(${TARGETS_PM}
+ PRIVATE
+ -Wall -Wextra -Wno-unused-parameter -Wno-comment)
+
+set_target_properties(${TARGETS_PM}
+ PROPERTIES
+ CXX_EXTENSIONS OFF
+ CXX_STANDARD 14
+ CXX_STANDARD_REQUIRED ON
+
+ C_EXTENSIONS OFF
+ C_STANDARD 99
+ C_STANDARD_REQUIRED ON
+)
+
+install(
+ TARGETS ${TARGET_PM}
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ COMPONENT "runtime"
+)
diff --git a/src/policy_manager/db/role.db b/src/policy_manager/db/role.db
new file mode 100644
index 0000000..2807fde
--- /dev/null
+++ b/src/policy_manager/db/role.db
@@ -0,0 +1,44 @@
+{
+ "roles":[
+ {
+ "category": "homescreen",
+ "role": "homescreen",
+ "area": "full",
+ },
+ {
+ "category": "map",
+ "role": "map",
+ "area": "full | normal | split.main",
+ },
+ {
+ "category": "general",
+ "role": "poi | music | video | browser | sdl | settings | mixer | radio | hvac | dashboard | debug",
+ "area": "normal",
+ },
+ {
+ "category": "phone",
+ "role": "phone",
+ "area": "normal",
+ },
+ {
+ "category": "splitable",
+ "role": "splitable1 | splitable2",
+ "area": "normal | split.main | split.sub",
+ },
+ {
+ "category": "popup",
+ "role": "popup",
+ "area": "on_screen",
+ },
+ {
+ "category": "system_alert",
+ "role": "system_alert",
+ "area": "on_screen",
+ },
+ {
+ "category": "tbt",
+ "role": "tbt",
+ "area": "hud",
+ }
+ ]
+} \ No newline at end of file
diff --git a/src/policy_manager/policy_manager.cpp b/src/policy_manager/policy_manager.cpp
new file mode 100644
index 0000000..61d1d7c
--- /dev/null
+++ b/src/policy_manager/policy_manager.cpp
@@ -0,0 +1,497 @@
+/*
+ * 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 <fstream>
+#include <sstream>
+#include <istream>
+#include <json-c/json.h>
+#include "policy_manager.hpp"
+#include "dummy_stm.h"
+#include "hmi-debug.h"
+
+
+namespace {
+
+static const char* kEventName[] = {
+ "activate",
+ "deactivate",
+ "car_stop",
+ "car_run",
+ "timer_expired",
+ "lamp_off",
+ "lamp_on"
+};
+
+static const int kEventNo[] = {
+ STM_EVT_NO_ACTIVATE,
+ STM_EVT_NO_DEACTIVATE,
+ STM_EVT_NO_CAR_STOP,
+ STM_EVT_NO_CAR_RUN,
+ STM_EVT_NO_TIMER_EXPIRED,
+ STM_EVT_NO_LAMP_OFF,
+ STM_EVT_NO_LAMP_ON
+};
+
+static const char* kCategoryName[] = {
+ "homescreen",
+ "map",
+ "general",
+ "splitable",
+ "popup",
+ "system_alert"
+};
+
+static const int kCategoryNo[] = {
+ STM_CTG_NO_HOMESCREEN,
+ STM_CTG_NO_MAP,
+ STM_CTG_NO_GENERAL,
+ STM_CTG_NO_SPLITABLE,
+ STM_CTG_NO_POPUP,
+ STM_CTG_NO_SYSTEM_ALERT
+};
+
+static const char* kAreaName[] = {
+ "full",
+ "normal",
+ "split.main",
+ "split.sub",
+ "onscreen"
+};
+
+static const int kAreaNo[] = {
+ STM_ARA_NO_FULL,
+ STM_ARA_NO_NORMAL,
+ STM_ARA_NO_SPLIT_MAIN,
+ STM_ARA_NO_SPLIT_SUB,
+ STM_ARA_NO_ON_SCREEN
+};
+
+// String for state
+const char* gStmCarStateNo2Name[] = {
+ "car_stop",
+ "car_run"
+};
+
+const char* gStmLampStateNo2Name[] = {
+ "lamp_off",
+ "lamp_on"
+};
+
+const char* gStmLayoutNo2Name[] = {
+ "none",
+ "pu",
+ "sa",
+ "m1",
+ "m2",
+ "mf",
+ "s1",
+ "s2",
+ "g",
+ "hs",
+};
+
+} // namespace
+
+PolicyManager::PolicyManager() :
+ eventname2no_(),
+ categoryname2no_(),
+ areaname2no_(),
+ role2category_(),
+ category2role_(),
+ role2defaultarea_(),
+ current_state_()
+{
+ HMI_DEBUG("wm:pm", "Call");
+}
+
+int PolicyManager::initialize() {
+ HMI_DEBUG("wm:pm", "Call");
+
+ int ret = 0;
+
+ // Create convert map
+ for (unsigned int i=0; i<(sizeof(kEventNo)/sizeof(int)); i++) {
+ HMI_DEBUG("wm:pm", "event name:%s no:%d", kEventName[i], kEventNo[i]);
+ this->eventname2no_[kEventName[i]] = kEventNo[i];
+ }
+
+ for (unsigned int i=0; i<(sizeof(kCategoryNo)/sizeof(int)); i++) {
+ HMI_DEBUG("wm:pm", "category name:%s no:%d", kCategoryName[i], kCategoryNo[i]);
+ this->categoryname2no_[kCategoryName[i]] = kCategoryNo[i];
+ }
+
+ for (unsigned int i=0; i<(sizeof(kAreaNo)/sizeof(int)); i++) {
+ HMI_DEBUG("wm:pm", "area name:%s no:%d", kAreaName[i], kAreaNo[i]);
+ this->areaname2no_[kAreaName[i]] = kAreaNo[i];
+ }
+
+ // Load role.db
+ ret = loadRoleDb();
+ if (0 > ret) {
+ HMI_ERROR("wm:pm", "Load role.db Error!!");
+ return ret;
+ }
+
+ // TODO:
+ // Initialize StateTransitioner
+ // stmInitialize();
+
+ return ret;
+}
+
+int PolicyManager::checkPolicy(json_object* json_in, json_object** json_out) {
+ HMI_DEBUG("wm:pm", "Call");
+
+ // Check arguments
+ if ((nullptr == json_in) || (nullptr == json_out)) {
+ HMI_ERROR("wm:pm", "Argument is NULL!!");
+ return -1;
+ }
+
+ // Get event from json_object
+ const char* event = getStringFromJson(json_in, "event");
+ int event_no = 0;
+ if (nullptr != event) {
+ // Convert name to number
+ event_no = this->eventname2no_[event];
+ HMI_DEBUG("wm:pm", "event(%s:%d)", event, event_no);
+ }
+
+ // Get role from json_object
+ const char* role = getStringFromJson(json_in, "role");
+ int category_no = 0;
+ if (nullptr != role) {
+ HMI_DEBUG("wm:pm", "role(%s)", role);
+
+ // Convert role to category
+ const char* category = this->role2category_[role].c_str();
+ if (0 == strcmp("", category)) {
+ HMI_ERROR("wm:pm", "Error!!");
+ return -1;
+ }
+ HMI_DEBUG("wm:pm", "category(%s)", category);
+
+ // Convert name to number
+ category_no = categoryname2no_[category];
+ HMI_DEBUG("wm:pm", "role(%s), category(%s:%d)", role, category, category_no);
+ }
+
+ // Get areat from json_object
+ const char* area = getStringFromJson(json_in, "area");
+ int area_no = 0;
+ if (nullptr != area) {
+ // Convert name to number
+ area_no = areaname2no_[area];
+ HMI_DEBUG("wm:pm", "area(%s:%d)", area, area_no);
+ }
+
+ // Transition state
+ HMI_DEBUG("wm:pm", "set event:0x%x", (event_no | category_no | area_no));
+ int ret = stmTransitionState((event_no | category_no | area_no),
+ &(this->current_state_));
+ if (0 > ret) {
+ HMI_ERROR("wm:pm", "Error!!");
+ return -1;
+ }
+
+ // Create result
+ // {
+ // "car": {
+ // "is_changed": <bool>,
+ // "state": <const char*>
+ // },
+ HMI_DEBUG("wm", "@@@@@ car state (is_changed:%d state:%d:%s)",
+ this->current_state_.car.is_changed,
+ this->current_state_.car.state,
+ gStmCarStateNo2Name[this->current_state_.car.state]);
+ this->addStateToJson("car",
+ this->current_state_.car.is_changed,
+ gStmCarStateNo2Name[this->current_state_.car.state],
+ json_out);
+
+ // "lamp": {
+ // "is_changed": <bool>,
+ // "state": <const char*>
+ // },
+ HMI_DEBUG("wm", "@@@@@ lamp state (is_changed:%d state:%d:%s)",
+ this->current_state_.lamp.is_changed,
+ this->current_state_.lamp.state,
+ gStmLampStateNo2Name[this->current_state_.lamp.state]);
+ this->addStateToJson("lamp",
+ this->current_state_.lamp.is_changed,
+ gStmLampStateNo2Name[this->current_state_.lamp.state],
+ json_out);
+
+ // "layers": [
+ // {
+ // "on_screen": {
+ // "is_changed": <bool>,
+ // "state": <const char*>
+ // }
+ // },
+ json_object* json_layer = json_object_new_array();
+ json_object* json_tmp = json_object_new_object();
+ this->addStateToJson("on_screen",
+ this->current_state_.layer.on_screen.is_changed,
+ gStmLayoutNo2Name[this->current_state_.layer.on_screen.state],
+ &json_tmp);
+ json_object_array_add(json_layer, json_tmp);
+
+ // {
+ // "apps": {
+ // "is_changed": <bool>,
+ // "state": <const char*>
+ // }
+ // },
+ json_tmp = json_object_new_object();
+ this->addStateToJson("apps",
+ this->current_state_.layer.apps.is_changed,
+ gStmLayoutNo2Name[this->current_state_.layer.apps.state],
+ &json_tmp);
+ json_object_array_add(json_layer, json_tmp);
+
+ // {
+ // "homescreen": {
+ // "is_changed": <bool>,
+ // "state": <const char*>
+ // }
+ // },
+ // ]
+ // }
+ json_tmp = json_object_new_object();
+ this->addStateToJson("homescreen",
+ this->current_state_.layer.homescreen.is_changed,
+ gStmLayoutNo2Name[this->current_state_.layer.homescreen.state],
+ &json_tmp);
+ json_object_array_add(json_layer, json_tmp);
+
+ // Add json array of layer
+ json_object_object_add(*json_out, "layers", json_layer);
+
+ HMI_DEBUG("wm:pm", "json_out.dump:%s", json_object_get_string(*json_out));
+
+ return 0;
+}
+
+std::string PolicyManager::roleToCategory(const char* role) {
+ return this->role2category_[role];
+}
+
+extern const char* kDefaultRoleDb;
+int PolicyManager::loadRoleDb() {
+ HMI_DEBUG("wm:pm", "Call");
+
+ std::string file_name;
+
+ // Get afm application installed dir
+ char const *afm_app_install_dir = getenv("AFM_APP_INSTALL_DIR");
+ HMI_DEBUG("wm:pm", "afm_app_install_dir:%s", afm_app_install_dir);
+
+ if (!afm_app_install_dir) {
+ HMI_ERROR("wm:pm", "AFM_APP_INSTALL_DIR is not defined");
+ }
+ else {
+ file_name = std::string(afm_app_install_dir) + std::string("/etc/role.db");
+ }
+
+ // Load role.db
+ HMI_DEBUG("wm:pm", "file_name:%s", file_name.c_str());
+ json_object* json_obj = json_object_from_file(file_name.c_str());
+ if (nullptr == json_obj) {
+ HMI_ERROR("wm:pm", "Could not open role.db, so use default role information");
+ json_obj = json_tokener_parse(kDefaultRoleDb);
+ }
+ HMI_DEBUG("wm:pm", "json_obj dump:%s", json_object_get_string(json_obj));
+
+ json_object* json_roles;
+ if (!json_object_object_get_ex(json_obj, "roles", &json_roles)) {
+ HMI_ERROR("wm:pm", "Parse Error!!");
+ return -1;
+ }
+
+ int len = json_object_array_length(json_roles);
+ HMI_DEBUG("wm:pm", "json_cfg len:%d", len);
+ HMI_DEBUG("wm:pm", "json_cfg dump:%s", json_object_get_string(json_roles));
+
+ json_object* json_tmp;
+ const char* category;
+ const char* roles;
+ const char* areas;
+ for (int i=0; i<len; i++) {
+ json_tmp = json_object_array_get_idx(json_roles, i);
+
+ category = this->getStringFromJson(json_tmp, "category");
+ roles = this->getStringFromJson(json_tmp, "role");
+ areas = this->getStringFromJson(json_tmp, "area");
+
+ if ((nullptr == category) || (nullptr == roles) || (nullptr == areas)) {
+ HMI_ERROR("wm:pm", "Parse Error!!");
+ return -1;
+ }
+
+ // Parse roles by '|'
+ std::vector<std::string> vct_roles;
+ vct_roles = this->parseString(std::string(roles), '|');
+
+ // Parse areas by '|'
+ std::vector<std::string> vct_areas;
+ vct_areas = this->parseString(std::string(areas), '|');
+
+ // Set role, category, default area
+ for (auto itr = vct_roles.begin(); itr != vct_roles.end(); ++itr) {
+ // Delete space from role and area name
+ std::string role = this->deleteSpace(*itr);
+ std::string area = this->deleteSpace(vct_areas[0]);
+
+ this->role2category_[role] = std::string(category);
+ this->role2defaultarea_[role] = area;
+ }
+
+ this->category2role_[std::string(category)] = std::string(roles);
+ }
+
+ // Check
+ HMI_DEBUG("wm:pm", "Check role2category_");
+ for (auto& x:this->role2category_){
+ HMI_DEBUG("wm:pm", "key:%s, val:%s", x.first.c_str(), x.second.c_str());
+ }
+
+ HMI_DEBUG("wm:pm", "Check role2defaultarea_");
+ for (auto& x:this->role2defaultarea_){
+ HMI_DEBUG("wm:pm", "key:%s, val:%s", x.first.c_str(), x.second.c_str());
+ }
+
+ HMI_DEBUG("wm:pm", "Check category2role_");
+ for (auto& x:this->category2role_){
+ HMI_DEBUG("wm:pm", "key:%s, val:%s", x.first.c_str(), x.second.c_str());
+ }
+
+ return 0;
+}
+
+const char* PolicyManager::getStringFromJson(json_object* obj, const char* key) {
+ if ((nullptr == obj) || (nullptr == key)) {
+ HMI_ERROR("wm:pm", "Argument is nullptr!!!");
+ return nullptr;
+ }
+
+ json_object* tmp;
+ if (!json_object_object_get_ex(obj, key, &tmp)) {
+ HMI_DEBUG("wm:pm", "Not found key \"%s\"", key);
+ return nullptr;
+ }
+
+ return json_object_get_string(tmp);
+}
+
+int PolicyManager::getIntFromJson(json_object* obj, const char* key) {
+ if ((nullptr == obj) || (nullptr == key)) {
+ HMI_ERROR("wm:pm", "Argument is nullptr!!!");
+ return 0;
+ }
+
+ json_object* tmp;
+ if (!json_object_object_get_ex(obj, key, &tmp)) {
+ HMI_DEBUG("wm:pm", "Not found key \"%s\"", key);
+ return 0;
+ }
+
+ return json_object_get_int(tmp);
+}
+
+void PolicyManager::addStateToJson(
+ const char* key, int is_changed, const char* state, json_object** json_out) {
+ if ((nullptr == key) || (nullptr == state) || (nullptr == json_out)) {
+ HMI_ERROR("wm:pm", "Argument is nullptr!!!");
+ return;
+ }
+
+ json_object* json_obj = json_object_new_object();
+ json_object_object_add(json_obj, "is_changed", json_object_new_boolean(is_changed));
+ if (is_changed) {
+ HMI_DEBUG("wm:pm", "%s: state changed (%s)", key, state);
+ json_object_object_add(json_obj, "state", json_object_new_string(state));
+ }
+ json_object_object_add(*json_out, key, json_obj);
+}
+
+std::vector<std::string> PolicyManager::parseString(std::string str, char delimiter) {
+ // Parse string by delimiter
+ std::vector<std::string> vct;
+ std::stringstream ss{str};
+ std::string buf;
+ while (std::getline(ss, buf, delimiter)) {
+ if (!buf.empty()) {
+ vct.push_back(buf);
+ }
+ }
+ return vct;
+}
+
+std::string PolicyManager::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;
+}
+
+const char* kDefaultRoleDb = "{ \
+ \"roles\":[ \
+ { \
+ \"category\": \"homescreen\", \
+ \"role\": \"homescreen\", \
+ \"area\": \"full\", \
+ }, \
+ { \
+ \"category\": \"map\", \
+ \"role\": \"map\", \
+ \"area\": \"full | normal | split.main\", \
+ }, \
+ { \
+ \"category\": \"general\", \
+ \"role\": \"poi | music | video | browser | sdl | settings | mixer | radio | hvac | dashboard | debug\", \
+ \"area\": \"normal\", \
+ }, \
+ { \
+ \"category\": \"phone\", \
+ \"role\": \"phone\", \
+ \"area\": \"normal\", \
+ }, \
+ { \
+ \"category\": \"splitable\", \
+ \"role\": \"splitable1 | splitable2\", \
+ \"area\": \"normal | split.main | split.sub\", \
+ }, \
+ { \
+ \"category\": \"popup\", \
+ \"role\": \"popup\", \
+ \"area\": \"on_screen\", \
+ }, \
+ { \
+ \"category\": \"system_alert\", \
+ \"role\": \"system_alert\", \
+ \"area\": \"on_screen\", \
+ }, \
+ { \
+ \"category\": \"tbt\", \
+ \"role\": \"tbt\", \
+ \"area\": \"hud\", \
+ } \
+ ] \
+}";
diff --git a/src/policy_manager/policy_manager.hpp b/src/policy_manager/policy_manager.hpp
new file mode 100644
index 0000000..500120c
--- /dev/null
+++ b/src/policy_manager/policy_manager.hpp
@@ -0,0 +1,67 @@
+/*
+ * 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_POLICY_MANAGER_HPP
+#define TMCAGLWM_POLICY_MANAGER_HPP
+
+
+#include <unordered_map>
+#include <vector>
+
+//namespace stm {
+extern "C" {
+#include "dummy_stm.h"
+}
+//} // namespace stm
+
+class PolicyManager {
+
+public:
+ explicit PolicyManager();
+ ~PolicyManager() = default;
+
+ int initialize();
+ int checkPolicy(json_object* json_in, json_object** json_out);
+ std::string roleToCategory(const char* role);
+private:
+ // Disable copy and move
+ PolicyManager(PolicyManager const &) = delete;
+ PolicyManager &operator=(PolicyManager const &) = delete;
+ PolicyManager(PolicyManager &&) = delete;
+ PolicyManager &operator=(PolicyManager &&) = delete;
+
+ // Convert map
+ std::unordered_map<std::string, int> eventname2no_;
+ std::unordered_map<std::string, int> categoryname2no_;
+ std::unordered_map<std::string, int> areaname2no_;
+
+ std::unordered_map<std::string, std::string> role2category_;
+ std::unordered_map<std::string, std::string> category2role_;
+ std::unordered_map<std::string, std::string> role2defaultarea_;
+
+ stm_state_t current_state_;
+
+ // Load role.db
+ int loadRoleDb();
+
+ const char* getStringFromJson(json_object* obj, const char* key);
+ int getIntFromJson(json_object* obj, const char* key);
+ void addStateToJson(const char* key, int is_changed, const char* state, json_object** json_out);
+ std::vector<std::string> parseString(std::string str, char delimiter);
+ std::string deleteSpace(std::string str);
+};
+
+#endif // TMCAGLWM_POLICY_MANAGER_HPP
diff --git a/src/policy_manager/zipc/category.db b/src/policy_manager/zipc/category.db
new file mode 100644
index 0000000..4867260
--- /dev/null
+++ b/src/policy_manager/zipc/category.db
@@ -0,0 +1,32 @@
+{
+ "categories":[
+ {
+ "name": "homescreen",
+ "role": "homescreen"
+ },
+ {
+ "name": "map",
+ "role": "map"
+ },
+ {
+ "name": "general",
+ "role": "poi | music | radio | video | browser | sdl | phone | settings | mixer | hvac | dashboard | fallback"
+ },
+ {
+ "name": "pop_up",
+ "role": "incoming_call"
+ },
+ {
+ "name": "system_alert",
+ "role": "system_alert"
+ },
+ {
+ "name": "tbt",
+ "role": "tbt"
+ },
+ {
+ "name": "splitable",
+ "role": "test_splitable1 | test_splitable2"
+ }
+ ]
+}
diff --git a/src/policy_manager/zipc/dummy_stm.c b/src/policy_manager/zipc/dummy_stm.c
new file mode 100644
index 0000000..3c9cba2
--- /dev/null
+++ b/src/policy_manager/zipc/dummy_stm.c
@@ -0,0 +1,212 @@
+
+#include <string.h>
+#include "dummy_stm.h"
+
+stm_state_t g_crr_state = {0};
+stm_state_t g_prv_state = {0};
+int g_prv_apps_state_car_stop = 0;
+
+int stmTransitionState(int event, stm_state_t* state) {
+ int event_no, category_no, area_no;
+ int apps_state, car_state, lamp_state;
+
+ event_no = event & STM_MSK_EVT_NO;
+ category_no = event & STM_MSK_CTG_NO;
+ area_no = event & STM_MSK_ARA_NO;
+
+ // Backup previous state
+ g_prv_state = g_crr_state;
+
+ // Get previous state
+ apps_state = g_prv_state.layer.apps.state;
+ car_state = g_prv_state.car.state;
+ lamp_state = g_prv_state.lamp.state;
+
+ // Clear current state
+ memset(&g_crr_state, 0, sizeof(g_crr_state));
+
+ switch (event_no) {
+ case STM_EVT_NO_ACTIVATE:
+ switch (category_no) {
+ case STM_CTG_NO_HOMESCREEN:
+ // Apps layer
+ g_crr_state.layer.apps.state = gStmLayoutNoNone;
+ g_crr_state.layer.apps.is_changed = STM_TRUE;
+
+ // Homescreen layer
+ g_crr_state.layer.homescreen.state = gStmLayoutNoHs;
+ g_crr_state.layer.homescreen.is_changed = STM_TRUE;
+ break;
+ case STM_CTG_NO_MAP:
+ switch (area_no) {
+ case STM_ARA_NO_FULL:
+ // Apps layer
+ switch (apps_state) {
+ case gStmLayoutNoMf:
+ // nop
+ break;
+ default:
+ g_crr_state.layer.apps.state = gStmLayoutNoMf;
+ g_crr_state.layer.apps.is_changed = STM_TRUE;
+ break;
+ }
+ break;
+ case STM_ARA_NO_NORMAL:
+ // Apps layer
+ switch (apps_state) {
+ case gStmLayoutNoM1:
+ // nop
+ break;
+ case gStmLayoutNoS1:
+ g_crr_state.layer.apps.state = gStmLayoutNoM2;
+ g_crr_state.layer.apps.is_changed = STM_TRUE;
+ break;
+ default:
+ g_crr_state.layer.apps.state = gStmLayoutNoM1;
+ g_crr_state.layer.apps.is_changed = STM_TRUE;
+ }
+ break;
+ case STM_ARA_NO_SPLIT_MAIN:
+ // Apps layer
+ switch (apps_state) {
+ case gStmLayoutNoS1:
+ case gStmLayoutNoS2:
+ g_crr_state.layer.apps.state = gStmLayoutNoS2;
+ g_crr_state.layer.apps.is_changed = STM_TRUE;
+ break;
+ default:
+ // nop
+ break;
+ }
+ break;
+ }
+ break;
+ case STM_CTG_NO_GENERAL:
+ switch (area_no) {
+ case STM_ARA_NO_NORMAL:
+ // Apps layer
+ switch (apps_state) {
+ case gStmLayoutNoMf:
+ // nop
+ break;
+ default:
+ g_crr_state.layer.apps.state = gStmLayoutNoG;
+ g_crr_state.layer.apps.is_changed = STM_TRUE;
+ break;
+ }
+ break;
+ default:
+ // nop
+ break;
+ }
+ break;
+ case STM_CTG_NO_SPLITABLE:
+ switch (area_no) {
+ case STM_ARA_NO_NORMAL:
+ // Apps layer
+ switch (apps_state) {
+ case gStmLayoutNoMf:
+ // nop
+ break;
+ case gStmLayoutNoS1:
+ g_crr_state.layer.apps.state = gStmLayoutNoS2;
+ g_crr_state.layer.apps.is_changed = STM_TRUE;
+ break;
+ default:
+ g_crr_state.layer.apps.state = gStmLayoutNoS1;
+ g_crr_state.layer.apps.is_changed = STM_TRUE;
+ break;
+ }
+ break;
+ case STM_ARA_NO_SPLIT_MAIN:
+ // Apps layer
+ switch (apps_state) {
+ case gStmLayoutNoS1:
+ g_crr_state.layer.apps.state = gStmLayoutNoS2;
+ g_crr_state.layer.apps.is_changed = STM_TRUE;
+ break;
+ case gStmLayoutNoS2:
+ g_crr_state.layer.apps.state = gStmLayoutNoS2;
+ g_crr_state.layer.apps.is_changed = STM_TRUE;
+ break;
+ default:
+ // nop
+ break;
+ }
+ break;
+ case STM_ARA_NO_SPLIT_SUB:
+ // Apps layer
+ switch (apps_state) {
+ case gStmLayoutNoM1:
+ g_crr_state.layer.apps.state = gStmLayoutNoM2;
+ g_crr_state.layer.apps.is_changed = STM_TRUE;
+ break;
+ case gStmLayoutNoM2:
+ g_crr_state.layer.apps.state = gStmLayoutNoM2;
+ g_crr_state.layer.apps.is_changed = STM_TRUE;
+ break;
+ case gStmLayoutNoS1:
+ g_crr_state.layer.apps.state = gStmLayoutNoS2;
+ g_crr_state.layer.apps.is_changed = STM_TRUE;
+ break;
+ case gStmLayoutNoS2:
+ g_crr_state.layer.apps.state = gStmLayoutNoS2;
+ g_crr_state.layer.apps.is_changed = STM_TRUE;
+ break;
+ default:
+ // nop
+ break;
+ }
+ break;
+ default:
+ // nop
+ break;
+ }
+ break;
+ default:
+ // nop
+ break;
+ }
+ break;
+ case STM_EVT_NO_CAR_STOP:
+ if (gStmCarStateNoStop != car_state) {
+ g_crr_state.layer.apps.state = g_prv_apps_state_car_stop;
+ g_crr_state.layer.apps.is_changed = STM_TRUE;
+
+ g_crr_state.car.state = gStmCarStateNoStop;
+ g_crr_state.car.is_changed = STM_TRUE;
+ }
+ break;
+ case STM_EVT_NO_CAR_RUN:
+ if (gStmCarStateNoRun != car_state) {
+ g_prv_apps_state_car_stop = apps_state;
+ g_crr_state.layer.apps.state = gStmLayoutNoM1;
+ g_crr_state.layer.apps.is_changed = STM_TRUE;
+
+ g_crr_state.car.state = gStmCarStateNoRun;
+ g_crr_state.car.is_changed = STM_TRUE;
+ }
+ break;
+ case STM_EVT_NO_LAMP_OFF:
+ if (gStmLampStateNoOff != lamp_state) {
+ g_crr_state.lamp.state = gStmLampStateNoOff;
+ g_crr_state.lamp.is_changed = STM_TRUE;
+ }
+ break;
+ case STM_EVT_NO_LAMP_ON:
+ if (gStmLampStateNoOn != lamp_state) {
+ g_crr_state.lamp.state = gStmLampStateNoOn;
+ g_crr_state.lamp.is_changed = STM_TRUE;
+ }
+ break;
+ default:
+ // nop
+ break;
+ }
+
+ // Copy current state for return
+ memcpy(state, &g_crr_state, sizeof(g_crr_state));
+
+ return 0;
+}
+
diff --git a/src/policy_manager/zipc/dummy_stm.h b/src/policy_manager/zipc/dummy_stm.h
new file mode 100644
index 0000000..18af99b
--- /dev/null
+++ b/src/policy_manager/zipc/dummy_stm.h
@@ -0,0 +1,127 @@
+/*
+ * 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_DUMMY_STM_HPP
+#define TMCAGLWM_DUMMY_STM_HPP
+
+// TODO: This file should be existed in STM
+
+//
+#define STM_TRUE 1
+#define STM_FALSE 0
+
+// Event number
+#define STM_EVT_NO_ACTIVATE 0x01
+#define STM_EVT_NO_DEACTIVATE 0x02
+#define STM_EVT_NO_CAR_STOP 0x03
+#define STM_EVT_NO_CAR_RUN 0x04
+#define STM_EVT_NO_TIMER_EXPIRED 0x05
+#define STM_EVT_NO_LAMP_OFF 0x06
+#define STM_EVT_NO_LAMP_ON 0x07
+
+// Category number
+#define STM_CTG_NO_HOMESCREEN 0x0100
+#define STM_CTG_NO_MAP 0x0200
+#define STM_CTG_NO_GENERAL 0x0300
+#define STM_CTG_NO_SPLITABLE 0x0400
+#define STM_CTG_NO_POPUP 0x0500
+#define STM_CTG_NO_SYSTEM_ALERT 0x0600
+
+// Area number
+#define STM_ARA_NO_FULL 0x010000
+#define STM_ARA_NO_NORMAL 0x020000
+#define STM_ARA_NO_SPLIT_MAIN 0x030000
+#define STM_ARA_NO_SPLIT_SUB 0x040000
+#define STM_ARA_NO_ON_SCREEN 0x050000
+
+// Mask
+#define STM_MSK_EVT_NO 0x0000FF
+#define STM_MSK_CTG_NO 0x00FF00
+#define STM_MSK_ARA_NO 0xFF0000
+
+// Enum for state
+enum stm_car_state_ {
+ gStmCarStateNoStop = 0,
+ gStmCarStateNoRun
+};
+
+enum stm_lamp_state_ {
+ gStmLampStateNoOff = 0,
+ gStmLampStateNoOn
+};
+
+enum stm_layout_ {
+ gStmLayoutNoNone = 0,
+ gStmLayoutNoPu,
+ gStmLayoutNoSa,
+ gStmLayoutNoM1,
+ gStmLayoutNoM2,
+ gStmLayoutNoMf,
+ gStmLayoutNoS1,
+ gStmLayoutNoS2,
+ gStmLayoutNoG,
+ gStmLayoutNoHs
+};
+
+
+#if 0
+// String for state
+const char* gStmCarStateNo2Name[] {
+ "car_stop",
+ "car_run"
+};
+
+const char* gStmLampStateNo2Name[] {
+ "lamp_off",
+ "lamp_on"
+};
+
+const char* gStmLayoutNo2Name[] {
+ "none",
+ "pu",
+ "sa",
+ "m1",
+ "m2",
+ "mf",
+ "s1",
+ "s2",
+ "g",
+ "hs",
+};
+#endif
+
+typedef struct stm_base_state_ {
+ int is_changed;
+ int state;
+} stm_base_state;
+
+typedef struct stm_layer_state_ {
+ stm_base_state on_screen;
+ stm_base_state apps;
+ stm_base_state homescreen;
+} stm_layer_state;
+
+// Struct for state
+typedef struct {
+ stm_base_state car;
+ stm_base_state lamp;
+ stm_layer_state layer;
+} stm_state_t;
+
+int stmTransitionState(int event_no, stm_state_t* state);
+
+
+#endif // TMCAGLWM_DUMMY_STM_HPP