From 530b7054a0f2800936ef312f016b9d55f9defddf Mon Sep 17 00:00:00 2001 From: Marcus Fritzsch Date: Tue, 12 Sep 2017 12:06:20 +0200 Subject: Move AFBClient to client-lib/ Signed-off-by: Marcus Fritzsch --- AFBClient.cpp | 461 ----------------------------------------------- AFBClient.h | 45 ----- client-lib/AFBClient.cpp | 461 +++++++++++++++++++++++++++++++++++++++++++++++ client-lib/AFBClient.h | 45 +++++ 4 files changed, 506 insertions(+), 506 deletions(-) delete mode 100644 AFBClient.cpp delete mode 100644 AFBClient.h create mode 100644 client-lib/AFBClient.cpp create mode 100644 client-lib/AFBClient.h diff --git a/AFBClient.cpp b/AFBClient.cpp deleted file mode 100644 index 1677509..0000000 --- a/AFBClient.cpp +++ /dev/null @@ -1,461 +0,0 @@ -#include "AFBClient.h" - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include - -#include - -extern "C" { -#include -#include -} - -#define UNUSED(x) (void)(x) - -// _ ___ _ -// ___| | __ _ ___ ___ |_ _|_ __ ___ _ __ | | -// / __| |/ _` / __/ __| | || '_ ` _ \| '_ \| | -// | (__| | (_| \__ \__ \ | || | | | | | |_) | | -// \___|_|\__,_|___/___/ |___|_| |_| |_| .__/|_| -// |_| -class AFBClient::Impl { - friend class AFBClient; - - // This is the AFBClient interface impl - int init(int port, char const *token); - int dispatch(); - - // WM API - int requestSurface(const char *label); - int activateSurface(const char *label); - int deactivateSurface(const char *label); - int endDraw(const char *label); - - 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; - - 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); -}; - -namespace { - -constexpr const int token_maxlen = 20; -constexpr const char *const wmAPI = "winman"; - -#ifdef NDEBUG -#define TRACE() -#define TRACEN(N) -#else -#define CONCAT_(X, Y) X##Y -#define CONCAT(X, Y) CONCAT_(X, Y) - -#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(); - reinterpret_cast(closure)->event( - event, json_object_get_string( - json_object_object_get(afb_wsj1_msg_object_j(msg), "data"))); -} - -/* called when wsj1 hangsup */ -void onHangup(void *closure, afb_wsj1 *wsj1) { - TRACE(); - UNUSED(closure); - UNUSED(wsj1); - fputs("Hangup, the WindowManager vanished\n", stderr); - exit(1); // XXX: there should be something ... *better* here. -} - -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; - -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 - -// _ ___ _ _ _ -// ___| | __ _ ___ ___ |_ _|_ __ ___ _ __ | | (_)_ __ ___ _ __ | | -// / __| |/ _` / __/ __| | || '_ ` _ \| '_ \| | | | '_ ` _ \| '_ \| | -// | (__| | (_| \__ \__ \ | || | | | | | |_) | | | | | | | | | |_) | | -// \___|_|\__,_|___/___/ |___|_| |_| |_| .__/|_| |_|_| |_| |_| .__/|_| -// |_| |_| -AFBClient::Impl::Impl() : wsj1{}, loop{}, labels(), handlers() { TRACE(); } - -AFBClient::Impl::~Impl() { - TRACE(); - afb_wsj1_unref(wsj1); - sd_event_unref(loop); -} - -int AFBClient::Impl::init(int port, char const *token) { - TRACE(); - char *uribuf = nullptr; - int rc = -1; - - 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 != 0; p++) { - if (isalnum(*p) == 0) { - fputs("Token is invalid\n", stderr); - rc = -EINVAL; - goto fail; - } - } - - if (port < 1 || port > 0xffff) { - fputs("Port is invalid\n", stderr); - rc = -EINVAL; - goto fail; - } - - /* get the default event loop */ - rc = sd_event_default(&this->loop); - if (rc < 0) { - fprintf(stderr, "Connection to default event loop failed: %s\n", - 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; - fprintf(stderr, "Connection to %s failed: %m\n", uribuf); - rc = -errno; - goto fail; - } - - return 0; - -fail: - return rc; -} - -int AFBClient::Impl::dispatch() { - std::lock_guard guard(dispatch_mutex); - return sd_event_run(this->loop, 1); -} - -int AFBClient::Impl::requestSurface(const char *label) { - TRACE(); - - if (this->labels.find(label) != this->labels.end()) { - fputs("Surface label already known!\n", stderr); - return -EINVAL; - } - - json_object *j = drawing_name_json_argument(label); - - int rc = -1; - /* send the request */ - 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")); - char *buf; - asprintf(&buf, "%d", id); - printf("setenv(\"QT_IVI_SURFACE_ID\", %s, 1)\n", buf); - if (setenv("QT_IVI_SURFACE_ID", buf, 1) != 0) { - fprintf(stderr, "putenv failed: %m\n"); - rc = -errno; - } else { - rc = 0; // Single point of success - } - } else { - fprintf(stderr, "Could not get surface ID from WM: %s\n", - j != nullptr ? json_object_to_json_string_ext( - j, JSON_C_TO_STRING_PRETTY) - : "no-info"); - rc = -EINVAL; - } - }); - - if (rc2 < 0) { - rc = rc2; - } - - if (rc >= 0) { - this->labels.insert(this->labels.end(), label); - } - - return rc; -} - -int AFBClient::Impl::activateSurface(const char *label) { - TRACE(); - 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 != 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 = 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 != 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 = 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 != nullptr ? json_object_to_json_string_ext( - j, JSON_C_TO_STRING_PRETTY) - : "no-info"); - } - }); -} - -void AFBClient::Impl::set_event_handler( - enum EventType et, std::function func) { - TRACE(); - - if (et >= 1 && et <= 6) { // Yeah ... just go with it! - 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(AFBClient::Event_, A)); \ - } while (false) - - ET("activated", Active); - ET("deactivated", Inactive); - ET("visible", Visible); - ET("invisible", Invisible); - ET("syncdraw", SyncDraw); - ET("flushdraw", FlushDraw); -#undef ET - - 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); - if (!oet.first) { - fprintf(stderr, "Unknown event type string '%s'\n", et); - return; - } - - auto i = this->handlers.find(oet.second); - if (i != this->handlers.end()) { - if (this->labels.find(label) != this->labels.end()) { - i->second(label); - } - } -} - -// _ _ _____ ____ ____ _ _ _ -// ___| | __ _ ___ ___ / \ | ___| __ ) / ___| (_) ___ _ __ | |_ -// / __| |/ _` / __/ __| / _ \ | |_ | _ \| | | | |/ _ \ '_ \| __| -// | (__| | (_| \__ \__ \ / ___ \| _| | |_) | |___| | | __/ | | | |_ -// \___|_|\__,_|___/___/ /_/ \_\_| |____/ \____|_|_|\___|_| |_|\__| -// -int AFBClient::init(int port, char const *token) { - return this->d->init(port, token); -} - -int AFBClient::dispatch() { return this->d->dispatch(); } - -int AFBClient::requestSurface(const char *label) { - return this->d->requestSurface(label); -} - -int AFBClient::activateSurface(const char *label) { - return this->d->activateSurface(label); -} - -int AFBClient::deactivateSurface(const char *label) { - return this->d->deactivateSurface(label); -} - -int AFBClient::endDraw(const char *label) { return this->d->endDraw(label); } - -void AFBClient::set_event_handler(enum EventType et, - std::function f) { - return this->d->set_event_handler(et, std::move(f)); -} - -AFBClient &AFBClient::instance() { - TRACE(); - static AFBClient obj; - return obj; -} - -AFBClient::AFBClient() : d(new Impl) {} - -AFBClient::~AFBClient() { delete d; } diff --git a/AFBClient.h b/AFBClient.h deleted file mode 100644 index 964e2b0..0000000 --- a/AFBClient.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef AFBCLIENT_H -#define AFBCLIENT_H - -#include - -class AFBClient { - AFBClient(); - ~AFBClient(); - - AFBClient(const AFBClient &) = delete; - AFBClient &operator=(const AFBClient &) = delete; - -public: - using handler_fun = std::function; - - enum EventType { - Event_Active = 1, - Event_Inactive, - - Event_Visible, - Event_Invisible, - - Event_SyncDraw, - Event_FlushDraw, - }; - - static AFBClient &instance(); - - int init(int port, char const *token); - int dispatch(); - - // WM API - int requestSurface(const char *label); - int activateSurface(const char *label); - int deactivateSurface(const char *label); - int endDraw(const char *label); - - void set_event_handler(enum EventType et, handler_fun f); - - struct Impl; - -private: - Impl *d; -}; -#endif // AFBCLIENT_H diff --git a/client-lib/AFBClient.cpp b/client-lib/AFBClient.cpp new file mode 100644 index 0000000..1677509 --- /dev/null +++ b/client-lib/AFBClient.cpp @@ -0,0 +1,461 @@ +#include "AFBClient.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include + +#include + +extern "C" { +#include +#include +} + +#define UNUSED(x) (void)(x) + +// _ ___ _ +// ___| | __ _ ___ ___ |_ _|_ __ ___ _ __ | | +// / __| |/ _` / __/ __| | || '_ ` _ \| '_ \| | +// | (__| | (_| \__ \__ \ | || | | | | | |_) | | +// \___|_|\__,_|___/___/ |___|_| |_| |_| .__/|_| +// |_| +class AFBClient::Impl { + friend class AFBClient; + + // This is the AFBClient interface impl + int init(int port, char const *token); + int dispatch(); + + // WM API + int requestSurface(const char *label); + int activateSurface(const char *label); + int deactivateSurface(const char *label); + int endDraw(const char *label); + + 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; + + 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); +}; + +namespace { + +constexpr const int token_maxlen = 20; +constexpr const char *const wmAPI = "winman"; + +#ifdef NDEBUG +#define TRACE() +#define TRACEN(N) +#else +#define CONCAT_(X, Y) X##Y +#define CONCAT(X, Y) CONCAT_(X, Y) + +#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(); + reinterpret_cast(closure)->event( + event, json_object_get_string( + json_object_object_get(afb_wsj1_msg_object_j(msg), "data"))); +} + +/* called when wsj1 hangsup */ +void onHangup(void *closure, afb_wsj1 *wsj1) { + TRACE(); + UNUSED(closure); + UNUSED(wsj1); + fputs("Hangup, the WindowManager vanished\n", stderr); + exit(1); // XXX: there should be something ... *better* here. +} + +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; + +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 + +// _ ___ _ _ _ +// ___| | __ _ ___ ___ |_ _|_ __ ___ _ __ | | (_)_ __ ___ _ __ | | +// / __| |/ _` / __/ __| | || '_ ` _ \| '_ \| | | | '_ ` _ \| '_ \| | +// | (__| | (_| \__ \__ \ | || | | | | | |_) | | | | | | | | | |_) | | +// \___|_|\__,_|___/___/ |___|_| |_| |_| .__/|_| |_|_| |_| |_| .__/|_| +// |_| |_| +AFBClient::Impl::Impl() : wsj1{}, loop{}, labels(), handlers() { TRACE(); } + +AFBClient::Impl::~Impl() { + TRACE(); + afb_wsj1_unref(wsj1); + sd_event_unref(loop); +} + +int AFBClient::Impl::init(int port, char const *token) { + TRACE(); + char *uribuf = nullptr; + int rc = -1; + + 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 != 0; p++) { + if (isalnum(*p) == 0) { + fputs("Token is invalid\n", stderr); + rc = -EINVAL; + goto fail; + } + } + + if (port < 1 || port > 0xffff) { + fputs("Port is invalid\n", stderr); + rc = -EINVAL; + goto fail; + } + + /* get the default event loop */ + rc = sd_event_default(&this->loop); + if (rc < 0) { + fprintf(stderr, "Connection to default event loop failed: %s\n", + 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; + fprintf(stderr, "Connection to %s failed: %m\n", uribuf); + rc = -errno; + goto fail; + } + + return 0; + +fail: + return rc; +} + +int AFBClient::Impl::dispatch() { + std::lock_guard guard(dispatch_mutex); + return sd_event_run(this->loop, 1); +} + +int AFBClient::Impl::requestSurface(const char *label) { + TRACE(); + + if (this->labels.find(label) != this->labels.end()) { + fputs("Surface label already known!\n", stderr); + return -EINVAL; + } + + json_object *j = drawing_name_json_argument(label); + + int rc = -1; + /* send the request */ + 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")); + char *buf; + asprintf(&buf, "%d", id); + printf("setenv(\"QT_IVI_SURFACE_ID\", %s, 1)\n", buf); + if (setenv("QT_IVI_SURFACE_ID", buf, 1) != 0) { + fprintf(stderr, "putenv failed: %m\n"); + rc = -errno; + } else { + rc = 0; // Single point of success + } + } else { + fprintf(stderr, "Could not get surface ID from WM: %s\n", + j != nullptr ? json_object_to_json_string_ext( + j, JSON_C_TO_STRING_PRETTY) + : "no-info"); + rc = -EINVAL; + } + }); + + if (rc2 < 0) { + rc = rc2; + } + + if (rc >= 0) { + this->labels.insert(this->labels.end(), label); + } + + return rc; +} + +int AFBClient::Impl::activateSurface(const char *label) { + TRACE(); + 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 != 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 = 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 != 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 = 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 != nullptr ? json_object_to_json_string_ext( + j, JSON_C_TO_STRING_PRETTY) + : "no-info"); + } + }); +} + +void AFBClient::Impl::set_event_handler( + enum EventType et, std::function func) { + TRACE(); + + if (et >= 1 && et <= 6) { // Yeah ... just go with it! + 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(AFBClient::Event_, A)); \ + } while (false) + + ET("activated", Active); + ET("deactivated", Inactive); + ET("visible", Visible); + ET("invisible", Invisible); + ET("syncdraw", SyncDraw); + ET("flushdraw", FlushDraw); +#undef ET + + 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); + if (!oet.first) { + fprintf(stderr, "Unknown event type string '%s'\n", et); + return; + } + + auto i = this->handlers.find(oet.second); + if (i != this->handlers.end()) { + if (this->labels.find(label) != this->labels.end()) { + i->second(label); + } + } +} + +// _ _ _____ ____ ____ _ _ _ +// ___| | __ _ ___ ___ / \ | ___| __ ) / ___| (_) ___ _ __ | |_ +// / __| |/ _` / __/ __| / _ \ | |_ | _ \| | | | |/ _ \ '_ \| __| +// | (__| | (_| \__ \__ \ / ___ \| _| | |_) | |___| | | __/ | | | |_ +// \___|_|\__,_|___/___/ /_/ \_\_| |____/ \____|_|_|\___|_| |_|\__| +// +int AFBClient::init(int port, char const *token) { + return this->d->init(port, token); +} + +int AFBClient::dispatch() { return this->d->dispatch(); } + +int AFBClient::requestSurface(const char *label) { + return this->d->requestSurface(label); +} + +int AFBClient::activateSurface(const char *label) { + return this->d->activateSurface(label); +} + +int AFBClient::deactivateSurface(const char *label) { + return this->d->deactivateSurface(label); +} + +int AFBClient::endDraw(const char *label) { return this->d->endDraw(label); } + +void AFBClient::set_event_handler(enum EventType et, + std::function f) { + return this->d->set_event_handler(et, std::move(f)); +} + +AFBClient &AFBClient::instance() { + TRACE(); + static AFBClient obj; + return obj; +} + +AFBClient::AFBClient() : d(new Impl) {} + +AFBClient::~AFBClient() { delete d; } diff --git a/client-lib/AFBClient.h b/client-lib/AFBClient.h new file mode 100644 index 0000000..964e2b0 --- /dev/null +++ b/client-lib/AFBClient.h @@ -0,0 +1,45 @@ +#ifndef AFBCLIENT_H +#define AFBCLIENT_H + +#include + +class AFBClient { + AFBClient(); + ~AFBClient(); + + AFBClient(const AFBClient &) = delete; + AFBClient &operator=(const AFBClient &) = delete; + +public: + using handler_fun = std::function; + + enum EventType { + Event_Active = 1, + Event_Inactive, + + Event_Visible, + Event_Invisible, + + Event_SyncDraw, + Event_FlushDraw, + }; + + static AFBClient &instance(); + + int init(int port, char const *token); + int dispatch(); + + // WM API + int requestSurface(const char *label); + int activateSurface(const char *label); + int deactivateSurface(const char *label); + int endDraw(const char *label); + + void set_event_handler(enum EventType et, handler_fun f); + + struct Impl; + +private: + Impl *d; +}; +#endif // AFBCLIENT_H -- cgit 1.2.3-korg