2 * Copyright (c) 2017 TOYOTA MOTOR CORPORATION
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
20 #include "window_manager.hpp"
21 #include "json_helper.hpp"
22 #include "applist.hpp"
26 #include <systemd/sd-event.h>
35 static const uint64_t kTimeOut = 3ULL; /* 3s */
37 /* DrawingArea name used by "{layout}.{area}" */
38 const char kNameLayoutNormal[] = "normal";
39 const char kNameLayoutSplit[] = "split";
40 const char kNameAreaFull[] = "full";
41 const char kNameAreaMain[] = "main";
42 const char kNameAreaSub[] = "sub";
44 /* Key for json obejct */
45 const char kKeyDrawingName[] = "drawing_name";
46 const char kKeyDrawingArea[] = "drawing_area";
47 const char kKeyDrawingRect[] = "drawing_rect";
48 const char kKeyX[] = "x";
49 const char kKeyY[] = "y";
50 const char kKeyWidth[] = "width";
51 const char kKeyHeight[] = "height";
52 const char kKeyWidthPixel[] = "width_pixel";
53 const char kKeyHeightPixel[] = "height_pixel";
54 const char kKeyWidthMm[] = "width_mm";
55 const char kKeyHeightMm[] = "height_mm";
56 const char kKeyScale[] = "scale";
57 const char kKeyIds[] = "ids";
59 static const vector<string> kListEventName{
69 static sd_event_source *g_timer_ev_src = nullptr;
70 static AppList g_app_list;
71 static WindowManager *g_context;
76 static int processTimerHandler(sd_event_source *s, uint64_t usec, void *userdata)
78 HMI_NOTICE("Time out occurs because the client replys endDraw slow, so revert the request");
79 reinterpret_cast<wm::WindowManager *>(userdata)->timerHandler();
83 static void onStateTransitioned(vector<WMAction> actions)
85 g_context->startTransitionWrapper(actions);
90 g_context->processError(WMError::LAYOUT_CHANGE_FAIL);
97 WindowManager::WindowManager()
100 const char *path = getenv("AFM_APP_INSTALL_DIR");
103 HMI_ERROR("AFM_APP_INSTALL_DIR is not defined");
107 this->lc = std::make_shared<LayerControl>(root);
109 HMI_DEBUG("Layer Controller initialized");
112 int WindowManager::init()
114 LayerControlCallbacks lmcb;
115 lmcb.surfaceCreated = [&](unsigned pid, unsigned surface){
116 this->surface_created(surface);
118 lmcb.surfaceDestroyed = [&](unsigned surface){
119 this->surface_removed(surface);
122 if(this->lc->init(lmcb) != WMError::SUCCESS)
127 // Store my context for calling callback from PolicyManager
130 // Initialize PMWrapper
131 this->pmw.initialize();
133 // Register callback to PolicyManager
134 this->pmw.registerCallback(onStateTransitioned, onError);
136 // Make afb event for subscriber
137 for (int i = Event_ScreenUpdated; i < Event_Error; i++)
139 map_afb_event[kListEventName[i]] = afb_api_make_event(afbBindingV3root, kListEventName[i].c_str());
142 const struct rect css_bg = this->lc->getAreaSize("fullscreen");
143 Screen screen = this->lc->getScreenInfo();
144 rectangle dp_bg(screen.width(), screen.height());
146 dp_bg.set_aspect(static_cast<double>(css_bg.w) / css_bg.h);
147 dp_bg.fit(screen.width(), screen.height());
148 dp_bg.center(screen.width(), screen.height());
149 HMI_DEBUG("SCALING: CSS BG(%dx%d) -> DDP %dx%d,(%dx%d)",
150 css_bg.w, css_bg.h, dp_bg.left(), dp_bg.top(), dp_bg.width(), dp_bg.height());
152 double scale = static_cast<double>(dp_bg.height()) / css_bg.h;
153 this->lc->setupArea(dp_bg, scale);
158 result<int> WindowManager::api_request_surface(char const *appid, char const *drawing_name)
160 string str_id = appid;
161 string role = drawing_name;
164 if(!g_app_list.contains(str_id))
166 lid = this->lc->getNewLayerID(role);
169 // register drawing_name as fallback and make it displayed.
170 lid = this->lc->getNewLayerID(string("fallback"));
171 HMI_DEBUG("%s is not registered in layers.json, then fallback as normal app", role);
174 return Err<int>("Designated role does not match any role, fallback is disabled");
177 this->lc->createNewLayer(lid);
178 // add client into the db
179 g_app_list.addClient(str_id, lid, role);
182 // generate surface ID for ivi-shell application
183 auto rname = this->id_alloc.lookup(role);
186 // name does not exist yet, allocate surface id...
187 auto id = int(this->id_alloc.generate_id(role));
188 this->tmp_surface2app[id] = {str_id, lid};
190 auto client = g_app_list.lookUpClient(str_id);
191 client->registerSurface(id);
196 // Check currently registered drawing names if it is already there.
197 return Err<int>("Surface already present");
200 char const *WindowManager::api_request_surface(char const *appid, char const *drawing_name,
203 string str_id = appid;
204 string role = drawing_name;
206 unsigned sid = std::stol(ivi_id);
207 HMI_DEBUG("This API(requestSurfaceXDG) is for XDG Application using runXDG");
209 * IVI-shell doesn't send surface_size event via ivi-wm protocol
210 * if the application is using XDG surface.
211 * So WM has to set surface size with original size here
213 WMError ret = this->lc->setXDGSurfaceOriginSize(sid);
216 HMI_ERROR("%s", errorDescription(ret));
217 HMI_WARNING("The main user of this API is runXDG");
221 if(!g_app_list.contains(str_id))
223 unsigned l_id = this->lc->getNewLayerID(role);
226 // register drawing_name as fallback and make it displayed.
227 l_id = this->lc->getNewLayerID("fallback");
228 HMI_DEBUG("%s is not registered in layers.json, then fallback as normal app", role);
231 return "Designated role does not match any role, fallback is disabled";
234 this->lc->createNewLayer(l_id);
235 // add client into the db
236 g_app_list.addClient(str_id, l_id, role);
239 auto rname = this->id_alloc.lookup(role);
243 return "Surface already present";
246 // register pair drawing_name and ivi_id
247 this->id_alloc.register_name_id(role, sid);
249 auto client = g_app_list.lookUpClient(str_id);
250 client->addSurface(sid);
255 void WindowManager::api_activate_window(char const *appid, char const *drawing_name,
256 char const *drawing_area, const reply_func &reply)
259 string role = drawing_name;
260 string area = drawing_area;
262 if(!g_app_list.contains(id))
264 reply("app doesn't request 'requestSurface' or 'setRole' yet");
267 auto client = g_app_list.lookUpClient(id);
269 Task task = Task::TASK_ALLOCATE;
270 unsigned req_num = 0;
271 WMError ret = WMError::UNKNOWN;
273 ret = this->setRequest(id, role, area, task, &req_num);
275 if(ret != WMError::SUCCESS)
277 HMI_ERROR(errorDescription(ret));
278 reply("Failed to set request");
283 if (req_num != g_app_list.currentRequestNumber())
285 // Add request, then invoked after the previous task is finished
286 HMI_SEQ_DEBUG(req_num, "request is accepted");
291 ret = this->checkPolicy(req_num);
293 if (ret != WMError::SUCCESS)
296 HMI_SEQ_ERROR(req_num, errorDescription(ret));
297 g_app_list.removeRequest(req_num);
298 this->processNextRequest();
302 void WindowManager::api_deactivate_window(char const *appid, char const *drawing_name,
303 const reply_func &reply)
307 string role = drawing_name;
308 string area = ""; //drawing_area;
309 Task task = Task::TASK_RELEASE;
310 unsigned req_num = 0;
311 WMError ret = WMError::UNKNOWN;
313 ret = this->setRequest(id, role, area, task, &req_num);
315 if (ret != WMError::SUCCESS)
317 HMI_ERROR(errorDescription(ret));
318 reply("Failed to set request");
323 if (req_num != g_app_list.currentRequestNumber())
325 // Add request, then invoked after the previous task is finished
326 HMI_SEQ_DEBUG(req_num, "request is accepted");
331 ret = this->checkPolicy(req_num);
333 if (ret != WMError::SUCCESS)
336 HMI_SEQ_ERROR(req_num, errorDescription(ret));
337 g_app_list.removeRequest(req_num);
338 this->processNextRequest();
342 void WindowManager::api_enddraw(char const *appid, char const *drawing_name)
345 string role = drawing_name;
346 unsigned current_req = g_app_list.currentRequestNumber();
347 bool result = g_app_list.setEndDrawFinished(current_req, id, role);
351 HMI_ERROR("%s is not in transition state", id.c_str());
355 if (g_app_list.endDrawFullfilled(current_req))
357 // do task for endDraw
359 WMError ret = this->doEndDraw(current_req);
361 if(ret != WMError::SUCCESS)
363 //this->emit_error();
365 // Undo state of PolicyManager
366 this->pmw.undoState();
367 this->lc->undoUpdate();
369 this->emitScreenUpdated(current_req);
370 HMI_SEQ_INFO(current_req, "Finish request status: %s", errorDescription(ret));
372 g_app_list.removeRequest(current_req);
374 this->processNextRequest();
378 HMI_SEQ_INFO(current_req, "Wait other App call endDraw");
383 bool WindowManager::api_subscribe(afb_req_t req, EventType event_id)
386 char* appid = afb_req_get_application_id(req);
387 if(event_id < Event_Val_Min || event_id > Event_Val_Max)
389 HMI_ERROR("not defined in Window Manager", event_id);
392 HMI_INFO("%s subscribe %s : %d", appid, kListEventName[event_id], event_id);
393 if(event_id == Event_ScreenUpdated)
395 // Event_ScreenUpdated should be emitted to subscriber
396 afb_event_t event = this->map_afb_event[kListEventName[event_id]];
397 int rc = afb_req_subscribe(req, event);
407 auto client = g_app_list.lookUpClient(id);
408 if(client != nullptr)
410 ret = client->subscribe(req, kListEventName[event_id]);
416 result<json_object *> WindowManager::api_get_display_info()
418 Screen screen = this->lc->getScreenInfo();
420 json_object *object = json_object_new_object();
421 json_object_object_add(object, kKeyWidthPixel, json_object_new_int(screen.width()));
422 json_object_object_add(object, kKeyHeightPixel, json_object_new_int(screen.height()));
424 json_object_object_add(object, kKeyWidthMm, json_object_new_int(0));
425 json_object_object_add(object, kKeyHeightMm, json_object_new_int(0));
426 json_object_object_add(object, kKeyScale, json_object_new_double(this->lc->scale()));
428 return Ok<json_object *>(object);
431 result<json_object *> WindowManager::api_get_area_info(char const *drawing_name)
435 string role = drawing_name;
437 // Check drawing name, surface/layer id
438 auto const &surface_id = this->id_alloc.lookup(role);
441 return Err<json_object *>("Surface does not exist");
444 // Set area rectangle
445 struct rect area_info = this->area_info[*surface_id];
446 json_object *object = json_object_new_object();
447 json_object_object_add(object, kKeyX, json_object_new_int(area_info.x));
448 json_object_object_add(object, kKeyY, json_object_new_int(area_info.y));
449 json_object_object_add(object, kKeyWidth, json_object_new_int(area_info.w));
450 json_object_object_add(object, kKeyHeight, json_object_new_int(area_info.h));
452 return Ok<json_object *>(object);
458 void WindowManager::surface_created(unsigned surface_id)
461 if(this->tmp_surface2app.count(surface_id) != 0)
463 string appid = this->tmp_surface2app[surface_id].appid;
464 auto client = g_app_list.lookUpClient(appid);
465 if(client != nullptr)
467 WMError ret = client->addSurface(surface_id);
468 HMI_INFO("Add surface %d to \"%s\"", surface_id, appid.c_str());
469 if(ret != WMError::SUCCESS)
471 HMI_ERROR("Failed to add surface to client %s", client->appID().c_str());
474 this->tmp_surface2app.erase(surface_id);
478 void WindowManager::surface_removed(unsigned surface_id)
480 HMI_DEBUG("Delete surface_id %u", surface_id);
481 this->id_alloc.remove_id(surface_id);
482 g_app_list.removeSurface(surface_id);
485 void WindowManager::removeClient(const string &appid)
487 HMI_DEBUG("Remove clinet %s from list", appid.c_str());
488 auto client = g_app_list.lookUpClient(appid);
489 this->lc->appTerminated(client);
490 g_app_list.removeClient(appid);
493 void WindowManager::exceptionProcessForTransition()
495 unsigned req_num = g_app_list.currentRequestNumber();
496 HMI_SEQ_NOTICE(req_num, "Process exception handling for request. Remove current request %d", req_num);
497 g_app_list.removeRequest(req_num);
498 HMI_SEQ_NOTICE(g_app_list.currentRequestNumber(), "Process next request if exists");
499 this->processNextRequest();
502 void WindowManager::timerHandler()
504 unsigned req_num = g_app_list.currentRequestNumber();
505 HMI_SEQ_DEBUG(req_num, "Timer expired remove Request");
506 g_app_list.reqDump();
507 g_app_list.removeRequest(req_num);
508 this->processNextRequest();
511 void WindowManager::startTransitionWrapper(vector<WMAction> &actions)
514 unsigned req_num = g_app_list.currentRequestNumber();
518 if (g_app_list.haveRequest())
520 HMI_SEQ_DEBUG(req_num, "There is no WMAction for this request");
521 goto proc_remove_request;
525 HMI_SEQ_DEBUG(req_num, "There is no request");
530 for (auto &act : actions)
535 auto const &surface_id = this->id_alloc.lookup(act.role);
536 if(surface_id == nullopt)
538 goto proc_remove_request;
540 string appid = g_app_list.getAppID(*surface_id, &found);
543 if (TaskVisible::INVISIBLE == act.visible)
545 // App is killed, so do not set this action
550 HMI_SEQ_ERROR(req_num, "appid which is visible is not found");
555 auto client = g_app_list.lookUpClient(appid);
556 act.req_num = req_num;
560 ret = g_app_list.setAction(req_num, act);
561 if (ret != WMError::SUCCESS)
563 HMI_SEQ_ERROR(req_num, "Setting action is failed");
568 HMI_SEQ_DEBUG(req_num, "Start transition.");
569 ret = this->startTransition(req_num);
570 if (ret != WMError::SUCCESS)
572 if (ret == WMError::NO_LAYOUT_CHANGE)
574 goto proc_remove_request;
578 HMI_SEQ_ERROR(req_num, "Transition state is failed");
587 HMI_SEQ_ERROR(req_num, errorDescription(ret));
588 this->pmw.undoState();
591 g_app_list.removeRequest(req_num);
592 this->processNextRequest();
595 void WindowManager::processError(WMError error)
597 unsigned req_num = g_app_list.currentRequestNumber();
600 HMI_SEQ_ERROR(req_num, errorDescription(error));
601 g_app_list.removeRequest(req_num);
602 this->processNextRequest();
605 WMError WindowManager::setRequest(const string& appid, const string &role, const string &area,
606 Task task, unsigned* req_num)
608 if (!g_app_list.contains(appid))
610 return WMError::NOT_REGISTERED;
613 auto client = g_app_list.lookUpClient(appid);
618 unsigned current = g_app_list.currentRequestNumber();
619 unsigned requested_num = g_app_list.getRequestNumber(appid);
620 if (requested_num != 0)
622 HMI_SEQ_INFO(requested_num,
623 "%s %s %s request is already queued", appid.c_str(), role.c_str(), area.c_str());
627 WMRequest req = WMRequest(appid, role, area, task);
628 unsigned new_req = g_app_list.addRequest(req);
630 g_app_list.reqDump();
632 HMI_SEQ_DEBUG(current, "%s start sequence with %s, %s", appid.c_str(), role.c_str(), area.c_str());
634 return WMError::SUCCESS;
637 WMError WindowManager::checkPolicy(unsigned req_num)
642 // get current trigger
644 WMError ret = WMError::LAYOUT_CHANGE_FAIL;
645 auto trigger = g_app_list.getRequest(req_num, &found);
648 ret = WMError::NO_ENTRY;
651 string req_area = trigger.area;
653 // Input event data to PolicyManager
654 if (0 > this->pmw.setInputEventData(trigger.task, trigger.role, trigger.area))
656 HMI_SEQ_ERROR(req_num, "Failed to set input event data to PolicyManager");
660 // Execute state transition of PolicyManager
661 if (0 > this->pmw.executeStateTransition())
663 HMI_SEQ_ERROR(req_num, "Failed to execute state transition of PolicyManager");
667 ret = WMError::SUCCESS;
669 g_app_list.reqDump();
674 WMError WindowManager::startTransition(unsigned req_num)
676 bool sync_draw_happen = false;
678 WMError ret = WMError::SUCCESS;
679 auto actions = g_app_list.getActions(req_num, &found);
682 ret = WMError::NO_ENTRY;
683 HMI_SEQ_ERROR(req_num,
684 "Window Manager bug :%s : Action is not set", errorDescription(ret));
688 g_app_list.reqDump();
689 for (const auto &action : actions)
691 if (action.visible == TaskVisible::VISIBLE)
693 sync_draw_happen = true;
694 struct rect r = this->lc->getAreaSize(action.area);
695 action.client->emitSyncDraw(action.area, r);
699 if (sync_draw_happen)
705 // deactivate only, no syncDraw
706 // Make it deactivate here
707 for (const auto &x : actions)
709 this->lc->visibilityChange(x);
710 x.client->emitActive(false);
711 x.client->emitVisible(false);
713 this->lc->renderLayers();
714 ret = WMError::NO_LAYOUT_CHANGE;
719 WMError WindowManager::doEndDraw(unsigned req_num)
723 auto actions = g_app_list.getActions(req_num, &found);
724 WMError ret = WMError::SUCCESS;
727 ret = WMError::NO_ENTRY;
731 HMI_SEQ_INFO(req_num, "do endDraw");
733 // layout change and make it visible
734 for (const auto &act : actions)
736 if(act.visible != TaskVisible::NO_CHANGE)
739 ret = this->lc->layoutChange(act);
740 if(ret != WMError::SUCCESS)
742 HMI_SEQ_WARNING(req_num,
743 "Failed to manipulate surfaces while state change : %s", errorDescription(ret));
746 ret = this->lc->visibilityChange(act);
748 act.client->emitActive((act.visible == VISIBLE));
749 act.client->emitVisible((act.visible == VISIBLE));
751 if (ret != WMError::SUCCESS)
753 HMI_SEQ_WARNING(req_num,
754 "Failed to manipulate surfaces while state change : %s", errorDescription(ret));
757 HMI_SEQ_DEBUG(req_num, "visible %s", act.role.c_str());
760 this->lc->renderLayers();
762 HMI_SEQ_INFO(req_num, "emit flushDraw");
764 for(const auto &act_flush : actions)
766 if(act_flush.visible == TaskVisible::VISIBLE)
768 act_flush.client->emitFlushDraw();
775 void WindowManager::emitScreenUpdated(unsigned req_num)
778 HMI_SEQ_DEBUG(req_num, "emit screen updated");
780 auto actions = g_app_list.getActions(req_num, &found);
783 HMI_SEQ_ERROR(req_num,
784 "Window Manager bug :%s : Action is not set",
785 errorDescription(WMError::NO_ENTRY));
789 // create json object
790 json_object *j = json_object_new_object();
791 json_object *jarray = json_object_new_array();
793 for(const auto& action: actions)
795 if(action.visible != TaskVisible::INVISIBLE)
797 json_object_array_add(jarray, json_object_new_string(action.client->appID().c_str()));
800 json_object_object_add(j, kKeyIds, jarray);
801 HMI_SEQ_INFO(req_num, "Visible app: %s", json_object_get_string(j));
803 int ret = afb_event_push(
804 this->map_afb_event[kListEventName[Event_ScreenUpdated]], j);
807 HMI_DEBUG("afb_event_push failed: %m");
811 void WindowManager::setTimer()
814 if (clock_gettime(CLOCK_BOOTTIME, &ts) != 0) {
815 HMI_ERROR("Could't set time (clock_gettime() returns with error");
819 HMI_SEQ_DEBUG(g_app_list.currentRequestNumber(), "Timer set activate");
820 if (g_timer_ev_src == nullptr)
822 // firsttime set into sd_event
823 int ret = sd_event_add_time(afb_api_get_event_loop(afbBindingV3root), &g_timer_ev_src,
824 CLOCK_BOOTTIME, (uint64_t)(ts.tv_sec + kTimeOut) * 1000000ULL, 1, processTimerHandler, this);
827 HMI_ERROR("Could't set timer");
832 // update timer limitation after second time
833 sd_event_source_set_time(g_timer_ev_src, (uint64_t)(ts.tv_sec + kTimeOut) * 1000000ULL);
834 sd_event_source_set_enabled(g_timer_ev_src, SD_EVENT_ONESHOT);
838 void WindowManager::stopTimer()
840 unsigned req_num = g_app_list.currentRequestNumber();
841 HMI_SEQ_DEBUG(req_num, "Timer stop");
842 int rc = sd_event_source_set_enabled(g_timer_ev_src, SD_EVENT_OFF);
845 HMI_SEQ_ERROR(req_num, "Timer stop failed");
849 void WindowManager::processNextRequest()
852 g_app_list.reqDump();
853 unsigned req_num = g_app_list.currentRequestNumber();
854 if (g_app_list.haveRequest())
856 HMI_SEQ_DEBUG(req_num, "Process next request");
857 WMError rc = checkPolicy(req_num);
858 if (rc != WMError::SUCCESS)
860 HMI_SEQ_ERROR(req_num, errorDescription(rc));
865 HMI_SEQ_DEBUG(req_num, "Nothing Request. Waiting Request");