Import source, backport to libweston 6.0
[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-6/compositor.h>
37 #include <libweston-6/config-parser.h>
38
39 #include "shared/os-compatibility.h"
40
41 #include "agl-shell-server-protocol.h"
42
43 void
44 ivi_set_desktop_surface(struct ivi_surface *surface)
45 {
46         assert(surface->role == IVI_SURFACE_ROLE_NONE);
47
48         surface->role = IVI_SURFACE_ROLE_DESKTOP;
49         wl_list_insert(&surface->ivi->surfaces, &surface->link);
50 }
51
52 /* TODO: Replace this with some callback or similar, to have
53  * adjustable window management policy.
54  */
55 void
56 ivi_reflow_outputs(struct ivi_compositor *ivi)
57 {
58         struct ivi_surface *surface;
59         struct ivi_output *output;
60         int i = 0;
61
62         if (wl_list_empty(&ivi->outputs))
63                 return;
64
65         output = wl_container_of(ivi->outputs.next, output, link);
66
67         wl_list_for_each(surface, &ivi->surfaces, link) {
68                 struct weston_desktop_surface *dsurface = surface->dsurface;
69
70                 int32_t w = output->area.width / 4;
71                 int32_t h = output->area.height / 2;
72                 int32_t x = output->output->x + output->area.x + w * (i % 4);
73                 int32_t y = output->output->y + output->area.y + h * (i / 4);
74
75                 if (surface->old_geom.width == -1) {
76                         weston_desktop_surface_set_size(dsurface, w, h);
77                         continue;
78                 } else {
79                         ivi_layout_set_mapped(surface);
80                         ivi_layout_set_position(surface, x, y, w, h);
81                 }
82
83                 if (++i == 8) {
84                         if (output->link.next == &ivi->outputs)
85                                 break;
86
87                         output = wl_container_of(output->link.next,
88                                                  output, link);
89                         i = 0;
90                 }
91         }
92
93         ivi_layout_commit(ivi);
94 }
95
96 void
97 ivi_layout_set_mapped(struct ivi_surface *surface)
98 {
99         surface->pending.flags |= IVI_SURFACE_PROP_MAP;
100 }
101
102 void
103 ivi_layout_set_position(struct ivi_surface *surface,
104                         int32_t x, int32_t y,
105                         int32_t width, int32_t height)
106 {
107         surface->pending.flags |= IVI_SURFACE_PROP_POSITION;
108         surface->pending.x = x;
109         surface->pending.y = y;
110         surface->pending.width = width;
111         surface->pending.height = height;
112 }
113
114 void
115 ivi_layout_commit(struct ivi_compositor *ivi)
116 {
117         struct ivi_surface *surface;
118
119         wl_list_for_each(surface, &ivi->surfaces, link) {
120                 struct weston_desktop_surface *dsurface = surface->dsurface;
121                 struct weston_surface *wsurface =
122                         weston_desktop_surface_get_surface(dsurface);
123                 struct weston_geometry geom;
124                 struct weston_view *view;
125
126                 /*
127                  * TODO: Hoist view into ivi_struct. It doesn't need ot be part
128                  * of the tagged union.
129                  */
130                 switch (surface->role) {
131                 case IVI_SURFACE_ROLE_DESKTOP:
132                         view = surface->desktop.view;
133                         break;
134                 case IVI_SURFACE_ROLE_BACKGROUND:
135                         view = surface->bg.view;
136                         break;
137                 case IVI_SURFACE_ROLE_PANEL:
138                         view = surface->panel.view;
139                         break;
140                 default:
141                         continue;
142                 }
143
144                 if (surface->pending.flags & IVI_SURFACE_PROP_MAP) {
145                         view = weston_desktop_surface_create_view(dsurface);
146                         wsurface->is_mapped = true;
147
148                         surface->desktop.view = view;
149                         weston_layer_entry_insert(&ivi->normal.view_list,
150                                                   &view->layer_link);
151                         weston_view_update_transform(view);
152                         weston_view_set_mask_infinite(view);
153                         weston_view_schedule_repaint(view);
154                 }
155
156                 geom = weston_desktop_surface_get_geometry(dsurface);
157
158                 if (surface->pending.flags & IVI_SURFACE_PROP_POSITION) {
159                         weston_desktop_surface_set_size(dsurface,
160                                                         surface->pending.width,
161                                                         surface->pending.height);
162                         weston_view_set_position(view,
163                                                  surface->pending.x - geom.x,
164                                                  surface->pending.y - geom.y);
165                 }
166
167                 surface->pending.flags = 0;
168         }
169 }
170
171 int
172 ivi_shell_init(struct ivi_compositor *ivi)
173 {
174         weston_layer_init(&ivi->background, ivi->compositor);
175         weston_layer_init(&ivi->normal, ivi->compositor);
176         weston_layer_init(&ivi->panel, ivi->compositor);
177         weston_layer_init(&ivi->fullscreen, ivi->compositor);
178
179         weston_layer_set_position(&ivi->background,
180                                   WESTON_LAYER_POSITION_BACKGROUND);
181         weston_layer_set_position(&ivi->normal,
182                                   WESTON_LAYER_POSITION_NORMAL);
183         weston_layer_set_position(&ivi->panel,
184                                   WESTON_LAYER_POSITION_UI);
185         weston_layer_set_position(&ivi->fullscreen,
186                                   WESTON_LAYER_POSITION_FULLSCREEN);
187
188         return 0;
189 }
190
191 static void
192 client_exec(const char *command, int fd)
193 {
194         sigset_t sig;
195         char s[32];
196
197         /* Don't give the child our signal mask */
198         sigfillset(&sig);
199         sigprocmask(SIG_UNBLOCK, &sig, NULL);
200
201         /* Launch clients as the user; don't give them the wrong euid */
202         if (seteuid(getuid()) == -1) {
203                 weston_log("seteuid failed: %s\n", strerror(errno));
204                 return;
205         }
206
207         /* Duplicate fd to unset the CLOEXEC flag. We don't need to worry about
208          * clobbering fd, as we'll exit/exec either way.
209          */
210         fd = dup(fd);
211         if (fd == -1) {
212                 weston_log("dup failed: %s\n", strerror(errno));
213                 return;
214         }
215
216         snprintf(s, sizeof s, "%d", fd);
217         setenv("WAYLAND_SOCKET", s, 1);
218
219         execl("/bin/sh", "/bin/sh", "-c", command, NULL);
220         weston_log("executing '%s' failed: %s", command, strerror(errno));
221 }
222
223 static struct wl_client *
224 launch_shell_client(struct ivi_compositor *ivi, const char *command)
225 {
226         struct wl_client *client;
227         int sock[2];
228         pid_t pid;
229
230         weston_log("launching' %s'\n", command);
231
232         if (os_socketpair_cloexec(AF_UNIX, SOCK_STREAM, 0, sock) < 0) {
233                 weston_log("socketpair failed while launching '%s': %s\n",
234                            command, strerror(errno));
235                 return NULL;
236         }
237
238         pid = fork();
239         if (pid == -1) {
240                 close(sock[0]);
241                 close(sock[1]);
242                 weston_log("fork failed while launching '%s': %s\n",
243                            command, strerror(errno));
244                 return NULL;
245         }
246
247         if (pid == 0) {
248                 client_exec(command, sock[1]);
249                 _Exit(EXIT_FAILURE);
250         }
251         close(sock[1]);
252
253         client = wl_client_create(ivi->compositor->wl_display, sock[0]);
254         if (!client) {
255                 close(sock[0]);
256                 weston_log("Failed to create wayland client for '%s'",
257                            command);
258                 return NULL;
259         }
260
261         return client;
262 }
263
264 int
265 ivi_launch_shell_client(struct ivi_compositor *ivi)
266 {
267         struct weston_config_section *section;
268         char *command = NULL;
269
270         section = weston_config_get_section(ivi->config, "shell-client",
271                                             NULL, NULL);
272         if (section)
273                 weston_config_section_get_string(section, "command",
274                                                  &command, NULL);
275
276         if (!command)
277                 return -1;
278
279         ivi->shell_client.client = launch_shell_client(ivi, command);
280         if (!ivi->shell_client.client)
281                 return -1;
282
283         return 0;
284 }
285
286 static void
287 shell_ready(struct wl_client *client, struct wl_resource *shell_res)
288 {
289         struct ivi_compositor *ivi = wl_resource_get_user_data(shell_res);
290         struct ivi_output *output;
291         struct ivi_surface *surface, *tmp;
292
293         /* Init already finished. Do nothing */
294         if (ivi->shell_client.ready)
295                 return;
296
297         ivi->shell_client.ready = true;
298         /* TODO: Create a black screen and remove it here */
299
300         wl_list_for_each(output, &ivi->outputs, link) {
301                 struct weston_desktop_surface *dsurf;
302                 struct weston_geometry geom;
303
304                 output->area.x = 0;
305                 output->area.y = 0;
306                 output->area.width = output->output->width;
307                 output->area.height = output->output->height;
308
309                 if (output->top) {
310                         dsurf = output->top->dsurface;
311                         geom = weston_desktop_surface_get_geometry(dsurf);
312
313                         output->area.y += geom.height;
314                         output->area.height -= geom.height;
315                 }
316                 if (output->bottom) {
317                         dsurf = output->bottom->dsurface;
318                         geom = weston_desktop_surface_get_geometry(dsurf);
319
320                         output->area.height -= geom.height;
321                 }
322                 if (output->left) {
323                         dsurf = output->left->dsurface;
324                         geom = weston_desktop_surface_get_geometry(dsurf);
325
326                         output->area.x += geom.width;
327                         output->area.width -= geom.width;
328                 }
329                 if (output->right) {
330                         dsurf = output->right->dsurface;
331                         geom = weston_desktop_surface_get_geometry(dsurf);
332
333                         output->area.width -= geom.width;
334                 }
335
336                 weston_log("Usable area: %dx%d+%d,%d\n",
337                            output->area.width, output->area.height,
338                            output->area.x, output->area.y);
339         }
340
341         wl_list_for_each_safe(surface, tmp, &ivi->pending_surfaces, link) {
342                 wl_list_remove(&surface->link);
343                 ivi_set_desktop_surface(surface);
344         }
345 }
346
347 static void
348 shell_set_background(struct wl_client *client,
349                      struct wl_resource *shell_res,
350                      struct wl_resource *surface_res,
351                      struct wl_resource *output_res)
352 {
353         struct weston_head *head = weston_head_from_resource(output_res);
354         struct weston_output *woutput = weston_head_get_output(head);
355         struct ivi_output *output = to_ivi_output(woutput);
356         struct weston_surface *wsurface = wl_resource_get_user_data(surface_res);
357         struct weston_desktop_surface *dsurface;
358         struct ivi_surface *surface;
359
360         dsurface = weston_surface_get_desktop_surface(wsurface);
361         if (!dsurface) {
362                 wl_resource_post_error(shell_res,
363                                        AGL_SHELL_ERROR_INVALID_ARGUMENT,
364                                        "surface must be a desktop surface");
365                 return;
366         }
367
368         surface = weston_desktop_surface_get_user_data(dsurface);
369         if (surface->role != IVI_SURFACE_ROLE_NONE) {
370                 wl_resource_post_error(shell_res,
371                                        AGL_SHELL_ERROR_INVALID_ARGUMENT,
372                                        "surface already has another ivi role");
373                 return;
374         }
375
376         if (output->background) {
377                 wl_resource_post_error(shell_res,
378                                        AGL_SHELL_ERROR_BACKGROUND_EXISTS,
379                                        "output already has background");
380                 return;
381         }
382
383         surface->role = IVI_SURFACE_ROLE_BACKGROUND;
384         surface->bg.output = output;
385         wl_list_remove(&surface->link);
386         wl_list_init(&surface->link);
387
388         output->background = surface;
389
390         weston_desktop_surface_set_maximized(dsurface, true);
391         weston_desktop_surface_set_size(dsurface,
392                                         output->output->width,
393                                         output->output->height);
394 }
395
396 static void
397 shell_set_panel(struct wl_client *client,
398                 struct wl_resource *shell_res,
399                 struct wl_resource *surface_res,
400                 struct wl_resource *output_res,
401                 uint32_t edge)
402 {
403         struct weston_head *head = weston_head_from_resource(output_res);
404         struct weston_output *woutput = weston_head_get_output(head);
405         struct ivi_output *output = to_ivi_output(woutput);
406         struct weston_surface *wsurface = wl_resource_get_user_data(surface_res);
407         struct weston_desktop_surface *dsurface;
408         struct ivi_surface *surface;
409         struct ivi_surface **member;
410         int32_t width = 0, height = 0;
411
412         dsurface = weston_surface_get_desktop_surface(wsurface);
413         if (!dsurface) {
414                 wl_resource_post_error(shell_res,
415                                        AGL_SHELL_ERROR_INVALID_ARGUMENT,
416                                        "surface must be a desktop surface");
417                 return;
418         }
419
420         surface = weston_desktop_surface_get_user_data(dsurface);
421         if (surface->role != IVI_SURFACE_ROLE_NONE) {
422                 wl_resource_post_error(shell_res,
423                                        AGL_SHELL_ERROR_INVALID_ARGUMENT,
424                                        "surface already has another ivi role");
425                 return;
426         }
427
428         switch (edge) {
429         case AGL_SHELL_EDGE_TOP:
430                 member = &output->top;
431                 break;
432         case AGL_SHELL_EDGE_BOTTOM:
433                 member = &output->bottom;
434                 break;
435         case AGL_SHELL_EDGE_LEFT:
436                 member = &output->left;
437                 break;
438         case AGL_SHELL_EDGE_RIGHT:
439                 member = &output->right;
440                 break;
441         default:
442                 wl_resource_post_error(shell_res,
443                                        AGL_SHELL_ERROR_INVALID_ARGUMENT,
444                                        "invalid edge for panel");
445                 return;
446         }
447
448         if (*member) {
449                 wl_resource_post_error(shell_res,
450                                        AGL_SHELL_ERROR_BACKGROUND_EXISTS,
451                                        "output already has panel on this edge");
452                 return;
453         }
454
455         surface->role = IVI_SURFACE_ROLE_PANEL;
456         surface->panel.output = output;
457         surface->panel.edge = edge;
458         wl_list_remove(&surface->link);
459         wl_list_init(&surface->link);
460
461         *member = surface;
462
463         switch (surface->panel.edge) {
464         case AGL_SHELL_EDGE_TOP:
465         case AGL_SHELL_EDGE_BOTTOM:
466                 width = woutput->width;
467                 break;
468         case AGL_SHELL_EDGE_LEFT:
469         case AGL_SHELL_EDGE_RIGHT:
470                 height = woutput->height;
471                 break;
472         }
473
474         weston_desktop_surface_set_size(dsurface, width, height);
475 }
476
477 static const struct agl_shell_interface agl_shell_implementation = {
478         .ready = shell_ready,
479         .set_background = shell_set_background,
480         .set_panel = shell_set_panel,
481 };
482
483 static void
484 unbind_agl_shell(struct wl_resource *resource)
485 {
486 }
487
488 static void
489 bind_agl_shell(struct wl_client *client,
490                void *data, uint32_t version, uint32_t id)
491 {
492         struct ivi_compositor *ivi = data;
493         struct wl_resource *resource;
494
495         resource = wl_resource_create(client, &agl_shell_interface,
496                                       1, id);
497         if (!resource) {
498                 wl_client_post_no_memory(client);
499                 return;
500         }
501
502 #if 0
503         if (ivi->shell_client.client != client) {
504                 wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
505                                        "client not authorized to use agl_shell");
506                 return;
507         }
508 #endif
509
510         if (ivi->shell_client.resource) {
511                 wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
512                                        "agl_shell has already been bound");
513                 return;
514         }
515
516         wl_resource_set_implementation(resource, &agl_shell_implementation,
517                                        ivi, unbind_agl_shell);
518         ivi->shell_client.resource = resource;
519 }
520
521 int
522 ivi_shell_create_global(struct ivi_compositor *ivi)
523 {
524         ivi->agl_shell = wl_global_create(ivi->compositor->wl_display,
525                                           &agl_shell_interface, 1,
526                                           ivi, bind_agl_shell);
527         if (!ivi->agl_shell) {
528                 weston_log("Failed to create wayland global.\n");
529                 return -1;
530         }
531
532         return 0;
533 }