Clean up source code
[apps/agl-service-windowmanager-2017.git] / src / main.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 <unistd.h>
18 #include <algorithm>
19 #include <mutex>
20 #include <json.h>
21 #include <json.hpp>
22 #include "app.hpp"
23 #include "result.hpp"
24 #include "json_helper.hpp"
25 #include "util.hpp"
26 #include "wayland.hpp"
27
28 extern "C" {
29 #include <afb/afb-binding.h>
30 #include <systemd/sd-event.h>
31 }
32
33 struct afb_instance {
34    std::unique_ptr<wl::display> display;
35    wm::App app;
36
37    afb_instance() : display{new wl::display}, app{this->display.get()} {}
38
39    int init();
40 };
41
42 struct afb_instance *g_afb_instance;
43 std::mutex binding_m;
44
45 int afb_instance::init() {
46    return this->app.init();
47 }
48
49 int display_event_callback(sd_event_source *evs, int /*fd*/, uint32_t events,
50                            void * /*data*/) {
51    ST();
52
53    if ((events & EPOLLHUP) != 0) {
54       HMI_ERROR("wm", "The compositor hung up, dying now.");
55       delete g_afb_instance;
56       g_afb_instance = nullptr;
57       goto error;
58    }
59
60    if ((events & EPOLLIN) != 0u) {
61       {
62          STN(display_read_events);
63          g_afb_instance->app.display->read_events();
64          g_afb_instance->app.set_pending_events();
65       }
66       {
67          // We want do dispatch pending wayland events from within
68          // the API context
69          STN(winman_ping_api_call);
70          afb_service_call("windowmanager", "ping", json_object_new_object(),
71                           [](void *c, int st, json_object *j) {
72                              STN(winman_ping_api_call_return);
73                           },
74                           nullptr);
75       }
76    }
77
78    return 0;
79
80 error:
81    sd_event_source_unref(evs);
82    if (getenv("WINMAN_EXIT_ON_HANGUP") != nullptr) {
83    exit(1);
84 }
85    return -1;
86 }
87
88 int _binding_init() {
89    HMI_NOTICE("wm", "WinMan ver. %s", WINMAN_VERSION_STRING);
90
91    if (g_afb_instance != nullptr) {
92       HMI_ERROR("wm", "Wayland context already initialized?");
93       return 0;
94    }
95
96    if (getenv("XDG_RUNTIME_DIR") == nullptr) {
97       HMI_ERROR("wm", "Environment variable XDG_RUNTIME_DIR not set");
98       goto error;
99    }
100
101    {
102       // wait until wayland compositor starts up.
103       int cnt = 0;
104       g_afb_instance = new afb_instance;
105       while (!g_afb_instance->display->ok()) {
106          cnt++;
107          if (20 <= cnt) {
108             HMI_ERROR("wm", "Could not connect to compositor");
109             goto error;
110          }
111          HMI_ERROR("wm", "Wait to start weston ...");
112          sleep(1);
113          delete g_afb_instance;
114          g_afb_instance = new afb_instance;
115       }
116    }
117
118    if (g_afb_instance->init() == -1) {
119       HMI_ERROR("wm", "Could not connect to compositor");
120       goto error;
121    }
122
123    {
124       int ret = sd_event_add_io(afb_daemon_get_event_loop(), nullptr,
125                                 g_afb_instance->display->get_fd(), EPOLLIN,
126                                 display_event_callback, g_afb_instance);
127       if (ret < 0) {
128          HMI_ERROR("wm", "Could not initialize afb_instance event handler: %d", -ret);
129          goto error;
130       }
131    }
132
133    atexit([] { delete g_afb_instance; });
134
135    return 0;
136
137 error:
138    delete g_afb_instance;
139    g_afb_instance = nullptr;
140    return -1;
141 }
142
143 int binding_init() noexcept {
144    try {
145       return _binding_init();
146    } catch (std::exception &e) {
147       HMI_ERROR("wm", "Uncaught exception in binding_init(): %s", e.what());
148    }
149    return -1;
150 }
151
152 void windowmanager_requestsurface(afb_req req) noexcept {
153    std::lock_guard<std::mutex> guard(binding_m);
154    #ifdef ST
155    ST();
156    #endif
157    if (g_afb_instance == nullptr) {
158       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
159       return;
160    }
161
162    try {
163    const char* a_drawing_name = afb_req_value(req, "drawing_name");
164    if(!a_drawing_name){
165        afb_req_fail(req, "failed", "Need char const* argument drawing_name");
166        return;
167    }
168
169    auto ret = g_afb_instance->app.api_request_surface(a_drawing_name);
170    if (ret.is_err()) {
171       afb_req_fail(req, "failed", ret.unwrap_err());
172       return;
173    }
174
175    afb_req_success(req, json_object_new_int(ret.unwrap()), "success");
176
177    } catch (std::exception &e) {
178       afb_req_fail_f(req, "failed", "Uncaught exception while calling requestsurface: %s", e.what());
179       return;
180    }
181
182 }
183
184 void windowmanager_requestsurfacexdg(afb_req req) noexcept {
185    std::lock_guard<std::mutex> guard(binding_m);
186    #ifdef ST
187    ST();
188    #endif
189    if (g_afb_instance == nullptr) {
190       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
191       return;
192    }
193
194    try {
195    json_object *jreq = afb_req_json(req);
196
197    json_object *j_drawing_name = nullptr;
198    if (! json_object_object_get_ex(jreq, "drawing_name", &j_drawing_name)) {
199       afb_req_fail(req, "failed", "Need char const* argument drawing_name");
200       return;
201    }
202    char const* a_drawing_name = json_object_get_string(j_drawing_name);
203
204    json_object *j_ivi_id = nullptr;
205    if (! json_object_object_get_ex(jreq, "ivi_id", &j_ivi_id)) {
206       afb_req_fail(req, "failed", "Need char const* argument ivi_id");
207       return;
208    }
209    char const* a_ivi_id = json_object_get_string(j_ivi_id);
210
211    auto ret = g_afb_instance->app.api_request_surface(a_drawing_name, a_ivi_id);
212    if (ret != nullptr) {
213       afb_req_fail(req, "failed", ret);
214       return;
215    }
216
217    afb_req_success(req, NULL, "success");
218    } catch (std::exception &e) {
219       afb_req_fail_f(req, "failed", "Uncaught exception while calling requestsurfacexdg: %s", e.what());
220       return;
221    }
222 }
223
224 void windowmanager_activatesurface(afb_req req) noexcept {
225    std::lock_guard<std::mutex> guard(binding_m);
226    #ifdef ST
227    ST();
228    #endif
229    if (g_afb_instance == nullptr) {
230       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
231       return;
232    }
233
234    try {
235    const char* a_drawing_name = afb_req_value(req, "drawing_name");
236    if(!a_drawing_name){
237        afb_req_fail(req, "failed", "Need char const* argument drawing_name");
238        return;
239    }
240
241    const char* a_drawing_area = afb_req_value(req, "drawing_area");
242    if(!a_drawing_area){
243        afb_req_fail(req, "failed", "Need char const* argument drawing_area");
244        return;
245    }
246
247    auto ret = g_afb_instance->app.api_activate_surface(a_drawing_name, a_drawing_area);
248    if (ret != nullptr) {
249       afb_req_fail(req, "failed", ret);
250       return;
251    }
252
253    afb_req_success(req, NULL, "success");
254    } catch (std::exception &e) {
255       afb_req_fail_f(req, "failed", "Uncaught exception while calling activatesurface: %s", e.what());
256       return;
257    }
258
259 }
260
261 void windowmanager_deactivatesurface(afb_req req) noexcept {
262    std::lock_guard<std::mutex> guard(binding_m);
263    #ifdef ST
264    ST();
265    #endif
266    if (g_afb_instance == nullptr) {
267       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
268       return;
269    }
270
271    try {
272    const char* a_drawing_name = afb_req_value(req, "drawing_name");
273    if(!a_drawing_name){
274        afb_req_fail(req, "failed", "Need char const* argument drawing_name");
275        return;
276    }
277
278    auto ret = g_afb_instance->app.api_deactivate_surface(a_drawing_name);
279    if (ret != nullptr) {
280       afb_req_fail(req, "failed", ret);
281       return;
282    }
283
284    afb_req_success(req, NULL, "success");
285    } catch (std::exception &e) {
286       afb_req_fail_f(req, "failed", "Uncaught exception while calling deactivatesurface: %s", e.what());
287       return;
288    }
289 }
290
291 void windowmanager_enddraw(afb_req req) noexcept {
292    std::lock_guard<std::mutex> guard(binding_m);
293    #ifdef ST
294    ST();
295    #endif
296    if (g_afb_instance == nullptr) {
297       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
298       return;
299    }
300
301    try {
302    const char* a_drawing_name = afb_req_value(req, "drawing_name");
303    if(!a_drawing_name){
304        afb_req_fail(req, "failed", "Need char const* argument drawing_name");
305        return;
306    }
307
308    auto ret = g_afb_instance->app.api_enddraw(a_drawing_name);
309    if (ret != nullptr) {
310       afb_req_fail(req, "failed", ret);
311       return;
312    }
313
314    afb_req_success(req, NULL, "success");
315    } catch (std::exception &e) {
316       afb_req_fail_f(req, "failed", "Uncaught exception while calling enddraw: %s", e.what());
317       return;
318    }
319
320 }
321
322 void windowmanager_wm_subscribe(afb_req req) noexcept {
323    std::lock_guard<std::mutex> guard(binding_m);
324    #ifdef ST
325    ST();
326    #endif
327    if (g_afb_instance == nullptr) {
328       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
329       return;
330    }
331
332    try {
333    json_object *jreq = afb_req_json(req);
334    json_object *j = nullptr;
335    if (! json_object_object_get_ex(jreq, "event", &j)) {
336       afb_req_fail(req, "failed", "Need char const* argument event");
337       return;
338    }
339    int event_type = json_object_get_int(j);
340    const char *event_name = g_afb_instance->app.kListEventName[event_type];
341    struct afb_event event = g_afb_instance->app.map_afb_event[event_name];
342    int ret = afb_req_subscribe(req, event);
343    if (ret) {
344       afb_req_fail(req, "failed", "Error: afb_req_subscribe()");
345       return;
346    }
347    afb_req_success(req, NULL, "success");
348
349    } catch (std::exception &e) {
350       afb_req_fail_f(req, "failed", "Uncaught exception while calling wm_subscribe: %s", e.what());
351       return;
352    }
353
354 }
355
356 void windowmanager_list_drawing_names(afb_req req) noexcept {
357    std::lock_guard<std::mutex> guard(binding_m);
358    #ifdef ST
359    ST();
360    #endif
361    if (g_afb_instance == nullptr) {
362       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
363       return;
364    }
365
366    try {
367
368    nlohmann::json j = g_afb_instance->app.id_alloc.name2id;
369    auto ret = wm::Ok(json_tokener_parse(j.dump().c_str()));
370    if (ret.is_err()) {
371       afb_req_fail(req, "failed", ret.unwrap_err());
372       return;
373    }
374
375    afb_req_success(req, ret.unwrap(), "success");
376
377    } catch (std::exception &e) {
378       afb_req_fail_f(req, "failed", "Uncaught exception while calling list_drawing_names: %s", e.what());
379       return;
380    }
381
382 }
383
384 void windowmanager_ping(afb_req req) noexcept {
385    std::lock_guard<std::mutex> guard(binding_m);
386    #ifdef ST
387    ST();
388    #endif
389    if (g_afb_instance == nullptr) {
390       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
391       return;
392    }
393
394    try {
395
396    g_afb_instance->app.api_ping();
397
398    afb_req_success(req, NULL, "success");
399
400    } catch (std::exception &e) {
401       afb_req_fail_f(req, "failed", "Uncaught exception while calling ping: %s", e.what());
402       return;
403    }
404 }
405
406 void windowmanager_debug_status(afb_req req) noexcept {
407    std::lock_guard<std::mutex> guard(binding_m);
408    #ifdef ST
409    ST();
410    #endif
411    if (g_afb_instance == nullptr) {
412       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
413       return;
414    }
415
416    try {
417
418    json_object *jr = json_object_new_object();
419    json_object_object_add(jr, "surfaces",
420                           to_json(g_afb_instance->app.controller->sprops));
421    json_object_object_add(jr, "layers", to_json(g_afb_instance->app.controller->lprops));
422
423    afb_req_success(req, jr, "success");
424
425    } catch (std::exception &e) {
426       afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_status: %s", e.what());
427       return;
428    }
429 }
430
431 void windowmanager_debug_layers(afb_req req) noexcept {
432    std::lock_guard<std::mutex> guard(binding_m);
433    #ifdef ST
434    ST();
435    #endif
436    if (g_afb_instance == nullptr) {
437       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
438       return;
439    }
440
441    try {
442    auto ret = wm::Ok(json_tokener_parse(g_afb_instance->app.layers.to_json().dump().c_str()));
443
444    afb_req_success(req, ret, "success");
445
446    } catch (std::exception &e) {
447       afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_layers: %s", e.what());
448       return;
449    }
450 }
451
452 void windowmanager_debug_surfaces(afb_req req) noexcept {
453    std::lock_guard<std::mutex> guard(binding_m);
454    #ifdef ST
455    ST();
456    #endif
457    if (g_afb_instance == nullptr) {
458       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
459       return;
460    }
461
462    try {
463
464    auto ret = wm::Ok(to_json(g_afb_instance->app.controller->sprops));
465    if (ret.is_err()) {
466       afb_req_fail(req, "failed", ret.unwrap_err());
467       return;
468    }
469
470    afb_req_success(req, ret.unwrap(), "success");
471
472    } catch (std::exception &e) {
473       afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_surfaces: %s", e.what());
474       return;
475    }
476
477 }
478
479 void windowmanager_debug_terminate(afb_req req) noexcept {
480    std::lock_guard<std::mutex> guard(binding_m);
481    #ifdef ST
482    ST();
483    #endif
484    if (g_afb_instance == nullptr) {
485       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
486       return;
487    }
488
489    try {
490
491    if (getenv("WINMAN_DEBUG_TERMINATE") != nullptr) {
492       raise(SIGKILL);  // afb-daemon kills it's pgroup using TERM, which
493                        // doesn't play well with perf
494    }
495
496    afb_req_success(req, NULL, "success");
497
498    } catch (std::exception &e) {
499       afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_terminate: %s", e.what());
500       return;
501    }
502
503 }
504
505 const struct afb_verb_v2 windowmanager_verbs[] = {
506    { "requestsurface", windowmanager_requestsurface, nullptr, nullptr, AFB_SESSION_NONE },
507    { "requestsurfacexdg", windowmanager_requestsurfacexdg, nullptr, nullptr, AFB_SESSION_NONE },
508    { "activatesurface", windowmanager_activatesurface, nullptr, nullptr, AFB_SESSION_NONE },
509    { "deactivatesurface", windowmanager_deactivatesurface, nullptr, nullptr, AFB_SESSION_NONE },
510    { "enddraw", windowmanager_enddraw, nullptr, nullptr, AFB_SESSION_NONE },
511    { "wm_subscribe", windowmanager_wm_subscribe, nullptr, nullptr, AFB_SESSION_NONE },
512    { "list_drawing_names", windowmanager_list_drawing_names, nullptr, nullptr, AFB_SESSION_NONE },
513    { "ping", windowmanager_ping, nullptr, nullptr, AFB_SESSION_NONE },
514    { "debug_status", windowmanager_debug_status, nullptr, nullptr, AFB_SESSION_NONE },
515    { "debug_layers", windowmanager_debug_layers, nullptr, nullptr, AFB_SESSION_NONE },
516    { "debug_surfaces", windowmanager_debug_surfaces, nullptr, nullptr, AFB_SESSION_NONE },
517    { "debug_terminate", windowmanager_debug_terminate, nullptr, nullptr, AFB_SESSION_NONE },
518    {}
519 };
520
521 extern "C" const struct afb_binding_v2 afbBindingV2 = {
522    "windowmanager", nullptr, nullptr, windowmanager_verbs, nullptr, binding_init, nullptr, 0};