util: logging to the afb daemon
[staging/windowmanager.git] / src / main.cpp
1 #include "json_helper.hpp"
2 #include "util.hpp"
3 #include "wayland.hpp"
4
5 #include <algorithm>
6 #include <json.h>
7
8 extern "C" {
9 #define AFB_BINDING_VERSION 2
10 #include <afb/afb-binding.h>
11 #include <systemd/sd-event.h>
12 }
13
14 namespace {
15 struct wayland {
16    std::unique_ptr<wl::display> display;
17    std::unique_ptr<genivi::controller> controller;
18    std::vector<std::unique_ptr<wl::output>> outputs;
19
20    wayland() : display(new wl::display), controller(nullptr), outputs() {}
21
22    int init();
23 };
24
25 struct wayland *g_wayland;
26
27 int wayland::init() {
28    if (!this->display->ok()) {
29       return -1;
30    }
31
32    this->display->r.add_global_handler("wl_output", [](wl_registry *r,
33                                                        uint32_t name,
34                                                        uint32_t v) {
35       g_wayland->outputs.emplace_back(std::make_unique<wl::output>(r, name, v));
36    });
37
38    this->display->r.add_global_handler(
39       "ivi_controller", [](wl_registry *r, uint32_t name, uint32_t v) {
40          g_wayland->controller =
41             std::make_unique<genivi::controller>(r, name, v);
42
43          // XXX: This protocol needs the output, so lets just add our mapping
44          // here...
45          g_wayland->controller->add_proxy_to_id_mapping(
46             g_wayland->outputs.back()->proxy.get(),
47             wl_proxy_get_id(reinterpret_cast<struct wl_proxy *>(
48                g_wayland->outputs.back()->proxy.get())));
49       });
50
51    // First level objects
52    this->display->roundtrip();
53    // Second level objects
54    this->display->roundtrip();
55    // Third level objects
56    this->display->roundtrip();
57
58    return 0;
59 }
60
61 //  _       _ _       _                         _    ____
62 // (_)_ __ (_) |_    | | __ _ _   _  ___  _   _| |_ / /\ \
63 // | | '_ \| | __|   | |/ _` | | | |/ _ \| | | | __| |  | |
64 // | | | | | | |_    | | (_| | |_| | (_) | |_| | |_| |  | |
65 // |_|_| |_|_|\__|___|_|\__,_|\__, |\___/ \__,_|\__| |  | |
66 //              |_____|       |___/                 \_\/_/
67 char const *init_layout() {
68    if (!g_wayland->controller) {
69       return "ivi_controller global not available";
70    }
71
72    if (g_wayland->outputs.empty()) {
73       return "no output was set up!";
74    }
75
76    auto &c = g_wayland->controller;
77
78    auto &o = g_wayland->outputs.front();
79    auto &s = c->screens.begin()->second;
80    auto &layers = c->layers;
81
82    // XXX: Write output dimensions to ivi controller...
83    c->output_size = genivi::size{uint32_t(o->width), uint32_t(o->height)};
84
85    // Clear scene
86    layers.clear();
87
88    // Clear screen
89    s->clear();
90
91    // Setup our dummy scene...
92    c->layer_create(100, 0, 0);   // bottom layer, anything else
93    c->layer_create(1000, 0, 0);  // top layer, mandelbrot
94
95    auto &l100 = c->layers[100];
96    auto &l1k = c->layers[1000];
97
98    // Set layers fullscreen
99    l100->set_destination_rectangle(0, 0, o->width, o->height);
100    l1k->set_destination_rectangle(0, 0, o->width, o->height);
101    l100->set_visibility(1);
102    l1k->set_visibility(1);
103
104    // Add layers to screen
105    s->set_render_order({100, 1000});
106
107    c->commit_changes();
108
109    g_wayland->display->flush();
110
111    return nullptr;
112 }
113
114 int display_event_callback(sd_event_source *evs, int fd, uint32_t events,
115                            void *data) {
116    if ((events & EPOLLHUP) != 0) {
117       AFB_ERROR("The compositor hung up, dying now.");
118       delete g_wayland;
119       g_wayland = nullptr;
120       goto error;
121    }
122
123    if (events & EPOLLIN) {
124       int ret = g_wayland->display->dispatch();
125       if (ret == -1) {
126          AFB_ERROR("wl_display_dipatch() returned error %d",
127                    g_wayland->display->get_error());
128          goto error;
129       }
130       g_wayland->display->flush();
131
132       // execute pending tasks, that is layout changes etc.
133       g_wayland->controller->execute_pending();
134       g_wayland->display->roundtrip();
135    }
136
137    return 0;
138
139 error:
140    sd_event_source_unref(evs);
141    return -1;
142 }
143
144 //  _     _           _ _                 _       _ _    ____
145 // | |__ (_)_ __   __| (_)_ __   __ _    (_)_ __ (_) |_ / /\ \
146 // | '_ \| | '_ \ / _` | | '_ \ / _` |   | | '_ \| | __| |  | |
147 // | |_) | | | | | (_| | | | | | (_| |   | | | | | | |_| |  | |
148 // |_.__/|_|_| |_|\__,_|_|_| |_|\__, |___|_|_| |_|_|\__| |  | |
149 //                              |___/_____|             \_\/_/
150 int binding_init_() {
151    lognotice("WinMan ver. %s", WINMAN_VERSION_STRING);
152
153    if (g_wayland != nullptr) {
154       AFB_ERROR("Wayland context already initialized?");
155       return 0;
156    }
157
158    if (getenv("XDG_RUNTIME_DIR") == nullptr) {
159       AFB_ERROR("Environment variable XDG_RUNTIME_DIR not set");
160       goto error;
161    }
162
163    g_wayland = new wayland;
164    if (g_wayland->init() == -1) {
165       AFB_ERROR("Could not connect to compositor");
166       goto error;
167    }
168
169    if (char const *e = init_layout()) {
170       AFB_ERROR("Could not init layout: %s", e);
171       goto error;
172    }
173
174    {
175       int ret = sd_event_add_io(afb_daemon_get_event_loop(), nullptr,
176                                 g_wayland->display->get_fd(), EPOLLIN,
177                                 display_event_callback, g_wayland);
178       if (ret < 0) {
179          AFB_ERROR("Could not initialize wayland event handler: %d", -ret);
180          goto error;
181       }
182    }
183
184    atexit([] { delete g_wayland; });
185
186    return 0;
187
188 error:
189    delete g_wayland;
190    g_wayland = nullptr;
191    return -1;
192 }
193
194 int binding_init() noexcept {
195    try {
196       return binding_init_();
197    } catch (std::exception &e) {
198       AFB_ERROR("Uncaught exception in binding_init(): %s", e.what());
199    }
200    return -1;
201 }
202
203 #define CHECK_WAYLAND()                                                    \
204    do {                                                                    \
205       if (g_wayland == nullptr) {                                          \
206          afb_req_fail(req, "failed",                                       \
207                       "Binding not initialized, did the compositor die?"); \
208          return;                                                           \
209       }                                                                    \
210    } while (0)
211
212 //      _      _                         _        _              ____
213 //   __| | ___| |__  _   _  __ _     ___| |_ __ _| |_ _   _ ___ / /\ \
214 //  / _` |/ _ \ '_ \| | | |/ _` |   / __| __/ _` | __| | | / __| |  | |
215 // | (_| |  __/ |_) | |_| | (_| |   \__ \ || (_| | |_| |_| \__ \ |  | |
216 //  \__,_|\___|_.__/ \__,_|\__, |___|___/\__\__,_|\__|\__,_|___/ |  | |
217 //                         |___/_____|                          \_\/_/
218 void debug_status(struct afb_req req) noexcept {
219    // Quick and dirty, dump current surfaces and layers
220    AFB_REQ_DEBUG(req, "status");
221
222    CHECK_WAYLAND();
223
224    auto o = json_object_new_object();
225    json_object_object_add(o, "surfaces",
226                           to_json(g_wayland->controller->surfaces));
227    json_object_object_add(o, "layers", to_json(g_wayland->controller->layers));
228    json_object_object_add(o, "screens",
229                           to_json(g_wayland->controller->screens));
230
231    afb_req_success(req, o, "status");
232 }
233
234 void debug_surfaces(afb_req req) noexcept {
235    CHECK_WAYLAND();
236
237    afb_req_success(req, to_json(g_wayland->controller->surfaces), "surfaces");
238 }
239
240 void debug_layers(afb_req req) noexcept {
241    CHECK_WAYLAND();
242
243    afb_req_success(req, to_json(g_wayland->controller->layers), "layers");
244 }
245
246 const struct afb_verb_v2 verbs[] = {
247    {"status", debug_status, NULL, NULL, AFB_SESSION_NONE_V2},
248    {"layers", debug_layers, NULL, NULL, AFB_SESSION_NONE_V2},
249    {"surfaces", debug_surfaces, NULL, NULL, AFB_SESSION_NONE_V2},
250    {},
251 };
252 }  // namespace
253
254 extern "C" const struct afb_binding_v2 afbBindingV2 = {
255    "winman", NULL, NULL, verbs, NULL, binding_init, NULL, 1};