Enable an application re-launch
[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 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_wm_subscribe(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    json_object *jreq = afb_req_json(req);
388    json_object *j = nullptr;
389    if (! json_object_object_get_ex(jreq, "event", &j)) {
390       afb_req_fail(req, "failed", "Need char const* argument event");
391       return;
392    }
393    int event_type = json_object_get_int(j);
394    const char *event_name = g_afb_instance->app.kListEventName[event_type];
395    struct afb_event event = g_afb_instance->app.map_afb_event[event_name];
396    int ret = afb_req_subscribe(req, event);
397    if (ret) {
398       afb_req_fail(req, "failed", "Error: afb_req_subscribe()");
399       return;
400    }
401    afb_req_success(req, NULL, "success");
402
403    } catch (std::exception &e) {
404       afb_req_fail_f(req, "failed", "Uncaught exception while calling wm_subscribe: %s", e.what());
405       return;
406    }
407
408 }
409
410 void windowmanager_list_drawing_names(afb_req req) noexcept {
411    std::lock_guard<std::mutex> guard(binding_m);
412    #ifdef ST
413    ST();
414    #endif
415    if (g_afb_instance == nullptr) {
416       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
417       return;
418    }
419
420    try {
421
422    nlohmann::json j = g_afb_instance->app.id_alloc.name2id;
423    auto ret = wm::Ok(json_tokener_parse(j.dump().c_str()));
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
431    } catch (std::exception &e) {
432       afb_req_fail_f(req, "failed", "Uncaught exception while calling list_drawing_names: %s", e.what());
433       return;
434    }
435
436 }
437
438 void windowmanager_ping(afb_req req) noexcept {
439    std::lock_guard<std::mutex> guard(binding_m);
440    #ifdef ST
441    ST();
442    #endif
443    if (g_afb_instance == nullptr) {
444       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
445       return;
446    }
447
448    try {
449
450    g_afb_instance->app.api_ping();
451
452    afb_req_success(req, NULL, "success");
453
454    } catch (std::exception &e) {
455       afb_req_fail_f(req, "failed", "Uncaught exception while calling ping: %s", e.what());
456       return;
457    }
458 }
459
460 void windowmanager_debug_status(afb_req req) noexcept {
461    std::lock_guard<std::mutex> guard(binding_m);
462    #ifdef ST
463    ST();
464    #endif
465    if (g_afb_instance == nullptr) {
466       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
467       return;
468    }
469
470    try {
471
472    json_object *jr = json_object_new_object();
473    json_object_object_add(jr, "surfaces",
474                           to_json(g_afb_instance->app.controller->sprops));
475    json_object_object_add(jr, "layers", to_json(g_afb_instance->app.controller->lprops));
476
477    afb_req_success(req, jr, "success");
478
479    } catch (std::exception &e) {
480       afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_status: %s", e.what());
481       return;
482    }
483 }
484
485 void windowmanager_debug_layers(afb_req req) noexcept {
486    std::lock_guard<std::mutex> guard(binding_m);
487    #ifdef ST
488    ST();
489    #endif
490    if (g_afb_instance == nullptr) {
491       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
492       return;
493    }
494
495    try {
496    auto ret = wm::Ok(json_tokener_parse(g_afb_instance->app.layers.to_json().dump().c_str()));
497
498    afb_req_success(req, ret, "success");
499
500    } catch (std::exception &e) {
501       afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_layers: %s", e.what());
502       return;
503    }
504 }
505
506 void windowmanager_debug_surfaces(afb_req req) noexcept {
507    std::lock_guard<std::mutex> guard(binding_m);
508    #ifdef ST
509    ST();
510    #endif
511    if (g_afb_instance == nullptr) {
512       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
513       return;
514    }
515
516    try {
517
518    auto ret = wm::Ok(to_json(g_afb_instance->app.controller->sprops));
519    if (ret.is_err()) {
520       afb_req_fail(req, "failed", ret.unwrap_err());
521       return;
522    }
523
524    afb_req_success(req, ret.unwrap(), "success");
525
526    } catch (std::exception &e) {
527       afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_surfaces: %s", e.what());
528       return;
529    }
530
531 }
532
533 void windowmanager_debug_terminate(afb_req req) noexcept {
534    std::lock_guard<std::mutex> guard(binding_m);
535    #ifdef ST
536    ST();
537    #endif
538    if (g_afb_instance == nullptr) {
539       afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
540       return;
541    }
542
543    try {
544
545    if (getenv("WINMAN_DEBUG_TERMINATE") != nullptr) {
546       raise(SIGKILL);  // afb-daemon kills it's pgroup using TERM, which
547                        // doesn't play well with perf
548    }
549
550    afb_req_success(req, NULL, "success");
551
552    } catch (std::exception &e) {
553       afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_terminate: %s", e.what());
554       return;
555    }
556
557 }
558
559 const struct afb_verb_v2 windowmanager_verbs[] = {
560    { "requestsurface", windowmanager_requestsurface, nullptr, nullptr, AFB_SESSION_NONE },
561    { "requestsurfacexdg", windowmanager_requestsurfacexdg, nullptr, nullptr, AFB_SESSION_NONE },
562    { "activatesurface", windowmanager_activatesurface, nullptr, nullptr, AFB_SESSION_NONE },
563    { "deactivatesurface", windowmanager_deactivatesurface, nullptr, nullptr, AFB_SESSION_NONE },
564    { "enddraw", windowmanager_enddraw, nullptr, nullptr, AFB_SESSION_NONE },
565    { "wm_subscribe", windowmanager_wm_subscribe, nullptr, nullptr, AFB_SESSION_NONE },
566    { "list_drawing_names", windowmanager_list_drawing_names, nullptr, nullptr, AFB_SESSION_NONE },
567    { "ping", windowmanager_ping, nullptr, nullptr, AFB_SESSION_NONE },
568    { "debug_status", windowmanager_debug_status, nullptr, nullptr, AFB_SESSION_NONE },
569    { "debug_layers", windowmanager_debug_layers, nullptr, nullptr, AFB_SESSION_NONE },
570    { "debug_surfaces", windowmanager_debug_surfaces, nullptr, nullptr, AFB_SESSION_NONE },
571    { "debug_terminate", windowmanager_debug_terminate, nullptr, nullptr, AFB_SESSION_NONE },
572    {}
573 };
574
575 extern "C" const struct afb_binding_v2 afbBindingV2 = {
576    "windowmanager", nullptr, nullptr, windowmanager_verbs, nullptr, binding_init, nullptr, 0};