X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?a=blobdiff_plain;f=src%2Fshell.c;h=16d924c2d9932108461fb4d608d57ec9d4a9c2c8;hb=754636273ffc6b872d6fb194009c857e76e7cec1;hp=a9be92909933e9eab8a8f5a2287ccd677f047b33;hpb=5ce0b5d5200f8d5e21e15981b0179fd429ee836d;p=src%2Fagl-compositor.git diff --git a/src/shell.c b/src/shell.c index a9be929..16d924c 100644 --- a/src/shell.c +++ b/src/shell.c @@ -1,5 +1,5 @@ /* - * Copyright © 2019 Collabora, Ltd. + * Copyright © 2019, 2022 Collabora, Ltd. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -24,6 +24,7 @@ */ #include "ivi-compositor.h" +#include "policy.h" #include #include @@ -32,40 +33,77 @@ #include #include #include +#include #include #include #include +#include #include "shared/os-compatibility.h" +#include "shared/helpers.h" +#include "shared/process-util.h" #include "agl-shell-server-protocol.h" #include "agl-shell-desktop-server-protocol.h" static void -create_black_surface_view(struct ivi_output *output); +create_black_curtain_view(struct ivi_output *output); -static void -insert_black_surface(struct ivi_output *output); +static uint32_t +reverse_orientation(uint32_t orientation); + +const char * +split_orientation_to_string(uint32_t orientation); void -ivi_set_desktop_surface(struct ivi_surface *surface) +agl_shell_desktop_advertise_application_id(struct ivi_compositor *ivi, + struct ivi_surface *surface) { struct desktop_client *dclient; - struct ivi_compositor *ivi = surface->ivi; - assert(surface->role == IVI_SURFACE_ROLE_NONE); + static bool display_adv = false; - surface->role = IVI_SURFACE_ROLE_DESKTOP; - wl_list_insert(&surface->ivi->surfaces, &surface->link); + if (surface->advertised_on_launch) + return; /* advertise to all desktop clients the new surface */ wl_list_for_each(dclient, &ivi->desktop_clients, link) { const char *app_id = weston_desktop_surface_get_app_id(surface->dsurface); + if (app_id == NULL) { + if (!display_adv) { + weston_log("WARNING app_is is null, unable to advertise\n"); + display_adv = true; + } + return; + } agl_shell_desktop_send_application(dclient->resource, app_id); + surface->advertised_on_launch = true; } } void +ivi_set_desktop_surface(struct ivi_surface *surface) +{ + struct ivi_compositor *ivi = surface->ivi; + assert(surface->role == IVI_SURFACE_ROLE_NONE); + + surface->role = IVI_SURFACE_ROLE_DESKTOP; + wl_list_insert(&surface->ivi->surfaces, &surface->link); + + agl_shell_desktop_advertise_application_id(ivi, surface); +} + +static void +ivi_set_background_surface(struct ivi_surface *surface) +{ + struct ivi_compositor *ivi = surface->ivi; + assert(surface->role == IVI_SURFACE_ROLE_BACKGROUND); + + wl_list_insert(&surface->ivi->surfaces, &surface->link); + agl_shell_desktop_advertise_application_id(ivi, surface); +} + +static void ivi_set_desktop_surface_popup(struct ivi_surface *surface) { struct ivi_compositor *ivi = surface->ivi; @@ -73,453 +111,1988 @@ ivi_set_desktop_surface_popup(struct ivi_surface *surface) surface->role = IVI_SURFACE_ROLE_POPUP; wl_list_insert(&ivi->surfaces, &surface->link); + + agl_shell_desktop_advertise_application_id(ivi, surface); } static void -ivi_set_pending_desktop_surface_popup(struct ivi_output *ioutput, - int x, int y, const char *app_id) +ivi_set_desktop_surface_fullscreen(struct ivi_surface *surface) { - struct ivi_compositor *ivi = ioutput->ivi; - size_t len_app_id = strlen(app_id); + struct ivi_compositor *ivi = surface->ivi; + assert(surface->role == IVI_SURFACE_ROLE_NONE); + surface->role = IVI_SURFACE_ROLE_FULLSCREEN; + wl_list_insert(&ivi->surfaces, &surface->link); + + agl_shell_desktop_advertise_application_id(ivi, surface); +} + +static void +ivi_set_desktop_surface_remote(struct ivi_surface *surface) +{ + struct ivi_compositor *ivi = surface->ivi; + struct weston_view *view; + struct ivi_output *output = surface->remote.output; + + assert(surface->role == IVI_SURFACE_ROLE_NONE); + + /* remote type are the same as desktop just that client can tell + * the compositor to start on another output */ + surface->role = IVI_SURFACE_ROLE_REMOTE; + + /* if thew black surface view is mapped on the mean we need + * to remove it in order to start showing the 'remote' surface + * just being added */ + view = output->fullscreen_view.fs->view; + if (view->is_mapped || view->surface->is_mapped) + remove_black_curtain(output); + + wl_list_insert(&ivi->surfaces, &surface->link); +} + + +static void +ivi_set_desktop_surface_split(struct ivi_surface *surface) +{ + struct ivi_compositor *ivi = surface->ivi; + assert(surface->role == IVI_SURFACE_ROLE_NONE); + + if (surface->split.orientation == AGL_SHELL_DESKTOP_APP_ROLE_SPLIT_VERTICAL) + surface->role = IVI_SURFACE_ROLE_SPLIT_V; + else + surface->role = IVI_SURFACE_ROLE_SPLIT_H; + + wl_list_insert(&ivi->surfaces, &surface->link); + + agl_shell_desktop_advertise_application_id(ivi, surface); +} + +static struct pending_popup * +ivi_ensure_popup(struct ivi_output *ioutput, int x, int y, int bx, int by, + int width, int height, const char *app_id) +{ struct pending_popup *p_popup = zalloc(sizeof(*p_popup)); + size_t len_app_id = strlen(app_id); + if (!p_popup) + return NULL; p_popup->app_id = zalloc(sizeof(char) * (len_app_id + 1)); + if (!p_popup->app_id) { + free(p_popup); + return NULL; + } memcpy(p_popup->app_id, app_id, len_app_id); p_popup->ioutput = ioutput; p_popup->x = x; p_popup->y = y; - wl_list_insert(&ivi->popup_pending_apps, &p_popup->link); + p_popup->bb.x = bx; + p_popup->bb.y = by; + p_popup->bb.width = width; + p_popup->bb.height = height; + + return p_popup; } static void -ivi_remove_pending_desktop_surface_popup(struct pending_popup *p_popup) +ivi_update_popup(struct ivi_output *ioutput, int x, int y, int bx, int by, + int width, int height, const char *app_id, struct pending_popup *p_popup) { - free(p_popup->app_id); + size_t len_app_id = strlen(app_id); + wl_list_remove(&p_popup->link); - free(p_popup); + wl_list_init(&p_popup->link); + + memset(p_popup->app_id, 0, strlen(app_id) + 1); + free(p_popup->app_id); + + p_popup->app_id = zalloc(sizeof(char) * (len_app_id + 1)); + if (!p_popup->app_id) + return; + memcpy(p_popup->app_id, app_id, len_app_id); + + p_popup->ioutput = ioutput; + p_popup->x = x; + p_popup->y = y; + + p_popup->bb.x = bx; + p_popup->bb.y = by; + p_popup->bb.width = width; + p_popup->bb.height = height; } -bool -ivi_check_pending_desktop_surface_popup(struct ivi_surface *surface) +static struct pending_fullscreen * +ivi_ensure_fullscreen(struct ivi_output *ioutput, const char *app_id) { - struct ivi_compositor *ivi = surface->ivi; - struct pending_popup *p_popup, *next_p_popup; - const char *_app_id = - weston_desktop_surface_get_app_id(surface->dsurface); - - wl_list_for_each_safe(p_popup, next_p_popup, - &ivi->popup_pending_apps, link) { - if (!strcmp(_app_id, p_popup->app_id)) { - surface->popup.output = p_popup->ioutput; - surface->popup.x = p_popup->x; - surface->popup.y = p_popup->y; + struct pending_fullscreen *p_fullscreen = zalloc(sizeof(*p_fullscreen)); + size_t len_app_id = strlen(app_id); - ivi_remove_pending_desktop_surface_popup(p_popup); - return true; - } + if (!p_fullscreen) + return NULL; + p_fullscreen->app_id = zalloc(sizeof(char) * (len_app_id + 1)); + if (!p_fullscreen->app_id) { + free(p_fullscreen); + return NULL; } + memcpy(p_fullscreen->app_id, app_id, len_app_id); - return false; + p_fullscreen->ioutput = ioutput; + return p_fullscreen; } -void -ivi_shell_init_black_fs(struct ivi_compositor *ivi) +static void +ivi_update_fullscreen(struct ivi_output *ioutput, const char *app_id, + struct pending_fullscreen *p_fullscreen) { - struct ivi_output *out; + size_t len_app_id = strlen(app_id); - wl_list_for_each(out, &ivi->outputs, link) { - create_black_surface_view(out); - insert_black_surface(out); + wl_list_remove(&p_fullscreen->link); + wl_list_init(&p_fullscreen->link); + + memset(p_fullscreen->app_id, 0, strlen(app_id) + 1); + free(p_fullscreen->app_id); + + p_fullscreen->app_id = zalloc(sizeof(char) * (len_app_id + 1)); + if (!p_fullscreen->app_id) + return; + memcpy(p_fullscreen->app_id, app_id, len_app_id); + + p_fullscreen->ioutput = ioutput; +} + +static struct pending_remote * +ivi_ensure_remote(struct ivi_output *ioutput, const char *app_id) +{ + struct pending_remote *p_remote = zalloc(sizeof(*p_remote)); + size_t len_app_id = strlen(app_id); + + if (!p_remote) + return NULL; + p_remote->app_id = zalloc(sizeof(char) * (len_app_id + 1)); + if (!p_remote->app_id) { + free(p_remote); + return NULL; } + memcpy(p_remote->app_id, app_id, len_app_id); + + p_remote->ioutput = ioutput; + return p_remote; } -int -ivi_shell_init(struct ivi_compositor *ivi) +static void +ivi_update_remote(struct ivi_output *ioutput, const char *app_id, + struct pending_remote *p_remote) { - weston_layer_init(&ivi->hidden, ivi->compositor); - weston_layer_init(&ivi->background, ivi->compositor); - weston_layer_init(&ivi->normal, ivi->compositor); - weston_layer_init(&ivi->panel, ivi->compositor); - weston_layer_init(&ivi->popup, ivi->compositor); - weston_layer_init(&ivi->fullscreen, ivi->compositor); + size_t len_app_id = strlen(app_id); - weston_layer_set_position(&ivi->hidden, - WESTON_LAYER_POSITION_HIDDEN); - weston_layer_set_position(&ivi->background, - WESTON_LAYER_POSITION_BACKGROUND); - weston_layer_set_position(&ivi->normal, - WESTON_LAYER_POSITION_NORMAL); - weston_layer_set_position(&ivi->panel, - WESTON_LAYER_POSITION_UI); - weston_layer_set_position(&ivi->popup, - WESTON_LAYER_POSITION_TOP_UI); - weston_layer_set_position(&ivi->fullscreen, - WESTON_LAYER_POSITION_FULLSCREEN); + wl_list_remove(&p_remote->link); + wl_list_init(&p_remote->link); - return 0; + memset(p_remote->app_id, 0, strlen(app_id) + 1); + free(p_remote->app_id); + + p_remote->app_id = zalloc(sizeof(char) * (len_app_id + 1)); + if (!p_remote->app_id) + return; + memcpy(p_remote->app_id, app_id, len_app_id); + + p_remote->ioutput = ioutput; } static void -ivi_shell_advertise_xdg_surfaces(struct ivi_compositor *ivi, struct wl_resource *resource) +ivi_set_pending_desktop_surface_popup(struct ivi_output *ioutput, int x, int y, int bx, + int by, int width, int height, const char *app_id) { - struct ivi_surface *surface; + struct ivi_compositor *ivi = ioutput->ivi; + struct pending_popup *p_popup = NULL; + struct pending_popup *popup; + + wl_list_for_each(popup, &ivi->popup_pending_apps, link) + if (!strcmp(app_id, popup->app_id)) + p_popup = popup; + + if (!p_popup) + p_popup = ivi_ensure_popup(ioutput, x, y, bx, by, width, height, app_id); + else + ivi_update_popup(ioutput, x, y, bx, by, width, height, app_id, p_popup); + if (!p_popup) + return; - wl_list_for_each(surface, &ivi->surfaces, link) { - const char *app_id = - weston_desktop_surface_get_app_id(surface->dsurface); - agl_shell_desktop_send_application(resource, app_id); - } + wl_list_insert(&ivi->popup_pending_apps, &p_popup->link); } static void -client_exec(const char *command, int fd) +ivi_set_pending_desktop_surface_fullscreen(struct ivi_output *ioutput, + const char *app_id) { - sigset_t sig; - char s[32]; + struct ivi_compositor *ivi = ioutput->ivi; + struct pending_fullscreen *p_fullscreen = NULL; + struct pending_fullscreen *fullscreen; + + wl_list_for_each(fullscreen, &ivi->fullscreen_pending_apps, link) + if (!strcmp(app_id, fullscreen->app_id)) + p_fullscreen = fullscreen; - /* Don't give the child our signal mask */ - sigfillset(&sig); - sigprocmask(SIG_UNBLOCK, &sig, NULL); + if (!p_fullscreen) + p_fullscreen = ivi_ensure_fullscreen(ioutput, app_id); + else + ivi_update_fullscreen(ioutput, app_id, p_fullscreen); - /* Launch clients as the user; don't give them the wrong euid */ - if (seteuid(getuid()) == -1) { - weston_log("seteuid failed: %s\n", strerror(errno)); + if (!p_fullscreen) return; - } + wl_list_insert(&ivi->fullscreen_pending_apps, &p_fullscreen->link); +} - /* Duplicate fd to unset the CLOEXEC flag. We don't need to worry about - * clobbering fd, as we'll exit/exec either way. - */ - fd = dup(fd); - if (fd == -1) { - weston_log("dup failed: %s\n", strerror(errno)); +static void +ivi_set_pending_desktop_surface_split(struct ivi_output *ioutput, + const char *app_id, uint32_t orientation) +{ + struct ivi_compositor *ivi = ioutput->ivi; + struct ivi_surface *surf; + size_t len_app_id = strlen(app_id); + struct pending_split *split; + + if (orientation != AGL_SHELL_DESKTOP_APP_ROLE_SPLIT_VERTICAL && + orientation != AGL_SHELL_DESKTOP_APP_ROLE_SPLIT_HORIZONTAL) + return; + + /* more than one is un-supported, do note we need to do + * conversion for surface roles instead of using the protocol ones */ + wl_list_for_each(surf, &ivi->surfaces, link) + if (surf->role == IVI_SURFACE_ROLE_SPLIT_V || + surf->role == IVI_SURFACE_ROLE_SPLIT_H) + return; + + split = zalloc(sizeof(*split)); + if (!split) + return; + split->app_id = zalloc(sizeof(char) * (len_app_id + 1)); + if (!split->app_id) { + free(split); return; } + memcpy(split->app_id, app_id, len_app_id); - snprintf(s, sizeof s, "%d", fd); - setenv("WAYLAND_SOCKET", s, 1); + split->ioutput = ioutput; + split->orientation = orientation; - execl("/bin/sh", "/bin/sh", "-c", command, NULL); - weston_log("executing '%s' failed: %s", command, strerror(errno)); + wl_list_insert(&ivi->split_pending_apps, &split->link); } -static struct wl_client * -launch_shell_client(struct ivi_compositor *ivi, const char *command) +void +ivi_set_pending_desktop_surface_remote(struct ivi_output *ioutput, + const char *app_id) { - struct wl_client *client; - int sock[2]; - pid_t pid; + struct ivi_compositor *ivi = ioutput->ivi; + struct pending_remote *remote; + struct pending_remote *p_remote = NULL; + + wl_list_for_each(remote, &ivi->remote_pending_apps, link) + if (!strcmp(app_id, remote->app_id)) + p_remote = remote; + + if (!p_remote) + p_remote = ivi_ensure_remote(ioutput, app_id); + else + ivi_update_remote(ioutput, app_id, p_remote); + if (!p_remote) + return; - weston_log("launching' %s'\n", command); + wl_list_insert(&ivi->remote_pending_apps, &p_remote->link); +} - if (os_socketpair_cloexec(AF_UNIX, SOCK_STREAM, 0, sock) < 0) { - weston_log("socketpair failed while launching '%s': %s\n", - command, strerror(errno)); - return NULL; - } - pid = fork(); - if (pid == -1) { - close(sock[0]); - close(sock[1]); - weston_log("fork failed while launching '%s': %s\n", - command, strerror(errno)); - return NULL; - } +static void +ivi_remove_pending_desktop_surface_split(struct pending_split *split) +{ + free(split->app_id); + wl_list_remove(&split->link); + free(split); +} - if (pid == 0) { - client_exec(command, sock[1]); - _Exit(EXIT_FAILURE); - } - close(sock[1]); +static void +ivi_remove_pending_desktop_surface_fullscreen(struct pending_fullscreen *fs) +{ + free(fs->app_id); + wl_list_remove(&fs->link); + free(fs); +} - client = wl_client_create(ivi->compositor->wl_display, sock[0]); - if (!client) { - close(sock[0]); - weston_log("Failed to create wayland client for '%s'", - command); - return NULL; - } +static void +ivi_remove_pending_desktop_surface_popup(struct pending_popup *p_popup) +{ + free(p_popup->app_id); + wl_list_remove(&p_popup->link); + free(p_popup); +} - return client; +static void +ivi_remove_pending_desktop_surface_remote(struct pending_remote *remote) +{ + free(remote->app_id); + wl_list_remove(&remote->link); + free(remote); } -int -ivi_launch_shell_client(struct ivi_compositor *ivi) +static bool +ivi_compositor_keep_pending_surfaces(struct ivi_surface *surface) { - struct weston_config_section *section; - char *command = NULL; + return surface->ivi->keep_pending_surfaces; +} - section = weston_config_get_section(ivi->config, "shell-client", - NULL, NULL); - if (section) - weston_config_section_get_string(section, "command", - &command, NULL); +static bool +ivi_check_pending_desktop_surface_popup(struct ivi_surface *surface) +{ + struct ivi_compositor *ivi = surface->ivi; + struct pending_popup *p_popup, *next_p_popup; + const char *_app_id = + weston_desktop_surface_get_app_id(surface->dsurface); - if (!command) - return -1; + if (wl_list_empty(&ivi->popup_pending_apps) || !_app_id) + return false; - ivi->shell_client.client = launch_shell_client(ivi, command); - if (!ivi->shell_client.client) - return -1; + wl_list_for_each_safe(p_popup, next_p_popup, + &ivi->popup_pending_apps, link) { + if (!strcmp(_app_id, p_popup->app_id)) { + surface->popup.output = p_popup->ioutput; + surface->popup.x = p_popup->x; + surface->popup.y = p_popup->y; - return 0; + surface->popup.bb.x = p_popup->bb.x; + surface->popup.bb.y = p_popup->bb.y; + surface->popup.bb.width = p_popup->bb.width; + surface->popup.bb.height = p_popup->bb.height; + + if (!ivi_compositor_keep_pending_surfaces(surface)) + ivi_remove_pending_desktop_surface_popup(p_popup); + return true; + } + } + + return false; } -static void -destroy_black_view(struct wl_listener *listener, void *data) +static bool +ivi_check_pending_desktop_surface_split(struct ivi_surface *surface) { - struct fullscreen_view *fs = - wl_container_of(listener, fs, fs_destroy); + struct pending_split *split_surf, *next_split_surf; + struct ivi_compositor *ivi = surface->ivi; + const char *_app_id = + weston_desktop_surface_get_app_id(surface->dsurface); + if (wl_list_empty(&ivi->split_pending_apps) || !_app_id) + return false; - if (fs && fs->fs) { - if (fs->fs->view && fs->fs->view->surface) { - weston_surface_destroy(fs->fs->view->surface); - fs->fs->view = NULL; + wl_list_for_each_safe(split_surf, next_split_surf, + &ivi->split_pending_apps, link) { + if (!strcmp(_app_id, split_surf->app_id)) { + surface->split.output = split_surf->ioutput; + surface->split.orientation = split_surf->orientation; + if (!ivi_compositor_keep_pending_surfaces(surface)) + ivi_remove_pending_desktop_surface_split(split_surf); + return true; } - - free(fs->fs); - wl_list_remove(&fs->fs_destroy.link); } -} + return false; +} -static void -create_black_surface_view(struct ivi_output *output) +static bool +ivi_check_pending_desktop_surface_fullscreen(struct ivi_surface *surface) { - struct weston_surface *surface = NULL; - struct weston_view *view; - struct ivi_compositor *ivi = output->ivi; - struct weston_compositor *wc= ivi->compositor; - struct weston_output *woutput = output->output; + struct pending_fullscreen *fs_surf, *next_fs_surf; + struct ivi_compositor *ivi = surface->ivi; + const char *_app_id = + weston_desktop_surface_get_app_id(surface->dsurface); - surface = weston_surface_create(wc); - view = weston_view_create(surface); + if (wl_list_empty(&ivi->fullscreen_pending_apps) || !_app_id) + return false; + + wl_list_for_each_safe(fs_surf, next_fs_surf, + &ivi->fullscreen_pending_apps, link) { + if (!strcmp(_app_id, fs_surf->app_id)) { + surface->fullscreen.output = fs_surf->ioutput; + if (!ivi_compositor_keep_pending_surfaces(surface)) + ivi_remove_pending_desktop_surface_fullscreen(fs_surf); + return true; + } + } - assert(view || surface); + return false; +} + +static bool +ivi_check_pending_desktop_surface_remote(struct ivi_surface *surface) +{ + struct pending_remote *remote_surf, *next_remote_surf; + struct ivi_compositor *ivi = surface->ivi; + const char *_app_id = + weston_desktop_surface_get_app_id(surface->dsurface); + + if (wl_list_empty(&ivi->remote_pending_apps) || !_app_id) + return false; + + wl_list_for_each_safe(remote_surf, next_remote_surf, + &ivi->remote_pending_apps, link) { + if (!strcmp(_app_id, remote_surf->app_id)) { + surface->remote.output = remote_surf->ioutput; + if (!ivi_compositor_keep_pending_surfaces(surface)) + ivi_remove_pending_desktop_surface_remote(remote_surf); + return true; + } + } + + return false; +} +void +ivi_check_pending_surface_desktop(struct ivi_surface *surface, + enum ivi_surface_role *role) +{ + struct ivi_compositor *ivi = surface->ivi; + struct wl_list *role_pending_list; + struct pending_popup *p_popup; + struct pending_split *p_split; + struct pending_fullscreen *p_fullscreen; + struct pending_remote *p_remote; + const char *app_id = + weston_desktop_surface_get_app_id(surface->dsurface); + + role_pending_list = &ivi->popup_pending_apps; + wl_list_for_each(p_popup, role_pending_list, link) { + if (app_id && !strcmp(app_id, p_popup->app_id)) { + *role = IVI_SURFACE_ROLE_POPUP; + return; + } + } + + role_pending_list = &ivi->split_pending_apps; + wl_list_for_each(p_split, role_pending_list, link) { + if (app_id && !strcmp(app_id, p_split->app_id)) { + *role = IVI_SURFACE_ROLE_SPLIT_V; + return; + } + } + + role_pending_list = &ivi->fullscreen_pending_apps; + wl_list_for_each(p_fullscreen, role_pending_list, link) { + if (app_id && !strcmp(app_id, p_fullscreen->app_id)) { + *role = IVI_SURFACE_ROLE_FULLSCREEN; + return; + } + } + + role_pending_list = &ivi->remote_pending_apps; + wl_list_for_each(p_remote, role_pending_list, link) { + if (app_id && !strcmp(app_id, p_remote->app_id)) { + *role = IVI_SURFACE_ROLE_REMOTE; + return; + } + } + + /* else, we are a regular desktop surface */ + *role = IVI_SURFACE_ROLE_DESKTOP; +} + +struct pending_app * +ivi_check_pending_app_type(struct ivi_surface *surface, enum ivi_surface_role role) +{ + struct pending_app *papp; + const char *app_id = NULL; + + app_id = weston_desktop_surface_get_app_id(surface->dsurface); + if (!app_id) + return NULL; + + wl_list_for_each(papp, &surface->ivi->pending_apps, link) { + if (strcmp(app_id, papp->app_id) == 0 && papp->role == role) + return papp; + } + + return NULL; +} + + +void +ivi_check_pending_desktop_surface(struct ivi_surface *surface) +{ + bool ret = false; + + ret = ivi_check_pending_desktop_surface_popup(surface); + if (ret) { + ivi_set_desktop_surface_popup(surface); + ivi_layout_popup_committed(surface); + return; + } + + ret = ivi_check_pending_desktop_surface_split(surface); + if (ret) { + ivi_set_desktop_surface_split(surface); + ivi_layout_split_committed(surface); + return; + } + + ret = ivi_check_pending_desktop_surface_fullscreen(surface); + if (ret) { + ivi_set_desktop_surface_fullscreen(surface); + ivi_layout_fullscreen_committed(surface); + return; + } + + ret = ivi_check_pending_desktop_surface_remote(surface); + if (ret) { + ivi_set_desktop_surface_remote(surface); + ivi_layout_remote_committed(surface); + return; + } + + /* new way of doing it */ + struct pending_app *papp = + ivi_check_pending_app_type(surface, IVI_SURFACE_ROLE_TILE); + if (papp) { + struct pending_app_tile *papp_tile = + container_of(papp, struct pending_app_tile, base); + + // handle the currently active surface + if (papp->ioutput->active) { + int width_prev_app = 0; + + if (papp_tile->width > 0) { + if (papp_tile->orientation == AGL_SHELL_TILE_ORIENTATION_TOP || + papp_tile->orientation == AGL_SHELL_TILE_ORIENTATION_BOTTOM) + width_prev_app = papp->ioutput->area.height - papp_tile->width; + else + width_prev_app = papp->ioutput->area.width - papp_tile->width; + } + _ivi_set_shell_surface_split(papp->ioutput->active, NULL, + reverse_orientation(papp_tile->orientation), + width_prev_app, false, false); + } + + surface->role = IVI_SURFACE_ROLE_TILE; + surface->current_completed_output = papp->ioutput; + wl_list_insert(&surface->ivi->surfaces, &surface->link); + + _ivi_set_shell_surface_split(surface, papp->ioutput, + papp_tile->orientation, papp_tile->width, + papp_tile->sticky, true); + + /* remove it from pending */ + wl_list_remove(&papp->link); + free(papp->app_id); + free(papp); + + return; + } + + /* if we end up here means we have a regular desktop app and + * try to activate it */ + ivi_set_desktop_surface(surface); + ivi_layout_desktop_committed(surface); +} + +void +ivi_shell_init_black_fs(struct ivi_compositor *ivi) +{ + struct ivi_output *out; + + wl_list_for_each(out, &ivi->outputs, link) { + create_black_curtain_view(out); + insert_black_curtain(out); + } +} + +int +ivi_shell_init(struct ivi_compositor *ivi) +{ + weston_layer_init(&ivi->hidden, ivi->compositor); + weston_layer_init(&ivi->background, ivi->compositor); + weston_layer_init(&ivi->normal, ivi->compositor); + weston_layer_init(&ivi->panel, ivi->compositor); + weston_layer_init(&ivi->popup, ivi->compositor); + weston_layer_init(&ivi->fullscreen, ivi->compositor); + + weston_layer_set_position(&ivi->hidden, + WESTON_LAYER_POSITION_HIDDEN); + weston_layer_set_position(&ivi->background, + WESTON_LAYER_POSITION_BACKGROUND); + weston_layer_set_position(&ivi->normal, + WESTON_LAYER_POSITION_NORMAL); + weston_layer_set_position(&ivi->panel, + WESTON_LAYER_POSITION_UI); + weston_layer_set_position(&ivi->popup, + WESTON_LAYER_POSITION_TOP_UI); + weston_layer_set_position(&ivi->fullscreen, + WESTON_LAYER_POSITION_FULLSCREEN); + + return 0; +} + + +static void +ivi_surf_destroy(struct ivi_surface *surf) +{ + struct weston_surface *wsurface = surf->view->surface; + + if (weston_surface_is_mapped(wsurface)) { + weston_desktop_surface_unlink_view(surf->view); + weston_view_destroy(surf->view); + } + + wl_list_remove(&surf->link); + free(surf); +} + +static void +ivi_shell_destroy_views_on_layer(struct weston_layer *layer) +{ + struct weston_view *view, *view_next; + + wl_list_for_each_safe(view, view_next, &layer->view_list.link, layer_link.link) { + struct ivi_surface *ivi_surf = + get_ivi_shell_surface(view->surface); + if (ivi_surf) + ivi_surf_destroy(ivi_surf); + } +} + +void +ivi_shell_finalize(struct ivi_compositor *ivi) +{ + struct ivi_output *output; + + ivi_shell_destroy_views_on_layer(&ivi->hidden); + weston_layer_fini(&ivi->hidden); + + ivi_shell_destroy_views_on_layer(&ivi->background); + weston_layer_fini(&ivi->background); + + ivi_shell_destroy_views_on_layer(&ivi->normal); + weston_layer_fini(&ivi->normal); + + ivi_shell_destroy_views_on_layer(&ivi->panel); + weston_layer_fini(&ivi->panel); + + ivi_shell_destroy_views_on_layer(&ivi->popup); + weston_layer_fini(&ivi->popup); + + wl_list_for_each(output, &ivi->outputs, link) { + if (output->fullscreen_view.fs && + output->fullscreen_view.fs->view) { + weston_surface_destroy(output->fullscreen_view.fs->view->surface); + output->fullscreen_view.fs->view = NULL; + } + } + weston_layer_fini(&ivi->fullscreen); +} + +static void +ivi_shell_advertise_xdg_surfaces(struct ivi_compositor *ivi, struct wl_resource *resource) +{ + struct ivi_surface *surface; + + wl_list_for_each(surface, &ivi->surfaces, link) { + const char *app_id = + weston_desktop_surface_get_app_id(surface->dsurface); + if (app_id == NULL) { + weston_log("WARNING app_is is null, unable to advertise\n"); + return; + } + agl_shell_desktop_send_application(resource, app_id); + } +} + +static struct wl_client * +client_launch(struct weston_compositor *compositor, + struct weston_process *proc, + const char *path, + weston_process_cleanup_func_t cleanup) +{ + struct wl_client *client = NULL; + struct custom_env child_env; + struct fdstr wayland_socket; + const char *fail_cloexec = "Couldn't unset CLOEXEC on client socket"; + const char *fail_seteuid = "Couldn't call seteuid"; + char *fail_exec; + char * const *argp; + char * const *envp; + sigset_t allsigs; + pid_t pid; + bool ret; + struct ivi_compositor *ivi; + size_t written __attribute__((unused)); + + weston_log("launching '%s'\n", path); + str_printf(&fail_exec, "Error: Couldn't launch client '%s'\n", path); + + custom_env_init_from_environ(&child_env); + custom_env_add_from_exec_string(&child_env, path); + + if (os_socketpair_cloexec(AF_UNIX, SOCK_STREAM, 0, + wayland_socket.fds) < 0) { + weston_log("client_launch: " + "socketpair failed while launching '%s': %s\n", + path, strerror(errno)); + custom_env_fini(&child_env); + return NULL; + } + fdstr_update_str1(&wayland_socket); + custom_env_set_env_var(&child_env, "WAYLAND_SOCKET", + wayland_socket.str1); + + argp = custom_env_get_argp(&child_env); + envp = custom_env_get_envp(&child_env); + + pid = fork(); + switch (pid) { + case 0: + /* Put the client in a new session so it won't catch signals + * intended for the parent. Sharing a session can be + * confusing when launching weston under gdb, as the ctrl-c + * intended for gdb will pass to the child, and weston + * will cleanly shut down when the child exits. + */ + setsid(); + + /* do not give our signal mask to the new process */ + sigfillset(&allsigs); + sigprocmask(SIG_UNBLOCK, &allsigs, NULL); + + /* Launch clients as the user. Do not launch clients with wrong euid. */ + if (seteuid(getuid()) == -1) { + written = write(STDERR_FILENO, fail_seteuid, strlen(fail_seteuid)); + _exit(EXIT_FAILURE); + } + + ret = fdstr_clear_cloexec_fd1(&wayland_socket); + if (!ret) { + written = write(STDERR_FILENO, fail_cloexec, strlen(fail_cloexec)); + _exit(EXIT_FAILURE); + } + + execve(argp[0], argp, envp); + + if (fail_exec) + written = write(STDERR_FILENO, fail_exec, strlen(fail_exec)); + _exit(EXIT_FAILURE); + + default: + close(wayland_socket.fds[1]); + ivi = weston_compositor_get_user_data(compositor); + client = wl_client_create(compositor->wl_display, + wayland_socket.fds[0]); + if (!client) { + custom_env_fini(&child_env); + close(wayland_socket.fds[0]); + free(fail_exec); + weston_log("client_launch: " + "wl_client_create failed while launching '%s'.\n", + path); + return NULL; + } + + proc->pid = pid; + proc->cleanup = cleanup; + wl_list_insert(&ivi->child_process_list, &proc->link); + break; + + case -1: + fdstr_close_all(&wayland_socket); + weston_log("client_launch: " + "fork failed while launching '%s': %s\n", path, + strerror(errno)); + break; + } + + custom_env_fini(&child_env); + free(fail_exec); + + return client; +} + +struct process_info { + struct weston_process proc; + char *path; +}; + +int +sigchld_handler(int signal_number, void *data) +{ + struct weston_process *p; + struct ivi_compositor *ivi = data; + int status; + pid_t pid; + + while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { + wl_list_for_each(p, &ivi->child_process_list, link) { + if (p->pid == pid) + break; + } + + if (&p->link == &ivi->child_process_list) { + weston_log("unknown child process exited\n"); + continue; + } + + wl_list_remove(&p->link); + wl_list_init(&p->link); + p->cleanup(p, status); + } + + if (pid < 0 && errno != ECHILD) + weston_log("waitpid error %s\n", strerror(errno)); + + return 1; +} + + +static void +process_handle_sigchld(struct weston_process *process, int status) +{ + struct process_info *pinfo = + container_of(process, struct process_info, proc); + + /* + * There are no guarantees whether this runs before or after + * the wl_client destructor. + */ + + if (WIFEXITED(status)) { + weston_log("%s exited with status %d\n", pinfo->path, + WEXITSTATUS(status)); + } else if (WIFSIGNALED(status)) { + weston_log("%s died on signal %d\n", pinfo->path, + WTERMSIG(status)); + } else { + weston_log("%s disappeared\n", pinfo->path); + } + + free(pinfo->path); + free(pinfo); +} + +int +ivi_launch_shell_client(struct ivi_compositor *ivi, const char *cmd_section, + struct wl_client **client) +{ + struct process_info *pinfo; + struct weston_config_section *section; + char *command = NULL; + + section = weston_config_get_section(ivi->config, cmd_section, NULL, NULL); + if (section) + weston_config_section_get_string(section, "command", &command, NULL); + + if (!command) + return -1; + + pinfo = zalloc(sizeof *pinfo); + if (!pinfo) + return -1; + + pinfo->path = strdup(command); + if (!pinfo->path) + goto out_free; + + *client = client_launch(ivi->compositor, &pinfo->proc, command, process_handle_sigchld); + if (!*client) + goto out_str; + + return 0; + +out_str: + free(pinfo->path); + +out_free: + free(pinfo); + + return -1; +} + +static void +destroy_black_curtain_view(struct wl_listener *listener, void *data) +{ + struct fullscreen_view *fs = + wl_container_of(listener, fs, fs_destroy); + + + if (fs && fs->fs) { + wl_list_remove(&fs->fs_destroy.link); + free(fs->fs); + } +} + + +static void +create_black_curtain_view(struct ivi_output *output) +{ + struct weston_surface *surface = NULL; + struct weston_view *view; + struct ivi_compositor *ivi = output->ivi; + struct weston_compositor *wc= ivi->compositor; + struct weston_output *woutput = output->output; + + if (!woutput) + return; + + surface = weston_surface_create(wc); + if (!surface) + return; + view = weston_view_create(surface); + if (!view) { + weston_surface_destroy(surface); + return; + } + + weston_surface_set_color(surface, 0.0, 0.0, 0.0, 1); + weston_surface_set_size(surface, woutput->width, woutput->height); + weston_view_set_position(view, woutput->x, woutput->y); + + output->fullscreen_view.fs = zalloc(sizeof(struct ivi_surface)); + if (!output->fullscreen_view.fs) { + weston_surface_destroy(surface); + return; + } + output->fullscreen_view.fs->view = view; + + output->fullscreen_view.fs_destroy.notify = destroy_black_curtain_view; + wl_signal_add(&woutput->destroy_signal, + &output->fullscreen_view.fs_destroy); +} + +bool +output_has_black_curtain(struct ivi_output *output) +{ + return (output->fullscreen_view.fs && + output->fullscreen_view.fs->view && + output->fullscreen_view.fs->view->is_mapped && + output->fullscreen_view.fs->view->surface->is_mapped); +} + +void +remove_black_curtain(struct ivi_output *output) +{ + struct weston_view *view; + + if ((!output && + !output->fullscreen_view.fs && + !output->fullscreen_view.fs->view) || + !output->fullscreen_view.fs) { + weston_log("Output %s doesn't have a surface installed!\n", output->name); + return; + } + + view = output->fullscreen_view.fs->view; + assert(view->is_mapped == true || + view->surface->is_mapped == true); + + view->is_mapped = false; + view->surface->is_mapped = false; + + weston_layer_entry_remove(&view->layer_link); + weston_view_update_transform(view); + + weston_view_damage_below(view); + + weston_log("Removed black curtain from output %s\n", output->output->name); +} + +void +insert_black_curtain(struct ivi_output *output) +{ + struct weston_view *view; + + if ((!output && + !output->fullscreen_view.fs && + !output->fullscreen_view.fs->view) || !output->output || + !output->fullscreen_view.fs) { + weston_log("Output %s doesn't have a surface installed!\n", output->name); + return; + } + + view = output->fullscreen_view.fs->view; + if (view->is_mapped || view->surface->is_mapped) + return; + + weston_layer_entry_remove(&view->layer_link); + weston_layer_entry_insert(&output->ivi->fullscreen.view_list, + &view->layer_link); + + view->is_mapped = true; + view->surface->is_mapped = true; + + weston_view_update_transform(view); + weston_view_damage_below(view); + + weston_log("Added black curtain to output %s\n", output->output->name); +} + +void +shell_send_app_state(struct ivi_compositor *ivi, const char *app_id, + enum agl_shell_app_state state) +{ + if (app_id && wl_resource_get_version(ivi->shell_client.resource) >= + AGL_SHELL_APP_STATE_SINCE_VERSION) { + + agl_shell_send_app_state(ivi->shell_client.resource, + app_id, state); + + if (ivi->shell_client.resource_ext) + agl_shell_send_app_state(ivi->shell_client.resource_ext, + app_id, state); + } +} + +void +shell_send_app_on_output(struct ivi_compositor *ivi, const char *app_id, + const char *output_name) +{ + if (app_id && ivi->shell_client.resource && + wl_resource_get_version(ivi->shell_client.resource) >= + AGL_SHELL_APP_ON_OUTPUT_SINCE_VERSION) { + + agl_shell_send_app_on_output(ivi->shell_client.resource, + app_id, output_name); + + if (ivi->shell_client.resource_ext) + agl_shell_send_app_on_output(ivi->shell_client.resource_ext, + app_id, output_name); + } +} + +static void +shell_ready(struct wl_client *client, struct wl_resource *shell_res) +{ + struct ivi_compositor *ivi = wl_resource_get_user_data(shell_res); + struct ivi_output *output; + struct ivi_surface *surface, *tmp; + + if (wl_resource_get_version(shell_res) >= + AGL_SHELL_BOUND_OK_SINCE_VERSION && + ivi->shell_client.status == BOUND_FAILED) { + wl_resource_post_error(shell_res, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "agl_shell (ready quest) has already " + "been bound. Check out bound_fail event"); + return; + } + + /* Init already finished. Do nothing */ + if (ivi->shell_client.ready) + return; + + + ivi->shell_client.ready = true; + + wl_list_for_each(output, &ivi->outputs, link) { + if (output->background && + output->background->role == IVI_SURFACE_ROLE_BACKGROUND) { + /* track the background surface role as a "regular" + * surface so we can activate it */ + ivi_set_background_surface(output->background); + remove_black_curtain(output); + } + + ivi_layout_init(ivi, output); + } + + wl_list_for_each_safe(surface, tmp, &ivi->pending_surfaces, link) { + const char *app_id; + + wl_list_remove(&surface->link); + wl_list_init(&surface->link); + ivi_check_pending_desktop_surface(surface); + surface->checked_pending = true; + app_id = weston_desktop_surface_get_app_id(surface->dsurface); + + shell_send_app_state(ivi, app_id, AGL_SHELL_APP_STATE_STARTED); + } +} + +static void +shell_set_background(struct wl_client *client, + struct wl_resource *shell_res, + struct wl_resource *surface_res, + struct wl_resource *output_res) +{ + struct weston_head *head = weston_head_from_resource(output_res); + struct weston_output *woutput = weston_head_get_output(head); + struct ivi_output *output = to_ivi_output(woutput); + struct weston_surface *wsurface = wl_resource_get_user_data(surface_res); + struct ivi_compositor *ivi = wl_resource_get_user_data(shell_res); + struct weston_desktop_surface *dsurface; + struct ivi_surface *surface; + + if ((wl_resource_get_version(shell_res) >= + AGL_SHELL_BOUND_OK_SINCE_VERSION && + ivi->shell_client.status == BOUND_FAILED) || + ivi->shell_client.resource_ext == shell_res) { + wl_resource_post_error(shell_res, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "agl_shell (set_background) has already " + "been bound. Check out bound_fail event"); + return; + } + + dsurface = weston_surface_get_desktop_surface(wsurface); + if (!dsurface) { + wl_resource_post_error(shell_res, + AGL_SHELL_ERROR_INVALID_ARGUMENT, + "surface must be a desktop surface"); + return; + } + + surface = weston_desktop_surface_get_user_data(dsurface); + if (surface->role != IVI_SURFACE_ROLE_NONE) { + wl_resource_post_error(shell_res, + AGL_SHELL_ERROR_INVALID_ARGUMENT, + "surface already has another ivi role"); + return; + } + + if (output->background) { + wl_resource_post_error(shell_res, + AGL_SHELL_ERROR_BACKGROUND_EXISTS, + "output already has background"); + return; + } + + surface->checked_pending = true; + surface->role = IVI_SURFACE_ROLE_BACKGROUND; + surface->bg.output = output; + wl_list_remove(&surface->link); + wl_list_init(&surface->link); + + output->background = surface; + + weston_desktop_surface_set_maximized(dsurface, true); + weston_desktop_surface_set_size(dsurface, + output->output->width, + output->output->height); +} + +static void +shell_set_panel(struct wl_client *client, + struct wl_resource *shell_res, + struct wl_resource *surface_res, + struct wl_resource *output_res, + uint32_t edge) +{ + struct weston_head *head = weston_head_from_resource(output_res); + struct weston_output *woutput = weston_head_get_output(head); + struct ivi_output *output = to_ivi_output(woutput); + struct weston_surface *wsurface = wl_resource_get_user_data(surface_res); + struct ivi_compositor *ivi = wl_resource_get_user_data(shell_res); + struct weston_desktop_surface *dsurface; + struct ivi_surface *surface; + struct ivi_surface **member; + int32_t width = 0, height = 0; + + if ((wl_resource_get_version(shell_res) >= + AGL_SHELL_BOUND_OK_SINCE_VERSION && + ivi->shell_client.status == BOUND_FAILED) || + ivi->shell_client.resource_ext == shell_res) { + wl_resource_post_error(shell_res, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "agl_shell (set_panel) has already been bound. " + "Check out bound_fail event"); + return; + } + + dsurface = weston_surface_get_desktop_surface(wsurface); + if (!dsurface) { + wl_resource_post_error(shell_res, + AGL_SHELL_ERROR_INVALID_ARGUMENT, + "surface must be a desktop surface"); + return; + } + + surface = weston_desktop_surface_get_user_data(dsurface); + if (surface->role != IVI_SURFACE_ROLE_NONE) { + wl_resource_post_error(shell_res, + AGL_SHELL_ERROR_INVALID_ARGUMENT, + "surface already has another ivi role"); + return; + } + + switch (edge) { + case AGL_SHELL_EDGE_TOP: + member = &output->top; + break; + case AGL_SHELL_EDGE_BOTTOM: + member = &output->bottom; + break; + case AGL_SHELL_EDGE_LEFT: + member = &output->left; + break; + case AGL_SHELL_EDGE_RIGHT: + member = &output->right; + break; + default: + wl_resource_post_error(shell_res, + AGL_SHELL_ERROR_INVALID_ARGUMENT, + "invalid edge for panel"); + return; + } + + if (*member) { + wl_resource_post_error(shell_res, + AGL_SHELL_ERROR_BACKGROUND_EXISTS, + "output already has panel on this edge"); + return; + } + + surface->checked_pending = true; + surface->role = IVI_SURFACE_ROLE_PANEL; + surface->panel.output = output; + surface->panel.edge = edge; + wl_list_remove(&surface->link); + wl_list_init(&surface->link); + + *member = surface; + + switch (surface->panel.edge) { + case AGL_SHELL_EDGE_TOP: + case AGL_SHELL_EDGE_BOTTOM: + width = woutput->width; + break; + case AGL_SHELL_EDGE_LEFT: + case AGL_SHELL_EDGE_RIGHT: + height = woutput->height; + break; + } + + weston_desktop_surface_set_size(dsurface, width, height); +} + +void +shell_advertise_app_state(struct ivi_compositor *ivi, const char *app_id, + const char *data, uint32_t app_state) +{ + struct desktop_client *dclient; + uint32_t app_role; + struct ivi_surface *surf = ivi_find_app(ivi, app_id); + struct ivi_policy *policy = ivi->policy; + + /* FIXME: should queue it here and see when binding agl-shell-desktop + * if there are any to be sent */ + if (!surf) + return; + + if (!app_id) + return; + + if (policy && policy->api.surface_advertise_state_change && + !policy->api.surface_advertise_state_change(surf, surf->ivi)) { + return; + } + + app_role = surf->role; + if (app_role == IVI_SURFACE_ROLE_POPUP) + app_role = AGL_SHELL_DESKTOP_APP_ROLE_POPUP; + + wl_list_for_each(dclient, &ivi->desktop_clients, link) + agl_shell_desktop_send_state_app(dclient->resource, app_id, + data, app_state, app_role); +} + +static void +shell_activate_app(struct wl_client *client, + struct wl_resource *shell_res, + const char *app_id, + struct wl_resource *output_res) +{ + struct weston_head *head; + struct weston_output *woutput; + struct ivi_compositor *ivi; + struct ivi_output *output; + + head = weston_head_from_resource(output_res); + if (!head) { + weston_log("Invalid output to activate '%s' on\n", app_id); + return; + } + + woutput = weston_head_get_output(head); + ivi = wl_resource_get_user_data(shell_res); + output = to_ivi_output(woutput); + + if (wl_resource_get_version(shell_res) >= + AGL_SHELL_BOUND_OK_SINCE_VERSION && + ivi->shell_client.status == BOUND_FAILED) { + wl_resource_post_error(shell_res, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "agl_shell has already been bound. " + "Check out bound_fail event"); + return; + } + + ivi_layout_activate(output, app_id); +} + +static void +shell_new_deactivate_app(struct wl_client *client, struct wl_resource *shell_res, + const char *app_id) +{ + struct ivi_compositor *ivi = wl_resource_get_user_data(shell_res); + + ivi_layout_deactivate(ivi, app_id); +} + +static void +shell_set_app_float(struct wl_client *client, struct wl_resource *shell_res, + const char *app_id, int32_t x_pos, int32_t y_pos) +{ + struct ivi_compositor *ivi = wl_resource_get_user_data(shell_res); + struct weston_output *output = get_focused_output(ivi->compositor); + struct ivi_output *ivi_output; + struct ivi_surface *surf = ivi_find_app(ivi, app_id); + + if (!output) + output = get_default_output(ivi->compositor); + + ivi_output = to_ivi_output(output); + + /* verify if already mapped as desktop role, regular, unmap it, so we + * can make it float */ + if (surf && surf->role == IVI_SURFACE_ROLE_DESKTOP) { + struct weston_view *ev = surf->view; + struct weston_desktop_surface *dsurf = surf->dsurface; + struct ivi_bounding_box bb = {}; + const char *prev_activate_app_id = NULL; + + /* XXX: if this is useful we could bring it as active then make + * it float, but avoid doing it for the moment */ + if (surf != ivi_output->active) + return; + + if (ivi_output->previous_active) + prev_activate_app_id = + weston_desktop_surface_get_app_id(ivi_output->previous_active->dsurface); + + /* only deactivate if previous_active is different, otherwise + * this will blow up, because we're trying to activate the same + * app_id as the this one! */ + if (prev_activate_app_id && strcmp(app_id, prev_activate_app_id)) { + ivi_layout_deactivate(ivi, app_id); + } else if (prev_activate_app_id) { + weston_layer_entry_remove(&ev->layer_link); + weston_view_geometry_dirty(ev); + weston_surface_damage(ev->surface); + } + + surf->hidden_layer_output = ivi_output; + + /* set attributes */ + surf->popup.output = ivi_output; + + surf->popup.x = x_pos; + surf->popup.y = y_pos; + surf->popup.bb = bb; + + + /* change the role */ + surf->role = IVI_SURFACE_ROLE_NONE; + + wl_list_remove(&surf->link); + wl_list_init(&surf->link); + + ivi_set_desktop_surface_popup(surf); + + weston_desktop_surface_set_maximized(dsurf, false); + weston_desktop_surface_set_size(dsurf, 0, 0); + + /* add to hidden layer */ + weston_layer_entry_insert(&ivi->hidden.view_list, &ev->layer_link); + weston_compositor_schedule_repaint(ivi->compositor); + + } else if (!surf || (surf && surf->role != IVI_SURFACE_ROLE_POPUP)) { + ivi_set_pending_desktop_surface_popup(ivi_output, x_pos, y_pos, + 0, 0, 0, 0, app_id); + } +} + +static void +shell_set_app_fullscreen(struct wl_client *client, + struct wl_resource *shell_res, const char *app_id) +{ + struct ivi_compositor *ivi = wl_resource_get_user_data(shell_res); + struct weston_output *output = get_focused_output(ivi->compositor); + struct ivi_output *ivi_output; + struct ivi_surface *surf = ivi_find_app(ivi, app_id); + + if (!output) + output = get_default_output(ivi->compositor); + + ivi_output = to_ivi_output(output); + + if (surf && surf->role == IVI_SURFACE_ROLE_DESKTOP) { + struct weston_view *ev = surf->view; + struct weston_desktop_surface *dsurf = surf->dsurface; + + if (surf != ivi_output->active) + return; + + weston_layer_entry_remove(&surf->view->layer_link); + weston_view_geometry_dirty(surf->view); + weston_surface_damage(surf->view->surface); + + surf->hidden_layer_output = ivi_output; + + /* set attributes */ + surf->fullscreen.output = ivi_output; + + /* change the role */ + surf->role = IVI_SURFACE_ROLE_NONE; + + wl_list_remove(&surf->link); + wl_list_init(&surf->link); + + ivi_set_desktop_surface_fullscreen(surf); + + weston_desktop_surface_set_maximized(dsurf, false); + weston_desktop_surface_set_fullscreen(dsurf, true); + weston_desktop_surface_set_size(dsurf, + ivi_output->output->width, + ivi_output->output->height); + + /* add to hidden layer */ + weston_layer_entry_insert(&ivi->hidden.view_list, &ev->layer_link); + weston_compositor_schedule_repaint(ivi->compositor); + } else if (!surf || (surf && surf->role != IVI_SURFACE_ROLE_FULLSCREEN)) { + ivi_set_pending_desktop_surface_fullscreen(ivi_output, app_id); + } +} + + +static void +shell_set_app_normal(struct wl_client *client, struct wl_resource *shell_res, + const char *app_id) +{ + struct ivi_compositor *ivi = wl_resource_get_user_data(shell_res); + struct ivi_surface *surf = ivi_find_app(ivi, app_id); + struct weston_output *output = get_focused_output(ivi->compositor); + struct ivi_output *ivi_output; + struct weston_desktop_surface *dsurf; + struct weston_geometry area = {}; + + + if (!surf || (surf && surf->role == IVI_SURFACE_ROLE_DESKTOP)) + return; + + if (!output) + output = get_default_output(ivi->compositor); + + dsurf = surf->dsurface; + ivi_output = to_ivi_output(output); + + weston_layer_entry_remove(&surf->view->layer_link); + weston_view_geometry_dirty(surf->view); + weston_surface_damage(surf->view->surface); + + /* change the role */ + surf->role = IVI_SURFACE_ROLE_NONE; + surf->state = NORMAL; + surf->desktop.pending_output = ivi_output; + + wl_list_remove(&surf->link); + wl_list_init(&surf->link); + + ivi_set_desktop_surface(surf); + + if (ivi_output->area_activation.width || + ivi_output->area_activation.height) + area = ivi_output->area_activation; + else + area = ivi_output->area; + + if (weston_desktop_surface_get_fullscreen(dsurf)) + weston_desktop_surface_set_fullscreen(dsurf, false); + + weston_desktop_surface_set_maximized(dsurf, true); + weston_desktop_surface_set_size(dsurf, area.width, area.height); + + /* add to hidden layer */ + weston_layer_entry_insert(&ivi->hidden.view_list, + &surf->view->layer_link); + weston_compositor_schedule_repaint(ivi->compositor); + +} + +static void +shell_desktop_activate_app(struct wl_client *client, + struct wl_resource *shell_res, + const char *app_id, const char *data, + struct wl_resource *output_res) +{ + struct weston_head *head = weston_head_from_resource(output_res); + struct weston_output *woutput = weston_head_get_output(head); + struct ivi_output *output = to_ivi_output(woutput); + + ivi_layout_activate(output, app_id); + shell_advertise_app_state(output->ivi, app_id, + data, AGL_SHELL_DESKTOP_APP_STATE_ACTIVATED); +} + +static void +shell_deactivate_app(struct wl_client *client, + struct wl_resource *shell_res, + const char *app_id) +{ + struct desktop_client *dclient = wl_resource_get_user_data(shell_res); + struct ivi_compositor *ivi = dclient->ivi; + + ivi_layout_deactivate(ivi, app_id); + shell_advertise_app_state(ivi, app_id, + NULL, AGL_SHELL_DESKTOP_APP_STATE_DEACTIVATED); +} + +static void +shell_destroy(struct wl_client *client, struct wl_resource *res) +{ + struct ivi_compositor *ivi = wl_resource_get_user_data(res); + + /* reset status in case bind_fail was sent */ + if (wl_resource_get_version(res) >= AGL_SHELL_BOUND_OK_SINCE_VERSION && + ivi->shell_client.status == BOUND_FAILED) + ivi->shell_client.status = BOUND_OK; +} + +static void +shell_set_activate_region(struct wl_client *client, struct wl_resource *res, + struct wl_resource *output, int x, int y, + int width, int height) +{ + struct ivi_compositor *ivi = wl_resource_get_user_data(res); + struct weston_head *head = weston_head_from_resource(output); + struct weston_output *woutput = weston_head_get_output(head); + struct ivi_output *ioutput = to_ivi_output(woutput); + + struct weston_geometry area = { .x = x, .y = y, + .width = width, + .height = height }; + if (ivi->shell_client.ready) + return; + + ioutput->area_activation = area; +} + +static void +shell_set_app_output(struct wl_client *client, struct wl_resource *res, + const char *app_id, struct wl_resource *output) +{ + struct ivi_compositor *ivi = wl_resource_get_user_data(res); + struct weston_head *head = weston_head_from_resource(output); + struct weston_output *woutput = weston_head_get_output(head); + struct ivi_output *ioutput = to_ivi_output(woutput); + struct ivi_surface *surf = ivi_find_app(ivi, app_id); + struct ivi_output *desktop_last_output; + struct ivi_output *current_completed_output; + + if (!app_id || !ioutput) + return; + + /* handle the case we're not mapped at all */ + if (!surf) { + ivi_set_pending_desktop_surface_remote(ioutput, app_id); + shell_send_app_on_output(ivi, app_id, woutput->name); + return; + } + + desktop_last_output = surf->desktop.last_output; + current_completed_output = surf->current_completed_output; + + if (surf->remote.output) + surf->hidden_layer_output = surf->remote.output; + else + surf->hidden_layer_output = desktop_last_output; + assert(surf->hidden_layer_output); + + if (ivi_surface_count_one(current_completed_output, IVI_SURFACE_ROLE_REMOTE) || + ivi_surface_count_one(current_completed_output, IVI_SURFACE_ROLE_DESKTOP)) { + if (!current_completed_output->background) + insert_black_curtain(current_completed_output); + } else { + ivi_layout_deactivate(ivi, app_id); + } + + /* update the remote output */ + surf->remote.output = ioutput; + + if (surf->role != IVI_SURFACE_ROLE_REMOTE) { + wl_list_remove(&surf->link); + wl_list_init(&surf->link); + + surf->role = IVI_SURFACE_ROLE_NONE; + ivi_set_desktop_surface_remote(surf); + } + + shell_send_app_on_output(ivi, app_id, woutput->name); +} + +static void +shell_set_app_position(struct wl_client *client, struct wl_resource *res, + const char *app_id, int32_t x, int32_t y) +{ + struct ivi_compositor *ivi = wl_resource_get_user_data(res); + struct ivi_surface *surf = ivi_find_app(ivi, app_id); + + if (!surf || !app_id || surf->role != IVI_SURFACE_ROLE_POPUP) + return; + + weston_view_set_position(surf->view, x, y); + weston_compositor_schedule_repaint(ivi->compositor); +} + +static void +shell_set_app_scale(struct wl_client *client, struct wl_resource *res, + const char *app_id, int32_t width, int32_t height) +{ + + struct ivi_compositor *ivi = wl_resource_get_user_data(res); + struct ivi_surface *surf = ivi_find_app(ivi, app_id); + + if (!surf || !app_id || surf->role != IVI_SURFACE_ROLE_POPUP) + return; + + weston_desktop_surface_set_size(surf->dsurface, width, height); + weston_compositor_schedule_repaint(ivi->compositor); +} + +static void +_ivi_set_pending_desktop_surface_split(struct wl_resource *output, + const char *app_id, uint32_t orientation) +{ + weston_log("%s() added split surface for app_id '%s' with " + "orientation %d to pending\n", __func__, app_id, orientation); + + struct weston_head *head = weston_head_from_resource(output); + struct weston_output *woutput = weston_head_get_output(head); + struct ivi_output *ivi_output = to_ivi_output(woutput); + + struct pending_app_tile *app_tile = zalloc(sizeof(*app_tile)); + + app_tile->base.app_id = strdup(app_id); + app_tile->base.ioutput = ivi_output; + app_tile->base.role = IVI_SURFACE_ROLE_TILE; + + app_tile->orientation = orientation; + wl_list_insert(&ivi_output->ivi->pending_apps, &app_tile->base.link); +} + + +const char * +split_orientation_to_string(uint32_t orientation) +{ + switch (orientation) { + case AGL_SHELL_TILE_ORIENTATION_LEFT: + return "tile_left"; + break; + case AGL_SHELL_TILE_ORIENTATION_RIGHT: + return "tile_right"; + break; + case AGL_SHELL_TILE_ORIENTATION_TOP: + return "title_top"; + break; + case AGL_SHELL_TILE_ORIENTATION_BOTTOM: + return "tile_bottom"; + break; + default: + return "none"; + } +} + +static uint32_t +reverse_orientation(uint32_t orientation) +{ + + switch (orientation) { + case AGL_SHELL_TILE_ORIENTATION_LEFT: + return AGL_SHELL_TILE_ORIENTATION_RIGHT; + break; + case AGL_SHELL_TILE_ORIENTATION_RIGHT: + return AGL_SHELL_TILE_ORIENTATION_LEFT; + break; + case AGL_SHELL_TILE_ORIENTATION_TOP: + return AGL_SHELL_TILE_ORIENTATION_BOTTOM; + break; + case AGL_SHELL_TILE_ORIENTATION_BOTTOM: + return AGL_SHELL_TILE_ORIENTATION_TOP; + break; + default: + return AGL_SHELL_TILE_ORIENTATION_NONE; + } +} + +void +_ivi_set_shell_surface_split(struct ivi_surface *surface, struct ivi_output *ioutput, + uint32_t orientation, uint32_t width, int32_t sticky, + bool to_activate) +{ + struct ivi_compositor *ivi = surface->ivi; + struct weston_view *ev = surface->view; + struct weston_geometry geom = {}; + struct ivi_output *output = NULL; + + int new_width, new_height; + int x, y; + + geom = weston_desktop_surface_get_geometry(surface->dsurface); + output = ivi_layout_get_output_from_surface(surface); + + if (!output) + output = ioutput; + + // if we don't supply a width we automatically default to doing a half + // split, each window taking half of the current output + switch (orientation) { + case AGL_SHELL_TILE_ORIENTATION_LEFT: + case AGL_SHELL_TILE_ORIENTATION_RIGHT: + if (width == 0) { + new_width = output->area.width / 2; + new_height = output->area.height; + } else { + new_width = width; + new_height = output->area.height; + } + break; + case AGL_SHELL_TILE_ORIENTATION_TOP: + case AGL_SHELL_TILE_ORIENTATION_BOTTOM: + if (width == 0) { + new_width = output->area.width; + new_height = output->area.height / 2; + } else { + new_width = output->area.width; + new_height = width; + } + break; + case AGL_SHELL_TILE_ORIENTATION_NONE: + /* use the current_completed_output because the sticky window + * might have changed the output area */ + new_width = surface->current_completed_output->area_activation.width; + new_height = surface->current_completed_output->area_activation.height; - weston_surface_set_color(surface, 0.0, 0.0, 0.0, 1); - weston_surface_set_size(surface, woutput->width, woutput->height); - weston_view_set_position(view, woutput->x, woutput->y); + if (new_width != output->area.width) + output->area.width = new_width; - output->fullscreen_view.fs = zalloc(sizeof(struct ivi_surface)); - output->fullscreen_view.fs->view = view; + if (new_height != output->area.height) + output->area.height = new_height; - output->fullscreen_view.fs_destroy.notify = destroy_black_view; - wl_signal_add(&woutput->destroy_signal, - &output->fullscreen_view.fs_destroy); -} + weston_log("Adjusting activation area " + "to %dX%d\n", output->area.width, output->area.height); -static void -remove_black_surface(struct ivi_output *output) -{ - struct weston_view *view = output->fullscreen_view.fs->view; + if (surface->sticky) { + surface->sticky = 0; + weston_log("Resetting sticky window\n"); + } - assert(view->is_mapped == true || - view->surface->is_mapped == true); + surface->role = surface->prev_role; + weston_log("Resetting tile role to previous role\n"); + break; + default: + /* nothing */ + assert(!"Invalid orientation passed"); - view->is_mapped = false; - view->surface->is_mapped = false; + } - weston_layer_entry_remove(&view->layer_link); - weston_view_update_transform(view); + x = output->area.x - geom.x; + y = output->area.y - geom.y; - weston_output_damage(output->output); -} + if (orientation == AGL_SHELL_TILE_ORIENTATION_RIGHT) + x += output->area.width - new_width; + else if (orientation == AGL_SHELL_TILE_ORIENTATION_BOTTOM) + y += output->area.height - new_height; -static void -insert_black_surface(struct ivi_output *output) -{ - struct weston_view *view = output->fullscreen_view.fs->view; - if (view->is_mapped || view->surface->is_mapped) - return; + if (to_activate) { + struct ivi_shell_seat *ivi_seat = NULL; + struct weston_seat *wseat = get_ivi_shell_weston_first_seat(ivi); - weston_layer_entry_remove(&view->layer_link); - weston_layer_entry_insert(&output->ivi->fullscreen.view_list, - &view->layer_link); + if (wseat) + ivi_seat = get_ivi_shell_seat(wseat); - view->is_mapped = true; - view->surface->is_mapped = true; + if (!weston_view_is_mapped(ev)) + weston_view_update_transform(ev); + else + weston_layer_entry_remove(&ev->layer_link); - weston_view_update_transform(view); - weston_output_damage(output->output); -} -static void -shell_ready(struct wl_client *client, struct wl_resource *shell_res) -{ - struct ivi_compositor *ivi = wl_resource_get_user_data(shell_res); - struct ivi_output *output; - struct ivi_surface *surface, *tmp; + // mark view as mapped + ev->is_mapped = true; + ev->surface->is_mapped = true; + surface->mapped = true; - /* Init already finished. Do nothing */ - if (ivi->shell_client.ready) - return; + // update older/new active surface + output->previous_active = output->active; + output->active = surface; - ivi->shell_client.ready = true; + // add to the layer and inflict damage + weston_view_set_output(ev, output->output); + weston_layer_entry_insert(&ivi->normal.view_list, &ev->layer_link); + weston_view_geometry_dirty(ev); + weston_surface_damage(ev->surface); - wl_list_for_each(output, &ivi->outputs, link) { - remove_black_surface(output); - ivi_layout_init(ivi, output); + // handle input / keyboard + if (ivi_seat) + ivi_shell_activate_surface(surface, ivi_seat, WESTON_ACTIVATE_FLAG_NONE); } - wl_list_for_each_safe(surface, tmp, &ivi->pending_surfaces, link) { - wl_list_remove(&surface->link); + weston_view_set_position(surface->view, x, y); + weston_desktop_surface_set_size(surface->dsurface, new_width, new_height); + weston_desktop_surface_set_orientation(surface->dsurface, orientation); + surface->orientation = orientation; - if (ivi_check_pending_desktop_surface_popup(surface)) { - ivi_set_desktop_surface_popup(surface); - } else { - ivi_set_desktop_surface(surface); - ivi_layout_desktop_committed(surface); - } - } -} + weston_compositor_schedule_repaint(ivi->compositor); -static void -shell_set_background(struct wl_client *client, - struct wl_resource *shell_res, - struct wl_resource *surface_res, - struct wl_resource *output_res) -{ - struct weston_head *head = weston_head_from_resource(output_res); - struct weston_output *woutput = weston_head_get_output(head); - struct ivi_output *output = to_ivi_output(woutput); - struct weston_surface *wsurface = wl_resource_get_user_data(surface_res); - struct weston_desktop_surface *dsurface; - struct ivi_surface *surface; + if (sticky) + surface->sticky = sticky; - dsurface = weston_surface_get_desktop_surface(wsurface); - if (!dsurface) { - wl_resource_post_error(shell_res, - AGL_SHELL_ERROR_INVALID_ARGUMENT, - "surface must be a desktop surface"); - return; + if (orientation != AGL_SHELL_TILE_ORIENTATION_NONE) { + surface->prev_role = surface->role; + surface->role = IVI_SURFACE_ROLE_TILE; + weston_log("Found split orientation different that none, " + "setting surface role to orientation tile\n"); } - surface = weston_desktop_surface_get_user_data(dsurface); - if (surface->role != IVI_SURFACE_ROLE_NONE) { - wl_resource_post_error(shell_res, - AGL_SHELL_ERROR_INVALID_ARGUMENT, - "surface already has another ivi role"); - return; - } + if (surface->sticky) { + if (orientation == AGL_SHELL_TILE_ORIENTATION_LEFT || + orientation == AGL_SHELL_TILE_ORIENTATION_RIGHT) + output->area.width -= new_width; - if (output->background) { - wl_resource_post_error(shell_res, - AGL_SHELL_ERROR_BACKGROUND_EXISTS, - "output already has background"); - return; + if (orientation == AGL_SHELL_TILE_ORIENTATION_TOP || + orientation == AGL_SHELL_TILE_ORIENTATION_BOTTOM) + output->area.height -= new_height; + + weston_log("Found sticky window, adjusting activation area " + "to %dX%d\n", output->area.width, output->area.height); } +} - surface->role = IVI_SURFACE_ROLE_BACKGROUND; - surface->bg.output = output; - wl_list_remove(&surface->link); - wl_list_init(&surface->link); +static int +shell_ivi_surf_count_split_surfaces(struct ivi_compositor *ivi) +{ + int count = 0; + struct ivi_surface *surf; - output->background = surface; + wl_list_for_each(surf, &ivi->surfaces, link) { + if (surf->orientation > AGL_SHELL_TILE_ORIENTATION_NONE) + count++; + } - weston_desktop_surface_set_maximized(dsurface, true); - weston_desktop_surface_set_size(dsurface, - output->output->width, - output->output->height); + return count; } -static void -shell_set_panel(struct wl_client *client, - struct wl_resource *shell_res, - struct wl_resource *surface_res, - struct wl_resource *output_res, - uint32_t edge) + +static +void shell_set_app_split(struct wl_client *client, struct wl_resource *res, + const char *app_id, uint32_t orientation, int32_t width, + int32_t sticky, struct wl_resource *output_res) { + struct ivi_surface *surf; + struct ivi_compositor *ivi = wl_resource_get_user_data(res); + struct weston_head *head = weston_head_from_resource(output_res); struct weston_output *woutput = weston_head_get_output(head); struct ivi_output *output = to_ivi_output(woutput); - struct weston_surface *wsurface = wl_resource_get_user_data(surface_res); - struct weston_desktop_surface *dsurface; - struct ivi_surface *surface; - struct ivi_surface **member; - int32_t width = 0, height = 0; - dsurface = weston_surface_get_desktop_surface(wsurface); - if (!dsurface) { - wl_resource_post_error(shell_res, - AGL_SHELL_ERROR_INVALID_ARGUMENT, - "surface must be a desktop surface"); + if (!app_id) return; - } - surface = weston_desktop_surface_get_user_data(dsurface); - if (surface->role != IVI_SURFACE_ROLE_NONE) { - wl_resource_post_error(shell_res, - AGL_SHELL_ERROR_INVALID_ARGUMENT, - "surface already has another ivi role"); + if (shell_ivi_surf_count_split_surfaces(ivi) > 2) { + weston_log("Found more than two split surfaces in tile orientation.\n"); return; } - switch (edge) { - case AGL_SHELL_EDGE_TOP: - member = &output->top; - break; - case AGL_SHELL_EDGE_BOTTOM: - member = &output->bottom; - break; - case AGL_SHELL_EDGE_LEFT: - member = &output->left; - break; - case AGL_SHELL_EDGE_RIGHT: - member = &output->right; - break; - default: - wl_resource_post_error(shell_res, - AGL_SHELL_ERROR_INVALID_ARGUMENT, - "invalid edge for panel"); + /* add it as pending until */ + surf = ivi_find_app(ivi, app_id); + if (!surf) { + _ivi_set_pending_desktop_surface_split(output_res, app_id, orientation); return; } - if (*member) { - wl_resource_post_error(shell_res, - AGL_SHELL_ERROR_BACKGROUND_EXISTS, - "output already has panel on this edge"); - return; - } + if (output->previous_active && output->background != output->previous_active) { + int width_prev_app = 0; + struct weston_view *ev = output->previous_active->view; + const char *prev_app_id = + weston_desktop_surface_get_app_id(output->previous_active->dsurface); + + if (orientation != AGL_SHELL_TILE_ORIENTATION_NONE) { + if (!weston_view_is_mapped(ev)) + weston_view_update_transform(ev); + else + weston_layer_entry_remove(&ev->layer_link); + + ev->is_mapped = true; + ev->surface->is_mapped = true; + output->previous_active->mapped = true; + + weston_view_set_output(ev, woutput); + weston_layer_entry_insert(&ivi->normal.view_list, + &ev->layer_link); + } else { + ev->is_mapped = false; + ev->surface->is_mapped = false; - surface->role = IVI_SURFACE_ROLE_PANEL; - surface->panel.output = output; - surface->panel.edge = edge; - wl_list_remove(&surface->link); - wl_list_init(&surface->link); + weston_layer_entry_remove(&ev->layer_link); - *member = surface; + weston_view_geometry_dirty(ev); + weston_surface_damage(ev->surface); + } - switch (surface->panel.edge) { - case AGL_SHELL_EDGE_TOP: - case AGL_SHELL_EDGE_BOTTOM: - width = woutput->width; - break; - case AGL_SHELL_EDGE_LEFT: - case AGL_SHELL_EDGE_RIGHT: - height = woutput->height; - break; + /* a 0 width means we have no explicit width set-up */ + if (width > 0) { + if (orientation == AGL_SHELL_TILE_ORIENTATION_TOP || + orientation == AGL_SHELL_TILE_ORIENTATION_BOTTOM) + width_prev_app = output->area.height - width; + else + width_prev_app = output->area.width - width; + } + + weston_log("Setting previous application '%s' to orientation '%s' width %d, not sticky\n", + prev_app_id, split_orientation_to_string(reverse_orientation(orientation)), + width_prev_app); + + _ivi_set_shell_surface_split(output->previous_active, NULL, + reverse_orientation(orientation), + width_prev_app, false, false); + + if (orientation == AGL_SHELL_TILE_ORIENTATION_NONE && + output->active == surf) { + output->active = output->previous_active; + } } - weston_desktop_surface_set_size(dsurface, width, height); + weston_log("Setting application '%s' to orientation '%s' width %d, %s\n", + app_id, split_orientation_to_string(orientation), + width, sticky == 1 ? "sticky" : "not sticky"); + _ivi_set_shell_surface_split(surf, NULL, orientation, width, sticky, false); } static void -shell_activate_app(struct wl_client *client, - struct wl_resource *shell_res, - const char *app_id, - struct wl_resource *output_res) +shell_ext_destroy(struct wl_client *client, struct wl_resource *res) { - struct weston_head *head = weston_head_from_resource(output_res); - struct weston_output *woutput = weston_head_get_output(head); - struct ivi_output *output = to_ivi_output(woutput); + struct ivi_compositor *ivi = wl_resource_get_user_data(res); + + ivi->shell_client_ext.doas_requested = false; + wl_resource_destroy(res); +} + +static void +shell_ext_doas(struct wl_client *client, struct wl_resource *res) +{ + struct ivi_compositor *ivi = wl_resource_get_user_data(res); + + ivi->shell_client_ext.doas_requested = true; + + if (ivi->shell_client_ext.resource && ivi->shell_client.resource) { + ivi->shell_client_ext.doas_requested_pending_bind = true; + + agl_shell_ext_send_doas_done(ivi->shell_client_ext.resource, + AGL_SHELL_EXT_DOAS_SHELL_CLIENT_STATUS_SUCCESS); + } else { + agl_shell_ext_send_doas_done(ivi->shell_client_ext.resource, + AGL_SHELL_EXT_DOAS_SHELL_CLIENT_STATUS_FAILED); + } - ivi_layout_activate(output, app_id); } static const struct agl_shell_interface agl_shell_implementation = { @@ -527,26 +2100,99 @@ static const struct agl_shell_interface agl_shell_implementation = { .set_background = shell_set_background, .set_panel = shell_set_panel, .activate_app = shell_activate_app, + .destroy = shell_destroy, + .set_activate_region = shell_set_activate_region, + .deactivate_app = shell_new_deactivate_app, + .set_app_float = shell_set_app_float, + .set_app_normal = shell_set_app_normal, + .set_app_fullscreen = shell_set_app_fullscreen, + .set_app_output = shell_set_app_output, + .set_app_position = shell_set_app_position, + .set_app_scale = shell_set_app_scale, + .set_app_split = shell_set_app_split, +}; + +static const struct agl_shell_ext_interface agl_shell_ext_implementation = { + .destroy = shell_ext_destroy, + .doas_shell_client = shell_ext_doas, }; static void shell_desktop_set_app_property(struct wl_client *client, struct wl_resource *shell_res, const char *app_id, uint32_t role, - int x, int y, struct wl_resource *output_res) + int x, int y, int bx, int by, + int width, int height, + struct wl_resource *output_res) { struct weston_head *head = weston_head_from_resource(output_res); struct weston_output *woutput = weston_head_get_output(head); struct ivi_output *output = to_ivi_output(woutput); - /* temporary store the app_id such that, when created to be check against */ - if (role == AGL_SHELL_DESKTOP_APP_ROLE_POPUP) - ivi_set_pending_desktop_surface_popup(output, x, y, app_id); + switch (role) { + case AGL_SHELL_DESKTOP_APP_ROLE_POPUP: + ivi_set_pending_desktop_surface_popup(output, x, y, bx, by, + width, height, app_id); + break; + case AGL_SHELL_DESKTOP_APP_ROLE_FULLSCREEN: + ivi_set_pending_desktop_surface_fullscreen(output, app_id); + break; + case AGL_SHELL_DESKTOP_APP_ROLE_SPLIT_VERTICAL: + case AGL_SHELL_DESKTOP_APP_ROLE_SPLIT_HORIZONTAL: + ivi_set_pending_desktop_surface_split(output, app_id, role); + break; + case AGL_SHELL_DESKTOP_APP_ROLE_REMOTE: + ivi_set_pending_desktop_surface_remote(output, app_id); + break; + default: + break; + } +} + +void +ivi_compositor_destroy_pending_surfaces(struct ivi_compositor *ivi) +{ + struct pending_popup *p_popup, *next_p_popup; + struct pending_split *split_surf, *next_split_surf; + struct pending_fullscreen *fs_surf, *next_fs_surf; + struct pending_remote *remote_surf, *next_remote_surf; + + wl_list_for_each_safe(p_popup, next_p_popup, + &ivi->popup_pending_apps, link) + ivi_remove_pending_desktop_surface_popup(p_popup); + + wl_list_for_each_safe(split_surf, next_split_surf, + &ivi->split_pending_apps, link) + ivi_remove_pending_desktop_surface_split(split_surf); + + wl_list_for_each_safe(fs_surf, next_fs_surf, + &ivi->fullscreen_pending_apps, link) + ivi_remove_pending_desktop_surface_fullscreen(fs_surf); + + wl_list_for_each_safe(remote_surf, next_remote_surf, + &ivi->remote_pending_apps, link) + ivi_remove_pending_desktop_surface_remote(remote_surf); +} + +static void +shell_desktop_set_app_property_mode(struct wl_client *client, + struct wl_resource *shell_res, uint32_t perm) +{ + struct desktop_client *dclient = wl_resource_get_user_data(shell_res); + if (perm) { + dclient->ivi->keep_pending_surfaces = true; + } else { + dclient->ivi->keep_pending_surfaces = false; + /* remove any previous pending surfaces */ + ivi_compositor_destroy_pending_surfaces(dclient->ivi); + } } static const struct agl_shell_desktop_interface agl_shell_desktop_implementation = { - .activate_app = shell_activate_app, - .set_app_property = shell_desktop_set_app_property + .activate_app = shell_desktop_activate_app, + .set_app_property = shell_desktop_set_app_property, + .deactivate_app = shell_deactivate_app, + .set_app_property_mode = shell_desktop_set_app_property_mode, }; static void @@ -557,32 +2203,31 @@ unbind_agl_shell(struct wl_resource *resource) struct ivi_surface *surf, *surf_tmp; ivi = wl_resource_get_user_data(resource); - wl_list_for_each(output, &ivi->outputs, link) { - free(output->background); - output->background = NULL; - - free(output->top); - output->top = NULL; - free(output->bottom); - output->bottom = NULL; - - free(output->left); - output->left = NULL; + /* reset status to allow other clients issue legit requests */ + if (wl_resource_get_version(resource) >= + AGL_SHELL_BOUND_OK_SINCE_VERSION && + ivi->shell_client.status == BOUND_FAILED) { + ivi->shell_client.status = BOUND_OK; + return; + } - free(output->right); - output->right = NULL; + wl_list_for_each(output, &ivi->outputs, link) { /* reset the active surf if there's one present */ if (output->active) { + struct weston_geometry area = {}; + output->active->view->is_mapped = false; output->active->view->surface->is_mapped = false; weston_layer_entry_remove(&output->active->view->layer_link); output->active = NULL; + + output->area_activation = area; } - insert_black_surface(output); + insert_black_curtain(output); } wl_list_for_each_safe(surf, surf_tmp, &ivi->surfaces, link) { @@ -600,40 +2245,118 @@ unbind_agl_shell(struct wl_resource *resource) ivi->shell_client.ready = false; ivi->shell_client.resource = NULL; + ivi->shell_client.resource_ext = NULL; ivi->shell_client.client = NULL; } +static void +unbind_agl_shell_ext(struct wl_resource *resource) +{ + struct ivi_compositor *ivi = wl_resource_get_user_data(resource); + + ivi->shell_client_ext.resource = NULL; + ivi->shell_client.resource_ext = NULL; + ivi->shell_client_ext.doas_requested = false; +} + static void bind_agl_shell(struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct ivi_compositor *ivi = data; struct wl_resource *resource; + struct ivi_policy *policy; + void *interface; + + policy = ivi->policy; + interface = (void *) &agl_shell_interface; + if (policy && policy->api.shell_bind_interface && + !policy->api.shell_bind_interface(client, interface)) { + wl_client_post_implementation_error(client, + "client not authorized to use agl_shell"); + return; + } - resource = wl_resource_create(client, &agl_shell_interface, - 1, id); + resource = wl_resource_create(client, &agl_shell_interface, version, id); if (!resource) { wl_client_post_no_memory(client); return; } -#if 0 - if (ivi->shell_client.client != client) { + if (ivi->shell_client.resource && wl_resource_get_version(resource) == 1) { wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT, - "client not authorized to use agl_shell"); + "agl_shell has already been bound (version 1)"); + wl_resource_destroy(resource); + return; + } + + // for agl_shell client proxy + if (ivi->shell_client.resource && + ivi->shell_client_ext.doas_requested_pending_bind) { + + ivi->shell_client_ext.doas_requested_pending_bind = false; + + // if there's one connected + if (ivi->shell_client.resource_ext) { + ivi->shell_client_ext.status = BOUND_FAILED; + agl_shell_send_bound_fail(ivi->shell_client.resource_ext); + wl_resource_destroy(resource); + return; + } + + wl_resource_set_implementation(resource, &agl_shell_implementation, + ivi, NULL); + ivi->shell_client.resource_ext = resource; + ivi->shell_client_ext.status = BOUND_OK; + agl_shell_send_bound_ok(ivi->shell_client.resource_ext); return; } -#endif + + // if we already have an agl_shell client if (ivi->shell_client.resource) { - wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT, - "agl_shell has already been bound"); + agl_shell_send_bound_fail(resource); + ivi->shell_client.status = BOUND_FAILED; + wl_resource_destroy(resource); return; } + // default agl_shell client wl_resource_set_implementation(resource, &agl_shell_implementation, ivi, unbind_agl_shell); ivi->shell_client.resource = resource; + + if (wl_resource_get_version(resource) >= + AGL_SHELL_BOUND_OK_SINCE_VERSION) { + /* if we land here we'll have BOUND_OK by default, + but still do the assignment */ + ivi->shell_client.status = BOUND_OK; + agl_shell_send_bound_ok(ivi->shell_client.resource); + } +} + +static void +bind_agl_shell_ext(struct wl_client *client, + void *data, uint32_t version, uint32_t id) +{ + struct ivi_compositor *ivi = data; + struct wl_resource *resource; + + resource = wl_resource_create(client, &agl_shell_ext_interface, version, id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + + if (ivi->shell_client_ext.resource) { + wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT, + "agl_shell_ext has already been bound"); + return; + } + + wl_resource_set_implementation(resource, &agl_shell_ext_implementation, + ivi, unbind_agl_shell_ext); + ivi->shell_client_ext.resource = resource; } static void @@ -651,8 +2374,20 @@ bind_agl_shell_desktop(struct wl_client *client, { struct ivi_compositor *ivi = data; struct wl_resource *resource; - struct desktop_client *dclient = zalloc(sizeof(*dclient)); + struct ivi_policy *policy; + struct desktop_client *dclient; + void *interface; + + policy = ivi->policy; + interface = (void *) &agl_shell_desktop_interface; + if (policy && policy->api.shell_bind_interface && + !policy->api.shell_bind_interface(client, interface)) { + wl_client_post_implementation_error(client, + "client not authorized to use agl_shell_desktop"); + return; + } + dclient = zalloc(sizeof(*dclient)); if (!dclient) { wl_client_post_no_memory(client); return; @@ -660,6 +2395,7 @@ bind_agl_shell_desktop(struct wl_client *client, resource = wl_resource_create(client, &agl_shell_desktop_interface, version, id); + dclient->ivi = ivi; if (!resource) { wl_client_post_no_memory(client); return; @@ -679,15 +2415,23 @@ int ivi_shell_create_global(struct ivi_compositor *ivi) { ivi->agl_shell = wl_global_create(ivi->compositor->wl_display, - &agl_shell_interface, 1, + &agl_shell_interface, 11, ivi, bind_agl_shell); if (!ivi->agl_shell) { weston_log("Failed to create wayland global.\n"); return -1; } + ivi->agl_shell_ext = wl_global_create(ivi->compositor->wl_display, + &agl_shell_ext_interface, 1, + ivi, bind_agl_shell_ext); + if (!ivi->agl_shell_ext) { + weston_log("Failed to create agl_shell_ext global.\n"); + return -1; + } + ivi->agl_shell_desktop = wl_global_create(ivi->compositor->wl_display, - &agl_shell_desktop_interface, 1, + &agl_shell_desktop_interface, 2, ivi, bind_agl_shell_desktop); if (!ivi->agl_shell_desktop) { weston_log("Failed to create wayland global (agl_shell_desktop).\n");