X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?a=blobdiff_plain;f=AFBClient.cpp;h=fbffb2c0bcaf2dd6e57822947eda469d9557fb55;hb=c720d0ed2d95b6f73a6e26bfda1ef853cd41be25;hp=cee49ad205501940b39adf4846fbc220b7969eb2;hpb=0ffdfa88ee21c533e2b3f3627c088ca12fc661d2;p=staging%2Fwindowmanager.git diff --git a/AFBClient.cpp b/AFBClient.cpp index cee49ad..fbffb2c 100644 --- a/AFBClient.cpp +++ b/AFBClient.cpp @@ -44,7 +44,7 @@ class AFBClient::Impl { int deactivateSurface(const char *label); int endDraw(const char *label); - void set_event_handler(enum EventType et, handler_fun f); + void set_event_handler(enum EventType et, handler_fun func); Impl(); ~Impl(); @@ -55,6 +55,10 @@ class AFBClient::Impl { std::set labels; std::map handlers; + int api_call(const char *verb, json_object *object, + const std::function &onReply); + void dispatch_internal(); + public: void event(char const *et, char const *label); }; @@ -79,7 +83,7 @@ constexpr const char *const wmAPI = "winman"; struct ScopeTrace { thread_local static int indent; char const *f{}; - ScopeTrace(char const *func) : f(func) { + 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); } @@ -110,86 +114,21 @@ void onHangup(void *closure, afb_wsj1 *wsj1) { TRACE(); UNUSED(closure); UNUSED(wsj1); - printf("ON-HANGUP\n"); - fflush(stdout); - exit(0); + fputs("Hangup, the WindowManager vanished\n", stderr); + exit(1); // XXX: there should be something ... *better* here. } -constexpr static struct afb_wsj1_itf itf = { +constexpr struct afb_wsj1_itf itf = { onHangup, onCall, onEvent, }; // XXX: I am not sure this is the right thing to do though... std::recursive_mutex dispatch_mutex; -void dispatch_internal(struct sd_event *loop) { - std::lock_guard guard(dispatch_mutex); - TRACE(); - sd_event_run(loop, -1); -} - -/// object will be json_object_put -int api_call(struct sd_event *loop, struct afb_wsj1 *wsj1, const char *verb, - json_object *object, - std::function onReply) { - TRACE(); - - // 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 - int rc = afb_wsj1_call_j( - 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), - afb_wsj1_msg_object_j(msg)); - }, - &wrappedOnReply); - - if (rc < 0) { - fprintf( - stderr, "calling %s/%s(%s) failed: %m\n", wmAPI, verb, - json_object_to_json_string_ext(object, JSON_C_TO_STRING_PRETTY)); - // Call the reply handler regardless with a NULL json_object* - onReply(false, nullptr); - } else { - // We need to dispatch 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)) { - std::lock_guard guard(dispatch_mutex); - if (!returned.load(std::memory_order_consume)) { - dispatch_internal(loop); - } - } - - // return the actual API call result - rc = call_rc; - } - - return rc; +json_object *drawing_name_json_argument(char const *label) { + json_object *j = json_object_new_object(); + json_object_object_add(j, "drawing_name", json_object_new_string(label)); + return j; } } // namespace @@ -206,7 +145,6 @@ AFBClient::Impl::~Impl() { TRACE(); afb_wsj1_unref(wsj1); sd_event_unref(loop); - loop = nullptr; } int AFBClient::Impl::init(int port, char const *token) { @@ -214,28 +152,34 @@ int AFBClient::Impl::init(int port, char const *token) { char *uribuf = nullptr; int rc = -1; - if (!token || strlen(token) > token_maxlen) { - fprintf(stderr, "Token is invalid\n"); + if (this->loop != nullptr && this->wsj1 != nullptr) { + fputs("AFBClient instance is already initialized!\n", stderr); + rc = -EALREADY; + goto fail; + } + + if ((token == nullptr) || strlen(token) > token_maxlen) { + fputs("Token is invalid\n", stderr); rc = -EINVAL; goto fail; } - for (char const *p = token; *p; p++) { - if (!isalnum(*p)) { - fprintf(stderr, "Token is invalid\n"); + for (char const *p = token; *p != 0; p++) { + if (isalnum(*p) == 0) { + fputs("Token is invalid\n", stderr); rc = -EINVAL; goto fail; } } if (port < 1 && port > 0xffff) { - fprintf(stderr, "Port is invalid\n"); + fputs("Port is invalid\n", stderr); rc = -EINVAL; goto fail; } /* get the default event loop */ - rc = sd_event_default(&loop); + rc = sd_event_default(&this->loop); if (rc < 0) { fprintf(stderr, "Connection to default event loop failed: %s\n", strerror(-rc)); @@ -245,10 +189,11 @@ int AFBClient::Impl::init(int port, char const *token) { asprintf(&uribuf, "ws://localhost:%d/api?token=%s", port, token); /* connect the websocket wsj1 to the uri given by the first argument */ - wsj1 = afb_ws_client_connect_wsj1( - loop, uribuf, const_cast(&itf), this); - if (wsj1 == nullptr) { - sd_event_unref(loop); + 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; fprintf(stderr, "Connection to %s failed: %m\n", uribuf); rc = -errno; goto fail; @@ -262,24 +207,23 @@ fail: int AFBClient::Impl::dispatch() { std::lock_guard guard(dispatch_mutex); - return sd_event_run(loop, 1); + return sd_event_run(this->loop, 1); } int AFBClient::Impl::requestSurface(const char *label) { TRACE(); if (this->labels.find(label) != this->labels.end()) { - fprintf(stderr, "Surface label already known!\n"); + fputs("Surface label already known!\n", stderr); return -EINVAL; } - json_object *jp = json_object_new_object(); - json_object_object_add(jp, "drawing_name", json_object_new_string(label)); + json_object *j = drawing_name_json_argument(label); int rc = -1; /* send the request */ - int rc2 = api_call( - loop, wsj1, "request_surface", jp, [&rc](bool ok, json_object *j) { + int rc2 = + this->api_call("request_surface", j, [&rc](bool ok, json_object *j) { if (ok) { int id = json_object_get_int(json_object_object_get(j, "response")); @@ -294,15 +238,16 @@ int AFBClient::Impl::requestSurface(const char *label) { } } else { fprintf(stderr, "Could not get surface ID from WM: %s\n", - j ? json_object_to_json_string_ext( - j, JSON_C_TO_STRING_PRETTY) - : "no-info"); + j != nullptr ? json_object_to_json_string_ext( + j, JSON_C_TO_STRING_PRETTY) + : "no-info"); rc = -EINVAL; } }); - if (rc2 < 0) + if (rc2 < 0) { rc = rc2; + } if (rc >= 0) { this->labels.insert(this->labels.end(), label); @@ -313,44 +258,39 @@ int AFBClient::Impl::requestSurface(const char *label) { int AFBClient::Impl::activateSurface(const char *label) { TRACE(); - json_object *j = json_object_new_object(); - json_object_object_add(j, "drawing_name", json_object_new_string(label)); - return api_call(loop, wsj1, "activate_surface", j, [](bool ok, - json_object *j) { + json_object *j = drawing_name_json_argument(label); + return this->api_call("activate_surface", j, [](bool ok, json_object *j) { if (!ok) { - fprintf( - stderr, "API Call activate_surface() failed: %s\n", - j ? json_object_to_json_string_ext(j, JSON_C_TO_STRING_PRETTY) - : "no-info"); + fprintf(stderr, "API Call activate_surface() failed: %s\n", + j != nullptr ? json_object_to_json_string_ext( + j, JSON_C_TO_STRING_PRETTY) + : "no-info"); } }); } int AFBClient::Impl::deactivateSurface(const char *label) { TRACE(); - json_object *j = json_object_new_object(); - json_object_object_add(j, "drawing_name", json_object_new_string(label)); - return api_call(loop, wsj1, "deactivate_surface", j, [](bool ok, - json_object *j) { + json_object *j = drawing_name_json_argument(label); + return this->api_call("deactivate_surface", j, [](bool ok, json_object *j) { if (!ok) { - fprintf( - stderr, "API Call deactivate_surface() failed: %s\n", - j ? json_object_to_json_string_ext(j, JSON_C_TO_STRING_PRETTY) - : "no-info"); + fprintf(stderr, "API Call deactivate_surface() failed: %s\n", + j != nullptr ? json_object_to_json_string_ext( + j, JSON_C_TO_STRING_PRETTY) + : "no-info"); } }); } int AFBClient::Impl::endDraw(const char *label) { TRACE(); - json_object *j = json_object_new_object(); - json_object_object_add(j, "drawing_name", json_object_new_string(label)); - return api_call(loop, wsj1, "enddraw", j, [](bool ok, json_object *j) { + json_object *j = drawing_name_json_argument(label); + return this->api_call("enddraw", j, [](bool ok, json_object *j) { if (!ok) { - fprintf( - stderr, "API Call endDraw() failed: %s\n", - j ? json_object_to_json_string_ext(j, JSON_C_TO_STRING_PRETTY) - : "no-info"); + fprintf(stderr, "API Call endDraw() failed: %s\n", + j != nullptr ? json_object_to_json_string_ext( + j, JSON_C_TO_STRING_PRETTY) + : "no-info"); } }); } @@ -369,16 +309,16 @@ 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) { + if (et2 != nullptr) { et = et2 + 1; } -#define ET(N, A) \ - do { \ - if (strcasecmp(et, N) == 0) \ - return std::make_pair( \ - true, CONCAT(AFBClient::Event_, A)); \ - } while (0) +#define ET(N, A) \ + do { \ + if (strcasecmp(et, N) == 0) \ + return std::pair( \ + true, CONCAT(AFBClient::Event_, A)); \ + } while (false) ET("activated", Active); ET("deactivated", Inactive); @@ -388,11 +328,81 @@ std::pair make_event_type(char const *et) { ET("flushdraw", FlushDraw); #undef ET - return std::make_pair(false, - AFBClient::Event_Active); + return std::pair(false, + AFBClient::Event_Active); } } // namespace +/// object will be json_object_put +int AFBClient::Impl::api_call( + const char *verb, json_object *object, + const std::function &onReply) { + TRACE(); + + // 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 + int 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 (rc < 0) { + fprintf( + stderr, "calling %s/%s(%s) failed: %m\n", wmAPI, verb, + json_object_to_json_string_ext(object, JSON_C_TO_STRING_PRETTY)); + // Call the reply handler regardless with a NULL json_object* + onReply(false, nullptr); + } else { + // We need to dispatch 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)) { + std::lock_guard guard(dispatch_mutex); + if (!returned.load(std::memory_order_consume)) { + this->dispatch_internal(); + } + } + + // return the actual API call result + rc = call_rc; + } + + return rc; +} + +void AFBClient::Impl::dispatch_internal() { + std::lock_guard guard(dispatch_mutex); + TRACE(); + sd_event_run(this->loop, -1); +} + void AFBClient::Impl::event(char const *et, char const *label) { TRACE(); auto oet = make_event_type(et);