f96950303011c77b2bc1b244af1a40030aa978db
[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 #include "policy.h"
28
29 #include <assert.h>
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <signal.h>
33 #include <string.h>
34 #include <sys/socket.h>
35 #include <sys/types.h>
36 #include <unistd.h>
37 #include <libweston/libweston.h>
38 #include <libweston/config-parser.h>
39
40 #include "shared/os-compatibility.h"
41
42 #include "agl-shell-server-protocol.h"
43 #include "agl-shell-desktop-server-protocol.h"
44
45 static void
46 create_black_surface_view(struct ivi_output *output);
47
48 static void
49 agl_shell_desktop_advertise_application_id(struct ivi_compositor *ivi,
50                                            struct ivi_surface *surface)
51 {
52         struct desktop_client *dclient;
53
54         /* advertise to all desktop clients the new surface */
55         wl_list_for_each(dclient, &ivi->desktop_clients, link) {
56                 const char *app_id =
57                         weston_desktop_surface_get_app_id(surface->dsurface);
58                 agl_shell_desktop_send_application(dclient->resource, app_id);
59         }
60 }
61
62 void
63 ivi_set_desktop_surface(struct ivi_surface *surface)
64 {
65         struct ivi_compositor *ivi = surface->ivi;
66         assert(surface->role == IVI_SURFACE_ROLE_NONE);
67
68         surface->role = IVI_SURFACE_ROLE_DESKTOP;
69         wl_list_insert(&surface->ivi->surfaces, &surface->link);
70
71         agl_shell_desktop_advertise_application_id(ivi, surface);
72 }
73
74 static void
75 ivi_set_desktop_surface_popup(struct ivi_surface *surface)
76 {
77         struct ivi_compositor *ivi = surface->ivi;
78         assert(surface->role == IVI_SURFACE_ROLE_NONE);
79
80         surface->role = IVI_SURFACE_ROLE_POPUP;
81         wl_list_insert(&ivi->surfaces, &surface->link);
82
83         agl_shell_desktop_advertise_application_id(ivi, surface);
84 }
85
86 static void
87 ivi_set_desktop_surface_fullscreen(struct ivi_surface *surface)
88 {
89         struct ivi_compositor *ivi = surface->ivi;
90         assert(surface->role == IVI_SURFACE_ROLE_NONE);
91
92         surface->role = IVI_SURFACE_ROLE_FULLSCREEN;
93         wl_list_insert(&ivi->surfaces, &surface->link);
94
95         agl_shell_desktop_advertise_application_id(ivi, surface);
96 }
97
98 static void
99 ivi_set_desktop_surface_remote(struct ivi_surface *surface)
100 {
101         struct ivi_compositor *ivi = surface->ivi;
102         struct weston_view *view;
103         struct ivi_output *output = surface->remote.output;
104
105         assert(surface->role == IVI_SURFACE_ROLE_NONE);
106
107         /* remote type are the same as desktop just that client can tell
108          * the compositor to start on another output */
109         surface->role = IVI_SURFACE_ROLE_REMOTE;
110
111         /* if thew black surface view is mapped on the mean we need
112          * to remove it in order to start showing the 'remote' surface
113          * just being added */
114         view = output->fullscreen_view.fs->view;
115         if (view->is_mapped || view->surface->is_mapped)
116                 remove_black_surface(output);
117
118         wl_list_insert(&ivi->surfaces, &surface->link);
119 }
120
121
122 static void
123 ivi_set_desktop_surface_split(struct ivi_surface *surface)
124 {
125         struct ivi_compositor *ivi = surface->ivi;
126         assert(surface->role == IVI_SURFACE_ROLE_NONE);
127
128         if (surface->split.orientation == AGL_SHELL_DESKTOP_APP_ROLE_SPLIT_VERTICAL)
129                 surface->role = IVI_SURFACE_ROLE_SPLIT_V;
130         else
131                 surface->role = IVI_SURFACE_ROLE_SPLIT_H;
132
133         wl_list_insert(&ivi->surfaces, &surface->link);
134
135         agl_shell_desktop_advertise_application_id(ivi, surface);
136 }
137
138 static void
139 ivi_set_pending_desktop_surface_popup(struct ivi_output *ioutput,
140                                       int x, int y, const char *app_id)
141 {
142         struct ivi_compositor *ivi = ioutput->ivi;
143         size_t len_app_id = strlen(app_id);
144
145         struct pending_popup *p_popup = zalloc(sizeof(*p_popup));
146
147         p_popup->app_id = zalloc(sizeof(char) * (len_app_id + 1));
148         memcpy(p_popup->app_id, app_id, len_app_id);
149         p_popup->ioutput = ioutput;
150         p_popup->x = x;
151         p_popup->y = y;
152
153         wl_list_insert(&ivi->popup_pending_apps, &p_popup->link);
154 }
155
156 static void
157 ivi_set_pending_desktop_surface_fullscreen(struct ivi_output *ioutput,
158                                            const char *app_id)
159 {
160         struct ivi_compositor *ivi = ioutput->ivi;
161         size_t len_app_id = strlen(app_id);
162
163         struct pending_fullscreen *fs = zalloc(sizeof(*fs));
164
165         fs->app_id = zalloc(sizeof(char) * (len_app_id + 1));
166         memcpy(fs->app_id, app_id, len_app_id);
167
168         fs->ioutput = ioutput;
169
170         wl_list_insert(&ivi->fullscreen_pending_apps, &fs->link);
171 }
172
173 static void
174 ivi_set_pending_desktop_surface_split(struct ivi_output *ioutput,
175                                       const char *app_id, uint32_t orientation)
176 {
177         struct ivi_compositor *ivi = ioutput->ivi;
178         struct ivi_surface *surf;
179         size_t len_app_id = strlen(app_id);
180         struct pending_split *split;
181
182         if (orientation != AGL_SHELL_DESKTOP_APP_ROLE_SPLIT_VERTICAL &&
183             orientation != AGL_SHELL_DESKTOP_APP_ROLE_SPLIT_HORIZONTAL)
184                 return;
185
186         /* more than one is un-supported, do note we need to do
187          * conversion for surface roles instead of using the protocol ones */
188         wl_list_for_each(surf, &ivi->surfaces, link)
189                 if (surf->role == IVI_SURFACE_ROLE_SPLIT_V ||
190                     surf->role == IVI_SURFACE_ROLE_SPLIT_H)
191                         return;
192
193         split = zalloc(sizeof(*split));
194         split->app_id = zalloc(sizeof(char) * (len_app_id + 1));
195         memcpy(split->app_id, app_id, len_app_id);
196
197         split->ioutput = ioutput;
198         split->orientation = orientation;
199
200         wl_list_insert(&ivi->split_pending_apps, &split->link);
201 }
202
203 void
204 ivi_set_pending_desktop_surface_remote(struct ivi_output *ioutput,
205                 const char *app_id)
206 {
207         struct ivi_compositor *ivi = ioutput->ivi;
208         size_t len_app_id = strlen(app_id);
209
210         struct pending_remote *remote = zalloc(sizeof(*remote));
211
212         remote->app_id = zalloc(sizeof(char) * (len_app_id + 1));
213         memcpy(remote->app_id, app_id, len_app_id);
214
215         remote->ioutput = ioutput;
216
217         wl_list_insert(&ivi->remote_pending_apps, &remote->link);
218 }
219
220
221 static void
222 ivi_remove_pending_desktop_surface_split(struct pending_split *split)
223 {
224         free(split->app_id);
225         wl_list_remove(&split->link);
226         free(split);
227 }
228
229 static void
230 ivi_remove_pending_desktop_surface_fullscreen(struct pending_fullscreen *fs)
231 {
232         free(fs->app_id);
233         wl_list_remove(&fs->link);
234         free(fs);
235 }
236
237 static void
238 ivi_remove_pending_desktop_surface_popup(struct pending_popup *p_popup)
239 {
240         free(p_popup->app_id);
241         wl_list_remove(&p_popup->link);
242         free(p_popup);
243 }
244
245 static void
246 ivi_remove_pending_desktop_surface_remote(struct pending_remote *remote)
247 {
248         free(remote->app_id);
249         wl_list_remove(&remote->link);
250         free(remote);
251 }
252
253 static bool
254 ivi_check_pending_desktop_surface_popup(struct ivi_surface *surface)
255 {
256         struct ivi_compositor *ivi = surface->ivi;
257         struct pending_popup *p_popup, *next_p_popup;
258         const char *_app_id =
259                         weston_desktop_surface_get_app_id(surface->dsurface);
260
261         if (wl_list_empty(&ivi->popup_pending_apps))
262                 return false;
263
264         wl_list_for_each_safe(p_popup, next_p_popup,
265                               &ivi->popup_pending_apps, link) {
266                 if (!strcmp(_app_id, p_popup->app_id)) {
267                         surface->popup.output = p_popup->ioutput;
268                         surface->popup.x = p_popup->x;
269                         surface->popup.y = p_popup->y;
270                         ivi_remove_pending_desktop_surface_popup(p_popup);
271                         return true;
272                 }
273         }
274
275         return false;
276 }
277
278 static bool
279 ivi_check_pending_desktop_surface_split(struct ivi_surface *surface)
280 {
281         struct pending_split *split_surf, *next_split_surf;
282         struct ivi_compositor *ivi = surface->ivi;
283         const char *_app_id =
284                         weston_desktop_surface_get_app_id(surface->dsurface);
285
286         if (wl_list_empty(&ivi->split_pending_apps))
287                 return false;
288
289         wl_list_for_each_safe(split_surf, next_split_surf,
290                               &ivi->split_pending_apps, link) {
291                 if (!strcmp(_app_id, split_surf->app_id)) {
292                         surface->split.output = split_surf->ioutput;
293                         surface->split.orientation = split_surf->orientation;
294                         ivi_remove_pending_desktop_surface_split(split_surf);
295                         return true;
296                 }
297         }
298
299         return false;
300 }
301
302 static bool
303 ivi_check_pending_desktop_surface_fullscreen(struct ivi_surface *surface)
304 {
305         struct pending_fullscreen *fs_surf, *next_fs_surf;
306         struct ivi_compositor *ivi = surface->ivi;
307         const char *_app_id =
308                         weston_desktop_surface_get_app_id(surface->dsurface);
309
310         if (wl_list_empty(&ivi->fullscreen_pending_apps))
311                 return false;
312
313         wl_list_for_each_safe(fs_surf, next_fs_surf,
314                               &ivi->fullscreen_pending_apps, link) {
315                 if (!strcmp(_app_id, fs_surf->app_id)) {
316                         surface->fullscreen.output = fs_surf->ioutput;
317                         ivi_remove_pending_desktop_surface_fullscreen(fs_surf);
318                         return true;
319                 }
320         }
321
322         return false;
323 }
324
325 static bool
326 ivi_check_pending_desktop_surface_remote(struct ivi_surface *surface)
327 {
328         struct pending_remote *remote_surf, *next_remote_surf;
329         struct ivi_compositor *ivi = surface->ivi;
330         const char *_app_id =
331                 weston_desktop_surface_get_app_id(surface->dsurface);
332
333         if (wl_list_empty(&ivi->remote_pending_apps))
334                 return false;
335
336         wl_list_for_each_safe(remote_surf, next_remote_surf,
337                               &ivi->remote_pending_apps, link) {
338                 if (!strcmp(_app_id, remote_surf->app_id)) {
339                         surface->remote.output = remote_surf->ioutput;
340                         ivi_remove_pending_desktop_surface_remote(remote_surf);
341                         return true;
342                 }
343         }
344
345         return false;
346 }
347
348
349 void
350 ivi_check_pending_desktop_surface(struct ivi_surface *surface)
351 {
352         bool ret = false;
353
354         ret = ivi_check_pending_desktop_surface_popup(surface);
355         if (ret) {
356                 ivi_set_desktop_surface_popup(surface);
357                 return;
358         }
359
360         ret = ivi_check_pending_desktop_surface_split(surface);
361         if (ret) {
362                 ivi_set_desktop_surface_split(surface);
363                 return;
364         }
365
366         ret = ivi_check_pending_desktop_surface_fullscreen(surface);
367         if (ret) {
368                 ivi_set_desktop_surface_fullscreen(surface);
369                 return;
370         }
371
372         ret = ivi_check_pending_desktop_surface_remote(surface);
373         if (ret) {
374                 ivi_set_desktop_surface_remote(surface);
375                 return;
376         }
377
378         /* if we end up here means we have a regular desktop app and
379          * try to activate it */
380         ivi_set_desktop_surface(surface);
381         ivi_layout_desktop_committed(surface);
382 }
383
384 void
385 ivi_shell_init_black_fs(struct ivi_compositor *ivi)
386 {
387         struct ivi_output *out;
388
389         wl_list_for_each(out, &ivi->outputs, link) {
390                 create_black_surface_view(out);
391                 insert_black_surface(out);
392         }
393 }
394
395 int
396 ivi_shell_init(struct ivi_compositor *ivi)
397 {
398         weston_layer_init(&ivi->hidden, ivi->compositor);
399         weston_layer_init(&ivi->background, ivi->compositor);
400         weston_layer_init(&ivi->normal, ivi->compositor);
401         weston_layer_init(&ivi->panel, ivi->compositor);
402         weston_layer_init(&ivi->popup, ivi->compositor);
403         weston_layer_init(&ivi->fullscreen, ivi->compositor);
404
405         weston_layer_set_position(&ivi->hidden,
406                                   WESTON_LAYER_POSITION_HIDDEN);
407         weston_layer_set_position(&ivi->background,
408                                   WESTON_LAYER_POSITION_BACKGROUND);
409         weston_layer_set_position(&ivi->normal,
410                                   WESTON_LAYER_POSITION_NORMAL);
411         weston_layer_set_position(&ivi->panel,
412                                   WESTON_LAYER_POSITION_UI);
413         weston_layer_set_position(&ivi->popup,
414                                   WESTON_LAYER_POSITION_TOP_UI);
415         weston_layer_set_position(&ivi->fullscreen,
416                                   WESTON_LAYER_POSITION_FULLSCREEN);
417
418         return 0;
419 }
420
421 static void
422 ivi_shell_advertise_xdg_surfaces(struct ivi_compositor *ivi, struct wl_resource *resource)
423 {
424         struct ivi_surface *surface;
425
426         wl_list_for_each(surface, &ivi->surfaces, link) {
427                 const char *app_id =
428                         weston_desktop_surface_get_app_id(surface->dsurface);
429                 agl_shell_desktop_send_application(resource, app_id);
430         }
431 }
432
433 static void
434 client_exec(const char *command, int fd)
435 {
436         sigset_t sig;
437         char s[32];
438
439         /* Don't give the child our signal mask */
440         sigfillset(&sig);
441         sigprocmask(SIG_UNBLOCK, &sig, NULL);
442
443         /* Launch clients as the user; don't give them the wrong euid */
444         if (seteuid(getuid()) == -1) {
445                 weston_log("seteuid failed: %s\n", strerror(errno));
446                 return;
447         }
448
449         /* Duplicate fd to unset the CLOEXEC flag. We don't need to worry about
450          * clobbering fd, as we'll exit/exec either way.
451          */
452         fd = dup(fd);
453         if (fd == -1) {
454                 weston_log("dup failed: %s\n", strerror(errno));
455                 return;
456         }
457
458         snprintf(s, sizeof s, "%d", fd);
459         setenv("WAYLAND_SOCKET", s, 1);
460
461         execl("/bin/sh", "/bin/sh", "-c", command, NULL);
462         weston_log("executing '%s' failed: %s", command, strerror(errno));
463 }
464
465 static struct wl_client *
466 launch_shell_client(struct ivi_compositor *ivi, const char *command)
467 {
468         struct wl_client *client;
469         int sock[2];
470         pid_t pid;
471
472         weston_log("launching' %s'\n", command);
473
474         if (os_socketpair_cloexec(AF_UNIX, SOCK_STREAM, 0, sock) < 0) {
475                 weston_log("socketpair failed while launching '%s': %s\n",
476                            command, strerror(errno));
477                 return NULL;
478         }
479
480         pid = fork();
481         if (pid == -1) {
482                 close(sock[0]);
483                 close(sock[1]);
484                 weston_log("fork failed while launching '%s': %s\n",
485                            command, strerror(errno));
486                 return NULL;
487         }
488
489         if (pid == 0) {
490                 client_exec(command, sock[1]);
491                 _Exit(EXIT_FAILURE);
492         }
493         close(sock[1]);
494
495         client = wl_client_create(ivi->compositor->wl_display, sock[0]);
496         if (!client) {
497                 close(sock[0]);
498                 weston_log("Failed to create wayland client for '%s'",
499                            command);
500                 return NULL;
501         }
502
503         return client;
504 }
505
506 int
507 ivi_launch_shell_client(struct ivi_compositor *ivi)
508 {
509         struct weston_config_section *section;
510         char *command = NULL;
511
512         section = weston_config_get_section(ivi->config, "shell-client",
513                                             NULL, NULL);
514         if (section)
515                 weston_config_section_get_string(section, "command",
516                                                  &command, NULL);
517
518         if (!command)
519                 return -1;
520
521         ivi->shell_client.client = launch_shell_client(ivi, command);
522         if (!ivi->shell_client.client)
523                 return -1;
524
525         return 0;
526 }
527
528 static void
529 destroy_black_view(struct wl_listener *listener, void *data)
530 {
531         struct fullscreen_view *fs =
532                 wl_container_of(listener, fs, fs_destroy);
533
534
535         if (fs && fs->fs) {
536                 if (fs->fs->view && fs->fs->view->surface) {
537                         weston_surface_destroy(fs->fs->view->surface);
538                         fs->fs->view = NULL;
539                 }
540
541                 free(fs->fs);
542                 wl_list_remove(&fs->fs_destroy.link);
543         }
544 }
545
546
547 static void
548 create_black_surface_view(struct ivi_output *output)
549 {
550         struct weston_surface *surface = NULL;
551         struct weston_view *view;
552         struct ivi_compositor *ivi = output->ivi;
553         struct weston_compositor *wc= ivi->compositor;
554         struct weston_output *woutput = output->output;
555
556         surface = weston_surface_create(wc);
557         view = weston_view_create(surface);
558
559         assert(view || surface);
560
561         weston_surface_set_color(surface, 0.0, 0.0, 0.0, 1);
562         weston_surface_set_size(surface, woutput->width, woutput->height);
563         weston_view_set_position(view, woutput->x, woutput->y);
564
565         output->fullscreen_view.fs = zalloc(sizeof(struct ivi_surface));
566         output->fullscreen_view.fs->view = view;
567
568         output->fullscreen_view.fs_destroy.notify = destroy_black_view;
569         wl_signal_add(&woutput->destroy_signal,
570                       &output->fullscreen_view.fs_destroy);
571 }
572
573 void
574 remove_black_surface(struct ivi_output *output)
575 {
576         struct weston_view *view;
577
578         if (!output &&
579             !output->fullscreen_view.fs &&
580             !output->fullscreen_view.fs->view) {
581                 weston_log("Output %s doesn't have a surface installed!\n", output->name);
582                 return;
583         }
584
585         view = output->fullscreen_view.fs->view;
586         assert(view->is_mapped == true ||
587                view->surface->is_mapped == true);
588
589         view->is_mapped = false;
590         view->surface->is_mapped = false;
591
592         weston_layer_entry_remove(&view->layer_link);
593         weston_view_update_transform(view);
594
595         weston_output_damage(output->output);
596 }
597
598 void
599 insert_black_surface(struct ivi_output *output)
600 {
601         struct weston_view *view;
602
603         if (!output &&
604             !output->fullscreen_view.fs &&
605             !output->fullscreen_view.fs->view) {
606                 weston_log("Output %s doesn't have a surface installed!\n", output->name);
607                 return;
608         }
609
610         view = output->fullscreen_view.fs->view;
611         if (view->is_mapped || view->surface->is_mapped)
612                 return;
613
614         weston_layer_entry_remove(&view->layer_link);
615         weston_layer_entry_insert(&output->ivi->fullscreen.view_list,
616                                   &view->layer_link);
617
618         view->is_mapped = true;
619         view->surface->is_mapped = true;
620
621         weston_view_update_transform(view);
622         weston_output_damage(output->output);
623 }
624
625 static void
626 shell_ready(struct wl_client *client, struct wl_resource *shell_res)
627 {
628         struct ivi_compositor *ivi = wl_resource_get_user_data(shell_res);
629         struct ivi_output *output;
630         struct ivi_surface *surface, *tmp;
631
632         /* Init already finished. Do nothing */
633         if (ivi->shell_client.ready)
634                 return;
635
636         ivi->shell_client.ready = true;
637
638         wl_list_for_each(output, &ivi->outputs, link) {
639                 if (output->background)
640                         remove_black_surface(output);
641                 ivi_layout_init(ivi, output);
642         }
643
644         wl_list_for_each_safe(surface, tmp, &ivi->pending_surfaces, link) {
645                 wl_list_remove(&surface->link);
646                 ivi_check_pending_desktop_surface(surface);
647         }
648 }
649
650 static void
651 shell_set_background(struct wl_client *client,
652                      struct wl_resource *shell_res,
653                      struct wl_resource *surface_res,
654                      struct wl_resource *output_res)
655 {
656         struct weston_head *head = weston_head_from_resource(output_res);
657         struct weston_output *woutput = weston_head_get_output(head);
658         struct ivi_output *output = to_ivi_output(woutput);
659         struct weston_surface *wsurface = wl_resource_get_user_data(surface_res);
660         struct weston_desktop_surface *dsurface;
661         struct ivi_surface *surface;
662
663         dsurface = weston_surface_get_desktop_surface(wsurface);
664         if (!dsurface) {
665                 wl_resource_post_error(shell_res,
666                                        AGL_SHELL_ERROR_INVALID_ARGUMENT,
667                                        "surface must be a desktop surface");
668                 return;
669         }
670
671         surface = weston_desktop_surface_get_user_data(dsurface);
672         if (surface->role != IVI_SURFACE_ROLE_NONE) {
673                 wl_resource_post_error(shell_res,
674                                        AGL_SHELL_ERROR_INVALID_ARGUMENT,
675                                        "surface already has another ivi role");
676                 return;
677         }
678
679         if (output->background) {
680                 wl_resource_post_error(shell_res,
681                                        AGL_SHELL_ERROR_BACKGROUND_EXISTS,
682                                        "output already has background");
683                 return;
684         }
685
686         surface->role = IVI_SURFACE_ROLE_BACKGROUND;
687         surface->bg.output = output;
688         wl_list_remove(&surface->link);
689         wl_list_init(&surface->link);
690
691         output->background = surface;
692
693         weston_desktop_surface_set_maximized(dsurface, true);
694         weston_desktop_surface_set_size(dsurface,
695                                         output->output->width,
696                                         output->output->height);
697 }
698
699 static void
700 shell_set_panel(struct wl_client *client,
701                 struct wl_resource *shell_res,
702                 struct wl_resource *surface_res,
703                 struct wl_resource *output_res,
704                 uint32_t edge)
705 {
706         struct weston_head *head = weston_head_from_resource(output_res);
707         struct weston_output *woutput = weston_head_get_output(head);
708         struct ivi_output *output = to_ivi_output(woutput);
709         struct weston_surface *wsurface = wl_resource_get_user_data(surface_res);
710         struct weston_desktop_surface *dsurface;
711         struct ivi_surface *surface;
712         struct ivi_surface **member;
713         int32_t width = 0, height = 0;
714
715         dsurface = weston_surface_get_desktop_surface(wsurface);
716         if (!dsurface) {
717                 wl_resource_post_error(shell_res,
718                                        AGL_SHELL_ERROR_INVALID_ARGUMENT,
719                                        "surface must be a desktop surface");
720                 return;
721         }
722
723         surface = weston_desktop_surface_get_user_data(dsurface);
724         if (surface->role != IVI_SURFACE_ROLE_NONE) {
725                 wl_resource_post_error(shell_res,
726                                        AGL_SHELL_ERROR_INVALID_ARGUMENT,
727                                        "surface already has another ivi role");
728                 return;
729         }
730
731         switch (edge) {
732         case AGL_SHELL_EDGE_TOP:
733                 member = &output->top;
734                 break;
735         case AGL_SHELL_EDGE_BOTTOM:
736                 member = &output->bottom;
737                 break;
738         case AGL_SHELL_EDGE_LEFT:
739                 member = &output->left;
740                 break;
741         case AGL_SHELL_EDGE_RIGHT:
742                 member = &output->right;
743                 break;
744         default:
745                 wl_resource_post_error(shell_res,
746                                        AGL_SHELL_ERROR_INVALID_ARGUMENT,
747                                        "invalid edge for panel");
748                 return;
749         }
750
751         if (*member) {
752                 wl_resource_post_error(shell_res,
753                                        AGL_SHELL_ERROR_BACKGROUND_EXISTS,
754                                        "output already has panel on this edge");
755                 return;
756         }
757
758         surface->role = IVI_SURFACE_ROLE_PANEL;
759         surface->panel.output = output;
760         surface->panel.edge = edge;
761         wl_list_remove(&surface->link);
762         wl_list_init(&surface->link);
763
764         *member = surface;
765
766         switch (surface->panel.edge) {
767         case AGL_SHELL_EDGE_TOP:
768         case AGL_SHELL_EDGE_BOTTOM:
769                 width = woutput->width;
770                 break;
771         case AGL_SHELL_EDGE_LEFT:
772         case AGL_SHELL_EDGE_RIGHT:
773                 height = woutput->height;
774                 break;
775         }
776
777         weston_desktop_surface_set_size(dsurface, width, height);
778 }
779
780
781 static void
782 shell_advertise_app_state(struct ivi_compositor *ivi, const char *app_id,
783                           const char *data, uint32_t app_state)
784 {
785         struct desktop_client *dclient;
786         uint32_t app_role;
787         struct ivi_surface *surf = ivi_find_app(ivi, app_id);
788         struct ivi_policy *policy = ivi->policy;
789
790         /* FIXME: should queue it here and see when binding agl-shell-desktop
791          * if there are any to be sent */
792         if (!surf)
793                 return;
794
795         if (policy && policy->api.surface_advertise_state_change &&
796             !policy->api.surface_advertise_state_change(surf, surf->ivi)) {
797                 return;
798         }
799
800         app_role = surf->role;
801         if (app_role == IVI_SURFACE_ROLE_POPUP)
802                 app_role = AGL_SHELL_DESKTOP_APP_ROLE_POPUP;
803
804         wl_list_for_each(dclient, &ivi->desktop_clients, link)
805                 agl_shell_desktop_send_state_app(dclient->resource, app_id,
806                                                  data, app_state, app_role);
807 }
808
809 static void
810 shell_activate_app(struct wl_client *client,
811                    struct wl_resource *shell_res,
812                    const char *app_id,
813                    struct wl_resource *output_res)
814 {
815         struct weston_head *head = weston_head_from_resource(output_res);
816         struct weston_output *woutput = weston_head_get_output(head);
817         struct ivi_output *output = to_ivi_output(woutput);
818
819         ivi_layout_activate(output, app_id);
820 }
821
822 static void
823 shell_desktop_activate_app(struct wl_client *client,
824                            struct wl_resource *shell_res,
825                            const char *app_id, const char *data,
826                            struct wl_resource *output_res)
827 {
828         struct weston_head *head = weston_head_from_resource(output_res);
829         struct weston_output *woutput = weston_head_get_output(head);
830         struct ivi_output *output = to_ivi_output(woutput);
831
832         ivi_layout_activate(output, app_id);
833         shell_advertise_app_state(output->ivi, app_id,
834                                   data, AGL_SHELL_DESKTOP_APP_STATE_ACTIVATED);
835 }
836
837 static void
838 shell_deactivate_app(struct wl_client *client,
839                    struct wl_resource *shell_res,
840                    const char *app_id)
841 {
842         struct desktop_client *dclient = wl_resource_get_user_data(shell_res);
843         struct ivi_compositor *ivi = dclient->ivi;
844
845         ivi_layout_deactivate(ivi, app_id);
846         shell_advertise_app_state(ivi, app_id,
847                                   NULL, AGL_SHELL_DESKTOP_APP_STATE_DEACTIVATED);
848 }
849
850 static const struct agl_shell_interface agl_shell_implementation = {
851         .ready = shell_ready,
852         .set_background = shell_set_background,
853         .set_panel = shell_set_panel,
854         .activate_app = shell_activate_app,
855 };
856
857 static void
858 shell_desktop_set_app_property(struct wl_client *client,
859                                struct wl_resource *shell_res,
860                                const char *app_id, uint32_t role,
861                                int x, int y, struct wl_resource *output_res)
862 {
863         struct weston_head *head = weston_head_from_resource(output_res);
864         struct weston_output *woutput = weston_head_get_output(head);
865         struct ivi_output *output = to_ivi_output(woutput);
866
867         switch (role) {
868         case AGL_SHELL_DESKTOP_APP_ROLE_POPUP:
869                 ivi_set_pending_desktop_surface_popup(output, x, y, app_id);
870                 break;
871         case AGL_SHELL_DESKTOP_APP_ROLE_FULLSCREEN:
872                 ivi_set_pending_desktop_surface_fullscreen(output, app_id);
873                 break;
874         case AGL_SHELL_DESKTOP_APP_ROLE_SPLIT_VERTICAL:
875         case AGL_SHELL_DESKTOP_APP_ROLE_SPLIT_HORIZONTAL:
876                 ivi_set_pending_desktop_surface_split(output, app_id, role);
877                 break;
878         case AGL_SHELL_DESKTOP_APP_ROLE_REMOTE:
879                 ivi_set_pending_desktop_surface_remote(output, app_id);
880                 break;
881         default:
882                 break;
883         }
884 }
885
886 static const struct agl_shell_desktop_interface agl_shell_desktop_implementation = {
887         .activate_app = shell_desktop_activate_app,
888         .set_app_property = shell_desktop_set_app_property,
889         .deactivate_app = shell_deactivate_app,
890 };
891
892 static void
893 unbind_agl_shell(struct wl_resource *resource)
894 {
895         struct ivi_compositor *ivi;
896         struct ivi_output *output;
897         struct ivi_surface *surf, *surf_tmp;
898
899         ivi = wl_resource_get_user_data(resource);
900         wl_list_for_each(output, &ivi->outputs, link) {
901                 /* reset the active surf if there's one present */
902                 if (output->active) {
903                         output->active->view->is_mapped = false;
904                         output->active->view->surface->is_mapped = false;
905
906                         weston_layer_entry_remove(&output->active->view->layer_link);
907                         output->active = NULL;
908                 }
909
910                 insert_black_surface(output);
911         }
912
913         wl_list_for_each_safe(surf, surf_tmp, &ivi->surfaces, link) {
914                 wl_list_remove(&surf->link);
915                 wl_list_init(&surf->link);
916         }
917
918         wl_list_for_each_safe(surf, surf_tmp, &ivi->pending_surfaces, link) {
919                 wl_list_remove(&surf->link);
920                 wl_list_init(&surf->link);
921         }
922
923         wl_list_init(&ivi->surfaces);
924         wl_list_init(&ivi->pending_surfaces);
925
926         ivi->shell_client.ready = false;
927         ivi->shell_client.resource = NULL;
928         ivi->shell_client.client = NULL;
929 }
930
931 static void
932 bind_agl_shell(struct wl_client *client,
933                void *data, uint32_t version, uint32_t id)
934 {
935         struct ivi_compositor *ivi = data;
936         struct wl_resource *resource;
937
938         resource = wl_resource_create(client, &agl_shell_interface,
939                                       1, id);
940         if (!resource) {
941                 wl_client_post_no_memory(client);
942                 return;
943         }
944
945 #if 0
946         if (ivi->shell_client.client != client) {
947                 wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
948                                        "client not authorized to use agl_shell");
949                 return;
950         }
951 #endif
952
953         if (ivi->shell_client.resource) {
954                 wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
955                                        "agl_shell has already been bound");
956                 return;
957         }
958
959         wl_resource_set_implementation(resource, &agl_shell_implementation,
960                                        ivi, unbind_agl_shell);
961         ivi->shell_client.resource = resource;
962 }
963
964 static void
965 unbind_agl_shell_desktop(struct wl_resource *resource)
966 {
967         struct desktop_client *dclient = wl_resource_get_user_data(resource);
968
969         wl_list_remove(&dclient->link);
970         free(dclient);
971 }
972
973 static void
974 bind_agl_shell_desktop(struct wl_client *client,
975                        void *data, uint32_t version, uint32_t id)
976 {
977         struct ivi_compositor *ivi = data;
978         struct wl_resource *resource;
979         struct desktop_client *dclient = zalloc(sizeof(*dclient));
980
981         if (!dclient) {
982                 wl_client_post_no_memory(client);
983                 return;
984         }
985
986         resource = wl_resource_create(client, &agl_shell_desktop_interface,
987                                       version, id);
988         dclient->ivi = ivi;
989         if (!resource) {
990                 wl_client_post_no_memory(client);
991                 return;
992         }
993
994         wl_resource_set_implementation(resource, &agl_shell_desktop_implementation,
995                                        dclient, unbind_agl_shell_desktop);
996
997         dclient->resource = resource;
998         wl_list_insert(&ivi->desktop_clients, &dclient->link);
999
1000         /* advertise xdg surfaces */
1001         ivi_shell_advertise_xdg_surfaces(ivi, resource);
1002 }
1003
1004 int
1005 ivi_shell_create_global(struct ivi_compositor *ivi)
1006 {
1007         ivi->agl_shell = wl_global_create(ivi->compositor->wl_display,
1008                                           &agl_shell_interface, 1,
1009                                           ivi, bind_agl_shell);
1010         if (!ivi->agl_shell) {
1011                 weston_log("Failed to create wayland global.\n");
1012                 return -1;
1013         }
1014
1015         ivi->agl_shell_desktop = wl_global_create(ivi->compositor->wl_display,
1016                                                   &agl_shell_desktop_interface, 1,
1017                                                   ivi, bind_agl_shell_desktop);
1018         if (!ivi->agl_shell_desktop) {
1019                 weston_log("Failed to create wayland global (agl_shell_desktop).\n");
1020                 return -1;
1021         }
1022
1023         return 0;
1024 }