Add agl-service-windowmanager-2017
[apps/agl-service-windowmanager-2017.git] / libwindowmanager / libwindowmanager.cpp
1 /*
2  * Copyright (c) 2017 TOYOTA MOTOR CORPORATION
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 #include "libwindowmanager.h"
18
19 #include <cassert>
20 #include <cctype>
21 #include <cerrno>
22 #include <cstdio>
23 #include <cstdlib>
24 #include <cstring>
25
26 #include <atomic>
27 #include <map>
28 #include <mutex>
29 #include <set>
30 #include <queue>
31
32 #include <unistd.h>
33
34 #include <systemd/sd-event.h>
35
36 #include <json-c/json.h>
37 #include <pthread.h>
38
39 extern "C" {
40 #include <afb/afb-ws-client.h>
41 #include <afb/afb-wsj1.h>
42 }
43
44 #define UNUSED(x) (void)(x)
45
46 //       _                 ___                 _
47 //   ___| | __ _ ___ ___  |_ _|_ __ ___  _ __ | |
48 //  / __| |/ _` / __/ __|  | || '_ ` _ \| '_ \| |
49 // | (__| | (_| \__ \__ \  | || | | | | | |_) | |
50 //  \___|_|\__,_|___/___/ |___|_| |_| |_| .__/|_|
51 //                                      |_|
52 class LibWindowmanager::Impl {
53     friend class LibWindowmanager;
54
55     // This is the LibWindowmanager interface impl
56     int init(int port, char const *token);
57
58     // WM API
59     int requestSurface(const char *label);
60     int activateSurface(const char *label);
61     int deactivateSurface(const char *label);
62     int endDraw(const char *label);
63
64     void set_event_handler(enum EventType et, handler_fun func);
65
66     Impl();
67     ~Impl();
68
69     struct afb_wsj1 *wsj1;
70     struct sd_event *loop;
71
72     std::set<std::string> labels;
73     std::map<EventType, handler_fun> handlers;
74     std::queue<std::pair<handler_fun, std::string>> handler_queue;
75
76     int api_call(const char *verb, json_object *object,
77                  const std::function<void(bool, json_object *)> &onReply);
78
79 public:
80     void event(char const *et, char const *label);
81 private:
82   int runEventLoop();
83 };
84
85 namespace {
86
87 constexpr const int token_maxlen = 20;
88 constexpr const char *const wmAPI = "windowmanager";
89
90 #define CONCAT_(X, Y) X##Y
91 #define CONCAT(X, Y) CONCAT_(X, Y)
92
93 #ifndef SCOPE_TRACING
94 #define TRACE()
95 #define TRACEN(N)
96 #else
97 #define TRACE() \
98     ScopeTrace __attribute__((unused)) CONCAT(trace_scope_, __LINE__)(__func__)
99 #define TRACEN(N) \
100     ScopeTrace __attribute__((unused)) CONCAT(named_trace_scope_, __LINE__)(#N)
101
102 struct ScopeTrace {
103     thread_local static int indent;
104     char const *f{};
105     explicit ScopeTrace(char const *func) : f(func) {
106         fprintf(stderr, "%*s%s -->\n", 2 * indent++, "", this->f);
107     }
108     ~ScopeTrace() { fprintf(stderr, "%*s%s <--\n", 2 * --indent, "", this->f); }
109 };
110 thread_local int ScopeTrace::indent = 0;
111 #endif
112
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) {
116     TRACE();
117     UNUSED(closure);
118     UNUSED(verb);
119     UNUSED(api);
120     UNUSED(msg);
121 }
122
123 /* called when wsj1 receives an event */
124 void onEvent(void *closure, const char *event, afb_wsj1_msg *msg) {
125     TRACE();
126
127     // check API name in event
128     if (0 != strncmp(wmAPI, event, strlen(wmAPI))) {
129         return;
130     }
131
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")));
135 }
136
137 /* called when wsj1 hangsup */
138 void onHangup(void *closure, afb_wsj1 *wsj1) {
139     TRACE();
140     UNUSED(closure);
141     UNUSED(wsj1);
142     fputs("Hangup, the WindowManager vanished\n", stderr);
143     exit(1);  // XXX: there should be something ... *better* here.
144 }
145
146 constexpr struct afb_wsj1_itf itf = {
147     onHangup, onCall, onEvent,
148 };
149
150 // XXX: I am not sure this is the right thing to do though...
151 std::recursive_mutex dispatch_mutex;
152
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));
156     return j;
157 }
158
159 }  // namespace
160
161 //       _                 ___                 _   _                 _
162 //   ___| | __ _ ___ ___  |_ _|_ __ ___  _ __ | | (_)_ __ ___  _ __ | |
163 //  / __| |/ _` / __/ __|  | || '_ ` _ \| '_ \| | | | '_ ` _ \| '_ \| |
164 // | (__| | (_| \__ \__ \  | || | | | | | |_) | | | | | | | | | |_) | |
165 //  \___|_|\__,_|___/___/ |___|_| |_| |_| .__/|_| |_|_| |_| |_| .__/|_|
166 //                                      |_|                   |_|
167 LibWindowmanager::Impl::Impl() : wsj1{}, loop{}, labels(), handlers() { TRACE(); }
168
169 LibWindowmanager::Impl::~Impl() {
170     TRACE();
171     afb_wsj1_unref(wsj1);
172     sd_event_unref(loop);
173 }
174
175 int LibWindowmanager::Impl::init(int port, char const *token) {
176     TRACE();
177     char *uribuf = nullptr;
178     int rc = -1;
179
180     if (this->loop != nullptr && this->wsj1 != nullptr) {
181         fputs("LibWindowmanager instance is already initialized!\n", stderr);
182         rc = -EALREADY;
183         goto fail;
184     }
185
186     if (token == nullptr) {
187         fputs("Token is invalid\n", stderr);
188         rc = -EINVAL;
189         goto fail;
190     }
191
192     if (port < 1 || port > 0xffff) {
193         fputs("Port is invalid\n", stderr);
194         rc = -EINVAL;
195         goto fail;
196     }
197
198     /* get the default event loop */
199     rc = sd_event_default(&this->loop);
200     if (rc < 0) {
201         fprintf(stderr, "Connection to default event loop failed: %s\n",
202                 strerror(-rc));
203         goto fail;
204     }
205
206     asprintf(&uribuf, "ws://localhost:%d/api?token=%s", port, token);
207
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);
215         rc = -errno;
216         goto fail;
217     }
218
219     this->runEventLoop();
220
221     return 0;
222
223 fail:
224     return rc;
225 }
226
227 int LibWindowmanager::Impl::requestSurface(const char *label) {
228     TRACE();
229
230     if (this->labels.find(label) != this->labels.end()) {
231         fputs("Surface label already known!\n", stderr);
232         return -EINVAL;
233     }
234
235     json_object *j = drawing_name_json_argument(label);
236
237     int rc = -1;
238     /* send the request */
239     int rc2 =
240         this->api_call("RequestSurface", j, [&rc](bool ok, json_object *j) {
241             if (ok) {
242                 int id =
243                     json_object_get_int(json_object_object_get(j, "response"));
244                 char *buf;
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");
249                     rc = -errno;
250                 } else {
251                     rc = 0;  // Single point of success
252                 }
253             } else {
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)
257                           : "no-info");
258                 rc = -EINVAL;
259             }
260         });
261
262     if (rc2 < 0) {
263         rc = rc2;
264     }
265
266     if (rc >= 0) {
267         this->labels.insert(this->labels.end(), label);
268     }
269
270     return rc;
271 }
272
273 int LibWindowmanager::Impl::activateSurface(const char *label) {
274     TRACE();
275     json_object *j = drawing_name_json_argument(label);
276     return this->api_call("ActivateSurface", j, [](bool ok, json_object *j) {
277         if (!ok) {
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)
281                   : "no-info");
282         }
283     });
284 }
285
286 int LibWindowmanager::Impl::deactivateSurface(const char *label) {
287     TRACE();
288     json_object *j = drawing_name_json_argument(label);
289     return this->api_call("DeactivateSurface", j, [](bool ok, json_object *j) {
290         if (!ok) {
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)
294                   : "no-info");
295         }
296     });
297 }
298
299 int LibWindowmanager::Impl::endDraw(const char *label) {
300     TRACE();
301     json_object *j = drawing_name_json_argument(label);
302     return this->api_call("EndDraw", j, [](bool ok, json_object *j) {
303         if (!ok) {
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)
307                   : "no-info");
308         }
309     });
310 }
311
312 void LibWindowmanager::Impl::set_event_handler(
313     enum EventType et, std::function<void(char const *)> func) {
314     TRACE();
315
316     if (et >= 1 && et <= 6) {  // Yeah ... just go with it!
317         this->handlers[et] = std::move(func);
318     }
319 }
320
321 namespace {
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
324     // get on with it.
325     char const *et2 = strchr(et, '/');
326     if (et2 != nullptr) {
327         et = et2 + 1;
328     }
329
330 #define ET(N, A)                                               \
331     do {                                                       \
332         if (strcasecmp(et, N) == 0)                            \
333             return std::pair<bool, LibWindowmanager::EventType>( \
334                 true, CONCAT(LibWindowmanager::Event_, A));           \
335     } while (false)
336
337     ET("active", Active);
338     ET("inactive", Inactive);
339     ET("visible", Visible);
340     ET("invisible", Invisible);
341     ET("syncdraw", SyncDraw);
342     ET("flushdraw", FlushDraw);
343 #undef ET
344
345     return std::pair<bool, LibWindowmanager::EventType>(false,
346                                                       LibWindowmanager::Event_Active);
347 }
348 }  // namespace
349
350 static void _on_reply_static(void *closure, struct afb_wsj1_msg *msg)
351 {
352     // nop
353 }
354
355
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) {
360     TRACE();
361
362     int rc = 0;
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.
369         int call_rc = 0;
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
377                 // json object.
378                 {
379                     TRACEN(onReply);
380                     onReply(ok, j);
381                 }
382                 returned.store(true, std::memory_order_release);
383             };
384
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) {
389                 TRACEN(callClosure);
390                 auto *onReply =
391                     reinterpret_cast<std::function<void(bool, json_object *)> *>(
392                         closure);
393                 (*onReply)(!(afb_wsj1_msg_is_reply_ok(msg) == 0),
394                            afb_wsj1_msg_object_j(msg));
395             },
396             &wrappedOnReply);
397
398         if (0 == rc) {
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);
404             }
405
406             // return the actual API call result
407             rc = call_rc;
408         }
409     }
410     else {
411         rc = afb_wsj1_call_j(this->wsj1, wmAPI, verb, object, _on_reply_static, this);
412     }
413
414     if (rc < 0) {
415         fprintf(
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);
420     }
421
422     return rc;
423 }
424
425 void LibWindowmanager::Impl::event(char const *et, char const *label) {
426     TRACE();
427     auto oet = make_event_type(et);
428     if (!oet.first) {
429         fprintf(stderr, "Unknown event type string '%s'\n", et);
430         return;
431     }
432
433     auto i = this->handlers.find(oet.second);
434     if (i != this->handlers.end()) {
435         if (this->labels.find(label) != this->labels.end()) {
436             i->second(label);
437         }
438     }
439 }
440
441 static void *event_loop_run(void *args){
442     struct sd_event* loop = (struct sd_event*)(args);
443     for(;;)
444         sd_event_run(loop, 30000000);
445 }
446
447 int LibWindowmanager::Impl::runEventLoop() {
448     if(this->wsj1 && this->loop)
449     {
450         pthread_t thread_id;
451         int ret = pthread_create(&thread_id, NULL, event_loop_run, this->loop);
452         if(ret != 0)
453         {
454             printf("Cannot run eventloop due to error:%d", errno);
455             return -1;
456         }
457         else
458             return thread_id;
459         }
460     else
461     {
462         printf("Connecting is not established yet");
463         return -1;
464     }
465 }
466
467 //       _                    _    _____ ____   ____ _ _            _
468 //   ___| | __ _ ___ ___     / \  |  ___| __ ) / ___| (_) ___ _ __ | |_
469 //  / __| |/ _` / __/ __|   / _ \ | |_  |  _ \| |   | | |/ _ \ '_ \| __|
470 // | (__| | (_| \__ \__ \  / ___ \|  _| | |_) | |___| | |  __/ | | | |_
471 //  \___|_|\__,_|___/___/ /_/   \_\_|   |____/ \____|_|_|\___|_| |_|\__|
472 //
473 int LibWindowmanager::init(int port, char const *token) {
474     return this->d->init(port, token);
475 }
476
477 int LibWindowmanager::requestSurface(const char *label) {
478     return this->d->requestSurface(label);
479 }
480
481 int LibWindowmanager::activateSurface(const char *label) {
482     return this->d->activateSurface(label);
483 }
484
485 int LibWindowmanager::deactivateSurface(const char *label) {
486     return this->d->deactivateSurface(label);
487 }
488
489 int LibWindowmanager::endDraw(const char *label) { return this->d->endDraw(label); }
490
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));
494 }
495
496 LibWindowmanager::LibWindowmanager() : d(new Impl) {}
497
498 LibWindowmanager::~LibWindowmanager() { delete d; }