shell: Allow the client shell to unbind/rebind to agl-shell interface
[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 int
53 ivi_shell_init(struct ivi_compositor *ivi)
54 {
55         weston_layer_init(&ivi->hidden, ivi->compositor);
56         weston_layer_init(&ivi->background, ivi->compositor);
57         weston_layer_init(&ivi->normal, ivi->compositor);
58         weston_layer_init(&ivi->panel, ivi->compositor);
59         weston_layer_init(&ivi->fullscreen, ivi->compositor);
60
61         weston_layer_set_position(&ivi->hidden,
62                                   WESTON_LAYER_POSITION_HIDDEN);
63         weston_layer_set_position(&ivi->background,
64                                   WESTON_LAYER_POSITION_BACKGROUND);
65         weston_layer_set_position(&ivi->normal,
66                                   WESTON_LAYER_POSITION_NORMAL);
67         weston_layer_set_position(&ivi->panel,
68                                   WESTON_LAYER_POSITION_UI);
69         weston_layer_set_position(&ivi->fullscreen,
70                                   WESTON_LAYER_POSITION_FULLSCREEN);
71
72         return 0;
73 }
74
75 static void
76 client_exec(const char *command, int fd)
77 {
78         sigset_t sig;
79         char s[32];
80
81         /* Don't give the child our signal mask */
82         sigfillset(&sig);
83         sigprocmask(SIG_UNBLOCK, &sig, NULL);
84
85         /* Launch clients as the user; don't give them the wrong euid */
86         if (seteuid(getuid()) == -1) {
87                 weston_log("seteuid failed: %s\n", strerror(errno));
88                 return;
89         }
90
91         /* Duplicate fd to unset the CLOEXEC flag. We don't need to worry about
92          * clobbering fd, as we'll exit/exec either way.
93          */
94         fd = dup(fd);
95         if (fd == -1) {
96                 weston_log("dup failed: %s\n", strerror(errno));
97                 return;
98         }
99
100         snprintf(s, sizeof s, "%d", fd);
101         setenv("WAYLAND_SOCKET", s, 1);
102
103         execl("/bin/sh", "/bin/sh", "-c", command, NULL);
104         weston_log("executing '%s' failed: %s", command, strerror(errno));
105 }
106
107 static struct wl_client *
108 launch_shell_client(struct ivi_compositor *ivi, const char *command)
109 {
110         struct wl_client *client;
111         int sock[2];
112         pid_t pid;
113
114         weston_log("launching' %s'\n", command);
115
116         if (os_socketpair_cloexec(AF_UNIX, SOCK_STREAM, 0, sock) < 0) {
117                 weston_log("socketpair failed while launching '%s': %s\n",
118                            command, strerror(errno));
119                 return NULL;
120         }
121
122         pid = fork();
123         if (pid == -1) {
124                 close(sock[0]);
125                 close(sock[1]);
126                 weston_log("fork failed while launching '%s': %s\n",
127                            command, strerror(errno));
128                 return NULL;
129         }
130
131         if (pid == 0) {
132                 client_exec(command, sock[1]);
133                 _Exit(EXIT_FAILURE);
134         }
135         close(sock[1]);
136
137         client = wl_client_create(ivi->compositor->wl_display, sock[0]);
138         if (!client) {
139                 close(sock[0]);
140                 weston_log("Failed to create wayland client for '%s'",
141                            command);
142                 return NULL;
143         }
144
145         return client;
146 }
147
148 int
149 ivi_launch_shell_client(struct ivi_compositor *ivi)
150 {
151         struct weston_config_section *section;
152         char *command = NULL;
153
154         section = weston_config_get_section(ivi->config, "shell-client",
155                                             NULL, NULL);
156         if (section)
157                 weston_config_section_get_string(section, "command",
158                                                  &command, NULL);
159
160         if (!command)
161                 return -1;
162
163         ivi->shell_client.client = launch_shell_client(ivi, command);
164         if (!ivi->shell_client.client)
165                 return -1;
166
167         return 0;
168 }
169
170 static void
171 shell_ready(struct wl_client *client, struct wl_resource *shell_res)
172 {
173         struct ivi_compositor *ivi = wl_resource_get_user_data(shell_res);
174         struct ivi_output *output;
175         struct ivi_surface *surface, *tmp;
176
177         /* Init already finished. Do nothing */
178         if (ivi->shell_client.ready)
179                 return;
180
181         ivi->shell_client.ready = true;
182         /* TODO: Create a black screen and remove it here */
183
184         wl_list_for_each(output, &ivi->outputs, link) {
185                 ivi_layout_init(ivi, output);
186         }
187
188         wl_list_for_each_safe(surface, tmp, &ivi->pending_surfaces, link) {
189                 wl_list_remove(&surface->link);
190                 ivi_set_desktop_surface(surface);
191         }
192 }
193
194 static void
195 shell_set_background(struct wl_client *client,
196                      struct wl_resource *shell_res,
197                      struct wl_resource *surface_res,
198                      struct wl_resource *output_res)
199 {
200         struct weston_head *head = weston_head_from_resource(output_res);
201         struct weston_output *woutput = weston_head_get_output(head);
202         struct ivi_output *output = to_ivi_output(woutput);
203         struct weston_surface *wsurface = wl_resource_get_user_data(surface_res);
204         struct weston_desktop_surface *dsurface;
205         struct ivi_surface *surface;
206
207         dsurface = weston_surface_get_desktop_surface(wsurface);
208         if (!dsurface) {
209                 wl_resource_post_error(shell_res,
210                                        AGL_SHELL_ERROR_INVALID_ARGUMENT,
211                                        "surface must be a desktop surface");
212                 return;
213         }
214
215         surface = weston_desktop_surface_get_user_data(dsurface);
216         if (surface->role != IVI_SURFACE_ROLE_NONE) {
217                 wl_resource_post_error(shell_res,
218                                        AGL_SHELL_ERROR_INVALID_ARGUMENT,
219                                        "surface already has another ivi role");
220                 return;
221         }
222
223         if (output->background) {
224                 wl_resource_post_error(shell_res,
225                                        AGL_SHELL_ERROR_BACKGROUND_EXISTS,
226                                        "output already has background");
227                 return;
228         }
229
230         surface->role = IVI_SURFACE_ROLE_BACKGROUND;
231         surface->bg.output = output;
232         wl_list_remove(&surface->link);
233         wl_list_init(&surface->link);
234
235         output->background = surface;
236
237         weston_desktop_surface_set_maximized(dsurface, true);
238         weston_desktop_surface_set_size(dsurface,
239                                         output->output->width,
240                                         output->output->height);
241 }
242
243 static void
244 shell_set_panel(struct wl_client *client,
245                 struct wl_resource *shell_res,
246                 struct wl_resource *surface_res,
247                 struct wl_resource *output_res,
248                 uint32_t edge)
249 {
250         struct weston_head *head = weston_head_from_resource(output_res);
251         struct weston_output *woutput = weston_head_get_output(head);
252         struct ivi_output *output = to_ivi_output(woutput);
253         struct weston_surface *wsurface = wl_resource_get_user_data(surface_res);
254         struct weston_desktop_surface *dsurface;
255         struct ivi_surface *surface;
256         struct ivi_surface **member;
257         int32_t width = 0, height = 0;
258
259         dsurface = weston_surface_get_desktop_surface(wsurface);
260         if (!dsurface) {
261                 wl_resource_post_error(shell_res,
262                                        AGL_SHELL_ERROR_INVALID_ARGUMENT,
263                                        "surface must be a desktop surface");
264                 return;
265         }
266
267         surface = weston_desktop_surface_get_user_data(dsurface);
268         if (surface->role != IVI_SURFACE_ROLE_NONE) {
269                 wl_resource_post_error(shell_res,
270                                        AGL_SHELL_ERROR_INVALID_ARGUMENT,
271                                        "surface already has another ivi role");
272                 return;
273         }
274
275         switch (edge) {
276         case AGL_SHELL_EDGE_TOP:
277                 member = &output->top;
278                 break;
279         case AGL_SHELL_EDGE_BOTTOM:
280                 member = &output->bottom;
281                 break;
282         case AGL_SHELL_EDGE_LEFT:
283                 member = &output->left;
284                 break;
285         case AGL_SHELL_EDGE_RIGHT:
286                 member = &output->right;
287                 break;
288         default:
289                 wl_resource_post_error(shell_res,
290                                        AGL_SHELL_ERROR_INVALID_ARGUMENT,
291                                        "invalid edge for panel");
292                 return;
293         }
294
295         if (*member) {
296                 wl_resource_post_error(shell_res,
297                                        AGL_SHELL_ERROR_BACKGROUND_EXISTS,
298                                        "output already has panel on this edge");
299                 return;
300         }
301
302         surface->role = IVI_SURFACE_ROLE_PANEL;
303         surface->panel.output = output;
304         surface->panel.edge = edge;
305         wl_list_remove(&surface->link);
306         wl_list_init(&surface->link);
307
308         *member = surface;
309
310         switch (surface->panel.edge) {
311         case AGL_SHELL_EDGE_TOP:
312         case AGL_SHELL_EDGE_BOTTOM:
313                 width = woutput->width;
314                 break;
315         case AGL_SHELL_EDGE_LEFT:
316         case AGL_SHELL_EDGE_RIGHT:
317                 height = woutput->height;
318                 break;
319         }
320
321         weston_desktop_surface_set_size(dsurface, width, height);
322 }
323
324 static void
325 shell_activate_app(struct wl_client *client,
326                    struct wl_resource *shell_res,
327                    const char *app_id,
328                    struct wl_resource *output_res)
329 {
330         struct weston_head *head = weston_head_from_resource(output_res);
331         struct weston_output *woutput = weston_head_get_output(head);
332         struct ivi_output *output = to_ivi_output(woutput);
333
334         ivi_layout_activate(output, app_id);
335 }
336
337 static const struct agl_shell_interface agl_shell_implementation = {
338         .ready = shell_ready,
339         .set_background = shell_set_background,
340         .set_panel = shell_set_panel,
341         .activate_app = shell_activate_app,
342 };
343
344 static void
345 unbind_agl_shell(struct wl_resource *resource)
346 {
347         struct ivi_compositor *ivi;
348         struct ivi_output *output;
349         struct ivi_surface *surf, *surf_tmp;
350
351         ivi = wl_resource_get_user_data(resource);
352         wl_list_for_each(output, &ivi->outputs, link) {
353                 free(output->background);
354                 output->background = NULL;
355
356                 free(output->top);
357                 output->top = NULL;
358
359                 free(output->bottom);
360                 output->bottom = NULL;
361
362                 free(output->left);
363                 output->left = NULL;
364
365                 free(output->right);
366                 output->right = NULL;
367
368                 /* reset the active surf if there's one present */
369                 if (output->active) {
370                         output->active->view->is_mapped = false;
371                         output->active->view->surface->is_mapped = false;
372
373                         weston_layer_entry_remove(&output->active->view->layer_link);
374                         output->active = NULL;
375                 }
376         }
377
378         wl_list_for_each_safe(surf, surf_tmp, &ivi->surfaces, link) {
379                 wl_list_remove(&surf->link);
380                 wl_list_init(&surf->link);
381         }
382
383         wl_list_for_each_safe(surf, surf_tmp, &ivi->pending_surfaces, link) {
384                 wl_list_remove(&surf->link);
385                 wl_list_init(&surf->link);
386         }
387
388         wl_list_init(&ivi->surfaces);
389         wl_list_init(&ivi->pending_surfaces);
390
391         ivi->shell_client.ready = false;
392         ivi->shell_client.resource = NULL;
393         ivi->shell_client.client = NULL;
394 }
395
396 static void
397 bind_agl_shell(struct wl_client *client,
398                void *data, uint32_t version, uint32_t id)
399 {
400         struct ivi_compositor *ivi = data;
401         struct wl_resource *resource;
402
403         resource = wl_resource_create(client, &agl_shell_interface,
404                                       1, id);
405         if (!resource) {
406                 wl_client_post_no_memory(client);
407                 return;
408         }
409
410 #if 0
411         if (ivi->shell_client.client != client) {
412                 wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
413                                        "client not authorized to use agl_shell");
414                 return;
415         }
416 #endif
417
418         if (ivi->shell_client.resource) {
419                 wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
420                                        "agl_shell has already been bound");
421                 return;
422         }
423
424         wl_resource_set_implementation(resource, &agl_shell_implementation,
425                                        ivi, unbind_agl_shell);
426         ivi->shell_client.resource = resource;
427 }
428
429 int
430 ivi_shell_create_global(struct ivi_compositor *ivi)
431 {
432         ivi->agl_shell = wl_global_create(ivi->compositor->wl_display,
433                                           &agl_shell_interface, 1,
434                                           ivi, bind_agl_shell);
435         if (!ivi->agl_shell) {
436                 weston_log("Failed to create wayland global.\n");
437                 return -1;
438         }
439
440         return 0;
441 }