src/shell: Add a wrappers for sending events with agl-shell
[src/agl-compositor.git] / clients / main-grpc.cpp
1 #include <cstdio>
2 #include <ctime>
3 #include <algorithm>
4 #include <queue>
5 #include <thread>
6 #include <mutex>
7 #include <condition_variable>
8
9 #include "shell.h"
10 #include "log.h"
11 #include "main-grpc.h"
12 #include "grpc-async-cb.h"
13
14 static int running = 1;
15
16 static void
17 agl_shell_bound_ok(void *data, struct agl_shell *agl_shell)
18 {
19         (void) agl_shell;
20
21         struct shell_data *sh = static_cast<struct shell_data *>(data);
22         sh->wait_for_bound = false;
23
24         sh->bound_ok = true;
25 }
26
27 static void
28 agl_shell_bound_fail(void *data, struct agl_shell *agl_shell)
29 {
30         (void) agl_shell;
31
32         struct shell_data *sh = static_cast<struct shell_data *>(data);
33         sh->wait_for_bound = false;
34
35         sh->bound_ok = false;
36 }
37
38 static void
39 agl_shell_app_state(void *data, struct agl_shell *agl_shell,
40                 const char *app_id, uint32_t state)
41 {
42         (void) agl_shell;
43         struct shell_data *sh = static_cast<struct shell_data *>(data);
44         LOG("got app_state event app_id %s,  state %d\n", app_id, state);
45
46         if (sh->server_context_list.empty())
47                 return;
48
49         ::agl_shell_ipc::AppState app;
50
51         sh->current_app_state.set_app_id(std::string(app_id));
52         sh->current_app_state.set_state(state);
53
54         auto start = sh->server_context_list.begin();
55         while (start != sh->server_context_list.end()) {
56                 LOG("writing to lister %p\n", static_cast<void *>(start->second));
57                 start->second->NextWrite();
58                 start++;
59         }
60 }
61
62 static const struct agl_shell_listener shell_listener = {
63         agl_shell_bound_ok,
64         agl_shell_bound_fail,
65         agl_shell_app_state,
66 };
67
68 static void
69 agl_shell_ext_doas_done(void *data, struct agl_shell_ext *agl_shell_ext, uint32_t status)
70 {
71         (void) agl_shell_ext;
72
73         struct shell_data *sh = static_cast<struct shell_data *>(data);
74         sh->wait_for_doas = false;
75
76         if (status == AGL_SHELL_EXT_DOAS_SHELL_CLIENT_STATUS_SUCCESS)
77                 sh->doas_ok = true;
78 }
79
80 static const struct agl_shell_ext_listener shell_ext_listener = {
81         agl_shell_ext_doas_done,
82 };
83
84 static void
85 display_handle_geometry(void *data, struct wl_output *wl_output,
86                 int x, int y, int physical_width, int physical_height,
87                 int subpixel, const char *make, const char *model, int transform)
88 {
89         (void) data;
90         (void) wl_output;
91         (void) x;
92         (void) y;
93         (void) physical_width;
94         (void) physical_height;
95         (void) subpixel;
96         (void) make;
97         (void) model;
98         (void) transform;
99 }
100
101 static void
102 display_handle_mode(void *data, struct wl_output *wl_output, uint32_t flags,
103                 int width, int height, int refresh)
104 {
105         (void) data;
106         (void) wl_output;
107         (void) flags;
108         (void) width;
109         (void) height;
110         (void) refresh;
111 }
112
113 static void
114 display_handle_done(void *data, struct wl_output *wl_output)
115 {
116         (void) data;
117         (void) wl_output;
118 }
119
120 static void
121 display_handle_scale(void *data, struct wl_output *wl_output, int32_t factor)
122 {
123         (void) data;
124         (void) wl_output;
125         (void) factor;
126 }
127
128
129 static void
130 display_handle_name(void *data, struct wl_output *wl_output, const char *name)
131 {
132         (void) wl_output;
133
134         struct window_output *woutput = static_cast<struct window_output *>(data);
135         woutput->name = strdup(name);
136 }
137
138 static void
139 display_handle_description(void *data, struct wl_output *wl_output, const char *description)
140 {
141         (void) data;
142         (void) wl_output;
143         (void) description;
144 }
145
146 static const struct wl_output_listener output_listener = {
147         display_handle_geometry,
148         display_handle_mode,
149         display_handle_done,
150         display_handle_scale,
151         display_handle_name,
152         display_handle_description,
153 };
154
155 static void
156 display_add_output(struct shell_data *sh, struct wl_registry *reg,
157                    uint32_t id, uint32_t version)
158 {
159         struct window_output *w_output;
160
161         w_output = new struct window_output;
162         w_output->shell_data = sh;
163
164         w_output->output =
165                 static_cast<struct wl_output *>(wl_registry_bind(reg, id,
166                                         &wl_output_interface,
167                                         std::min(version, static_cast<uint32_t>(4))));
168
169         wl_list_insert(&sh->output_list, &w_output->link);
170         wl_output_add_listener(w_output->output, &output_listener, w_output);
171 }
172
173 static void
174 destroy_output(struct window_output *w_output)
175 {
176         free(w_output->name);
177         wl_list_remove(&w_output->link);
178         free(w_output);
179 }
180
181 static void
182 global_add(void *data, struct wl_registry *reg, uint32_t id,
183                 const char *interface, uint32_t version)
184 {
185
186         struct shell_data *sh = static_cast<struct shell_data *>(data);
187
188         if (!sh)
189                 return;
190
191         if (strcmp(interface, agl_shell_interface.name) == 0) {
192                 sh->shell =
193                         static_cast<struct agl_shell *>(wl_registry_bind(reg, id,
194                                         &agl_shell_interface, std::min(static_cast<uint32_t>(3),
195                                                                         version)));
196                 agl_shell_add_listener(sh->shell, &shell_listener, data);
197                 sh->version = version;
198         } else if (strcmp(interface, "wl_output") == 0) {
199                 display_add_output(sh, reg, id, version);
200         }
201 }
202
203 static void
204 global_remove(void *data, struct wl_registry *reg, uint32_t id)
205 {
206         /* Don't care */
207         (void) data;
208         (void) reg;
209         (void) id;
210 }
211
212 static void
213 global_add_ext(void *data, struct wl_registry *reg, uint32_t id,
214                 const char *interface, uint32_t version)
215 {
216         struct shell_data *sh = static_cast<struct shell_data *>(data);
217
218         if (!sh)
219                 return;
220
221         if (strcmp(interface, agl_shell_ext_interface.name) == 0) {
222                 sh->shell_ext =
223                         static_cast<struct agl_shell_ext *>(wl_registry_bind(reg, id,
224                                         &agl_shell_ext_interface, std::min(static_cast<uint32_t>(1),
225                                                                            version)));
226                 agl_shell_ext_add_listener(sh->shell_ext,
227                                            &shell_ext_listener, data);
228         }
229 }
230
231 static void
232 global_remove_ext(void *data, struct wl_registry *reg, uint32_t id)
233 {
234         /* Don't care */
235         (void) data;
236         (void) reg;
237         (void) id;
238 }
239
240 static const struct wl_registry_listener registry_ext_listener = {
241         global_add_ext,
242         global_remove_ext,
243 };
244
245 static const struct wl_registry_listener registry_listener = {
246         global_add,
247         global_remove,
248 };
249
250 static void
251 register_shell_ext(struct wl_display *wl_display, struct shell_data *sh)
252 {
253         struct wl_registry *registry;
254
255         registry = wl_display_get_registry(wl_display);
256
257         wl_registry_add_listener(registry, &registry_ext_listener, sh);
258
259         wl_display_roundtrip(wl_display);
260         wl_registry_destroy(registry);
261 }
262
263 static void
264 register_shell(struct wl_display *wl_display, struct shell_data *sh)
265 {
266         struct wl_registry *registry;
267
268         wl_list_init(&sh->output_list);
269
270         registry = wl_display_get_registry(wl_display);
271
272         wl_registry_add_listener(registry, &registry_listener, sh);
273
274         wl_display_roundtrip(wl_display);
275         wl_registry_destroy(registry);
276 }
277
278 static void
279 destroy_shell_data(struct shell_data *sh)
280 {
281         struct window_output *w_output, *w_output_next;
282
283         wl_list_for_each_safe(w_output, w_output_next, &sh->output_list, link)
284                 destroy_output(w_output);
285
286         wl_display_flush(sh->wl_display);
287         wl_display_disconnect(sh->wl_display);
288
289         delete sh;
290 }
291
292 static struct shell_data *
293 start_agl_shell_client(void)
294 {
295         int ret = 0;
296         struct wl_display *wl_display;
297
298         wl_display = wl_display_connect(NULL);
299
300         struct shell_data *sh = new struct shell_data;
301
302         sh->wl_display = wl_display;
303         sh->wait_for_doas = true;
304         sh->wait_for_bound = true;
305
306         register_shell_ext(wl_display, sh);
307
308         // check for agl_shell_ext
309         if (!sh->shell_ext) {
310                 fprintf(stderr, "Failed to bind to agl_shell_ext interface\n");
311                 delete sh;
312                 return nullptr;
313         }
314
315         if (wl_list_empty(&sh->output_list)) {
316                 fprintf(stderr, "Failed get any outputs!\n");
317                 delete sh;
318                 return nullptr;
319         }
320
321         agl_shell_ext_doas_shell_client(sh->shell_ext);
322         while (ret != -1 && sh->wait_for_doas) {
323                 ret = wl_display_dispatch(sh->wl_display);
324                 if (sh->wait_for_doas)
325                         continue;
326         }
327
328         if (!sh->doas_ok) {
329                 fprintf(stderr, "Failed to get doas_done event\n");
330                 delete sh;
331                 return nullptr;
332         }
333
334         // bind to agl-shell
335         register_shell(wl_display, sh);
336         while (ret != -1 && sh->wait_for_bound) {
337                 ret = wl_display_dispatch(sh->wl_display);
338                 if (sh->wait_for_bound)
339                         continue;
340         }
341
342         // at this point, we can't do anything about it
343         if (!sh->bound_ok) {
344                 fprintf(stderr, "Failed to get bound_ok event!\n");
345                 delete sh;
346                 return nullptr;
347         }
348
349         fprintf(stderr, "agl_shell/agl_shell_ext interface OK\n");
350
351         return sh; 
352 }
353
354 static void
355 start_grpc_server(Shell *aglShell)
356 {
357         // instantiante the grpc server
358         std::string server_address(kDefaultGrpcServiceAddress);
359         GrpcServiceImpl service{aglShell};
360
361         grpc::EnableDefaultHealthCheckService(true);
362         grpc::reflection::InitProtoReflectionServerBuilderPlugin();
363
364         grpc::ServerBuilder builder;
365         builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
366         builder.RegisterService(&service);
367
368         std::unique_ptr<grpc::Server> server(builder.BuildAndStart());
369         LOG("Server listening on %s\n", server_address.c_str());
370
371         server->Wait();
372 }
373
374 int main(int argc, char **argv)
375 {
376         (void) argc;
377         (void) argv;
378         Shell *aglShell;
379         int ret = 0;
380
381         // do not start right up, give shell client time to boot up
382         struct timespec ts = {};
383
384         clock_gettime(CLOCK_MONOTONIC, &ts);
385         ts.tv_sec = 2;
386         ts.tv_nsec = 0;
387
388         nanosleep(&ts, NULL);
389
390         struct shell_data *sh = start_agl_shell_client();
391         if (!sh) {
392                 LOG("Failed to initialize agl-shell/agl-shell-ext\n");
393                 exit(EXIT_FAILURE);
394         }
395
396         std::shared_ptr<struct agl_shell> agl_shell{sh->shell, agl_shell_destroy};
397         aglShell = new Shell(agl_shell, sh);
398
399         std::thread thread(start_grpc_server, aglShell);
400
401         // serve wayland requests
402         while (running && ret != -1) {
403                 ret = wl_display_dispatch(sh->wl_display);
404         }
405
406         destroy_shell_data(sh);
407         return 0;
408 }