2 * Copyright (c) 2017 TOYOTA MOTOR CORPORATION
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
24 #include "json_helper.hpp"
26 #include "wayland.hpp"
29 #include <afb/afb-binding.h>
30 #include <systemd/sd-event.h>
34 std::unique_ptr<wl::display> display;
37 afb_instance() : display{new wl::display}, app{this->display.get()} {}
42 struct afb_instance *g_afb_instance;
45 int afb_instance::init() {
46 return this->app.init();
49 int display_event_callback(sd_event_source *evs, int /*fd*/, uint32_t events,
53 if ((events & EPOLLHUP) != 0) {
54 HMI_ERROR("wm", "The compositor hung up, dying now.");
55 delete g_afb_instance;
56 g_afb_instance = nullptr;
60 if ((events & EPOLLIN) != 0u) {
62 STN(display_read_events);
63 g_afb_instance->app.display->read_events();
64 g_afb_instance->app.set_pending_events();
67 // We want do dispatch pending wayland events from within
69 STN(winman_ping_api_call);
70 afb_service_call("windowmanager", "ping", json_object_new_object(),
71 [](void *c, int st, json_object *j) {
72 STN(winman_ping_api_call_return);
81 sd_event_source_unref(evs);
82 if (getenv("WINMAN_EXIT_ON_HANGUP") != nullptr) {
89 HMI_NOTICE("wm", "WinMan ver. %s", WINMAN_VERSION_STRING);
91 if (g_afb_instance != nullptr) {
92 HMI_ERROR("wm", "Wayland context already initialized?");
96 if (getenv("XDG_RUNTIME_DIR") == nullptr) {
97 HMI_ERROR("wm", "Environment variable XDG_RUNTIME_DIR not set");
102 // wait until wayland compositor starts up.
104 g_afb_instance = new afb_instance;
105 while (!g_afb_instance->display->ok()) {
108 HMI_ERROR("wm", "Could not connect to compositor");
111 HMI_ERROR("wm", "Wait to start weston ...");
113 delete g_afb_instance;
114 g_afb_instance = new afb_instance;
118 if (g_afb_instance->init() == -1) {
119 HMI_ERROR("wm", "Could not connect to compositor");
124 int ret = sd_event_add_io(afb_daemon_get_event_loop(), nullptr,
125 g_afb_instance->display->get_fd(), EPOLLIN,
126 display_event_callback, g_afb_instance);
128 HMI_ERROR("wm", "Could not initialize afb_instance event handler: %d", -ret);
133 atexit([] { delete g_afb_instance; });
138 delete g_afb_instance;
139 g_afb_instance = nullptr;
143 int binding_init() noexcept {
145 return _binding_init();
146 } catch (std::exception &e) {
147 HMI_ERROR("wm", "Uncaught exception in binding_init(): %s", e.what());
152 void windowmanager_requestsurface(afb_req req) noexcept {
153 std::lock_guard<std::mutex> guard(binding_m);
157 if (g_afb_instance == nullptr) {
158 afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
163 const char* a_drawing_name = afb_req_value(req, "drawing_name");
165 afb_req_fail(req, "failed", "Need char const* argument drawing_name");
169 auto ret = g_afb_instance->app.api_request_surface(a_drawing_name);
171 afb_req_fail(req, "failed", ret.unwrap_err());
175 afb_req_success(req, json_object_new_int(ret.unwrap()), "success");
177 } catch (std::exception &e) {
178 afb_req_fail_f(req, "failed", "Uncaught exception while calling requestsurface: %s", e.what());
184 void windowmanager_requestsurfacexdg(afb_req req) noexcept {
185 std::lock_guard<std::mutex> guard(binding_m);
189 if (g_afb_instance == nullptr) {
190 afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
195 json_object *jreq = afb_req_json(req);
197 json_object *j_drawing_name = nullptr;
198 if (! json_object_object_get_ex(jreq, "drawing_name", &j_drawing_name)) {
199 afb_req_fail(req, "failed", "Need char const* argument drawing_name");
202 char const* a_drawing_name = json_object_get_string(j_drawing_name);
204 json_object *j_ivi_id = nullptr;
205 if (! json_object_object_get_ex(jreq, "ivi_id", &j_ivi_id)) {
206 afb_req_fail(req, "failed", "Need char const* argument ivi_id");
209 char const* a_ivi_id = json_object_get_string(j_ivi_id);
211 auto ret = g_afb_instance->app.api_request_surface(a_drawing_name, a_ivi_id);
212 if (ret != nullptr) {
213 afb_req_fail(req, "failed", ret);
217 afb_req_success(req, NULL, "success");
218 } catch (std::exception &e) {
219 afb_req_fail_f(req, "failed", "Uncaught exception while calling requestsurfacexdg: %s", e.what());
224 void windowmanager_activatesurface(afb_req req) noexcept {
225 std::lock_guard<std::mutex> guard(binding_m);
229 if (g_afb_instance == nullptr) {
230 afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
235 const char* a_drawing_name = afb_req_value(req, "drawing_name");
237 afb_req_fail(req, "failed", "Need char const* argument drawing_name");
241 const char* a_drawing_area = afb_req_value(req, "drawing_area");
243 afb_req_fail(req, "failed", "Need char const* argument drawing_area");
247 auto ret = g_afb_instance->app.api_activate_surface(a_drawing_name, a_drawing_area);
248 if (ret != nullptr) {
249 afb_req_fail(req, "failed", ret);
253 afb_req_success(req, NULL, "success");
254 } catch (std::exception &e) {
255 afb_req_fail_f(req, "failed", "Uncaught exception while calling activatesurface: %s", e.what());
261 void windowmanager_deactivatesurface(afb_req req) noexcept {
262 std::lock_guard<std::mutex> guard(binding_m);
266 if (g_afb_instance == nullptr) {
267 afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
272 const char* a_drawing_name = afb_req_value(req, "drawing_name");
274 afb_req_fail(req, "failed", "Need char const* argument drawing_name");
278 auto ret = g_afb_instance->app.api_deactivate_surface(a_drawing_name);
279 if (ret != nullptr) {
280 afb_req_fail(req, "failed", ret);
284 afb_req_success(req, NULL, "success");
285 } catch (std::exception &e) {
286 afb_req_fail_f(req, "failed", "Uncaught exception while calling deactivatesurface: %s", e.what());
291 void windowmanager_enddraw(afb_req req) noexcept {
292 std::lock_guard<std::mutex> guard(binding_m);
296 if (g_afb_instance == nullptr) {
297 afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
302 const char* a_drawing_name = afb_req_value(req, "drawing_name");
304 afb_req_fail(req, "failed", "Need char const* argument drawing_name");
308 auto ret = g_afb_instance->app.api_enddraw(a_drawing_name);
309 if (ret != nullptr) {
310 afb_req_fail(req, "failed", ret);
314 afb_req_success(req, NULL, "success");
315 } catch (std::exception &e) {
316 afb_req_fail_f(req, "failed", "Uncaught exception while calling enddraw: %s", e.what());
322 void windowmanager_wm_subscribe(afb_req req) noexcept {
323 std::lock_guard<std::mutex> guard(binding_m);
327 if (g_afb_instance == nullptr) {
328 afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
333 json_object *jreq = afb_req_json(req);
334 json_object *j = nullptr;
335 if (! json_object_object_get_ex(jreq, "event", &j)) {
336 afb_req_fail(req, "failed", "Need char const* argument event");
339 int event_type = json_object_get_int(j);
340 const char *event_name = g_afb_instance->app.kListEventName[event_type];
341 struct afb_event event = g_afb_instance->app.map_afb_event[event_name];
342 int ret = afb_req_subscribe(req, event);
344 afb_req_fail(req, "failed", "Error: afb_req_subscribe()");
347 afb_req_success(req, NULL, "success");
349 } catch (std::exception &e) {
350 afb_req_fail_f(req, "failed", "Uncaught exception while calling wm_subscribe: %s", e.what());
356 void windowmanager_list_drawing_names(afb_req req) noexcept {
357 std::lock_guard<std::mutex> guard(binding_m);
361 if (g_afb_instance == nullptr) {
362 afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
368 nlohmann::json j = g_afb_instance->app.id_alloc.name2id;
369 auto ret = wm::Ok(json_tokener_parse(j.dump().c_str()));
371 afb_req_fail(req, "failed", ret.unwrap_err());
375 afb_req_success(req, ret.unwrap(), "success");
377 } catch (std::exception &e) {
378 afb_req_fail_f(req, "failed", "Uncaught exception while calling list_drawing_names: %s", e.what());
384 void windowmanager_ping(afb_req req) noexcept {
385 std::lock_guard<std::mutex> guard(binding_m);
389 if (g_afb_instance == nullptr) {
390 afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
396 g_afb_instance->app.api_ping();
398 afb_req_success(req, NULL, "success");
400 } catch (std::exception &e) {
401 afb_req_fail_f(req, "failed", "Uncaught exception while calling ping: %s", e.what());
406 void windowmanager_debug_status(afb_req req) noexcept {
407 std::lock_guard<std::mutex> guard(binding_m);
411 if (g_afb_instance == nullptr) {
412 afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
418 json_object *jr = json_object_new_object();
419 json_object_object_add(jr, "surfaces",
420 to_json(g_afb_instance->app.controller->sprops));
421 json_object_object_add(jr, "layers", to_json(g_afb_instance->app.controller->lprops));
423 afb_req_success(req, jr, "success");
425 } catch (std::exception &e) {
426 afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_status: %s", e.what());
431 void windowmanager_debug_layers(afb_req req) noexcept {
432 std::lock_guard<std::mutex> guard(binding_m);
436 if (g_afb_instance == nullptr) {
437 afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
442 auto ret = wm::Ok(json_tokener_parse(g_afb_instance->app.layers.to_json().dump().c_str()));
444 afb_req_success(req, ret, "success");
446 } catch (std::exception &e) {
447 afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_layers: %s", e.what());
452 void windowmanager_debug_surfaces(afb_req req) noexcept {
453 std::lock_guard<std::mutex> guard(binding_m);
457 if (g_afb_instance == nullptr) {
458 afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
464 auto ret = wm::Ok(to_json(g_afb_instance->app.controller->sprops));
466 afb_req_fail(req, "failed", ret.unwrap_err());
470 afb_req_success(req, ret.unwrap(), "success");
472 } catch (std::exception &e) {
473 afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_surfaces: %s", e.what());
479 void windowmanager_debug_terminate(afb_req req) noexcept {
480 std::lock_guard<std::mutex> guard(binding_m);
484 if (g_afb_instance == nullptr) {
485 afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
491 if (getenv("WINMAN_DEBUG_TERMINATE") != nullptr) {
492 raise(SIGKILL); // afb-daemon kills it's pgroup using TERM, which
493 // doesn't play well with perf
496 afb_req_success(req, NULL, "success");
498 } catch (std::exception &e) {
499 afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_terminate: %s", e.what());
505 const struct afb_verb_v2 windowmanager_verbs[] = {
506 { "requestsurface", windowmanager_requestsurface, nullptr, nullptr, AFB_SESSION_NONE },
507 { "requestsurfacexdg", windowmanager_requestsurfacexdg, nullptr, nullptr, AFB_SESSION_NONE },
508 { "activatesurface", windowmanager_activatesurface, nullptr, nullptr, AFB_SESSION_NONE },
509 { "deactivatesurface", windowmanager_deactivatesurface, nullptr, nullptr, AFB_SESSION_NONE },
510 { "enddraw", windowmanager_enddraw, nullptr, nullptr, AFB_SESSION_NONE },
511 { "wm_subscribe", windowmanager_wm_subscribe, nullptr, nullptr, AFB_SESSION_NONE },
512 { "list_drawing_names", windowmanager_list_drawing_names, nullptr, nullptr, AFB_SESSION_NONE },
513 { "ping", windowmanager_ping, nullptr, nullptr, AFB_SESSION_NONE },
514 { "debug_status", windowmanager_debug_status, nullptr, nullptr, AFB_SESSION_NONE },
515 { "debug_layers", windowmanager_debug_layers, nullptr, nullptr, AFB_SESSION_NONE },
516 { "debug_surfaces", windowmanager_debug_surfaces, nullptr, nullptr, AFB_SESSION_NONE },
517 { "debug_terminate", windowmanager_debug_terminate, nullptr, nullptr, AFB_SESSION_NONE },
521 extern "C" const struct afb_binding_v2 afbBindingV2 = {
522 "windowmanager", nullptr, nullptr, windowmanager_verbs, nullptr, binding_init, nullptr, 0};