cf1214a5637824f55be324d4f058d281db2f4b06
[src/agl-compositor.git] / src / shell.c
1 /*
2  * Copyright © 2019 Collabora, Ltd.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files (the
6  * "Software"), to deal in the Software without restriction, including
7  * without limitation the rights to use, copy, modify, merge, publish,
8  * distribute, sublicense, and/or sell copies of the Software, and to
9  * permit persons to whom the Software is furnished to do so, subject to
10  * the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the
13  * next paragraph) shall be included in all copies or substantial
14  * portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19  * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23  * SOFTWARE.
24  */
25
26 #include "ivi-compositor.h"
27
28 #include <assert.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <signal.h>
32 #include <string.h>
33 #include <sys/socket.h>
34 #include <sys/types.h>
35 #include <unistd.h>
36 #include <libweston/libweston.h>
37 #include <libweston/config-parser.h>
38
39 #include "shared/os-compatibility.h"
40
41 #include "agl-shell-server-protocol.h"
42 #include "agl-shell-desktop-server-protocol.h"
43
44 static void
45 create_black_surface_view(struct ivi_output *output);
46
47 static void
48 insert_black_surface(struct ivi_output *output);
49
50 void
51 ivi_set_desktop_surface(struct ivi_surface *surface)
52 {
53         struct desktop_client *dclient;
54         struct ivi_compositor *ivi = surface->ivi;
55         assert(surface->role == IVI_SURFACE_ROLE_NONE);
56
57         surface->role = IVI_SURFACE_ROLE_DESKTOP;
58         wl_list_insert(&surface->ivi->surfaces, &surface->link);
59
60         /* advertise to all desktop clients the new surface */
61         wl_list_for_each(dclient, &ivi->desktop_clients, link) {
62                 const char *app_id =
63                         weston_desktop_surface_get_app_id(surface->dsurface);
64                 agl_shell_desktop_send_application(dclient->resource, app_id);
65         }
66 }
67
68 void
69 ivi_set_desktop_surface_popup(struct ivi_surface *surface)
70 {
71         struct ivi_compositor *ivi = surface->ivi;
72         assert(surface->role == IVI_SURFACE_ROLE_NONE);
73
74         surface->role = IVI_SURFACE_ROLE_POPUP;
75         wl_list_insert(&ivi->surfaces, &surface->link);
76 }
77
78 static void
79 ivi_set_pending_desktop_surface_popup(struct ivi_output *ioutput,
80                                       int x, int y, const char *app_id)
81 {
82         struct ivi_compositor *ivi = ioutput->ivi;
83         size_t len_app_id = strlen(app_id);
84
85         struct pending_popup *p_popup = zalloc(sizeof(*p_popup));
86
87         p_popup->app_id = zalloc(sizeof(char) * (len_app_id + 1));
88         memcpy(p_popup->app_id, app_id, len_app_id);
89         p_popup->ioutput = ioutput;
90         p_popup->x = x;
91         p_popup->y = y;
92
93         wl_list_insert(&ivi->popup_pending_apps, &p_popup->link);
94 }
95
96 static void
97 ivi_remove_pending_desktop_surface_popup(struct pending_popup *p_popup)
98 {
99         free(p_popup->app_id);
100         wl_list_remove(&p_popup->link);
101         free(p_popup);
102 }
103
104 bool
105 ivi_check_pending_desktop_surface_popup(struct ivi_surface *surface)
106 {
107         struct ivi_compositor *ivi = surface->ivi;
108         struct pending_popup *p_popup, *next_p_popup;
109         const char *_app_id =
110                         weston_desktop_surface_get_app_id(surface->dsurface);
111
112         wl_list_for_each_safe(p_popup, next_p_popup,
113                               &ivi->popup_pending_apps, link) {
114                 if (!strcmp(_app_id, p_popup->app_id)) {
115                         surface->popup.output = p_popup->ioutput;
116                         surface->popup.x = p_popup->x;
117                         surface->popup.y = p_popup->y;
118
119                         ivi_remove_pending_desktop_surface_popup(p_popup);
120                         return true;
121                 }
122         }
123
124         return false;
125 }
126
127 void
128 ivi_shell_init_black_fs(struct ivi_compositor *ivi)
129 {
130         struct ivi_output *out;
131
132         wl_list_for_each(out, &ivi->outputs, link) {
133                 create_black_surface_view(out);
134                 insert_black_surface(out);
135         }
136 }
137
138 int
139 ivi_shell_init(struct ivi_compositor *ivi)
140 {
141         weston_layer_init(&ivi->hidden, ivi->compositor);
142         weston_layer_init(&ivi->background, ivi->compositor);
143         weston_layer_init(&ivi->normal, ivi->compositor);
144         weston_layer_init(&ivi->panel, ivi->compositor);
145         weston_layer_init(&ivi->popup, ivi->compositor);
146         weston_layer_init(&ivi->fullscreen, ivi->compositor);
147
148         weston_layer_set_position(&ivi->hidden,
149                                   WESTON_LAYER_POSITION_HIDDEN);
150         weston_layer_set_position(&ivi->background,
151                                   WESTON_LAYER_POSITION_BACKGROUND);
152         weston_layer_set_position(&ivi->normal,
153                                   WESTON_LAYER_POSITION_NORMAL);
154         weston_layer_set_position(&ivi->panel,
155                                   WESTON_LAYER_POSITION_UI);
156         weston_layer_set_position(&ivi->popup,
157                                   WESTON_LAYER_POSITION_TOP_UI);
158         weston_layer_set_position(&ivi->fullscreen,
159                                   WESTON_LAYER_POSITION_FULLSCREEN);
160
161         return 0;
162 }
163
164 static void
165 ivi_shell_advertise_xdg_surfaces(struct ivi_compositor *ivi, struct wl_resource *resource)
166 {
167         struct ivi_surface *surface;
168
169         wl_list_for_each(surface, &ivi->surfaces, link) {
170                 const char *app_id =
171                         weston_desktop_surface_get_app_id(surface->dsurface);
172                 agl_shell_desktop_send_application(resource, app_id);
173         }
174 }
175
176 static void
177 client_exec(const char *command, int fd)
178 {
179         sigset_t sig;
180         char s[32];
181
182         /* Don't give the child our signal mask */
183         sigfillset(&sig);
184         sigprocmask(SIG_UNBLOCK, &sig, NULL);
185
186         /* Launch clients as the user; don't give them the wrong euid */
187         if (seteuid(getuid()) == -1) {
188                 weston_log("seteuid failed: %s\n", strerror(errno));
189                 return;
190         }
191
192         /* Duplicate fd to unset the CLOEXEC flag. We don't need to worry about
193          * clobbering fd, as we'll exit/exec either way.
194          */
195         fd = dup(fd);
196         if (fd == -1) {
197                 weston_log("dup failed: %s\n", strerror(errno));
198                 return;
199         }
200
201         snprintf(s, sizeof s, "%d", fd);
202         setenv("WAYLAND_SOCKET", s, 1);
203
204         execl("/bin/sh", "/bin/sh", "-c", command, NULL);
205         weston_log("executing '%s' failed: %s", command, strerror(errno));
206 }
207
208 static struct wl_client *
209 launch_shell_client(struct ivi_compositor *ivi, const char *command)
210 {
211         struct wl_client *client;
212         int sock[2];
213         pid_t pid;
214
215         weston_log("launching' %s'\n", command);
216
217         if (os_socketpair_cloexec(AF_UNIX, SOCK_STREAM, 0, sock) < 0) {
218                 weston_log("socketpair failed while launching '%s': %s\n",
219                            command, strerror(errno));
220                 return NULL;
221         }
222
223         pid = fork();
224         if (pid == -1) {
225                 close(sock[0]);
226                 close(sock[1]);
227                 weston_log("fork failed while launching '%s': %s\n",
228                            command, strerror(errno));
229                 return NULL;
230         }
231
232         if (pid == 0) {
233                 client_exec(command, sock[1]);
234                 _Exit(EXIT_FAILURE);
235         }
236         close(sock[1]);
237
238         client = wl_client_create(ivi->compositor->wl_display, sock[0]);
239         if (!client) {
240                 close(sock[0]);
241                 weston_log("Failed to create wayland client for '%s'",
242                            command);
243                 return NULL;
244         }
245
246         return client;
247 }
248
249 int
250 ivi_launch_shell_client(struct ivi_compositor *ivi)
251 {
252         struct weston_config_section *section;
253         char *command = NULL;
254
255         section = weston_config_get_section(ivi->config, "shell-client",
256                                             NULL, NULL);
257         if (section)
258                 weston_config_section_get_string(section, "command",
259                                                  &command, NULL);
260
261         if (!command)
262                 return -1;
263
264         ivi->shell_client.client = launch_shell_client(ivi, command);
265         if (!ivi->shell_client.client)
266                 return -1;
267
268         return 0;
269 }
270
271 static void
272 destroy_black_view(struct wl_listener *listener, void *data)
273 {
274         struct fullscreen_view *fs =
275                 wl_container_of(listener, fs, fs_destroy);
276
277
278         if (fs && fs->fs) {
279                 if (fs->fs->view && fs->fs->view->surface) {
280                         weston_surface_destroy(fs->fs->view->surface);
281                         fs->fs->view = NULL;
282                 }
283
284                 free(fs->fs);
285                 wl_list_remove(&fs->fs_destroy.link);
286         }
287 }
288
289
290 static void
291 create_black_surface_view(struct ivi_output *output)
292 {
293         struct weston_surface *surface = NULL;
294         struct weston_view *view;
295         struct ivi_compositor *ivi = output->ivi;
296         struct weston_compositor *wc= ivi->compositor;
297         struct weston_output *woutput = output->output;
298
299         surface = weston_surface_create(wc);
300         view = weston_view_create(surface);
301
302         assert(view || surface);
303
304         weston_surface_set_color(surface, 0.0, 0.0, 0.0, 1);
305         weston_surface_set_size(surface, woutput->width, woutput->height);
306         weston_view_set_position(view, woutput->x, woutput->y);
307
308         output->fullscreen_view.fs = zalloc(sizeof(struct ivi_surface));
309         output->fullscreen_view.fs->view = view;
310
311         output->fullscreen_view.fs_destroy.notify = destroy_black_view;
312         wl_signal_add(&woutput->destroy_signal,
313                       &output->fullscreen_view.fs_destroy);
314 }
315
316 static void
317 remove_black_surface(struct ivi_output *output)
318 {
319         struct weston_view *view = output->fullscreen_view.fs->view;
320
321         assert(view->is_mapped == true ||
322                view->surface->is_mapped == true);
323
324         view->is_mapped = false;
325         view->surface->is_mapped = false;
326
327         weston_layer_entry_remove(&view->layer_link);
328         weston_view_update_transform(view);
329
330         weston_output_damage(output->output);
331 }
332
333 static void
334 insert_black_surface(struct ivi_output *output)
335 {
336         struct weston_view *view = output->fullscreen_view.fs->view;
337
338         if (view->is_mapped || view->surface->is_mapped)
339                 return;
340
341         weston_layer_entry_remove(&view->layer_link);
342         weston_layer_entry_insert(&output->ivi->fullscreen.view_list,
343                                   &view->layer_link);
344
345         view->is_mapped = true;
346         view->surface->is_mapped = true;
347
348         weston_view_update_transform(view);
349         weston_output_damage(output->output);
350 }
351
352 static void
353 shell_ready(struct wl_client *client, struct wl_resource *shell_res)
354 {
355         struct ivi_compositor *ivi = wl_resource_get_user_data(shell_res);
356         struct ivi_output *output;
357         struct ivi_surface *surface, *tmp;
358
359         /* Init already finished. Do nothing */
360         if (ivi->shell_client.ready)
361                 return;
362
363         ivi->shell_client.ready = true;
364
365         wl_list_for_each(output, &ivi->outputs, link) {
366                 remove_black_surface(output);
367                 ivi_layout_init(ivi, output);
368         }
369
370         wl_list_for_each_safe(surface, tmp, &ivi->pending_surfaces, link) {
371                 wl_list_remove(&surface->link);
372
373                 if (ivi_check_pending_desktop_surface_popup(surface)) {
374                         ivi_set_desktop_surface_popup(surface);
375                 } else {
376                         ivi_set_desktop_surface(surface);
377                         ivi_layout_desktop_committed(surface);
378                 }
379         }
380 }
381
382 static void
383 shell_set_background(struct wl_client *client,
384                      struct wl_resource *shell_res,
385                      struct wl_resource *surface_res,
386                      struct wl_resource *output_res)
387 {
388         struct weston_head *head = weston_head_from_resource(output_res);
389         struct weston_output *woutput = weston_head_get_output(head);
390         struct ivi_output *output = to_ivi_output(woutput);
391         struct weston_surface *wsurface = wl_resource_get_user_data(surface_res);
392         struct weston_desktop_surface *dsurface;
393         struct ivi_surface *surface;
394
395         dsurface = weston_surface_get_desktop_surface(wsurface);
396         if (!dsurface) {
397                 wl_resource_post_error(shell_res,
398                                        AGL_SHELL_ERROR_INVALID_ARGUMENT,
399                                        "surface must be a desktop surface");
400                 return;
401         }
402
403         surface = weston_desktop_surface_get_user_data(dsurface);
404         if (surface->role != IVI_SURFACE_ROLE_NONE) {
405                 wl_resource_post_error(shell_res,
406                                        AGL_SHELL_ERROR_INVALID_ARGUMENT,
407                                        "surface already has another ivi role");
408                 return;
409         }
410
411         if (output->background) {
412                 wl_resource_post_error(shell_res,
413                                        AGL_SHELL_ERROR_BACKGROUND_EXISTS,
414                                        "output already has background");
415                 return;
416         }
417
418         surface->role = IVI_SURFACE_ROLE_BACKGROUND;
419         surface->bg.output = output;
420         wl_list_remove(&surface->link);
421         wl_list_init(&surface->link);
422
423         output->background = surface;
424
425         weston_desktop_surface_set_maximized(dsurface, true);
426         weston_desktop_surface_set_size(dsurface,
427                                         output->output->width,
428                                         output->output->height);
429 }
430
431 static void
432 shell_set_panel(struct wl_client *client,
433                 struct wl_resource *shell_res,
434                 struct wl_resource *surface_res,
435                 struct wl_resource *output_res,
436                 uint32_t edge)
437 {
438         struct weston_head *head = weston_head_from_resource(output_res);
439         struct weston_output *woutput = weston_head_get_output(head);
440         struct ivi_output *output = to_ivi_output(woutput);
441         struct weston_surface *wsurface = wl_resource_get_user_data(surface_res);
442         struct weston_desktop_surface *dsurface;
443         struct ivi_surface *surface;
444         struct ivi_surface **member;
445         int32_t width = 0, height = 0;
446
447         dsurface = weston_surface_get_desktop_surface(wsurface);
448         if (!dsurface) {
449                 wl_resource_post_error(shell_res,
450                                        AGL_SHELL_ERROR_INVALID_ARGUMENT,
451                                        "surface must be a desktop surface");
452                 return;
453         }
454
455         surface = weston_desktop_surface_get_user_data(dsurface);
456         if (surface->role != IVI_SURFACE_ROLE_NONE) {
457                 wl_resource_post_error(shell_res,
458                                        AGL_SHELL_ERROR_INVALID_ARGUMENT,
459                                        "surface already has another ivi role");
460                 return;
461         }
462
463         switch (edge) {
464         case AGL_SHELL_EDGE_TOP:
465                 member = &output->top;
466                 break;
467         case AGL_SHELL_EDGE_BOTTOM:
468                 member = &output->bottom;
469                 break;
470         case AGL_SHELL_EDGE_LEFT:
471                 member = &output->left;
472                 break;
473         case AGL_SHELL_EDGE_RIGHT:
474                 member = &output->right;
475                 break;
476         default:
477                 wl_resource_post_error(shell_res,
478                                        AGL_SHELL_ERROR_INVALID_ARGUMENT,
479                                        "invalid edge for panel");
480                 return;
481         }
482
483         if (*member) {
484                 wl_resource_post_error(shell_res,
485                                        AGL_SHELL_ERROR_BACKGROUND_EXISTS,
486                                        "output already has panel on this edge");
487                 return;
488         }
489
490         surface->role = IVI_SURFACE_ROLE_PANEL;
491         surface->panel.output = output;
492         surface->panel.edge = edge;
493         wl_list_remove(&surface->link);
494         wl_list_init(&surface->link);
495
496         *member = surface;
497
498         switch (surface->panel.edge) {
499         case AGL_SHELL_EDGE_TOP:
500         case AGL_SHELL_EDGE_BOTTOM:
501                 width = woutput->width;
502                 break;
503         case AGL_SHELL_EDGE_LEFT:
504         case AGL_SHELL_EDGE_RIGHT:
505                 height = woutput->height;
506                 break;
507         }
508
509         weston_desktop_surface_set_size(dsurface, width, height);
510 }
511
512 static void
513 shell_activate_app(struct wl_client *client,
514                    struct wl_resource *shell_res,
515                    const char *app_id,
516                    struct wl_resource *output_res)
517 {
518         struct weston_head *head = weston_head_from_resource(output_res);
519         struct weston_output *woutput = weston_head_get_output(head);
520         struct ivi_output *output = to_ivi_output(woutput);
521
522         ivi_layout_activate(output, app_id);
523 }
524
525 static const struct agl_shell_interface agl_shell_implementation = {
526         .ready = shell_ready,
527         .set_background = shell_set_background,
528         .set_panel = shell_set_panel,
529         .activate_app = shell_activate_app,
530 };
531
532 static void
533 shell_desktop_set_app_property(struct wl_client *client,
534                                struct wl_resource *shell_res,
535                                const char *app_id, uint32_t role,
536                                int x, int y, struct wl_resource *output_res)
537 {
538         struct weston_head *head = weston_head_from_resource(output_res);
539         struct weston_output *woutput = weston_head_get_output(head);
540         struct ivi_output *output = to_ivi_output(woutput);
541
542         /* temporary store the app_id such that, when created to be check against */
543         if (role == AGL_SHELL_DESKTOP_APP_ROLE_POPUP)
544                 ivi_set_pending_desktop_surface_popup(output, x, y, app_id);
545 }
546
547 static const struct agl_shell_desktop_interface agl_shell_desktop_implementation = {
548         .activate_app = shell_activate_app,
549         .set_app_property = shell_desktop_set_app_property
550 };
551
552 static void
553 unbind_agl_shell(struct wl_resource *resource)
554 {
555         struct ivi_compositor *ivi;
556         struct ivi_output *output;
557         struct ivi_surface *surf, *surf_tmp;
558
559         ivi = wl_resource_get_user_data(resource);
560         wl_list_for_each(output, &ivi->outputs, link) {
561                 free(output->background);
562                 output->background = NULL;
563
564                 free(output->top);
565                 output->top = NULL;
566
567                 free(output->bottom);
568                 output->bottom = NULL;
569
570                 free(output->left);
571                 output->left = NULL;
572
573                 free(output->right);
574                 output->right = NULL;
575
576                 /* reset the active surf if there's one present */
577                 if (output->active) {
578                         output->active->view->is_mapped = false;
579                         output->active->view->surface->is_mapped = false;
580
581                         weston_layer_entry_remove(&output->active->view->layer_link);
582                         output->active = NULL;
583                 }
584
585                 insert_black_surface(output);
586         }
587
588         wl_list_for_each_safe(surf, surf_tmp, &ivi->surfaces, link) {
589                 wl_list_remove(&surf->link);
590                 wl_list_init(&surf->link);
591         }
592
593         wl_list_for_each_safe(surf, surf_tmp, &ivi->pending_surfaces, link) {
594                 wl_list_remove(&surf->link);
595                 wl_list_init(&surf->link);
596         }
597
598         wl_list_init(&ivi->surfaces);
599         wl_list_init(&ivi->pending_surfaces);
600
601         ivi->shell_client.ready = false;
602         ivi->shell_client.resource = NULL;
603         ivi->shell_client.client = NULL;
604 }
605
606 static void
607 bind_agl_shell(struct wl_client *client,
608                void *data, uint32_t version, uint32_t id)
609 {
610         struct ivi_compositor *ivi = data;
611         struct wl_resource *resource;
612
613         resource = wl_resource_create(client, &agl_shell_interface,
614                                       1, id);
615         if (!resource) {
616                 wl_client_post_no_memory(client);
617                 return;
618         }
619
620 #if 0
621         if (ivi->shell_client.client != client) {
622                 wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
623                                        "client not authorized to use agl_shell");
624                 return;
625         }
626 #endif
627
628         if (ivi->shell_client.resource) {
629                 wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
630                                        "agl_shell has already been bound");
631                 return;
632         }
633
634         wl_resource_set_implementation(resource, &agl_shell_implementation,
635                                        ivi, unbind_agl_shell);
636         ivi->shell_client.resource = resource;
637 }
638
639 static void
640 unbind_agl_shell_desktop(struct wl_resource *resource)
641 {
642         struct desktop_client *dclient = wl_resource_get_user_data(resource);
643
644         wl_list_remove(&dclient->link);
645         free(dclient);
646 }
647
648 static void
649 bind_agl_shell_desktop(struct wl_client *client,
650                        void *data, uint32_t version, uint32_t id)
651 {
652         struct ivi_compositor *ivi = data;
653         struct wl_resource *resource;
654         struct desktop_client *dclient = zalloc(sizeof(*dclient));
655
656         if (!dclient) {
657                 wl_client_post_no_memory(client);
658                 return;
659         }
660
661         resource = wl_resource_create(client, &agl_shell_desktop_interface,
662                                       version, id);
663         dclient->ivi = ivi;
664         if (!resource) {
665                 wl_client_post_no_memory(client);
666                 return;
667         }
668
669         wl_resource_set_implementation(resource, &agl_shell_desktop_implementation,
670                                        dclient, unbind_agl_shell_desktop);
671
672         dclient->resource = resource;
673         wl_list_insert(&ivi->desktop_clients, &dclient->link);
674
675         /* advertise xdg surfaces */
676         ivi_shell_advertise_xdg_surfaces(ivi, resource);
677 }
678
679 int
680 ivi_shell_create_global(struct ivi_compositor *ivi)
681 {
682         ivi->agl_shell = wl_global_create(ivi->compositor->wl_display,
683                                           &agl_shell_interface, 1,
684                                           ivi, bind_agl_shell);
685         if (!ivi->agl_shell) {
686                 weston_log("Failed to create wayland global.\n");
687                 return -1;
688         }
689
690         ivi->agl_shell_desktop = wl_global_create(ivi->compositor->wl_display,
691                                                   &agl_shell_desktop_interface, 1,
692                                                   ivi, bind_agl_shell_desktop);
693         if (!ivi->agl_shell_desktop) {
694                 weston_log("Failed to create wayland global (agl_shell_desktop).\n");
695                 return -1;
696         }
697
698         return 0;
699 }