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