/* * 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 "libwindowmanager.h" #include "hmi-debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern "C" { #include #include } #define UNUSED(x) (void)(x) /** * @class LibWindowmanager::Impl */ class LibWindowmanager::Impl { friend class LibWindowmanager; const std::vector kListEventName{ std::string("active"), std::string("inactive"), std::string("visible"), std::string("invisible"), std::string("syncDraw"), std::string("flushDraw"), std::string("screenUpdated"), std::string("error") }; /* Key for json obejct */ const char *kKeyDrawingName = "drawing_name"; const char *kKeyDrawingArea = "drawing_area"; // This is the LibWindowmanager interface impl int init(int port, char const *token); // WM API int requestSurface(json_object *object); int requestSurfaceXDG(json_object *object); int activateWindow(json_object *object); int deactivateWindow(json_object *object); int endDraw(json_object *object); int getDisplayInfo(json_object *object); int getAreaInfo(json_object *in_obj, json_object *out_obj); void set_event_handler(enum EventType et, handler_fun func); Impl(); ~Impl(); struct afb_wsj1 *wsj1; struct sd_event *loop; std::set labels; std::map handlers; std::queue> handler_queue; int api_call(const char *verb, json_object *object, const std::function &onReply); public: void event(char const *et, json_object *object); private: int runEventLoop(); }; namespace { constexpr const int token_maxlen = 20; constexpr const char *const wmAPI = "windowmanager"; #define CONCAT_(X, Y) X##Y #define CONCAT(X, Y) CONCAT_(X, Y) #ifndef SCOPE_TRACING #define TRACE() #define TRACEN(N) #else #define TRACE() \ ScopeTrace __attribute__((unused)) CONCAT(trace_scope_, __LINE__)(__func__) #define TRACEN(N) \ ScopeTrace __attribute__((unused)) CONCAT(named_trace_scope_, __LINE__)(#N) struct ScopeTrace { thread_local static int indent; char const *f{}; explicit ScopeTrace(char const *func) : f(func) { fprintf(stderr, "%*s%s -->\n", 2 * indent++, "", this->f); } ~ScopeTrace() { fprintf(stderr, "%*s%s <--\n", 2 * --indent, "", this->f); } }; thread_local int ScopeTrace::indent = 0; #endif /* called when wsj1 receives a method invocation */ void onCall(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg) { TRACE(); UNUSED(closure); UNUSED(verb); UNUSED(api); UNUSED(msg); } /* called when wsj1 receives an event */ void onEvent(void *closure, const char *event, afb_wsj1_msg *msg) { TRACE(); // check API name in event if (0 != strncmp(wmAPI, event, strlen(wmAPI))) { HMI_ERROR("libwm", "Unknown event: %s", event); return; } json_object *val; if (json_object_object_get_ex(afb_wsj1_msg_object_j(msg), "data", &val)) { reinterpret_cast(closure)->event(event, val); } else { HMI_ERROR("libwm", "Not found key \"data\""); } } /* called when wsj1 hangsup */ void onHangup(void *closure, afb_wsj1 *wsj1) { TRACE(); UNUSED(closure); UNUSED(wsj1); HMI_ERROR("libwm", "Hangup, the WindowManager vanished"); } constexpr struct afb_wsj1_itf itf = { onHangup, onCall, onEvent, }; } // namespace /** * @class LibWindowmanager::Impl::Impl */ LibWindowmanager::Impl::Impl() : wsj1{}, loop{}, labels(), handlers() { TRACE(); } LibWindowmanager::Impl::~Impl() { TRACE(); afb_wsj1_unref(wsj1); sd_event_unref(loop); } int LibWindowmanager::Impl::init(int port, char const *token) { TRACE(); HMI_DEBUG("libwm", "called"); char *uribuf = nullptr; int rc = -1; if (this->loop != nullptr && this->wsj1 != nullptr) { HMI_ERROR("libwm", "LibWindowmanager instance is already initialized!"); rc = -EALREADY; goto fail; } if (token == nullptr) { HMI_ERROR("libwm", "Token is invalid"); rc = -EINVAL; goto fail; } if (port < 1 || port > 0xffff) { HMI_ERROR("libwm", "Port is invalid"); rc = -EINVAL; goto fail; } /* get the default event loop */ rc = sd_event_default(&this->loop); if (rc < 0) { HMI_ERROR("libwm", "Connection to default event loop failed: %s", strerror(-rc)); goto fail; } asprintf(&uribuf, "ws://localhost:%d/api?token=%s", port, token); /* connect the websocket wsj1 to the uri given by the first argument */ this->wsj1 = afb_ws_client_connect_wsj1( this->loop, uribuf, const_cast(&itf), this); if (this->wsj1 == nullptr) { sd_event_unref(this->loop); this->loop = nullptr; HMI_ERROR("libwm", "Connection to %s failed: %m", uribuf); rc = -errno; goto fail; } this->runEventLoop(); return 0; fail: return rc; } int LibWindowmanager::Impl::requestSurface(json_object *object) { TRACE(); HMI_DEBUG("libwm", "called"); json_object *val; const char *tmp_label; if (json_object_object_get_ex(object, this->kKeyDrawingName, &val)) { tmp_label = json_object_get_string(val); } else { HMI_DEBUG("libwm", "Not found key \"%s\"", this->kKeyDrawingName); return -EINVAL; } // DrawingName in "object" is overwrited in api_call("RequestSurface") // So it is neccesary to copy it. const char *label = std::string(tmp_label).c_str(); if (this->labels.find(label) != this->labels.end()) { HMI_ERROR("libwm", "Surface label already known!"); return -EINVAL; } // Store application name first // because it may not return from setenv HMI_DEBUG("libwm", "Insert application name: %s\n", label); this->labels.insert(this->labels.end(), label); int rc = -1; /* send the request */ int rc2 = this->api_call("RequestSurface", object, [&rc](bool ok, json_object *j) { if (ok) { json_object *val; if (json_object_object_get_ex(j, "response", &val)) { rc = json_object_get_int(val); } else { HMI_ERROR("libwm", "Not found key \"response\""); rc = -EINVAL; return; } } }); if (rc2 < 0) { rc = rc2; } if (rc < 0) { HMI_ERROR("libwm", "Erase application name: %s", label); this->labels.erase(label); } return rc; } int LibWindowmanager::Impl::requestSurfaceXDG(json_object *object) { TRACE(); HMI_DEBUG("libwm", "called"); json_object *val; const char *tmp_label; if (json_object_object_get_ex(object, this->kKeyDrawingName, &val)) { tmp_label = json_object_get_string(val); } else { HMI_DEBUG("libwm", "Not found key \"%s\"", this->kKeyDrawingName); return -EINVAL; } // DrawingName in "object" is overwrited in api_call("RequestSurface") // So it is neccesary to copy it. const char *label = std::string(tmp_label).c_str(); if (this->labels.find(label) != this->labels.end()) { HMI_ERROR("libwm", "Surface label already known!"); return -EINVAL; } // Store application name first // because it may not return from setenv HMI_DEBUG("libwm", "Insert application name: %s\n", label); this->labels.insert(this->labels.end(), label); /* send the request */ int rc = this->api_call("RequestSurfaceXDG", object, [](bool ok, json_object *j) { if (!ok) { HMI_ERROR("libwm", "Could not get surface ID from WM: %s", j != nullptr ? json_object_to_json_string_ext( j, JSON_C_TO_STRING_PRETTY) : "no-info"); } }); if (rc != 0) { HMI_ERROR("libwm", "Erase application name: %s", label); this->labels.erase(label); } return rc; } int LibWindowmanager::Impl::activateWindow(json_object *object) { TRACE(); HMI_DEBUG("libwm", "called"); return this->api_call("ActivateWindow", object, [](bool ok, json_object *j) { if (!ok) { HMI_ERROR("libwm", "API Call activateWindow() failed: %s", j != nullptr ? json_object_to_json_string_ext( j, JSON_C_TO_STRING_PRETTY) : "no-info"); } }); } int LibWindowmanager::Impl::deactivateWindow(json_object *object) { TRACE(); HMI_DEBUG("libwm", "called"); return this->api_call("DeactivateWindow", object, [](bool ok, json_object *j) { if (!ok) { HMI_ERROR("libwm", "API Call deactivateWindow() failed: %s", j != nullptr ? json_object_to_json_string_ext( j, JSON_C_TO_STRING_PRETTY) : "no-info"); } }); } int LibWindowmanager::Impl::endDraw(json_object *object) { TRACE(); HMI_DEBUG("libwm", "called"); return this->api_call("EndDraw", object, [](bool ok, json_object *j) { if (!ok) { HMI_ERROR("libwm", "API Call endDraw() failed: %s", j != nullptr ? json_object_to_json_string_ext( j, JSON_C_TO_STRING_PRETTY) : "no-info"); } }); } int LibWindowmanager::Impl::getDisplayInfo(json_object *object) { TRACE(); HMI_DEBUG("libwm", "called"); if (nullptr == object) { HMI_ERROR("libwm", "Argment is NULL!"); return -EINVAL; } if ((nullptr == this->loop) || (nullptr == this->wsj1)) { HMI_ERROR("libwm", "LibWindowmanager is not initialized!"); return -EPERM; } int w_px, h_px, w_mm, h_mm; int rc = -1; /* send the request */ int rc2 = this->api_call("GetDisplayInfo", nullptr, [&rc, &w_px, &h_px, &w_mm, &h_mm](bool ok, json_object *j) { if (ok) { json_object *val; if (json_object_object_get_ex(j, "response", &val)) { HMI_DEBUG("libwm", "responce:%s", json_object_get_string(val)); json_object *j_w_px = nullptr; if (!json_object_object_get_ex(val, "width_pixel", &j_w_px)) { HMI_DEBUG("libwm", "Not found key \"width_pixel\""); rc = -EINVAL; return; } w_px = json_object_get_int(j_w_px); json_object *j_h_px = nullptr; if (!json_object_object_get_ex(val, "height_pixel", &j_h_px)) { HMI_DEBUG("libwm", "Not found key \"height_pixel\""); rc = -EINVAL; return; } h_px = json_object_get_int(j_h_px); json_object *j_w_mm = nullptr; if (!json_object_object_get_ex(val, "width_mm", &j_w_mm)) { HMI_DEBUG("libwm", "Not found key \"width_mm\""); rc = -EINVAL; return; } w_mm = json_object_get_int(j_w_mm); json_object *j_h_mm = nullptr; if (!json_object_object_get_ex(val, "height_mm", &j_h_mm)) { HMI_DEBUG("libwm", "Not found key \"height_mm\""); rc = -EINVAL; return; } h_mm = json_object_get_int(j_h_mm); rc = 0; } else { HMI_ERROR("libwm", "Not found key \"response\""); rc = -EINVAL; return; } } else { HMI_ERROR("libwm", "Windowmanager-service is not initialized: %s", j != nullptr ? json_object_to_json_string_ext( j, JSON_C_TO_STRING_PRETTY) : "no-info"); rc = -EPERM; } }); if (0 > rc2) { HMI_ERROR("libwm", "api_call() failed"); rc = rc2; } if (0 == rc) { json_object_object_add(object, "width_pixel", json_object_new_int(w_px)); json_object_object_add(object, "height_pixel", json_object_new_int(h_px)); json_object_object_add(object, "width_mm", json_object_new_int(w_mm)); json_object_object_add(object, "height_mm", json_object_new_int(h_mm)); } return rc; } int LibWindowmanager::Impl::getAreaInfo(json_object *in_obj, json_object *out_obj) { TRACE(); HMI_DEBUG("libwm", "called"); if (nullptr == in_obj) { HMI_ERROR("libwm", "Argment is NULL!"); return -EINVAL; } if ((nullptr == this->loop) || (nullptr == this->wsj1)) { HMI_ERROR("libwm", "LibWindowmanager is not initialized!"); return -EPERM; } int x, y, w, h; int rc = -1; /* send the request */ int rc2 = this->api_call("GetAreaInfo", in_obj, [&rc, &x, &y, &w, &h](bool ok, json_object *j) { if (ok) { json_object *val; HMI_DEBUG("libwm", "j:%s", json_object_get_string(j)); if (json_object_object_get_ex(j, "response", &val)) { json_object *j_x = nullptr; if (!json_object_object_get_ex(val, "x", &j_x)) { HMI_DEBUG("libwm", "Not found key \"x\""); rc = -EINVAL; return; } x = json_object_get_int(j_x); json_object *j_y = nullptr; if (!json_object_object_get_ex(val, "y", &j_y)) { HMI_DEBUG("libwm", "Not found key \"y\""); rc = -EINVAL; return; } y = json_object_get_int(j_y); json_object *j_w = nullptr; if (!json_object_object_get_ex(val, "width", &j_w)) { HMI_DEBUG("libwm", "Not found key \"width\""); rc = -EINVAL; return; } w = json_object_get_int(j_w); json_object *j_h = nullptr; if (!json_object_object_get_ex(val, "height", &j_h)) { HMI_DEBUG("libwm", "Not found key \"height\""); rc = -EINVAL; return; } h = json_object_get_int(j_h); rc = 0; HMI_DEBUG("libwm", "responce:%s", json_object_get_string(val)); } else { HMI_ERROR("libwm", "Not found key \"response\""); rc = -EINVAL; return; } } else { HMI_ERROR("libwm", "Could not get area rect: %s", j != nullptr ? json_object_to_json_string_ext( j, JSON_C_TO_STRING_PRETTY) : "no-info"); rc = -EINVAL; } }); if (0 > rc2) { HMI_ERROR("libwm", "api_call() failed"); rc = rc2; } if (0 == rc) { json_object_object_add(out_obj, "x", json_object_new_int(x)); json_object_object_add(out_obj, "y", json_object_new_int(y)); json_object_object_add(out_obj, "width", json_object_new_int(w)); json_object_object_add(out_obj, "height", json_object_new_int(h)); } return rc; } static void _on_reply_static(void *closure, struct afb_wsj1_msg *msg) { } void LibWindowmanager::Impl::set_event_handler(enum EventType et, handler_fun func) { TRACE(); HMI_DEBUG("libwm", "called"); // Subscribe event struct json_object* j = json_object_new_object(); json_object_object_add(j, "event", json_object_new_int(et)); int ret = afb_wsj1_call_j(this->wsj1, wmAPI, "wm_subscribe", j, _on_reply_static, this); if (0 > ret) { HMI_ERROR("libwm", "Failed to subscribe event: %s", kListEventName[et].c_str()); } // Set event handler if (et >= Event_Active && et <= Event_FlushDraw) { this->handlers[et] = std::move(func); } } namespace { std::pair make_event_type(char const *et) { // Event have the form "$API/$EVENT", just try to find the first / and // get on with it. char const *et2 = strchr(et, '/'); if (et2 != nullptr) { et = et2 + 1; } #define ET(N, A) \ do { \ if (strcasecmp(et, N) == 0) \ return std::pair( \ true, CONCAT(LibWindowmanager::Event_, A)); \ } while (false) ET("active", Active); ET("inactive", Inactive); ET("visible", Visible); ET("invisible", Invisible); ET("syncdraw", SyncDraw); ET("flushdraw", FlushDraw); #undef ET return std::pair(false, LibWindowmanager::Event_Active); } } // namespace /// object will be json_object_put int LibWindowmanager::Impl::api_call( const char *verb, json_object *object, const std::function &onReply) { TRACE(); int rc = 0; if ((0 == strcmp("RequestSurface", verb)) || (0 == strcmp("GetDisplayInfo", verb)) || (0 == strcmp("GetAreaInfo", verb))) { // We need to wrap the actual onReply call once in order to // *look* like a normal functions pointer (std::functions<> // with captures cannot convert to function pointers). // Alternatively we could setup a local struct and use it as // closure, but I think it is cleaner this way. int call_rc = 0; std::atomic returned{}; returned.store(false, std::memory_order_relaxed); std::function wrappedOnReply = [&returned, &call_rc, &onReply](bool ok, json_object *j) { TRACEN(wrappedOnReply); call_rc = ok ? 0 : -EINVAL; // We know it failed, but there may be an explanation in the // json object. { TRACEN(onReply); onReply(ok, j); } returned.store(true, std::memory_order_release); }; // make the actual call, use wrappedOnReply as closure rc = afb_wsj1_call_j( this->wsj1, wmAPI, verb, object, [](void *closure, afb_wsj1_msg *msg) { TRACEN(callClosure); auto *onReply = reinterpret_cast *>( closure); (*onReply)(!(afb_wsj1_msg_is_reply_ok(msg) == 0), afb_wsj1_msg_object_j(msg)); }, &wrappedOnReply); if (0 == rc) { // We need to wait until "returned" got set, this is necessary // if events get triggered by the call (and would be dispatched before // the actual call-reply). while (!returned.load(std::memory_order_consume)) { sd_event_run(loop, 16); } // return the actual API call result rc = call_rc; } } else { rc = afb_wsj1_call_j(this->wsj1, wmAPI, verb, object, _on_reply_static, this); } if (rc < 0) { HMI_ERROR("libwm", "calling %s/%s failed: %m", wmAPI, verb); } return rc; } void LibWindowmanager::Impl::event(char const *et, json_object *object) { TRACE(); auto oet = make_event_type(et); if (!oet.first) { HMI_ERROR("libwm", "Unknown event type string '%s'", et); return; } auto i = this->handlers.find(oet.second); if (i != this->handlers.end()) { json_object *val; const char *label; if (json_object_object_get_ex(object, this->kKeyDrawingName, &val)) { label = json_object_get_string(val); } else { HMI_ERROR("libwm", "Not found key \"%s\"\n", this->kKeyDrawingName); return; } if (this->labels.find(label) != this->labels.end()) { i->second(object); } } } static void *event_loop_run(void *args){ struct sd_event* loop = (struct sd_event*)(args); for(;;) sd_event_run(loop, 30000000); } int LibWindowmanager::Impl::runEventLoop() { if(this->wsj1 && this->loop) { pthread_t thread_id; int ret = pthread_create(&thread_id, NULL, event_loop_run, this->loop); if(ret != 0) { printf("Cannot run eventloop due to error:%d", errno); return -1; } else return thread_id; } else { printf("Connecting is not established yet"); return -1; } } /** * @class LibWindowmanager */ int LibWindowmanager::init(int port, char const *token) { return this->d->init(port, token); } int LibWindowmanager::requestSurface(json_object *object) { return this->d->requestSurface(object); } int LibWindowmanager::requestSurfaceXDG(json_object *object) { return this->d->requestSurfaceXDG(object); } int LibWindowmanager::activateWindow(json_object *object) { return this->d->activateWindow(object); } int LibWindowmanager::deactivateWindow(json_object *object) { return this->d->deactivateWindow(object); } // This API is deprecated, please use new API int LibWindowmanager::activateSurface(json_object *object) { return this->activateWindow(object); } // This API is deprecated, please use new API int LibWindowmanager::deactivateSurface(json_object *object) { return this->deactivateWindow(object); } int LibWindowmanager::endDraw(json_object *object) { return this->d->endDraw(object); } int LibWindowmanager::getDisplayInfo(json_object *object) { return this->d->getDisplayInfo(object); } int LibWindowmanager::getAreaInfo(json_object *in_obj, json_object *out_obj) { return this->d->getAreaInfo(in_obj, out_obj); } int LibWindowmanager::getAreaInfo(const char *label, json_object *out_obj) { json_object *object = json_object_new_object(); json_object_object_add(object, this->kKeyDrawingName, json_object_new_string(label)); return this->d->getAreaInfo(object, out_obj); } void LibWindowmanager::set_event_handler(enum EventType et, handler_fun f) { return this->d->set_event_handler(et, std::move(f)); } LibWindowmanager::LibWindowmanager() : d(new Impl) {} LibWindowmanager::~LibWindowmanager() { delete d; }