protocol: Add agl-shell-desktop protocol
[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         assert(surface->role == IVI_SURFACE_ROLE_NONE);
54
55         surface->role = IVI_SURFACE_ROLE_DESKTOP;
56         wl_list_insert(&surface->ivi->surfaces, &surface->link);
57 }
58
59 void
60 ivi_shell_init_black_fs(struct ivi_compositor *ivi)
61 {
62         struct ivi_output *out;
63
64         wl_list_for_each(out, &ivi->outputs, link) {
65                 create_black_surface_view(out);
66                 insert_black_surface(out);
67         }
68 }
69
70 int
71 ivi_shell_init(struct ivi_compositor *ivi)
72 {
73         weston_layer_init(&ivi->hidden, ivi->compositor);
74         weston_layer_init(&ivi->background, ivi->compositor);
75         weston_layer_init(&ivi->normal, ivi->compositor);
76         weston_layer_init(&ivi->panel, ivi->compositor);
77         weston_layer_init(&ivi->fullscreen, ivi->compositor);
78
79         weston_layer_set_position(&ivi->hidden,
80                                   WESTON_LAYER_POSITION_HIDDEN);
81         weston_layer_set_position(&ivi->background,
82                                   WESTON_LAYER_POSITION_BACKGROUND);
83         weston_layer_set_position(&ivi->normal,
84                                   WESTON_LAYER_POSITION_NORMAL);
85         weston_layer_set_position(&ivi->panel,
86                                   WESTON_LAYER_POSITION_UI);
87         weston_layer_set_position(&ivi->fullscreen,
88                                   WESTON_LAYER_POSITION_FULLSCREEN);
89
90         return 0;
91 }
92
93 static void
94 client_exec(const char *command, int fd)
95 {
96         sigset_t sig;
97         char s[32];
98
99         /* Don't give the child our signal mask */
100         sigfillset(&sig);
101         sigprocmask(SIG_UNBLOCK, &sig, NULL);
102
103         /* Launch clients as the user; don't give them the wrong euid */
104         if (seteuid(getuid()) == -1) {
105                 weston_log("seteuid failed: %s\n", strerror(errno));
106                 return;
107         }
108
109         /* Duplicate fd to unset the CLOEXEC flag. We don't need to worry about
110          * clobbering fd, as we'll exit/exec either way.
111          */
112         fd = dup(fd);
113         if (fd == -1) {
114                 weston_log("dup failed: %s\n", strerror(errno));
115                 return;
116         }
117
118         snprintf(s, sizeof s, "%d", fd);
119         setenv("WAYLAND_SOCKET", s, 1);
120
121         execl("/bin/sh", "/bin/sh", "-c", command, NULL);
122         weston_log("executing '%s' failed: %s", command, strerror(errno));
123 }
124
125 static struct wl_client *
126 launch_shell_client(struct ivi_compositor *ivi, const char *command)
127 {
128         struct wl_client *client;
129         int sock[2];
130         pid_t pid;
131
132         weston_log("launching' %s'\n", command);
133
134         if (os_socketpair_cloexec(AF_UNIX, SOCK_STREAM, 0, sock) < 0) {
135                 weston_log("socketpair failed while launching '%s': %s\n",
136                            command, strerror(errno));
137                 return NULL;
138         }
139
140         pid = fork();
141         if (pid == -1) {
142                 close(sock[0]);
143                 close(sock[1]);
144                 weston_log("fork failed while launching '%s': %s\n",
145                            command, strerror(errno));
146                 return NULL;
147         }
148
149         if (pid == 0) {
150                 client_exec(command, sock[1]);
151                 _Exit(EXIT_FAILURE);
152         }
153         close(sock[1]);
154
155         client = wl_client_create(ivi->compositor->wl_display, sock[0]);
156         if (!client) {
157                 close(sock[0]);
158                 weston_log("Failed to create wayland client for '%s'",
159                            command);
160                 return NULL;
161         }
162
163         return client;
164 }
165
166 int
167 ivi_launch_shell_client(struct ivi_compositor *ivi)
168 {
169         struct weston_config_section *section;
170         char *command = NULL;
171
172         section = weston_config_get_section(ivi->config, "shell-client",
173                                             NULL, NULL);
174         if (section)
175                 weston_config_section_get_string(section, "command",
176                                                  &command, NULL);
177
178         if (!command)
179                 return -1;
180
181         ivi->shell_client.client = launch_shell_client(ivi, command);
182         if (!ivi->shell_client.client)
183                 return -1;
184
185         return 0;
186 }
187
188 static void
189 destroy_black_view(struct wl_listener *listener, void *data)
190 {
191         struct fullscreen_view *fs =
192                 wl_container_of(listener, fs, fs_destroy);
193
194
195         if (fs && fs->fs) {
196                 if (fs->fs->view && fs->fs->view->surface) {
197                         weston_surface_destroy(fs->fs->view->surface);
198                         fs->fs->view = NULL;
199                 }
200
201                 free(fs->fs);
202                 wl_list_remove(&fs->fs_destroy.link);
203         }
204 }
205
206
207 static void
208 create_black_surface_view(struct ivi_output *output)
209 {
210         struct weston_surface *surface = NULL;
211         struct weston_view *view;
212         struct ivi_compositor *ivi = output->ivi;
213         struct weston_compositor *wc= ivi->compositor;
214         struct weston_output *woutput = output->output;
215
216         surface = weston_surface_create(wc);
217         view = weston_view_create(surface);
218
219         assert(view || surface);
220
221         weston_surface_set_color(surface, 0.0, 0.0, 0.0, 1);
222         weston_surface_set_size(surface, woutput->width, woutput->height);
223         weston_view_set_position(view, woutput->x, woutput->y);
224
225         output->fullscreen_view.fs = zalloc(sizeof(struct ivi_surface));
226         output->fullscreen_view.fs->view = view;
227
228         output->fullscreen_view.fs_destroy.notify = destroy_black_view;
229         wl_signal_add(&woutput->destroy_signal,
230                       &output->fullscreen_view.fs_destroy);
231 }
232
233 static void
234 remove_black_surface(struct ivi_output *output)
235 {
236         struct weston_view *view = output->fullscreen_view.fs->view;
237
238         assert(view->is_mapped == true ||
239                view->surface->is_mapped == true);
240
241         view->is_mapped = false;
242         view->surface->is_mapped = false;
243
244         weston_layer_entry_remove(&view->layer_link);
245         weston_view_update_transform(view);
246
247         weston_output_damage(output->output);
248 }
249
250 static void
251 insert_black_surface(struct ivi_output *output)
252 {
253         struct weston_view *view = output->fullscreen_view.fs->view;
254
255         if (view->is_mapped || view->surface->is_mapped)
256                 return;
257
258         weston_layer_entry_remove(&view->layer_link);
259         weston_layer_entry_insert(&output->ivi->fullscreen.view_list,
260                                   &view->layer_link);
261
262         view->is_mapped = true;
263         view->surface->is_mapped = true;
264
265         weston_view_update_transform(view);
266         weston_output_damage(output->output);
267 }
268
269 static void
270 shell_ready(struct wl_client *client, struct wl_resource *shell_res)
271 {
272         struct ivi_compositor *ivi = wl_resource_get_user_data(shell_res);
273         struct ivi_output *output;
274         struct ivi_surface *surface, *tmp;
275
276         /* Init already finished. Do nothing */
277         if (ivi->shell_client.ready)
278                 return;
279
280         ivi->shell_client.ready = true;
281
282         wl_list_for_each(output, &ivi->outputs, link) {
283                 remove_black_surface(output);
284                 ivi_layout_init(ivi, output);
285         }
286
287         wl_list_for_each_safe(surface, tmp, &ivi->pending_surfaces, link) {
288                 wl_list_remove(&surface->link);
289                 ivi_set_desktop_surface(surface);
290         }
291 }
292
293 static void
294 shell_set_background(struct wl_client *client,
295                      struct wl_resource *shell_res,
296                      struct wl_resource *surface_res,
297                      struct wl_resource *output_res)
298 {
299         struct weston_head *head = weston_head_from_resource(output_res);
300         struct weston_output *woutput = weston_head_get_output(head);
301         struct ivi_output *output = to_ivi_output(woutput);
302         struct weston_surface *wsurface = wl_resource_get_user_data(surface_res);
303         struct weston_desktop_surface *dsurface;
304         struct ivi_surface *surface;
305
306         dsurface = weston_surface_get_desktop_surface(wsurface);
307         if (!dsurface) {
308                 wl_resource_post_error(shell_res,
309                                        AGL_SHELL_ERROR_INVALID_ARGUMENT,
310                                        "surface must be a desktop surface");
311                 return;
312         }
313
314         surface = weston_desktop_surface_get_user_data(dsurface);
315         if (surface->role != IVI_SURFACE_ROLE_NONE) {
316                 wl_resource_post_error(shell_res,
317                                        AGL_SHELL_ERROR_INVALID_ARGUMENT,
318                                        "surface already has another ivi role");
319                 return;
320         }
321
322         if (output->background) {
323                 wl_resource_post_error(shell_res,
324                                        AGL_SHELL_ERROR_BACKGROUND_EXISTS,
325                                        "output already has background");
326                 return;
327         }
328
329         surface->role = IVI_SURFACE_ROLE_BACKGROUND;
330         surface->bg.output = output;
331         wl_list_remove(&surface->link);
332         wl_list_init(&surface->link);
333
334         output->background = surface;
335
336         weston_desktop_surface_set_maximized(dsurface, true);
337         weston_desktop_surface_set_size(dsurface,
338                                         output->output->width,
339                                         output->output->height);
340 }
341
342 static void
343 shell_set_panel(struct wl_client *client,
344                 struct wl_resource *shell_res,
345                 struct wl_resource *surface_res,
346                 struct wl_resource *output_res,
347                 uint32_t edge)
348 {
349         struct weston_head *head = weston_head_from_resource(output_res);
350         struct weston_output *woutput = weston_head_get_output(head);
351         struct ivi_output *output = to_ivi_output(woutput);
352         struct weston_surface *wsurface = wl_resource_get_user_data(surface_res);
353         struct weston_desktop_surface *dsurface;
354         struct ivi_surface *surface;
355         struct ivi_surface **member;
356         int32_t width = 0, height = 0;
357
358         dsurface = weston_surface_get_desktop_surface(wsurface);
359         if (!dsurface) {
360                 wl_resource_post_error(shell_res,
361                                        AGL_SHELL_ERROR_INVALID_ARGUMENT,
362                                        "surface must be a desktop surface");
363                 return;
364         }
365
366         surface = weston_desktop_surface_get_user_data(dsurface);
367         if (surface->role != IVI_SURFACE_ROLE_NONE) {
368                 wl_resource_post_error(shell_res,
369                                        AGL_SHELL_ERROR_INVALID_ARGUMENT,
370                                        "surface already has another ivi role");
371                 return;
372         }
373
374         switch (edge) {
375         case AGL_SHELL_EDGE_TOP:
376                 member = &output->top;
377                 break;
378         case AGL_SHELL_EDGE_BOTTOM:
379                 member = &output->bottom;
380                 break;
381         case AGL_SHELL_EDGE_LEFT:
382                 member = &output->left;
383                 break;
384         case AGL_SHELL_EDGE_RIGHT:
385                 member = &output->right;
386                 break;
387         default:
388                 wl_resource_post_error(shell_res,
389                                        AGL_SHELL_ERROR_INVALID_ARGUMENT,
390                                        "invalid edge for panel");
391                 return;
392         }
393
394         if (*member) {
395                 wl_resource_post_error(shell_res,
396                                        AGL_SHELL_ERROR_BACKGROUND_EXISTS,
397                                        "output already has panel on this edge");
398                 return;
399         }
400
401         surface->role = IVI_SURFACE_ROLE_PANEL;
402         surface->panel.output = output;
403         surface->panel.edge = edge;
404         wl_list_remove(&surface->link);
405         wl_list_init(&surface->link);
406
407         *member = surface;
408
409         switch (surface->panel.edge) {
410         case AGL_SHELL_EDGE_TOP:
411         case AGL_SHELL_EDGE_BOTTOM:
412                 width = woutput->width;
413                 break;
414         case AGL_SHELL_EDGE_LEFT:
415         case AGL_SHELL_EDGE_RIGHT:
416                 height = woutput->height;
417                 break;
418         }
419
420         weston_desktop_surface_set_size(dsurface, width, height);
421 }
422
423 static void
424 shell_activate_app(struct wl_client *client,
425                    struct wl_resource *shell_res,
426                    const char *app_id,
427                    struct wl_resource *output_res)
428 {
429         struct weston_head *head = weston_head_from_resource(output_res);
430         struct weston_output *woutput = weston_head_get_output(head);
431         struct ivi_output *output = to_ivi_output(woutput);
432
433         ivi_layout_activate(output, app_id);
434 }
435
436 static const struct agl_shell_interface agl_shell_implementation = {
437         .ready = shell_ready,
438         .set_background = shell_set_background,
439         .set_panel = shell_set_panel,
440         .activate_app = shell_activate_app,
441 };
442
443 static const struct agl_shell_desktop_interface agl_shell_desktop_implementation = {
444         .activate_app = shell_activate_app,
445 };
446
447 static void
448 unbind_agl_shell(struct wl_resource *resource)
449 {
450         struct ivi_compositor *ivi;
451         struct ivi_output *output;
452         struct ivi_surface *surf, *surf_tmp;
453
454         ivi = wl_resource_get_user_data(resource);
455         wl_list_for_each(output, &ivi->outputs, link) {
456                 free(output->background);
457                 output->background = NULL;
458
459                 free(output->top);
460                 output->top = NULL;
461
462                 free(output->bottom);
463                 output->bottom = NULL;
464
465                 free(output->left);
466                 output->left = NULL;
467
468                 free(output->right);
469                 output->right = NULL;
470
471                 /* reset the active surf if there's one present */
472                 if (output->active) {
473                         output->active->view->is_mapped = false;
474                         output->active->view->surface->is_mapped = false;
475
476                         weston_layer_entry_remove(&output->active->view->layer_link);
477                         output->active = NULL;
478                 }
479
480                 insert_black_surface(output);
481         }
482
483         wl_list_for_each_safe(surf, surf_tmp, &ivi->surfaces, link) {
484                 wl_list_remove(&surf->link);
485                 wl_list_init(&surf->link);
486         }
487
488         wl_list_for_each_safe(surf, surf_tmp, &ivi->pending_surfaces, link) {
489                 wl_list_remove(&surf->link);
490                 wl_list_init(&surf->link);
491         }
492
493         wl_list_init(&ivi->surfaces);
494         wl_list_init(&ivi->pending_surfaces);
495
496         ivi->shell_client.ready = false;
497         ivi->shell_client.resource = NULL;
498         ivi->shell_client.client = NULL;
499 }
500
501 static void
502 bind_agl_shell(struct wl_client *client,
503                void *data, uint32_t version, uint32_t id)
504 {
505         struct ivi_compositor *ivi = data;
506         struct wl_resource *resource;
507
508         resource = wl_resource_create(client, &agl_shell_interface,
509                                       1, id);
510         if (!resource) {
511                 wl_client_post_no_memory(client);
512                 return;
513         }
514
515 #if 0
516         if (ivi->shell_client.client != client) {
517                 wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
518                                        "client not authorized to use agl_shell");
519                 return;
520         }
521 #endif
522
523         if (ivi->shell_client.resource) {
524                 wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
525                                        "agl_shell has already been bound");
526                 return;
527         }
528
529         wl_resource_set_implementation(resource, &agl_shell_implementation,
530                                        ivi, unbind_agl_shell);
531         ivi->shell_client.resource = resource;
532 }
533
534 static void
535 unbind_agl_shell_desktop(struct wl_resource *resource)
536 {
537         struct desktop_client *dclient = wl_resource_get_user_data(resource);
538
539         wl_list_remove(&dclient->link);
540         free(dclient);
541 }
542
543 static void
544 bind_agl_shell_desktop(struct wl_client *client,
545                        void *data, uint32_t version, uint32_t id)
546 {
547         struct ivi_compositor *ivi = data;
548         struct wl_resource *resource;
549         struct desktop_client *dclient = zalloc(sizeof(*dclient));
550
551         if (!dclient) {
552                 wl_client_post_no_memory(client);
553                 return;
554         }
555
556         resource = wl_resource_create(client, &agl_shell_desktop_interface,
557                                       version, id);
558         if (!resource) {
559                 wl_client_post_no_memory(client);
560                 return;
561         }
562
563         wl_resource_set_implementation(resource, &agl_shell_desktop_implementation,
564                                        dclient, unbind_agl_shell_desktop);
565
566         dclient->resource = resource;
567         wl_list_insert(&ivi->desktop_clients, &dclient->link);
568 }
569
570 int
571 ivi_shell_create_global(struct ivi_compositor *ivi)
572 {
573         ivi->agl_shell = wl_global_create(ivi->compositor->wl_display,
574                                           &agl_shell_interface, 1,
575                                           ivi, bind_agl_shell);
576         if (!ivi->agl_shell) {
577                 weston_log("Failed to create wayland global.\n");
578                 return -1;
579         }
580
581         ivi->agl_shell_desktop = wl_global_create(ivi->compositor->wl_display,
582                                                   &agl_shell_desktop_interface, 1,
583                                                   ivi, bind_agl_shell_desktop);
584         if (!ivi->agl_shell_desktop) {
585                 weston_log("Failed to create wayland global (agl_shell_desktop).\n");
586                 return -1;
587         }
588
589         return 0;
590 }