Add APIs which can get information about the display and the surface area
[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    auto ret = g_afb_instance->app.api_activate_surface(a_drawing_name, a_drawing_area);
302    if (ret != nullptr) {
303       afb_req_fail(req, "failed", ret);
304       return;
305    }
306
307    afb_req_success(req, NULL, "success");
308    } catch (std::exception &e) {
309       afb_req_fail_f(req, "failed", "Uncaught exception while calling activatesurface: %s", e.what());
310       return;
311    }
312
313 }
314
315 void windowmanager_deactivatesurface(afb_req req) noexcept {
316    std::lock_guard<std::mutex> guard(binding_m);
317    #ifdef ST
318    ST();
319    #endif
320    if (g_afb_instance == nullptr) {
321       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
322       return;
323    }
324
325    try {
326    const char* a_drawing_name = afb_req_value(req, "drawing_name");
327    if(!a_drawing_name){
328        afb_req_fail(req, "failed", "Need char const* argument drawing_name");
329        return;
330    }
331
332    auto ret = g_afb_instance->app.api_deactivate_surface(a_drawing_name);
333    if (ret != nullptr) {
334       afb_req_fail(req, "failed", ret);
335       return;
336    }
337
338    afb_req_success(req, NULL, "success");
339    } catch (std::exception &e) {
340       afb_req_fail_f(req, "failed", "Uncaught exception while calling deactivatesurface: %s", e.what());
341       return;
342    }
343 }
344
345 void windowmanager_enddraw(afb_req req) noexcept {
346    std::lock_guard<std::mutex> guard(binding_m);
347    #ifdef ST
348    ST();
349    #endif
350    if (g_afb_instance == nullptr) {
351       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
352       return;
353    }
354
355    try {
356    const char* a_drawing_name = afb_req_value(req, "drawing_name");
357    if(!a_drawing_name){
358        afb_req_fail(req, "failed", "Need char const* argument drawing_name");
359        return;
360    }
361
362    auto ret = g_afb_instance->app.api_enddraw(a_drawing_name);
363    if (ret != nullptr) {
364       afb_req_fail(req, "failed", ret);
365       return;
366    }
367
368    afb_req_success(req, NULL, "success");
369    } catch (std::exception &e) {
370       afb_req_fail_f(req, "failed", "Uncaught exception while calling enddraw: %s", e.what());
371       return;
372    }
373
374 }
375
376 void windowmanager_getdisplayinfo_thunk(afb_req req) noexcept {
377    std::lock_guard<std::mutex> guard(binding_m);
378    #ifdef ST
379    ST();
380    #endif
381    if (g_afb_instance == nullptr) {
382       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
383       return;
384    }
385
386    try {
387    auto ret = g_afb_instance->app.api_get_display_info();
388    if (ret.is_err()) {
389       afb_req_fail(req, "failed", ret.unwrap_err());
390       return;
391    }
392
393    afb_req_success(req, ret.unwrap(), "success");
394    } catch (std::exception &e) {
395       afb_req_fail_f(req, "failed", "Uncaught exception while calling getdisplayinfo: %s", e.what());
396       return;
397    }
398
399 }
400
401 void windowmanager_getareainfo_thunk(afb_req req) noexcept {
402    std::lock_guard<std::mutex> guard(binding_m);
403    #ifdef ST
404    ST();
405    #endif
406    if (g_afb_instance == nullptr) {
407       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
408       return;
409    }
410
411    try {
412    json_object *jreq = afb_req_json(req);
413
414    json_object *j_drawing_name = nullptr;
415    if (! json_object_object_get_ex(jreq, "drawing_name", &j_drawing_name)) {
416       afb_req_fail(req, "failed", "Need char const* argument drawing_name");
417       return;
418    }
419    char const* a_drawing_name = json_object_get_string(j_drawing_name);
420
421    auto ret = g_afb_instance->app.api_get_area_info(a_drawing_name);
422    if (ret.is_err()) {
423       afb_req_fail(req, "failed", ret.unwrap_err());
424       return;
425    }
426
427    afb_req_success(req, ret.unwrap(), "success");
428    } catch (std::exception &e) {
429       afb_req_fail_f(req, "failed", "Uncaught exception while calling getareainfo: %s", e.what());
430       return;
431    }
432
433 }
434
435 void windowmanager_wm_subscribe(afb_req req) noexcept {
436    std::lock_guard<std::mutex> guard(binding_m);
437    #ifdef ST
438    ST();
439    #endif
440    if (g_afb_instance == nullptr) {
441       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
442       return;
443    }
444
445    try {
446    json_object *jreq = afb_req_json(req);
447    json_object *j = nullptr;
448    if (! json_object_object_get_ex(jreq, "event", &j)) {
449       afb_req_fail(req, "failed", "Need char const* argument event");
450       return;
451    }
452    int event_type = json_object_get_int(j);
453    const char *event_name = g_afb_instance->app.kListEventName[event_type];
454    struct afb_event event = g_afb_instance->app.map_afb_event[event_name];
455    int ret = afb_req_subscribe(req, event);
456    if (ret) {
457       afb_req_fail(req, "failed", "Error: afb_req_subscribe()");
458       return;
459    }
460    afb_req_success(req, NULL, "success");
461
462    } catch (std::exception &e) {
463       afb_req_fail_f(req, "failed", "Uncaught exception while calling wm_subscribe: %s", e.what());
464       return;
465    }
466
467 }
468
469 void windowmanager_list_drawing_names(afb_req req) noexcept {
470    std::lock_guard<std::mutex> guard(binding_m);
471    #ifdef ST
472    ST();
473    #endif
474    if (g_afb_instance == nullptr) {
475       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
476       return;
477    }
478
479    try {
480
481    nlohmann::json j = g_afb_instance->app.id_alloc.name2id;
482    auto ret = wm::Ok(json_tokener_parse(j.dump().c_str()));
483    if (ret.is_err()) {
484       afb_req_fail(req, "failed", ret.unwrap_err());
485       return;
486    }
487
488    afb_req_success(req, ret.unwrap(), "success");
489
490    } catch (std::exception &e) {
491       afb_req_fail_f(req, "failed", "Uncaught exception while calling list_drawing_names: %s", e.what());
492       return;
493    }
494
495 }
496
497 void windowmanager_ping(afb_req req) noexcept {
498    std::lock_guard<std::mutex> guard(binding_m);
499    #ifdef ST
500    ST();
501    #endif
502    if (g_afb_instance == nullptr) {
503       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
504       return;
505    }
506
507    try {
508
509    g_afb_instance->app.api_ping();
510
511    afb_req_success(req, NULL, "success");
512
513    } catch (std::exception &e) {
514       afb_req_fail_f(req, "failed", "Uncaught exception while calling ping: %s", e.what());
515       return;
516    }
517 }
518
519 void windowmanager_debug_status(afb_req req) noexcept {
520    std::lock_guard<std::mutex> guard(binding_m);
521    #ifdef ST
522    ST();
523    #endif
524    if (g_afb_instance == nullptr) {
525       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
526       return;
527    }
528
529    try {
530
531    json_object *jr = json_object_new_object();
532    json_object_object_add(jr, "surfaces",
533                           to_json(g_afb_instance->app.controller->sprops));
534    json_object_object_add(jr, "layers", to_json(g_afb_instance->app.controller->lprops));
535
536    afb_req_success(req, jr, "success");
537
538    } catch (std::exception &e) {
539       afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_status: %s", e.what());
540       return;
541    }
542 }
543
544 void windowmanager_debug_layers(afb_req req) noexcept {
545    std::lock_guard<std::mutex> guard(binding_m);
546    #ifdef ST
547    ST();
548    #endif
549    if (g_afb_instance == nullptr) {
550       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
551       return;
552    }
553
554    try {
555    auto ret = wm::Ok(json_tokener_parse(g_afb_instance->app.layers.to_json().dump().c_str()));
556
557    afb_req_success(req, ret, "success");
558
559    } catch (std::exception &e) {
560       afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_layers: %s", e.what());
561       return;
562    }
563 }
564
565 void windowmanager_debug_surfaces(afb_req req) noexcept {
566    std::lock_guard<std::mutex> guard(binding_m);
567    #ifdef ST
568    ST();
569    #endif
570    if (g_afb_instance == nullptr) {
571       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
572       return;
573    }
574
575    try {
576
577    auto ret = wm::Ok(to_json(g_afb_instance->app.controller->sprops));
578    if (ret.is_err()) {
579       afb_req_fail(req, "failed", ret.unwrap_err());
580       return;
581    }
582
583    afb_req_success(req, ret.unwrap(), "success");
584
585    } catch (std::exception &e) {
586       afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_surfaces: %s", e.what());
587       return;
588    }
589
590 }
591
592 void windowmanager_debug_terminate(afb_req req) noexcept {
593    std::lock_guard<std::mutex> guard(binding_m);
594    #ifdef ST
595    ST();
596    #endif
597    if (g_afb_instance == nullptr) {
598       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
599       return;
600    }
601
602    try {
603
604    if (getenv("WINMAN_DEBUG_TERMINATE") != nullptr) {
605       raise(SIGKILL);  // afb-daemon kills it's pgroup using TERM, which
606                        // doesn't play well with perf
607    }
608
609    afb_req_success(req, NULL, "success");
610
611    } catch (std::exception &e) {
612       afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_terminate: %s", e.what());
613       return;
614    }
615
616 }
617
618 const struct afb_verb_v2 windowmanager_verbs[] = {
619    { "requestsurface", windowmanager_requestsurface, nullptr, nullptr, AFB_SESSION_NONE },
620    { "requestsurfacexdg", windowmanager_requestsurfacexdg, nullptr, nullptr, AFB_SESSION_NONE },
621    { "activatesurface", windowmanager_activatesurface, nullptr, nullptr, AFB_SESSION_NONE },
622    { "deactivatesurface", windowmanager_deactivatesurface, nullptr, nullptr, AFB_SESSION_NONE },
623    { "enddraw", windowmanager_enddraw, nullptr, nullptr, AFB_SESSION_NONE },
624    { "getdisplayinfo", windowmanager_getdisplayinfo_thunk, nullptr, nullptr, AFB_SESSION_NONE },
625    { "getareainfo", windowmanager_getareainfo_thunk, nullptr, nullptr, AFB_SESSION_NONE },
626    { "wm_subscribe", windowmanager_wm_subscribe, nullptr, nullptr, AFB_SESSION_NONE },
627    { "list_drawing_names", windowmanager_list_drawing_names, nullptr, nullptr, AFB_SESSION_NONE },
628    { "ping", windowmanager_ping, nullptr, nullptr, AFB_SESSION_NONE },
629    { "debug_status", windowmanager_debug_status, nullptr, nullptr, AFB_SESSION_NONE },
630    { "debug_layers", windowmanager_debug_layers, nullptr, nullptr, AFB_SESSION_NONE },
631    { "debug_surfaces", windowmanager_debug_surfaces, nullptr, nullptr, AFB_SESSION_NONE },
632    { "debug_terminate", windowmanager_debug_terminate, nullptr, nullptr, AFB_SESSION_NONE },
633    {}
634 };
635
636 extern "C" const struct afb_binding_v2 afbBindingV2 = {
637    "windowmanager", nullptr, nullptr, windowmanager_verbs, nullptr, binding_init, nullptr, 0};