AFBClient: fix NDEBUG usage
[staging/windowmanager.git] / AFBClient.cpp
1 #include "AFBClient.h"
2
3 #include <cctype>
4 #include <stdlib.h>
5 #include <stdio.h>
6 #include <errno.h>
7 #include <string.h>
8 #include <unistd.h>
9
10 #define UNUSED(x) (void)(x)
11
12 namespace {
13
14 constexpr const int token_maxlen = 20;
15 constexpr const char *const wmAPI = "winman";
16
17 #ifdef NDEBUG
18 #define TRACE()
19 #else
20 #define TRACE() \
21     ScopeTrace __attribute__((unused)) trace_scope_here__(__PRETTY_FUNCTION__)
22
23 struct ScopeTrace {
24     static int indent;
25     char const *f{};
26     ScopeTrace(char const *func) : f(func) {
27         fprintf(stderr, "%*s%s -->\n", 2 * indent++, "", this->f);
28     }
29     ~ScopeTrace() { fprintf(stderr, "%*s%s <--\n", 2 * --indent, "", this->f); }
30 };
31 int ScopeTrace::indent = 0;
32 #endif
33
34 /* called when wsj1 receives a method invocation */
35 void onCall(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg)
36 {
37    TRACE();
38     UNUSED(closure);
39     int rc;
40     printf("ON-CALL %s/%s:\n%s\n", api, verb,
41            json_object_to_json_string_ext(afb_wsj1_msg_object_j(msg),
42                                           JSON_C_TO_STRING_PRETTY));
43     fflush(stdout);
44     rc = afb_wsj1_reply_error_s(msg, "\"unimplemented\"", nullptr);
45     if (rc < 0)
46         fprintf(stderr, "replying failed: %m\n");
47 }
48
49 /* called when wsj1 receives an event */
50 void onEvent(void *closure, const char *event, afb_wsj1_msg *msg)
51 {
52    TRACE();
53     UNUSED(closure);
54     printf("ON-EVENT %s:\n%s\n", event,
55            json_object_to_json_string_ext(afb_wsj1_msg_object_j(msg),
56                                           JSON_C_TO_STRING_PRETTY));
57     fflush(stdout);
58 }
59
60 /* called when wsj1 hangsup */
61 void onHangup(void *closure, afb_wsj1 *wsj1)
62 {
63    TRACE();
64     UNUSED(closure);
65     UNUSED(wsj1);
66     printf("ON-HANGUP\n");
67     fflush(stdout);
68     exit(0);
69 }
70
71 /* called when wsj1 receives a reply */
72 void onReply(void *closure, afb_wsj1_msg *msg)
73 {
74    TRACE();
75     printf("ON-REPLY %s: %s\n%s\n", (char*)closure,
76            afb_wsj1_msg_is_reply_ok(msg) ? "OK" : "ERROR",
77            json_object_to_json_string_ext(afb_wsj1_msg_object_j(msg),
78                                           JSON_C_TO_STRING_PRETTY));
79     fflush(stdout);
80     free(closure);
81 }
82
83 }  // namespace
84
85 AFBClient &AFBClient::instance()
86 {
87    TRACE();
88    static AFBClient obj;
89    return obj;
90 }
91
92 AFBClient::AFBClient() : wsj1{}, itf{}, loop{}
93 {
94    TRACE();
95     ///* itinializing the callback interface for wsj1 */
96     itf.on_hangup = onHangup;
97     itf.on_call = onCall;
98     itf.on_event = onEvent;
99 }
100
101 AFBClient::~AFBClient()
102 {
103    TRACE();
104    sd_event_unref(loop);
105    loop = nullptr;
106 }
107
108 int AFBClient::init(int port, char const *token)
109 {
110    TRACE();
111     char *uribuf = nullptr;
112     int rc = -1;
113
114     if (!token || strlen(token) > token_maxlen) {
115        fprintf(stderr, "Token is invalid\n");
116        rc = -EINVAL;
117        goto fail;
118     }
119
120     for (char const *p = token; *p; p++) {
121        if (!isalnum(*p)) {
122           fprintf(stderr, "Token is invalid\n");
123           rc = -EINVAL;
124           goto fail;
125        }
126     }
127
128     if (port < 1 && port > 0xffff) {
129        fprintf(stderr, "Port is invalid\n");
130        rc = -EINVAL;
131        goto fail;
132     }
133
134     /* get the default event loop */
135     rc = sd_event_default(&loop);
136     if (rc < 0) {
137         fprintf(stderr, "Connection to default event loop failed: %s\n", strerror(-rc));
138         goto fail;
139     }
140
141     asprintf(&uribuf, "ws://localhost:%d/api?token=%s", port, token);
142
143     /* connect the websocket wsj1 to the uri given by the first argument */
144     wsj1 = afb_ws_client_connect_wsj1(loop, uribuf, &itf, nullptr);
145     if (wsj1 == nullptr) {
146         sd_event_unref(loop);
147         fprintf(stderr, "Connection to %s failed: %m\n", uribuf);
148         rc = -errno;
149         goto fail;
150     }
151
152     return 0;
153
154 fail:
155     return rc;
156 }
157
158 int AFBClient::dispatch(uint64_t timeout) {
159    TRACE();
160     return sd_event_run(loop, timeout);
161 }
162
163 int AFBClient::requestSurface(const char *label)
164 {
165    TRACE();
166    constexpr char const *verb = "request_surface";
167
168    json_object *jp = json_object_new_object();
169    json_object_object_add(jp, "drawing_name", json_object_new_string(label));
170
171    // std::experimental::optional look-alike
172    struct optional {
173       int value;
174       bool is_not_set;
175    };
176
177    constexpr struct optional const nullopt = {0, true};
178    auto id = nullopt;
179
180    /* send the request */
181    int rc = afb_wsj1_call_j(
182       wsj1, wmAPI, verb, jp,
183       [](void *closure, afb_wsj1_msg *msg) {
184          if (afb_wsj1_msg_is_reply_ok(msg)) {
185             int id = json_object_get_int(
186                json_object_object_get(afb_wsj1_msg_object_j(msg), "response"));
187             auto oid = (optional *)closure;
188             *oid = optional{id};
189          } else
190             fprintf(stderr, "wrong request surface reply received!\n");
191       },
192       (void *)&id);
193
194    if (rc < 0) {
195       fprintf(stderr, "calling %s/%s(%s) failed: %m\n", wmAPI, verb,
196               json_object_to_json_string(jp));
197    } else {
198       // Lets make this call sync here...
199       dispatch(-1);
200
201       if (! id.is_not_set) {
202           char *buf;
203           asprintf(&buf, "%d", id.value);
204           printf("setenv(\"QT_IVI_SURFACE_ID\", %s, 1)\n", buf);
205           if (setenv("QT_IVI_SURFACE_ID", buf, 1) != 0) {
206               fprintf(stderr, "putenv failed: %m\n");
207           } else {
208               rc = 0; // Single point of success
209           }
210       } else {
211           fprintf(stderr, "Could not get surface ID from WM\n");
212           rc = -EINVAL;
213       }
214    }
215
216    return rc;
217 }
218
219 int AFBClient::activateSurface(const char *label)
220 {
221    TRACE();
222
223     const char begin[] = "{\"drawing_name\":\"";
224     const char end[] = "\"}";
225     const char verb[] = "activate_surface";
226     char *parameter = (char *)malloc(strlen(begin) +
227                                      strlen(label) +
228                                      strlen(end) + 1);
229     strcpy(parameter, begin);
230     strcat(parameter, label);
231     strcat(parameter, end);
232     call(wmAPI, verb, parameter);
233
234     // Sync this one too
235     dispatch(-1);
236
237     return 0;
238 }
239
240 int AFBClient::deactivateSurface(const char *label)
241 {
242    TRACE();
243     json_object *j = json_object_new_object();
244     json_object_object_add(j, "drawing_name", json_object_new_string(label));
245     call(wmAPI, "deactivate_surface", json_object_to_json_string(j));
246     json_object_put(j);
247     dispatch(-1);
248     return 0;
249 }
250
251 int AFBClient::endDraw(const char *label)
252 {
253    TRACE();
254     json_object *j = json_object_new_object();
255     json_object_object_add(j, "drawing_name", json_object_new_string(label));
256     call(wmAPI, "enddraw", json_object_to_json_string(j));
257     json_object_put(j);
258     dispatch(-1);
259     return 0;
260 }
261
262 /* makes a call */
263 void AFBClient::call(const char *api, const char *verb, const char *object)
264 {
265    TRACE();
266     static int num = 0;
267     char *key;
268     int rc;
269
270     fflush(stdout);
271
272     /* allocates an id for the request */
273     rc = asprintf(&key, "%d:%s/%s", ++num, api, verb);
274
275     /* send the request */
276     rc = afb_wsj1_call_s(wsj1, api, verb, object, onReply, key);
277     if (rc < 0)
278         fprintf(stderr, "calling %s/%s(%s) failed: %m\n", api, verb, object);
279
280     fflush(stdout);
281 }
282
283 void AFBClient::set_event_handler(enum EventType at, std::function<void(char const *)> func) {
284    TRACE();
285    // XXX todo
286 }