Change reply timing
[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_ivi_wm.hpp"
27
28 extern "C" {
29 #include <afb/afb-binding.h>
30 #include <systemd/sd-event.h>
31 }
32
33 typedef struct wmClientCtxt{
34     std::string name;
35     wmClientCtxt(const char* appName){
36         name = appName;
37     }
38 } wmClientCtxt;
39
40 struct afb_instance {
41    std::unique_ptr<wl::display> display;
42    wm::App app;
43
44    afb_instance() : display{new wl::display}, app{this->display.get()} {}
45
46    int init();
47 };
48
49 struct afb_instance *g_afb_instance;
50 std::mutex binding_m;
51
52 int afb_instance::init() {
53    return this->app.init();
54 }
55
56 int display_event_callback(sd_event_source *evs, int /*fd*/, uint32_t events,
57                            void * /*data*/) {
58    ST();
59
60    if ((events & EPOLLHUP) != 0) {
61       HMI_ERROR("wm", "The compositor hung up, dying now.");
62       delete g_afb_instance;
63       g_afb_instance = nullptr;
64       goto error;
65    }
66
67    if ((events & EPOLLIN) != 0u) {
68       {
69          STN(display_read_events);
70          g_afb_instance->app.display->read_events();
71          g_afb_instance->app.set_pending_events();
72       }
73       {
74          // We want do dispatch pending wayland events from within
75          // the API context
76          STN(winman_ping_api_call);
77          afb_service_call("windowmanager", "ping", json_object_new_object(),
78                           [](void *c, int st, json_object *j) {
79                              STN(winman_ping_api_call_return);
80                           },
81                           nullptr);
82       }
83    }
84
85    return 0;
86
87 error:
88    sd_event_source_unref(evs);
89    if (getenv("WINMAN_EXIT_ON_HANGUP") != nullptr) {
90    exit(1);
91 }
92    return -1;
93 }
94
95 int _binding_init() {
96    HMI_NOTICE("wm", "WinMan ver. %s", WINMAN_VERSION_STRING);
97
98    if (g_afb_instance != nullptr) {
99       HMI_ERROR("wm", "Wayland context already initialized?");
100       return 0;
101    }
102
103    if (getenv("XDG_RUNTIME_DIR") == nullptr) {
104       HMI_ERROR("wm", "Environment variable XDG_RUNTIME_DIR not set");
105       goto error;
106    }
107
108    {
109       // wait until wayland compositor starts up.
110       int cnt = 0;
111       g_afb_instance = new afb_instance;
112       while (!g_afb_instance->display->ok()) {
113          cnt++;
114          if (20 <= cnt) {
115             HMI_ERROR("wm", "Could not connect to compositor");
116             goto error;
117          }
118          HMI_ERROR("wm", "Wait to start weston ...");
119          sleep(1);
120          delete g_afb_instance;
121          g_afb_instance = new afb_instance;
122       }
123    }
124
125    if (g_afb_instance->init() == -1) {
126       HMI_ERROR("wm", "Could not connect to compositor");
127       goto error;
128    }
129
130    {
131       int ret = sd_event_add_io(afb_daemon_get_event_loop(), nullptr,
132                                 g_afb_instance->display->get_fd(), EPOLLIN,
133                                 display_event_callback, g_afb_instance);
134       if (ret < 0) {
135          HMI_ERROR("wm", "Could not initialize afb_instance event handler: %d", -ret);
136          goto error;
137       }
138    }
139
140    atexit([] { delete g_afb_instance; });
141
142    return 0;
143
144 error:
145    delete g_afb_instance;
146    g_afb_instance = nullptr;
147    return -1;
148 }
149
150 int binding_init() noexcept {
151    try {
152       return _binding_init();
153    } catch (std::exception &e) {
154       HMI_ERROR("wm", "Uncaught exception in binding_init(): %s", e.what());
155    }
156    return -1;
157 }
158
159 static bool checkFirstReq(afb_req req){
160     wmClientCtxt* ctxt = (wmClientCtxt*)afb_req_context_get(req);
161     return (ctxt) ? false : true;
162 }
163
164 static void cbRemoveClientCtxt(void* data){
165     wmClientCtxt* ctxt = (wmClientCtxt*)data;
166     if(ctxt == nullptr){
167         return;
168     }
169     HMI_DEBUG("wm","remove app %s", ctxt->name.c_str());
170     // Lookup surfaceID and remove it because App is dead.
171     auto pSid = g_afb_instance->app.id_alloc.lookup(ctxt->name.c_str());
172     if(pSid){
173         auto sid = *pSid;
174         g_afb_instance->app.id_alloc.remove_id(sid);
175         g_afb_instance->app.layers.remove_surface(sid);
176         g_afb_instance->app.controller->sprops.erase(sid);
177         g_afb_instance->app.controller->surfaces.erase(sid);
178         HMI_DEBUG("wm", "delete surfaceID %d", sid);
179     }
180     delete ctxt;
181 }
182
183 void windowmanager_requestsurface(afb_req req) noexcept {
184    std::lock_guard<std::mutex> guard(binding_m);
185    #ifdef ST
186    ST();
187    #endif
188    if (g_afb_instance == nullptr) {
189       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
190       return;
191    }
192
193    try {
194    const char* a_drawing_name = afb_req_value(req, "drawing_name");
195    if(!a_drawing_name){
196        afb_req_fail(req, "failed", "Need char const* argument drawing_name");
197        return;
198    }
199
200    /* Create Security Context */
201    bool isFirstReq = checkFirstReq(req);
202    if(!isFirstReq){
203        wmClientCtxt* ctxt = (wmClientCtxt*)afb_req_context_get(req);
204        HMI_DEBUG("wm", "You're %s.", ctxt->name.c_str());
205        if(ctxt->name != std::string(a_drawing_name)){
206            afb_req_fail_f(req, "failed", "Dont request with other name: %s for now", a_drawing_name);
207            HMI_DEBUG("wm", "Don't request with other name: %s for now", a_drawing_name);
208            return;
209        }
210    }
211
212    auto ret = g_afb_instance->app.api_request_surface(a_drawing_name);
213
214    if(isFirstReq){
215        wmClientCtxt* ctxt = new wmClientCtxt(a_drawing_name);
216        HMI_DEBUG("wm", "create session for %s", ctxt->name.c_str());
217        afb_req_session_set_LOA(req, 1);
218        afb_req_context_set(req, ctxt, cbRemoveClientCtxt);
219    }
220    else{
221        HMI_DEBUG("wm", "session already created for %s", a_drawing_name);
222    }
223
224    if (ret.is_err()) {
225       afb_req_fail(req, "failed", ret.unwrap_err());
226       return;
227    }
228
229    afb_req_success(req, json_object_new_int(ret.unwrap()), "success");
230
231    } catch (std::exception &e) {
232       afb_req_fail_f(req, "failed", "Uncaught exception while calling requestsurface: %s", e.what());
233       return;
234    }
235
236 }
237
238 void windowmanager_requestsurfacexdg(afb_req req) noexcept {
239    std::lock_guard<std::mutex> guard(binding_m);
240    #ifdef ST
241    ST();
242    #endif
243    if (g_afb_instance == nullptr) {
244       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
245       return;
246    }
247
248    try {
249    json_object *jreq = afb_req_json(req);
250
251    json_object *j_drawing_name = nullptr;
252    if (! json_object_object_get_ex(jreq, "drawing_name", &j_drawing_name)) {
253       afb_req_fail(req, "failed", "Need char const* argument drawing_name");
254       return;
255    }
256    char const* a_drawing_name = json_object_get_string(j_drawing_name);
257
258    json_object *j_ivi_id = nullptr;
259    if (! json_object_object_get_ex(jreq, "ivi_id", &j_ivi_id)) {
260       afb_req_fail(req, "failed", "Need char const* argument ivi_id");
261       return;
262    }
263    char const* a_ivi_id = json_object_get_string(j_ivi_id);
264
265    auto ret = g_afb_instance->app.api_request_surface(a_drawing_name, a_ivi_id);
266    if (ret != nullptr) {
267       afb_req_fail(req, "failed", ret);
268       return;
269    }
270
271    afb_req_success(req, NULL, "success");
272    } catch (std::exception &e) {
273       afb_req_fail_f(req, "failed", "Uncaught exception while calling requestsurfacexdg: %s", e.what());
274       return;
275    }
276 }
277
278 void windowmanager_activatesurface(afb_req req) noexcept {
279    std::lock_guard<std::mutex> guard(binding_m);
280    #ifdef ST
281    ST();
282    #endif
283    if (g_afb_instance == nullptr) {
284       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
285       return;
286    }
287
288    try {
289    const char* a_drawing_name = afb_req_value(req, "drawing_name");
290    if(!a_drawing_name){
291        afb_req_fail(req, "failed", "Need char const* argument drawing_name");
292        return;
293    }
294
295    const char* a_drawing_area = afb_req_value(req, "drawing_area");
296    if(!a_drawing_area){
297        afb_req_fail(req, "failed", "Need char const* argument drawing_area");
298        return;
299    }
300
301    g_afb_instance->app.api_activate_surface(a_drawing_name, a_drawing_area,
302       [&req](const char* errmsg){
303       if (errmsg != nullptr) {
304          HMI_ERROR("wm", errmsg);
305          afb_req_fail(req, "failed", errmsg);
306          return;
307       }
308       afb_req_success(req, NULL, "success");
309    });
310
311    } catch (std::exception &e) {
312       HMI_WARNING("wm", "failed", "Uncaught exception while calling activatesurface: %s", e.what());
313       return;
314    }
315
316 }
317
318 void windowmanager_deactivatesurface(afb_req req) noexcept {
319    std::lock_guard<std::mutex> guard(binding_m);
320    #ifdef ST
321    ST();
322    #endif
323    if (g_afb_instance == nullptr) {
324       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
325       return;
326    }
327
328    try {
329    const char* a_drawing_name = afb_req_value(req, "drawing_name");
330    if(!a_drawing_name){
331        afb_req_fail(req, "failed", "Need char const* argument drawing_name");
332        return;
333    }
334
335    g_afb_instance->app.api_deactivate_surface(a_drawing_name,
336       [&req](const char* errmsg){
337       if (errmsg != nullptr) {
338          HMI_ERROR("wm", errmsg);
339          afb_req_fail(req, "failed", errmsg);
340          return;
341       }
342       afb_req_success(req, NULL, "success");
343    });
344
345    } catch (std::exception &e) {
346       HMI_WARNING("wm", "Uncaught exception while calling deactivatesurface: %s", e.what());
347       return;
348    }
349 }
350
351 void windowmanager_enddraw(afb_req req) noexcept {
352    std::lock_guard<std::mutex> guard(binding_m);
353    #ifdef ST
354    ST();
355    #endif
356    if (g_afb_instance == nullptr) {
357       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
358       return;
359    }
360
361    try {
362    const char* a_drawing_name = afb_req_value(req, "drawing_name");
363    if(!a_drawing_name){
364        afb_req_fail(req, "failed", "Need char const* argument drawing_name");
365        return;
366    }
367    afb_req_success(req, NULL, "success");
368
369    g_afb_instance->app.api_enddraw(a_drawing_name);
370
371    } catch (std::exception &e) {
372       HMI_WARNING("wm", "failed", "Uncaught exception while calling enddraw: %s", e.what());
373       return;
374    }
375
376 }
377
378 void windowmanager_getdisplayinfo_thunk(afb_req req) noexcept {
379    std::lock_guard<std::mutex> guard(binding_m);
380    #ifdef ST
381    ST();
382    #endif
383    if (g_afb_instance == nullptr) {
384       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
385       return;
386    }
387
388    try {
389    auto ret = g_afb_instance->app.api_get_display_info();
390    if (ret.is_err()) {
391       afb_req_fail(req, "failed", ret.unwrap_err());
392       return;
393    }
394
395    afb_req_success(req, ret.unwrap(), "success");
396    } catch (std::exception &e) {
397       afb_req_fail_f(req, "failed", "Uncaught exception while calling getdisplayinfo: %s", e.what());
398       return;
399    }
400
401 }
402
403 void windowmanager_getareainfo_thunk(afb_req req) noexcept {
404    std::lock_guard<std::mutex> guard(binding_m);
405    #ifdef ST
406    ST();
407    #endif
408    if (g_afb_instance == nullptr) {
409       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
410       return;
411    }
412
413    try {
414    json_object *jreq = afb_req_json(req);
415
416    json_object *j_drawing_name = nullptr;
417    if (! json_object_object_get_ex(jreq, "drawing_name", &j_drawing_name)) {
418       afb_req_fail(req, "failed", "Need char const* argument drawing_name");
419       return;
420    }
421    char const* a_drawing_name = json_object_get_string(j_drawing_name);
422
423    auto ret = g_afb_instance->app.api_get_area_info(a_drawing_name);
424    if (ret.is_err()) {
425       afb_req_fail(req, "failed", ret.unwrap_err());
426       return;
427    }
428
429    afb_req_success(req, ret.unwrap(), "success");
430    } catch (std::exception &e) {
431       afb_req_fail_f(req, "failed", "Uncaught exception while calling getareainfo: %s", e.what());
432       return;
433    }
434
435 }
436
437 void windowmanager_wm_subscribe(afb_req req) noexcept {
438    std::lock_guard<std::mutex> guard(binding_m);
439    #ifdef ST
440    ST();
441    #endif
442    if (g_afb_instance == nullptr) {
443       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
444       return;
445    }
446
447    try {
448    json_object *jreq = afb_req_json(req);
449    json_object *j = nullptr;
450    if (! json_object_object_get_ex(jreq, "event", &j)) {
451       afb_req_fail(req, "failed", "Need char const* argument event");
452       return;
453    }
454    int event_type = json_object_get_int(j);
455    const char *event_name = g_afb_instance->app.kListEventName[event_type];
456    struct afb_event event = g_afb_instance->app.map_afb_event[event_name];
457    int ret = afb_req_subscribe(req, event);
458    if (ret) {
459       afb_req_fail(req, "failed", "Error: afb_req_subscribe()");
460       return;
461    }
462    afb_req_success(req, NULL, "success");
463
464    } catch (std::exception &e) {
465       afb_req_fail_f(req, "failed", "Uncaught exception while calling wm_subscribe: %s", e.what());
466       return;
467    }
468
469 }
470
471 void windowmanager_list_drawing_names(afb_req req) noexcept {
472    std::lock_guard<std::mutex> guard(binding_m);
473    #ifdef ST
474    ST();
475    #endif
476    if (g_afb_instance == nullptr) {
477       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
478       return;
479    }
480
481    try {
482
483    nlohmann::json j = g_afb_instance->app.id_alloc.name2id;
484    auto ret = wm::Ok(json_tokener_parse(j.dump().c_str()));
485    if (ret.is_err()) {
486       afb_req_fail(req, "failed", ret.unwrap_err());
487       return;
488    }
489
490    afb_req_success(req, ret.unwrap(), "success");
491
492    } catch (std::exception &e) {
493       afb_req_fail_f(req, "failed", "Uncaught exception while calling list_drawing_names: %s", e.what());
494       return;
495    }
496
497 }
498
499 void windowmanager_ping(afb_req req) noexcept {
500    std::lock_guard<std::mutex> guard(binding_m);
501    #ifdef ST
502    ST();
503    #endif
504    if (g_afb_instance == nullptr) {
505       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
506       return;
507    }
508
509    try {
510
511    g_afb_instance->app.api_ping();
512
513    afb_req_success(req, NULL, "success");
514
515    } catch (std::exception &e) {
516       afb_req_fail_f(req, "failed", "Uncaught exception while calling ping: %s", e.what());
517       return;
518    }
519 }
520
521 void windowmanager_debug_status(afb_req req) noexcept {
522    std::lock_guard<std::mutex> guard(binding_m);
523    #ifdef ST
524    ST();
525    #endif
526    if (g_afb_instance == nullptr) {
527       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
528       return;
529    }
530
531    try {
532
533    json_object *jr = json_object_new_object();
534    json_object_object_add(jr, "surfaces",
535                           to_json(g_afb_instance->app.controller->sprops));
536    json_object_object_add(jr, "layers", to_json(g_afb_instance->app.controller->lprops));
537
538    afb_req_success(req, jr, "success");
539
540    } catch (std::exception &e) {
541       afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_status: %s", e.what());
542       return;
543    }
544 }
545
546 void windowmanager_debug_layers(afb_req req) noexcept {
547    std::lock_guard<std::mutex> guard(binding_m);
548    #ifdef ST
549    ST();
550    #endif
551    if (g_afb_instance == nullptr) {
552       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
553       return;
554    }
555
556    try {
557    auto ret = wm::Ok(json_tokener_parse(g_afb_instance->app.layers.to_json().dump().c_str()));
558
559    afb_req_success(req, ret, "success");
560
561    } catch (std::exception &e) {
562       afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_layers: %s", e.what());
563       return;
564    }
565 }
566
567 void windowmanager_debug_surfaces(afb_req req) noexcept {
568    std::lock_guard<std::mutex> guard(binding_m);
569    #ifdef ST
570    ST();
571    #endif
572    if (g_afb_instance == nullptr) {
573       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
574       return;
575    }
576
577    try {
578
579    auto ret = wm::Ok(to_json(g_afb_instance->app.controller->sprops));
580    if (ret.is_err()) {
581       afb_req_fail(req, "failed", ret.unwrap_err());
582       return;
583    }
584
585    afb_req_success(req, ret.unwrap(), "success");
586
587    } catch (std::exception &e) {
588       afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_surfaces: %s", e.what());
589       return;
590    }
591
592 }
593
594 void windowmanager_debug_terminate(afb_req req) noexcept {
595    std::lock_guard<std::mutex> guard(binding_m);
596    #ifdef ST
597    ST();
598    #endif
599    if (g_afb_instance == nullptr) {
600       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
601       return;
602    }
603
604    try {
605
606    if (getenv("WINMAN_DEBUG_TERMINATE") != nullptr) {
607       raise(SIGKILL);  // afb-daemon kills it's pgroup using TERM, which
608                        // doesn't play well with perf
609    }
610
611    afb_req_success(req, NULL, "success");
612
613    } catch (std::exception &e) {
614       afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_terminate: %s", e.what());
615       return;
616    }
617
618 }
619
620 const struct afb_verb_v2 windowmanager_verbs[] = {
621    { "requestsurface", windowmanager_requestsurface, nullptr, nullptr, AFB_SESSION_NONE },
622    { "requestsurfacexdg", windowmanager_requestsurfacexdg, nullptr, nullptr, AFB_SESSION_NONE },
623    { "activatesurface", windowmanager_activatesurface, nullptr, nullptr, AFB_SESSION_NONE },
624    { "deactivatesurface", windowmanager_deactivatesurface, nullptr, nullptr, AFB_SESSION_NONE },
625    { "enddraw", windowmanager_enddraw, nullptr, nullptr, AFB_SESSION_NONE },
626    { "getdisplayinfo", windowmanager_getdisplayinfo_thunk, nullptr, nullptr, AFB_SESSION_NONE },
627    { "getareainfo", windowmanager_getareainfo_thunk, nullptr, nullptr, AFB_SESSION_NONE },
628    { "wm_subscribe", windowmanager_wm_subscribe, nullptr, nullptr, AFB_SESSION_NONE },
629    { "list_drawing_names", windowmanager_list_drawing_names, nullptr, nullptr, AFB_SESSION_NONE },
630    { "ping", windowmanager_ping, nullptr, nullptr, AFB_SESSION_NONE },
631    { "debug_status", windowmanager_debug_status, nullptr, nullptr, AFB_SESSION_NONE },
632    { "debug_layers", windowmanager_debug_layers, nullptr, nullptr, AFB_SESSION_NONE },
633    { "debug_surfaces", windowmanager_debug_surfaces, nullptr, nullptr, AFB_SESSION_NONE },
634    { "debug_terminate", windowmanager_debug_terminate, nullptr, nullptr, AFB_SESSION_NONE },
635    {}
636 };
637
638 extern "C" const struct afb_binding_v2 afbBindingV2 = {
639    "windowmanager", nullptr, nullptr, windowmanager_verbs, nullptr, binding_init, nullptr, 0};