AFBClient: move api_call() to Impl as it's now private anyway.
[staging/windowmanager.git] / AFBClient.cpp
1 #include "AFBClient.h"
2
3 #include <cassert>
4 #include <cctype>
5 #include <cerrno>
6 #include <cstdio>
7 #include <cstdlib>
8 #include <cstring>
9
10 #include <atomic>
11 #include <map>
12 #include <mutex>
13 #include <set>
14
15 #include <unistd.h>
16
17 #include <systemd/sd-event.h>
18
19 #include <json-c/json.h>
20
21 extern "C" {
22 #include <afb/afb-ws-client.h>
23 #include <afb/afb-wsj1.h>
24 }
25
26 #define UNUSED(x) (void)(x)
27
28 //       _                 ___                 _
29 //   ___| | __ _ ___ ___  |_ _|_ __ ___  _ __ | |
30 //  / __| |/ _` / __/ __|  | || '_ ` _ \| '_ \| |
31 // | (__| | (_| \__ \__ \  | || | | | | | |_) | |
32 //  \___|_|\__,_|___/___/ |___|_| |_| |_| .__/|_|
33 //                                      |_|
34 class AFBClient::Impl {
35     friend class AFBClient;
36
37     // This is the AFBClient interface impl
38     int init(int port, char const *token);
39     int dispatch();
40
41     // WM API
42     int requestSurface(const char *label);
43     int activateSurface(const char *label);
44     int deactivateSurface(const char *label);
45     int endDraw(const char *label);
46
47     void set_event_handler(enum EventType et, handler_fun func);
48
49     Impl();
50     ~Impl();
51
52     struct afb_wsj1 *wsj1;
53     struct sd_event *loop;
54
55     std::set<std::string> labels;
56     std::map<EventType, handler_fun> handlers;
57
58     int api_call(const char *verb, json_object *object,
59                  const std::function<void(bool, json_object *)> &onReply);
60
61 public:
62     void event(char const *et, char const *label);
63 };
64
65 namespace {
66
67 constexpr const int token_maxlen = 20;
68 constexpr const char *const wmAPI = "winman";
69
70 #ifdef NDEBUG
71 #define TRACE()
72 #define TRACEN(N)
73 #else
74 #define CONCAT_(X, Y) X##Y
75 #define CONCAT(X, Y) CONCAT_(X, Y)
76
77 #define TRACE() \
78     ScopeTrace __attribute__((unused)) CONCAT(trace_scope_, __LINE__)(__func__)
79 #define TRACEN(N) \
80     ScopeTrace __attribute__((unused)) CONCAT(named_trace_scope_, __LINE__)(#N)
81
82 struct ScopeTrace {
83     thread_local static int indent;
84     char const *f{};
85     explicit ScopeTrace(char const *func) : f(func) {
86         fprintf(stderr, "%*s%s -->\n", 2 * indent++, "", this->f);
87     }
88     ~ScopeTrace() { fprintf(stderr, "%*s%s <--\n", 2 * --indent, "", this->f); }
89 };
90 thread_local int ScopeTrace::indent = 0;
91 #endif
92
93 /* called when wsj1 receives a method invocation */
94 void onCall(void *closure, const char *api, const char *verb,
95             struct afb_wsj1_msg *msg) {
96     TRACE();
97     UNUSED(closure);
98     UNUSED(verb);
99     UNUSED(api);
100     UNUSED(msg);
101 }
102
103 /* called when wsj1 receives an event */
104 void onEvent(void *closure, const char *event, afb_wsj1_msg *msg) {
105     TRACE();
106     reinterpret_cast<AFBClient::Impl *>(closure)->event(
107         event, json_object_get_string(
108                    json_object_object_get(afb_wsj1_msg_object_j(msg), "data")));
109 }
110
111 /* called when wsj1 hangsup */
112 void onHangup(void *closure, afb_wsj1 *wsj1) {
113     TRACE();
114     UNUSED(closure);
115     UNUSED(wsj1);
116     fputs("Hangup, the WindowManager vanished\n", stderr);
117     exit(1);
118 }
119
120 constexpr struct afb_wsj1_itf itf = {
121     onHangup, onCall, onEvent,
122 };
123
124 // XXX: I am not sure this is the right thing to do though...
125 std::recursive_mutex dispatch_mutex;
126
127 void dispatch_internal(struct sd_event *loop) {
128     std::lock_guard<std::recursive_mutex> guard(dispatch_mutex);
129     TRACE();
130     sd_event_run(loop, -1);
131 }
132
133 }  // namespace
134
135 //       _                 ___                 _   _                 _
136 //   ___| | __ _ ___ ___  |_ _|_ __ ___  _ __ | | (_)_ __ ___  _ __ | |
137 //  / __| |/ _` / __/ __|  | || '_ ` _ \| '_ \| | | | '_ ` _ \| '_ \| |
138 // | (__| | (_| \__ \__ \  | || | | | | | |_) | | | | | | | | | |_) | |
139 //  \___|_|\__,_|___/___/ |___|_| |_| |_| .__/|_| |_|_| |_| |_| .__/|_|
140 //                                      |_|                   |_|
141 AFBClient::Impl::Impl() : wsj1{}, loop{}, labels(), handlers() { TRACE(); }
142
143 AFBClient::Impl::~Impl() {
144     TRACE();
145     afb_wsj1_unref(wsj1);
146     sd_event_unref(loop);
147 }
148
149 int AFBClient::Impl::init(int port, char const *token) {
150     TRACE();
151     char *uribuf = nullptr;
152     int rc = -1;
153
154     if (this->loop != nullptr && this->wsj1 != nullptr) {
155         fputs("AFBClient instance is already initialized!\n", stderr);
156         rc = -EALREADY;
157         goto fail;
158     }
159
160     if ((token == nullptr) || strlen(token) > token_maxlen) {
161         fputs("Token is invalid\n", stderr);
162         rc = -EINVAL;
163         goto fail;
164     }
165
166     for (char const *p = token; *p != 0; p++) {
167         if (isalnum(*p) == 0) {
168             fputs("Token is invalid\n", stderr);
169             rc = -EINVAL;
170             goto fail;
171         }
172     }
173
174     if (port < 1 && port > 0xffff) {
175         fputs("Port is invalid\n", stderr);
176         rc = -EINVAL;
177         goto fail;
178     }
179
180     /* get the default event loop */
181     rc = sd_event_default(&this->loop);
182     if (rc < 0) {
183         fprintf(stderr, "Connection to default event loop failed: %s\n",
184                 strerror(-rc));
185         goto fail;
186     }
187
188     asprintf(&uribuf, "ws://localhost:%d/api?token=%s", port, token);
189
190     /* connect the websocket wsj1 to the uri given by the first argument */
191     this->wsj1 = afb_ws_client_connect_wsj1(
192         this->loop, uribuf, const_cast<struct afb_wsj1_itf *>(&itf), this);
193     if (this->wsj1 == nullptr) {
194         sd_event_unref(this->loop);
195         this->loop = nullptr;
196         fprintf(stderr, "Connection to %s failed: %m\n", uribuf);
197         rc = -errno;
198         goto fail;
199     }
200
201     return 0;
202
203 fail:
204     return rc;
205 }
206
207 int AFBClient::Impl::dispatch() {
208     std::lock_guard<std::recursive_mutex> guard(dispatch_mutex);
209     return sd_event_run(this->loop, 1);
210 }
211
212 json_object *drawing_name_json_argument(char const *label) {
213     json_object *j = json_object_new_object();
214     json_object_object_add(j, "drawing_name", json_object_new_string(label));
215     return j;
216 }
217
218 int AFBClient::Impl::requestSurface(const char *label) {
219     TRACE();
220
221     if (this->labels.find(label) != this->labels.end()) {
222         fputs("Surface label already known!\n", stderr);
223         return -EINVAL;
224     }
225
226     json_object *j = drawing_name_json_argument(label);
227
228     int rc = -1;
229     /* send the request */
230     int rc2 = api_call("request_surface", j, [&rc](bool ok, json_object *j) {
231         if (ok) {
232             int id = json_object_get_int(json_object_object_get(j, "response"));
233             char *buf;
234             asprintf(&buf, "%d", id);
235             printf("setenv(\"QT_IVI_SURFACE_ID\", %s, 1)\n", buf);
236             if (setenv("QT_IVI_SURFACE_ID", buf, 1) != 0) {
237                 fprintf(stderr, "putenv failed: %m\n");
238                 rc = -errno;
239             } else {
240                 rc = 0;  // Single point of success
241             }
242         } else {
243             fprintf(stderr, "Could not get surface ID from WM: %s\n",
244                     j != nullptr ? json_object_to_json_string_ext(
245                                        j, JSON_C_TO_STRING_PRETTY)
246                                  : "no-info");
247             rc = -EINVAL;
248         }
249     });
250
251     if (rc2 < 0) {
252         rc = rc2;
253     }
254
255     if (rc >= 0) {
256         this->labels.insert(this->labels.end(), label);
257     }
258
259     return rc;
260 }
261
262 int AFBClient::Impl::activateSurface(const char *label) {
263     TRACE();
264     json_object *j = drawing_name_json_argument(label);
265     return api_call("activate_surface", j, [](bool ok, json_object *j) {
266         if (!ok) {
267             fprintf(stderr, "API Call activate_surface() failed: %s\n",
268                     j != nullptr ? json_object_to_json_string_ext(
269                                        j, JSON_C_TO_STRING_PRETTY)
270                                  : "no-info");
271         }
272     });
273 }
274
275 int AFBClient::Impl::deactivateSurface(const char *label) {
276     TRACE();
277     json_object *j = drawing_name_json_argument(label);
278     return api_call("deactivate_surface", j, [](bool ok, json_object *j) {
279         if (!ok) {
280             fprintf(stderr, "API Call deactivate_surface() failed: %s\n",
281                     j != nullptr ? json_object_to_json_string_ext(
282                                        j, JSON_C_TO_STRING_PRETTY)
283                                  : "no-info");
284         }
285     });
286 }
287
288 int AFBClient::Impl::endDraw(const char *label) {
289     TRACE();
290     json_object *j = drawing_name_json_argument(label);
291     return api_call("enddraw", j, [](bool ok, json_object *j) {
292         if (!ok) {
293             fprintf(stderr, "API Call endDraw() failed: %s\n",
294                     j != nullptr ? json_object_to_json_string_ext(
295                                        j, JSON_C_TO_STRING_PRETTY)
296                                  : "no-info");
297         }
298     });
299 }
300
301 void AFBClient::Impl::set_event_handler(
302     enum EventType et, std::function<void(char const *)> func) {
303     TRACE();
304
305     if (et >= 1 && et <= 6) {  // Yeah ... just go with it!
306         this->handlers[et] = std::move(func);
307     }
308 }
309
310 namespace {
311 std::pair<bool, AFBClient::EventType> make_event_type(char const *et) {
312     // Event have the form "$API/$EVENT", just try to find the first / and
313     // get on with it.
314     char const *et2 = strchr(et, '/');
315     if (et2 != nullptr) {
316         et = et2 + 1;
317     }
318
319 #define ET(N, A)                                          \
320     do {                                                  \
321         if (strcasecmp(et, N) == 0)                       \
322             return std::pair<bool, AFBClient::EventType>( \
323                 true, CONCAT(AFBClient::Event_, A));      \
324     } while (false)
325
326     ET("activated", Active);
327     ET("deactivated", Inactive);
328     ET("visible", Visible);
329     ET("invisible", Invisible);
330     ET("syncdraw", SyncDraw);
331     ET("flushdraw", FlushDraw);
332 #undef ET
333
334     return std::pair<bool, AFBClient::EventType>(false,
335                                                  AFBClient::Event_Active);
336 }
337 }  // namespace
338
339 /// object will be json_object_put
340 int AFBClient::Impl::api_call(
341     const char *verb, json_object *object,
342     const std::function<void(bool, json_object *)> &onReply) {
343     TRACE();
344
345     // We need to wrap the actual onReply call once in order to
346     // *look* like a normal functions pointer (std::functions<>
347     // with captures cannot convert to function pointers).
348     // Alternatively we could setup a local struct and use it as
349     // closure, but I think it is cleaner this way.
350     int call_rc = 0;
351     std::atomic<bool> returned{};
352     returned.store(false, std::memory_order_relaxed);
353     std::function<void(bool, json_object *)> wrappedOnReply =
354         [&returned, &call_rc, &onReply](bool ok, json_object *j) {
355             TRACEN(wrappedOnReply);
356             call_rc = ok ? 0 : -EINVAL;
357             // We know it failed, but there may be an explanation in the
358             // json object.
359             {
360                 TRACEN(onReply);
361                 onReply(ok, j);
362             }
363             returned.store(true, std::memory_order_release);
364         };
365
366     // make the actual call, use wrappedOnReply as closure
367     int rc = afb_wsj1_call_j(
368         this->wsj1, wmAPI, verb, object,
369         [](void *closure, afb_wsj1_msg *msg) {
370             TRACEN(callClosure);
371             auto *onReply =
372                 reinterpret_cast<std::function<void(bool, json_object *)> *>(
373                     closure);
374             (*onReply)(!(afb_wsj1_msg_is_reply_ok(msg) == 0),
375                        afb_wsj1_msg_object_j(msg));
376         },
377         &wrappedOnReply);
378
379     if (rc < 0) {
380         fprintf(
381             stderr, "calling %s/%s(%s) failed: %m\n", wmAPI, verb,
382             json_object_to_json_string_ext(object, JSON_C_TO_STRING_PRETTY));
383         // Call the reply handler regardless with a NULL json_object*
384         onReply(false, nullptr);
385     } else {
386         // We need to dispatch until "returned" got set, this is necessary
387         // if events get triggered by the call (and would be dispatched before
388         // the actual call-reply).
389         while (!returned.load(std::memory_order_consume)) {
390             std::lock_guard<std::recursive_mutex> guard(dispatch_mutex);
391             if (!returned.load(std::memory_order_consume)) {
392                 dispatch_internal(this->loop);
393             }
394         }
395
396         // return the actual API call result
397         rc = call_rc;
398     }
399
400     return rc;
401 }
402
403 void AFBClient::Impl::event(char const *et, char const *label) {
404     TRACE();
405     auto oet = make_event_type(et);
406     if (!oet.first) {
407         fprintf(stderr, "Unknown event type string '%s'\n", et);
408         return;
409     }
410
411     auto i = this->handlers.find(oet.second);
412     if (i != this->handlers.end()) {
413         if (this->labels.find(label) != this->labels.end()) {
414             i->second(label);
415         }
416     }
417 }
418
419 //       _                    _    _____ ____   ____ _ _            _
420 //   ___| | __ _ ___ ___     / \  |  ___| __ ) / ___| (_) ___ _ __ | |_
421 //  / __| |/ _` / __/ __|   / _ \ | |_  |  _ \| |   | | |/ _ \ '_ \| __|
422 // | (__| | (_| \__ \__ \  / ___ \|  _| | |_) | |___| | |  __/ | | | |_
423 //  \___|_|\__,_|___/___/ /_/   \_\_|   |____/ \____|_|_|\___|_| |_|\__|
424 //
425 int AFBClient::init(int port, char const *token) {
426     return this->d->init(port, token);
427 }
428
429 int AFBClient::dispatch() { return this->d->dispatch(); }
430
431 int AFBClient::requestSurface(const char *label) {
432     return this->d->requestSurface(label);
433 }
434
435 int AFBClient::activateSurface(const char *label) {
436     return this->d->activateSurface(label);
437 }
438
439 int AFBClient::deactivateSurface(const char *label) {
440     return this->d->deactivateSurface(label);
441 }
442
443 int AFBClient::endDraw(const char *label) { return this->d->endDraw(label); }
444
445 void AFBClient::set_event_handler(enum EventType et,
446                                   std::function<void(char const *label)> f) {
447     return this->d->set_event_handler(et, std::move(f));
448 }
449
450 AFBClient &AFBClient::instance() {
451     TRACE();
452     static AFBClient obj;
453     return obj;
454 }
455
456 AFBClient::AFBClient() : d(new Impl) {}
457
458 AFBClient::~AFBClient() { delete d; }