agl-shell-desktop: Add the ability to hide client windows
[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 void
526 shell_deactivate_app(struct wl_client *client,
527                    struct wl_resource *shell_res,
528                    const char *app_id)
529 {
530         struct desktop_client *dclient = wl_resource_get_user_data(shell_res);
531         ivi_layout_deactivate(dclient->ivi, app_id);
532 }
533
534 static const struct agl_shell_interface agl_shell_implementation = {
535         .ready = shell_ready,
536         .set_background = shell_set_background,
537         .set_panel = shell_set_panel,
538         .activate_app = shell_activate_app,
539 };
540
541 static void
542 shell_desktop_set_app_property(struct wl_client *client,
543                                struct wl_resource *shell_res,
544                                const char *app_id, uint32_t role,
545                                int x, int y, struct wl_resource *output_res)
546 {
547         struct weston_head *head = weston_head_from_resource(output_res);
548         struct weston_output *woutput = weston_head_get_output(head);
549         struct ivi_output *output = to_ivi_output(woutput);
550
551         /* temporary store the app_id such that, when created to be check against */
552         if (role == AGL_SHELL_DESKTOP_APP_ROLE_POPUP)
553                 ivi_set_pending_desktop_surface_popup(output, x, y, app_id);
554 }
555
556 static const struct agl_shell_desktop_interface agl_shell_desktop_implementation = {
557         .activate_app = shell_activate_app,
558         .set_app_property = shell_desktop_set_app_property,
559         .deactivate_app = shell_deactivate_app,
560 };
561
562 static void
563 unbind_agl_shell(struct wl_resource *resource)
564 {
565         struct ivi_compositor *ivi;
566         struct ivi_output *output;
567         struct ivi_surface *surf, *surf_tmp;
568
569         ivi = wl_resource_get_user_data(resource);
570         wl_list_for_each(output, &ivi->outputs, link) {
571                 free(output->background);
572                 output->background = NULL;
573
574                 free(output->top);
575                 output->top = NULL;
576
577                 free(output->bottom);
578                 output->bottom = NULL;
579
580                 free(output->left);
581                 output->left = NULL;
582
583                 free(output->right);
584                 output->right = NULL;
585
586                 /* reset the active surf if there's one present */
587                 if (output->active) {
588                         output->active->view->is_mapped = false;
589                         output->active->view->surface->is_mapped = false;
590
591                         weston_layer_entry_remove(&output->active->view->layer_link);
592                         output->active = NULL;
593                 }
594
595                 insert_black_surface(output);
596         }
597
598         wl_list_for_each_safe(surf, surf_tmp, &ivi->surfaces, link) {
599                 wl_list_remove(&surf->link);
600                 wl_list_init(&surf->link);
601         }
602
603         wl_list_for_each_safe(surf, surf_tmp, &ivi->pending_surfaces, link) {
604                 wl_list_remove(&surf->link);
605                 wl_list_init(&surf->link);
606         }
607
608         wl_list_init(&ivi->surfaces);
609         wl_list_init(&ivi->pending_surfaces);
610
611         ivi->shell_client.ready = false;
612         ivi->shell_client.resource = NULL;
613         ivi->shell_client.client = NULL;
614 }
615
616 static void
617 bind_agl_shell(struct wl_client *client,
618                void *data, uint32_t version, uint32_t id)
619 {
620         struct ivi_compositor *ivi = data;
621         struct wl_resource *resource;
622
623         resource = wl_resource_create(client, &agl_shell_interface,
624                                       1, id);
625         if (!resource) {
626                 wl_client_post_no_memory(client);
627                 return;
628         }
629
630 #if 0
631         if (ivi->shell_client.client != client) {
632                 wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
633                                        "client not authorized to use agl_shell");
634                 return;
635         }
636 #endif
637
638         if (ivi->shell_client.resource) {
639                 wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
640                                        "agl_shell has already been bound");
641                 return;
642         }
643
644         wl_resource_set_implementation(resource, &agl_shell_implementation,
645                                        ivi, unbind_agl_shell);
646         ivi->shell_client.resource = resource;
647 }
648
649 static void
650 unbind_agl_shell_desktop(struct wl_resource *resource)
651 {
652         struct desktop_client *dclient = wl_resource_get_user_data(resource);
653
654         wl_list_remove(&dclient->link);
655         free(dclient);
656 }
657
658 static void
659 bind_agl_shell_desktop(struct wl_client *client,
660                        void *data, uint32_t version, uint32_t id)
661 {
662         struct ivi_compositor *ivi = data;
663         struct wl_resource *resource;
664         struct desktop_client *dclient = zalloc(sizeof(*dclient));
665
666         if (!dclient) {
667                 wl_client_post_no_memory(client);
668                 return;
669         }
670
671         resource = wl_resource_create(client, &agl_shell_desktop_interface,
672                                       version, id);
673         dclient->ivi = ivi;
674         if (!resource) {
675                 wl_client_post_no_memory(client);
676                 return;
677         }
678
679         wl_resource_set_implementation(resource, &agl_shell_desktop_implementation,
680                                        dclient, unbind_agl_shell_desktop);
681
682         dclient->resource = resource;
683         wl_list_insert(&ivi->desktop_clients, &dclient->link);
684
685         /* advertise xdg surfaces */
686         ivi_shell_advertise_xdg_surfaces(ivi, resource);
687 }
688
689 int
690 ivi_shell_create_global(struct ivi_compositor *ivi)
691 {
692         ivi->agl_shell = wl_global_create(ivi->compositor->wl_display,
693                                           &agl_shell_interface, 1,
694                                           ivi, bind_agl_shell);
695         if (!ivi->agl_shell) {
696                 weston_log("Failed to create wayland global.\n");
697                 return -1;
698         }
699
700         ivi->agl_shell_desktop = wl_global_create(ivi->compositor->wl_display,
701                                                   &agl_shell_desktop_interface, 1,
702                                                   ivi, bind_agl_shell_desktop);
703         if (!ivi->agl_shell_desktop) {
704                 weston_log("Failed to create wayland global (agl_shell_desktop).\n");
705                 return -1;
706         }
707
708         return 0;
709 }