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