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