[Local]:4th step for blocking sequence
[apps/agl-service-windowmanager.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(
213        afb_req_get_application_id(req), a_drawing_name);
214
215    if(isFirstReq){
216        wmClientCtxt* ctxt = new wmClientCtxt(a_drawing_name);
217        HMI_DEBUG("wm", "create session for %s", ctxt->name.c_str());
218        afb_req_session_set_LOA(req, 1);
219        afb_req_context_set(req, ctxt, cbRemoveClientCtxt);
220    }
221    else{
222        HMI_DEBUG("wm", "session already created for %s", a_drawing_name);
223    }
224
225    if (ret.is_err()) {
226       afb_req_fail(req, "failed", ret.unwrap_err());
227       return;
228    }
229
230    afb_req_success(req, json_object_new_int(ret.unwrap()), "success");
231
232    } catch (std::exception &e) {
233       afb_req_fail_f(req, "failed", "Uncaught exception while calling requestsurface: %s", e.what());
234       return;
235    }
236
237 }
238
239 void windowmanager_requestsurfacexdg(afb_req req) noexcept {
240    std::lock_guard<std::mutex> guard(binding_m);
241    #ifdef ST
242    ST();
243    #endif
244    if (g_afb_instance == nullptr) {
245       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
246       return;
247    }
248
249    try {
250    json_object *jreq = afb_req_json(req);
251
252    json_object *j_drawing_name = nullptr;
253    if (! json_object_object_get_ex(jreq, "drawing_name", &j_drawing_name)) {
254       afb_req_fail(req, "failed", "Need char const* argument drawing_name");
255       return;
256    }
257    char const* a_drawing_name = json_object_get_string(j_drawing_name);
258
259    json_object *j_ivi_id = nullptr;
260    if (! json_object_object_get_ex(jreq, "ivi_id", &j_ivi_id)) {
261       afb_req_fail(req, "failed", "Need char const* argument ivi_id");
262       return;
263    }
264    char const* a_ivi_id = json_object_get_string(j_ivi_id);
265
266    auto ret = g_afb_instance->app.api_request_surface(
267        afb_req_get_application_id(req), a_drawing_name, a_ivi_id);
268    if (ret != nullptr) {
269       afb_req_fail(req, "failed", ret);
270       return;
271    }
272
273    afb_req_success(req, NULL, "success");
274    } catch (std::exception &e) {
275       afb_req_fail_f(req, "failed", "Uncaught exception while calling requestsurfacexdg: %s", e.what());
276       return;
277    }
278 }
279
280 void windowmanager_activatesurface(afb_req req) noexcept {
281    std::lock_guard<std::mutex> guard(binding_m);
282    #ifdef ST
283    ST();
284    #endif
285    if (g_afb_instance == nullptr) {
286       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
287       return;
288    }
289
290    try {
291    const char* a_drawing_name = afb_req_value(req, "drawing_name");
292    if(!a_drawing_name){
293        afb_req_fail(req, "failed", "Need char const* argument drawing_name");
294        return;
295    }
296
297    const char* a_drawing_area = afb_req_value(req, "drawing_area");
298    if(!a_drawing_area){
299        afb_req_fail(req, "failed", "Need char const* argument drawing_area");
300        return;
301    }
302
303    g_afb_instance->app.api_activate_surface(
304       afb_req_get_application_id(req),
305       a_drawing_name, a_drawing_area,
306       [&req](const char* errmsg){
307       if (errmsg != nullptr) {
308          HMI_ERROR("wm", errmsg);
309          afb_req_fail(req, "failed", errmsg);
310          return;
311       }
312       afb_req_success(req, NULL, "success");
313    });
314
315    } catch (std::exception &e) {
316       HMI_WARNING("wm", "failed", "Uncaught exception while calling activatesurface: %s", e.what());
317       return;
318    }
319
320 }
321
322 void windowmanager_deactivatesurface(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    const char* a_drawing_name = afb_req_value(req, "drawing_name");
334    if(!a_drawing_name){
335        afb_req_fail(req, "failed", "Need char const* argument drawing_name");
336        return;
337    }
338
339    g_afb_instance->app.api_deactivate_surface(
340        afb_req_get_application_id(req), a_drawing_name,
341       [&req](const char* errmsg){
342       if (errmsg != nullptr) {
343          HMI_ERROR("wm", errmsg);
344          afb_req_fail(req, "failed", errmsg);
345          return;
346       }
347       afb_req_success(req, NULL, "success");
348    });
349
350    } catch (std::exception &e) {
351       HMI_WARNING("wm", "Uncaught exception while calling deactivatesurface: %s", e.what());
352       return;
353    }
354 }
355
356 void windowmanager_enddraw(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    const char* a_drawing_name = afb_req_value(req, "drawing_name");
368    if(!a_drawing_name){
369        afb_req_fail(req, "failed", "Need char const* argument drawing_name");
370        return;
371    }
372    afb_req_success(req, NULL, "success");
373
374    g_afb_instance->app.api_enddraw(
375        afb_req_get_application_id(req), a_drawing_name);
376
377    } catch (std::exception &e) {
378       HMI_WARNING("wm", "failed", "Uncaught exception while calling enddraw: %s", e.what());
379       return;
380    }
381
382 }
383
384 void windowmanager_getdisplayinfo_thunk(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    auto ret = g_afb_instance->app.api_get_display_info();
396    if (ret.is_err()) {
397       afb_req_fail(req, "failed", ret.unwrap_err());
398       return;
399    }
400
401    afb_req_success(req, ret.unwrap(), "success");
402    } catch (std::exception &e) {
403       afb_req_fail_f(req, "failed", "Uncaught exception while calling getdisplayinfo: %s", e.what());
404       return;
405    }
406
407 }
408
409 void windowmanager_getareainfo_thunk(afb_req req) noexcept {
410    std::lock_guard<std::mutex> guard(binding_m);
411    #ifdef ST
412    ST();
413    #endif
414    if (g_afb_instance == nullptr) {
415       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
416       return;
417    }
418
419    try {
420    json_object *jreq = afb_req_json(req);
421
422    json_object *j_drawing_name = nullptr;
423    if (! json_object_object_get_ex(jreq, "drawing_name", &j_drawing_name)) {
424       afb_req_fail(req, "failed", "Need char const* argument drawing_name");
425       return;
426    }
427    char const* a_drawing_name = json_object_get_string(j_drawing_name);
428
429    auto ret = g_afb_instance->app.api_get_area_info(a_drawing_name);
430    if (ret.is_err()) {
431       afb_req_fail(req, "failed", ret.unwrap_err());
432       return;
433    }
434
435    afb_req_success(req, ret.unwrap(), "success");
436    } catch (std::exception &e) {
437       afb_req_fail_f(req, "failed", "Uncaught exception while calling getareainfo: %s", e.what());
438       return;
439    }
440
441 }
442
443 void windowmanager_wm_subscribe(afb_req req) noexcept {
444    std::lock_guard<std::mutex> guard(binding_m);
445    #ifdef ST
446    ST();
447    #endif
448    if (g_afb_instance == nullptr) {
449       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
450       return;
451    }
452
453    try {
454    json_object *jreq = afb_req_json(req);
455    json_object *j = nullptr;
456    if (! json_object_object_get_ex(jreq, "event", &j)) {
457       afb_req_fail(req, "failed", "Need char const* argument event");
458       return;
459    }
460    int event_type = json_object_get_int(j);
461    const char *event_name = g_afb_instance->app.kListEventName[event_type];
462    struct afb_event event = g_afb_instance->app.map_afb_event[event_name];
463    int ret = afb_req_subscribe(req, event);
464    if (ret) {
465       afb_req_fail(req, "failed", "Error: afb_req_subscribe()");
466       return;
467    }
468    afb_req_success(req, NULL, "success");
469
470    } catch (std::exception &e) {
471       afb_req_fail_f(req, "failed", "Uncaught exception while calling wm_subscribe: %s", e.what());
472       return;
473    }
474
475 }
476
477 void windowmanager_list_drawing_names(afb_req req) noexcept {
478    std::lock_guard<std::mutex> guard(binding_m);
479    #ifdef ST
480    ST();
481    #endif
482    if (g_afb_instance == nullptr) {
483       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
484       return;
485    }
486
487    try {
488
489    nlohmann::json j = g_afb_instance->app.id_alloc.name2id;
490    auto ret = wm::Ok(json_tokener_parse(j.dump().c_str()));
491    if (ret.is_err()) {
492       afb_req_fail(req, "failed", ret.unwrap_err());
493       return;
494    }
495
496    afb_req_success(req, ret.unwrap(), "success");
497
498    } catch (std::exception &e) {
499       afb_req_fail_f(req, "failed", "Uncaught exception while calling list_drawing_names: %s", e.what());
500       return;
501    }
502
503 }
504
505 void windowmanager_ping(afb_req req) noexcept {
506    std::lock_guard<std::mutex> guard(binding_m);
507    #ifdef ST
508    ST();
509    #endif
510    if (g_afb_instance == nullptr) {
511       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
512       return;
513    }
514
515    try {
516
517    g_afb_instance->app.api_ping();
518
519    afb_req_success(req, NULL, "success");
520
521    } catch (std::exception &e) {
522       afb_req_fail_f(req, "failed", "Uncaught exception while calling ping: %s", e.what());
523       return;
524    }
525 }
526
527 void windowmanager_debug_status(afb_req req) noexcept {
528    std::lock_guard<std::mutex> guard(binding_m);
529    #ifdef ST
530    ST();
531    #endif
532    if (g_afb_instance == nullptr) {
533       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
534       return;
535    }
536
537    try {
538
539    json_object *jr = json_object_new_object();
540    json_object_object_add(jr, "surfaces",
541                           to_json(g_afb_instance->app.controller->sprops));
542    json_object_object_add(jr, "layers", to_json(g_afb_instance->app.controller->lprops));
543
544    afb_req_success(req, jr, "success");
545
546    } catch (std::exception &e) {
547       afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_status: %s", e.what());
548       return;
549    }
550 }
551
552 void windowmanager_debug_layers(afb_req req) noexcept {
553    std::lock_guard<std::mutex> guard(binding_m);
554    #ifdef ST
555    ST();
556    #endif
557    if (g_afb_instance == nullptr) {
558       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
559       return;
560    }
561
562    try {
563    auto ret = wm::Ok(json_tokener_parse(g_afb_instance->app.layers.to_json().dump().c_str()));
564
565    afb_req_success(req, ret, "success");
566
567    } catch (std::exception &e) {
568       afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_layers: %s", e.what());
569       return;
570    }
571 }
572
573 void windowmanager_debug_surfaces(afb_req req) noexcept {
574    std::lock_guard<std::mutex> guard(binding_m);
575    #ifdef ST
576    ST();
577    #endif
578    if (g_afb_instance == nullptr) {
579       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
580       return;
581    }
582
583    try {
584
585    auto ret = wm::Ok(to_json(g_afb_instance->app.controller->sprops));
586    if (ret.is_err()) {
587       afb_req_fail(req, "failed", ret.unwrap_err());
588       return;
589    }
590
591    afb_req_success(req, ret.unwrap(), "success");
592
593    } catch (std::exception &e) {
594       afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_surfaces: %s", e.what());
595       return;
596    }
597
598 }
599
600 void windowmanager_debug_terminate(afb_req req) noexcept {
601    std::lock_guard<std::mutex> guard(binding_m);
602    #ifdef ST
603    ST();
604    #endif
605    if (g_afb_instance == nullptr) {
606       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
607       return;
608    }
609
610    try {
611
612    if (getenv("WINMAN_DEBUG_TERMINATE") != nullptr) {
613       raise(SIGKILL);  // afb-daemon kills it's pgroup using TERM, which
614                        // doesn't play well with perf
615    }
616
617    afb_req_success(req, NULL, "success");
618
619    } catch (std::exception &e) {
620       afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_terminate: %s", e.what());
621       return;
622    }
623
624 }
625
626 const struct afb_verb_v2 windowmanager_verbs[] = {
627    { "requestsurface", windowmanager_requestsurface, nullptr, nullptr, AFB_SESSION_NONE },
628    { "requestsurfacexdg", windowmanager_requestsurfacexdg, nullptr, nullptr, AFB_SESSION_NONE },
629    { "activatesurface", windowmanager_activatesurface, nullptr, nullptr, AFB_SESSION_LOA_1 },
630    { "deactivatesurface", windowmanager_deactivatesurface, nullptr, nullptr, AFB_SESSION_LOA_1 },
631    { "enddraw", windowmanager_enddraw, nullptr, nullptr, AFB_SESSION_LOA_1 },
632    { "getdisplayinfo", windowmanager_getdisplayinfo_thunk, nullptr, nullptr, AFB_SESSION_NONE },
633    { "getareainfo", windowmanager_getareainfo_thunk, nullptr, nullptr, AFB_SESSION_LOA_1 },
634    { "wm_subscribe", windowmanager_wm_subscribe, nullptr, nullptr, AFB_SESSION_NONE },
635    { "list_drawing_names", windowmanager_list_drawing_names, nullptr, nullptr, AFB_SESSION_NONE },
636    { "ping", windowmanager_ping, nullptr, nullptr, AFB_SESSION_NONE },
637    { "debug_status", windowmanager_debug_status, nullptr, nullptr, AFB_SESSION_NONE },
638    { "debug_layers", windowmanager_debug_layers, nullptr, nullptr, AFB_SESSION_NONE },
639    { "debug_surfaces", windowmanager_debug_surfaces, nullptr, nullptr, AFB_SESSION_NONE },
640    { "debug_terminate", windowmanager_debug_terminate, nullptr, nullptr, AFB_SESSION_NONE },
641    {}
642 };
643
644 extern "C" const struct afb_binding_v2 afbBindingV2 = {
645    "windowmanager", nullptr, nullptr, windowmanager_verbs, nullptr, binding_init, nullptr, 0};