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.
17 #include "libwindowmanager.h"
34 #include <systemd/sd-event.h>
36 #include <json-c/json.h>
40 #include <afb/afb-ws-client.h>
41 #include <afb/afb-wsj1.h>
44 #define UNUSED(x) (void)(x)
47 // ___| | __ _ ___ ___ |_ _|_ __ ___ _ __ | |
48 // / __| |/ _` / __/ __| | || '_ ` _ \| '_ \| |
49 // | (__| | (_| \__ \__ \ | || | | | | | |_) | |
50 // \___|_|\__,_|___/___/ |___|_| |_| |_| .__/|_|
52 class LibWindowmanager::Impl {
53 friend class LibWindowmanager;
55 // This is the LibWindowmanager interface impl
56 int init(int port, char const *token);
59 int requestSurface(const char *label);
60 int activateSurface(const char *label);
61 int deactivateSurface(const char *label);
62 int endDraw(const char *label);
64 void set_event_handler(enum EventType et, handler_fun func);
69 struct afb_wsj1 *wsj1;
70 struct sd_event *loop;
72 std::set<std::string> labels;
73 std::map<EventType, handler_fun> handlers;
74 std::queue<std::pair<handler_fun, std::string>> handler_queue;
76 int api_call(const char *verb, json_object *object,
77 const std::function<void(bool, json_object *)> &onReply);
80 void event(char const *et, char const *label);
87 constexpr const int token_maxlen = 20;
88 constexpr const char *const wmAPI = "windowmanager";
90 #define CONCAT_(X, Y) X##Y
91 #define CONCAT(X, Y) CONCAT_(X, Y)
98 ScopeTrace __attribute__((unused)) CONCAT(trace_scope_, __LINE__)(__func__)
100 ScopeTrace __attribute__((unused)) CONCAT(named_trace_scope_, __LINE__)(#N)
103 thread_local static int indent;
105 explicit ScopeTrace(char const *func) : f(func) {
106 fprintf(stderr, "%*s%s -->\n", 2 * indent++, "", this->f);
108 ~ScopeTrace() { fprintf(stderr, "%*s%s <--\n", 2 * --indent, "", this->f); }
110 thread_local int ScopeTrace::indent = 0;
113 /* called when wsj1 receives a method invocation */
114 void onCall(void *closure, const char *api, const char *verb,
115 struct afb_wsj1_msg *msg) {
123 /* called when wsj1 receives an event */
124 void onEvent(void *closure, const char *event, afb_wsj1_msg *msg) {
127 // check API name in event
128 if (0 != strncmp(wmAPI, event, strlen(wmAPI))) {
132 reinterpret_cast<LibWindowmanager::Impl *>(closure)->event(
133 event, json_object_get_string(
134 json_object_object_get(afb_wsj1_msg_object_j(msg), "data")));
137 /* called when wsj1 hangsup */
138 void onHangup(void *closure, afb_wsj1 *wsj1) {
142 fputs("Hangup, the WindowManager vanished\n", stderr);
143 exit(1); // XXX: there should be something ... *better* here.
146 constexpr struct afb_wsj1_itf itf = {
147 onHangup, onCall, onEvent,
150 // XXX: I am not sure this is the right thing to do though...
151 std::recursive_mutex dispatch_mutex;
153 json_object *drawing_name_json_argument(char const *label) {
154 json_object *j = json_object_new_object();
155 json_object_object_add(j, "drawing_name", json_object_new_string(label));
162 // ___| | __ _ ___ ___ |_ _|_ __ ___ _ __ | | (_)_ __ ___ _ __ | |
163 // / __| |/ _` / __/ __| | || '_ ` _ \| '_ \| | | | '_ ` _ \| '_ \| |
164 // | (__| | (_| \__ \__ \ | || | | | | | |_) | | | | | | | | | |_) | |
165 // \___|_|\__,_|___/___/ |___|_| |_| |_| .__/|_| |_|_| |_| |_| .__/|_|
167 LibWindowmanager::Impl::Impl() : wsj1{}, loop{}, labels(), handlers() { TRACE(); }
169 LibWindowmanager::Impl::~Impl() {
171 afb_wsj1_unref(wsj1);
172 sd_event_unref(loop);
175 int LibWindowmanager::Impl::init(int port, char const *token) {
177 char *uribuf = nullptr;
180 if (this->loop != nullptr && this->wsj1 != nullptr) {
181 fputs("LibWindowmanager instance is already initialized!\n", stderr);
186 if (token == nullptr) {
187 fputs("Token is invalid\n", stderr);
192 if (port < 1 || port > 0xffff) {
193 fputs("Port is invalid\n", stderr);
198 /* get the default event loop */
199 rc = sd_event_default(&this->loop);
201 fprintf(stderr, "Connection to default event loop failed: %s\n",
206 asprintf(&uribuf, "ws://localhost:%d/api?token=%s", port, token);
208 /* connect the websocket wsj1 to the uri given by the first argument */
209 this->wsj1 = afb_ws_client_connect_wsj1(
210 this->loop, uribuf, const_cast<struct afb_wsj1_itf *>(&itf), this);
211 if (this->wsj1 == nullptr) {
212 sd_event_unref(this->loop);
213 this->loop = nullptr;
214 fprintf(stderr, "Connection to %s failed: %m\n", uribuf);
219 this->runEventLoop();
227 int LibWindowmanager::Impl::requestSurface(const char *label) {
230 if (this->labels.find(label) != this->labels.end()) {
231 fputs("Surface label already known!\n", stderr);
235 json_object *j = drawing_name_json_argument(label);
238 /* send the request */
240 this->api_call("RequestSurface", j, [&rc](bool ok, json_object *j) {
243 json_object_get_int(json_object_object_get(j, "response"));
245 asprintf(&buf, "%d", id);
246 printf("setenv(\"QT_IVI_SURFACE_ID\", %s, 1)\n", buf);
247 if (setenv("QT_IVI_SURFACE_ID", buf, 1) != 0) {
248 fprintf(stderr, "putenv failed: %m\n");
251 rc = 0; // Single point of success
254 fprintf(stderr, "Could not get surface ID from WM: %s\n",
255 j != nullptr ? json_object_to_json_string_ext(
256 j, JSON_C_TO_STRING_PRETTY)
267 this->labels.insert(this->labels.end(), label);
273 int LibWindowmanager::Impl::activateSurface(const char *label) {
275 json_object *j = drawing_name_json_argument(label);
276 return this->api_call("ActivateSurface", j, [](bool ok, json_object *j) {
278 fprintf(stderr, "API Call activate_surface() failed: %s\n",
279 j != nullptr ? json_object_to_json_string_ext(
280 j, JSON_C_TO_STRING_PRETTY)
286 int LibWindowmanager::Impl::deactivateSurface(const char *label) {
288 json_object *j = drawing_name_json_argument(label);
289 return this->api_call("DeactivateSurface", j, [](bool ok, json_object *j) {
291 fprintf(stderr, "API Call deactivate_surface() failed: %s\n",
292 j != nullptr ? json_object_to_json_string_ext(
293 j, JSON_C_TO_STRING_PRETTY)
299 int LibWindowmanager::Impl::endDraw(const char *label) {
301 json_object *j = drawing_name_json_argument(label);
302 return this->api_call("EndDraw", j, [](bool ok, json_object *j) {
304 fprintf(stderr, "API Call endDraw() failed: %s\n",
305 j != nullptr ? json_object_to_json_string_ext(
306 j, JSON_C_TO_STRING_PRETTY)
312 void LibWindowmanager::Impl::set_event_handler(
313 enum EventType et, std::function<void(char const *)> func) {
316 if (et >= 1 && et <= 6) { // Yeah ... just go with it!
317 this->handlers[et] = std::move(func);
322 std::pair<bool, LibWindowmanager::EventType> make_event_type(char const *et) {
323 // Event have the form "$API/$EVENT", just try to find the first / and
325 char const *et2 = strchr(et, '/');
326 if (et2 != nullptr) {
332 if (strcasecmp(et, N) == 0) \
333 return std::pair<bool, LibWindowmanager::EventType>( \
334 true, CONCAT(LibWindowmanager::Event_, A)); \
337 ET("active", Active);
338 ET("inactive", Inactive);
339 ET("visible", Visible);
340 ET("invisible", Invisible);
341 ET("syncdraw", SyncDraw);
342 ET("flushdraw", FlushDraw);
345 return std::pair<bool, LibWindowmanager::EventType>(false,
346 LibWindowmanager::Event_Active);
350 static void _on_reply_static(void *closure, struct afb_wsj1_msg *msg)
356 /// object will be json_object_put
357 int LibWindowmanager::Impl::api_call(
358 const char *verb, json_object *object,
359 const std::function<void(bool, json_object *)> &onReply) {
363 if (0 == strcmp("RequestSurface", verb)) {
364 // We need to wrap the actual onReply call once in order to
365 // *look* like a normal functions pointer (std::functions<>
366 // with captures cannot convert to function pointers).
367 // Alternatively we could setup a local struct and use it as
368 // closure, but I think it is cleaner this way.
370 std::atomic<bool> returned{};
371 returned.store(false, std::memory_order_relaxed);
372 std::function<void(bool, json_object *)> wrappedOnReply =
373 [&returned, &call_rc, &onReply](bool ok, json_object *j) {
374 TRACEN(wrappedOnReply);
375 call_rc = ok ? 0 : -EINVAL;
376 // We know it failed, but there may be an explanation in the
382 returned.store(true, std::memory_order_release);
385 // make the actual call, use wrappedOnReply as closure
386 rc = afb_wsj1_call_j(
387 this->wsj1, wmAPI, verb, object,
388 [](void *closure, afb_wsj1_msg *msg) {
391 reinterpret_cast<std::function<void(bool, json_object *)> *>(
393 (*onReply)(!(afb_wsj1_msg_is_reply_ok(msg) == 0),
394 afb_wsj1_msg_object_j(msg));
399 // We need to wait until "returned" got set, this is necessary
400 // if events get triggered by the call (and would be dispatched before
401 // the actual call-reply).
402 while (!returned.load(std::memory_order_consume)) {
403 sd_event_run(loop, 16);
406 // return the actual API call result
411 rc = afb_wsj1_call_j(this->wsj1, wmAPI, verb, object, _on_reply_static, this);
416 stderr, "calling %s/%s(%s) failed: %m\n", wmAPI, verb,
417 json_object_to_json_string_ext(object, JSON_C_TO_STRING_PRETTY));
418 // Call the reply handler regardless with a NULL json_object*
419 onReply(false, nullptr);
425 void LibWindowmanager::Impl::event(char const *et, char const *label) {
427 auto oet = make_event_type(et);
429 fprintf(stderr, "Unknown event type string '%s'\n", et);
433 auto i = this->handlers.find(oet.second);
434 if (i != this->handlers.end()) {
435 if (this->labels.find(label) != this->labels.end()) {
441 static void *event_loop_run(void *args){
442 struct sd_event* loop = (struct sd_event*)(args);
444 sd_event_run(loop, 30000000);
447 int LibWindowmanager::Impl::runEventLoop() {
448 if(this->wsj1 && this->loop)
451 int ret = pthread_create(&thread_id, NULL, event_loop_run, this->loop);
454 printf("Cannot run eventloop due to error:%d", errno);
462 printf("Connecting is not established yet");
467 // _ _ _____ ____ ____ _ _ _
468 // ___| | __ _ ___ ___ / \ | ___| __ ) / ___| (_) ___ _ __ | |_
469 // / __| |/ _` / __/ __| / _ \ | |_ | _ \| | | | |/ _ \ '_ \| __|
470 // | (__| | (_| \__ \__ \ / ___ \| _| | |_) | |___| | | __/ | | | |_
471 // \___|_|\__,_|___/___/ /_/ \_\_| |____/ \____|_|_|\___|_| |_|\__|
473 int LibWindowmanager::init(int port, char const *token) {
474 return this->d->init(port, token);
477 int LibWindowmanager::requestSurface(const char *label) {
478 return this->d->requestSurface(label);
481 int LibWindowmanager::activateSurface(const char *label) {
482 return this->d->activateSurface(label);
485 int LibWindowmanager::deactivateSurface(const char *label) {
486 return this->d->deactivateSurface(label);
489 int LibWindowmanager::endDraw(const char *label) { return this->d->endDraw(label); }
491 void LibWindowmanager::set_event_handler(enum EventType et,
492 std::function<void(char const *label)> f) {
493 return this->d->set_event_handler(et, std::move(f));
496 LibWindowmanager::LibWindowmanager() : d(new Impl) {}
498 LibWindowmanager::~LibWindowmanager() { delete d; }