X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?a=blobdiff_plain;f=src%2Fshell.c;h=aaf04688a7e1d00fb17586b8c3fcac660e9a5e35;hb=0b766cf978b8b100caecd4c61464e1a683685072;hp=efcb102e2e3363c21bce4828780712566932e02b;hpb=71deafe03f41738bfcd7eafd21c3262c29f45ee7;p=src%2Fagl-compositor.git diff --git a/src/shell.c b/src/shell.c index efcb102..aaf0468 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 @@ -33,11 +33,15 @@ #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" @@ -47,19 +51,7 @@ #endif static void -create_black_surface_view(struct ivi_output *output); - -static struct ivi_surface * -get_ivi_shell_surface(struct weston_surface *wsurface) -{ - if (weston_surface_is_desktop_surface(wsurface)) { - struct weston_desktop_surface *dsurface = - weston_surface_get_desktop_surface(wsurface); - return weston_desktop_surface_get_user_data(dsurface); - } - - return NULL; -} +create_black_curtain_view(struct ivi_output *output); void agl_shell_desktop_advertise_application_id(struct ivi_compositor *ivi, @@ -99,6 +91,16 @@ ivi_set_desktop_surface(struct ivi_surface *surface) 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) { @@ -223,7 +225,7 @@ ivi_set_desktop_surface_remote(struct ivi_surface *surface) * just being added */ view = output->fullscreen_view.fs->view; if (view->is_mapped || view->surface->is_mapped) - remove_black_surface(output); + remove_black_curtain(output); if (output->type == OUTPUT_WALTHAM) ivi_output_notify_waltham_plugin(surface); @@ -479,7 +481,7 @@ ivi_set_pending_desktop_surface_remote(struct ivi_output *ioutput, if (!p_remote) return; - wl_list_insert(&ivi->remote_pending_apps, &remote->link); + wl_list_insert(&ivi->remote_pending_apps, &p_remote->link); } @@ -638,14 +640,9 @@ ivi_check_pending_surface_desktop(struct ivi_surface *surface, const char *app_id = weston_desktop_surface_get_app_id(surface->dsurface); - if (!app_id) { - *role = IVI_SURFACE_ROLE_NONE; - return; - } - role_pending_list = &ivi->popup_pending_apps; wl_list_for_each(p_popup, role_pending_list, link) { - if (!strcmp(app_id, p_popup->app_id)) { + if (app_id && !strcmp(app_id, p_popup->app_id)) { *role = IVI_SURFACE_ROLE_POPUP; return; } @@ -653,7 +650,7 @@ ivi_check_pending_surface_desktop(struct ivi_surface *surface, role_pending_list = &ivi->split_pending_apps; wl_list_for_each(p_split, role_pending_list, link) { - if (!strcmp(app_id, p_split->app_id)) { + if (app_id && !strcmp(app_id, p_split->app_id)) { *role = IVI_SURFACE_ROLE_SPLIT_V; return; } @@ -661,7 +658,7 @@ ivi_check_pending_surface_desktop(struct ivi_surface *surface, role_pending_list = &ivi->fullscreen_pending_apps; wl_list_for_each(p_fullscreen, role_pending_list, link) { - if (!strcmp(app_id, p_fullscreen->app_id)) { + if (app_id && !strcmp(app_id, p_fullscreen->app_id)) { *role = IVI_SURFACE_ROLE_FULLSCREEN; return; } @@ -669,7 +666,7 @@ ivi_check_pending_surface_desktop(struct ivi_surface *surface, role_pending_list = &ivi->remote_pending_apps; wl_list_for_each(p_remote, role_pending_list, link) { - if (!strcmp(app_id, p_remote->app_id)) { + if (app_id && !strcmp(app_id, p_remote->app_id)) { *role = IVI_SURFACE_ROLE_REMOTE; return; } @@ -725,8 +722,8 @@ ivi_shell_init_black_fs(struct ivi_compositor *ivi) struct ivi_output *out; wl_list_for_each(out, &ivi->outputs, link) { - create_black_surface_view(out); - insert_black_surface(out); + create_black_curtain_view(out); + insert_black_curtain(out); } } @@ -784,18 +781,11 @@ ivi_shell_destroy_views_on_layer(struct weston_layer *layer) } } -static void -ivi_shell_destroy_views_on_fullscreen_layer(struct ivi_compositor *ivi) -{ - struct ivi_output *ivi_output; - - wl_list_for_each(ivi_output, &ivi->outputs, link) - weston_surface_destroy(ivi_output->fullscreen_view.fs->view->surface); -} - 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); @@ -811,7 +801,12 @@ ivi_shell_finalize(struct ivi_compositor *ivi) ivi_shell_destroy_views_on_layer(&ivi->popup); weston_layer_fini(&ivi->popup); - ivi_shell_destroy_views_on_fullscreen_layer(ivi); + wl_list_for_each(output, &ivi->outputs, link) { + if (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); } @@ -831,103 +826,215 @@ ivi_shell_advertise_xdg_surfaces(struct ivi_compositor *ivi, struct wl_resource } } -static void -client_exec(const char *command, int fd) +static struct wl_client * +client_launch(struct weston_compositor *compositor, + struct weston_process *proc, + const char *path, + weston_process_cleanup_func_t cleanup) { - sigset_t sig; - char s[32]; + 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)); - /* Don't give the child our signal mask */ - sigfillset(&sig); - sigprocmask(SIG_UNBLOCK, &sig, NULL); + weston_log("launching '%s'\n", path); + str_printf(&fail_exec, "Error: Couldn't launch client '%s'\n", path); - /* Launch clients as the user; don't give them the wrong euid */ - if (seteuid(getuid()) == -1) { - weston_log("seteuid failed: %s\n", strerror(errno)); - return; + 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); - /* 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)); - return; + 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; } - snprintf(s, sizeof s, "%d", fd); - setenv("WAYLAND_SOCKET", s, 1); + custom_env_fini(&child_env); + free(fail_exec); - execl("/bin/sh", "/bin/sh", "-c", command, NULL); - weston_log("executing '%s' failed: %s", command, strerror(errno)); + return client; } -static struct wl_client * -launch_shell_client(struct ivi_compositor *ivi, const char *command) +struct process_info { + struct weston_process proc; + char *path; +}; + +int +sigchld_handler(int signal_number, void *data) { - struct wl_client *client; - int sock[2]; + struct weston_process *p; + struct ivi_compositor *ivi = data; + int status; pid_t pid; - weston_log("launching' %s'\n", command); + while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { + wl_list_for_each(p, &ivi->child_process_list, link) { + if (p->pid == pid) + break; + } - 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; - } + if (&p->link == &ivi->child_process_list) { + weston_log("unknown child process exited\n"); + continue; + } - 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; + wl_list_remove(&p->link); + wl_list_init(&p->link); + p->cleanup(p, status); } - if (pid == 0) { - client_exec(command, sock[1]); - _Exit(EXIT_FAILURE); - } - close(sock[1]); + if (pid < 0 && errno != ECHILD) + weston_log("waitpid error %s\n", strerror(errno)); - 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; + 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); } - return client; + free(pinfo->path); + free(pinfo); } int -ivi_launch_shell_client(struct ivi_compositor *ivi) +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, "shell-client", - NULL, NULL); + section = weston_config_get_section(ivi->config, cmd_section, NULL, NULL); if (section) - weston_config_section_get_string(section, "command", - &command, NULL); + weston_config_section_get_string(section, "command", &command, NULL); if (!command) return -1; - ivi->shell_client.client = launch_shell_client(ivi, command); - if (!ivi->shell_client.client) + 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_view(struct wl_listener *listener, void *data) +destroy_black_curtain_view(struct wl_listener *listener, void *data) { struct fullscreen_view *fs = wl_container_of(listener, fs, fs_destroy); @@ -941,7 +1048,7 @@ destroy_black_view(struct wl_listener *listener, void *data) static void -create_black_surface_view(struct ivi_output *output) +create_black_curtain_view(struct ivi_output *output) { struct weston_surface *surface = NULL; struct weston_view *view; @@ -972,13 +1079,21 @@ create_black_surface_view(struct ivi_output *output) } output->fullscreen_view.fs->view = view; - output->fullscreen_view.fs_destroy.notify = destroy_black_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->view && + output->fullscreen_view.fs->view->is_mapped && + output->fullscreen_view.fs->view->surface->is_mapped); +} + void -remove_black_surface(struct ivi_output *output) +remove_black_curtain(struct ivi_output *output) { struct weston_view *view; @@ -1000,10 +1115,12 @@ remove_black_surface(struct ivi_output *output) 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_surface(struct ivi_output *output) +insert_black_curtain(struct ivi_output *output) { struct weston_view *view; @@ -1027,6 +1144,24 @@ insert_black_surface(struct ivi_output *output) 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); + } } static void @@ -1036,23 +1171,43 @@ shell_ready(struct wl_client *client, struct wl_resource *shell_res) struct ivi_output *output; struct ivi_surface *surface, *tmp; + if (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; + } + /* 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) - remove_black_surface(output); + 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); } } @@ -1066,9 +1221,19 @@ shell_set_background(struct wl_client *client, 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 (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 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, @@ -1117,11 +1282,21 @@ shell_set_panel(struct wl_client *client, 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 (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 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, @@ -1227,8 +1402,17 @@ shell_activate_app(struct wl_client *client, { struct weston_head *head = weston_head_from_resource(output_res); struct weston_output *woutput = weston_head_get_output(head); + struct ivi_compositor *ivi = wl_resource_get_user_data(shell_res); struct ivi_output *output = to_ivi_output(woutput); + if (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); } @@ -1260,11 +1444,63 @@ shell_deactivate_app(struct wl_client *client, 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 (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_ext_destroy(struct wl_client *client, struct wl_resource *res) +{ + 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; + agl_shell_ext_send_doas_done(ivi->shell_client_ext.resource, + AGL_SHELL_EXT_DOAS_SHELL_CLIENT_STATUS_SUCCESS); +} + static const struct agl_shell_interface agl_shell_implementation = { .ready = shell_ready, .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 +}; + +static const struct agl_shell_ext_interface agl_shell_ext_implementation = { + .destroy = shell_ext_destroy, + .doas_shell_client = shell_ext_doas, }; static void @@ -1353,6 +1589,13 @@ unbind_agl_shell(struct wl_resource *resource) struct ivi_surface *surf, *surf_tmp; ivi = wl_resource_get_user_data(resource); + + /* reset status to allow other clients issue legit requests */ + if (ivi->shell_client.status == BOUND_FAILED) { + ivi->shell_client.status = BOUND_OK; + return; + } + wl_list_for_each(output, &ivi->outputs, link) { /* reset the active surf if there's one present */ if (output->active) { @@ -1363,7 +1606,7 @@ unbind_agl_shell(struct wl_resource *resource) output->active = NULL; } - insert_black_surface(output); + insert_black_curtain(output); } wl_list_for_each_safe(surf, surf_tmp, &ivi->surfaces, link) { @@ -1384,6 +1627,14 @@ unbind_agl_shell(struct wl_resource *resource) 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; +} + static void bind_agl_shell(struct wl_client *client, void *data, uint32_t version, uint32_t id) @@ -1402,22 +1653,80 @@ bind_agl_shell(struct wl_client *client, 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 (ivi->shell_client.resource) { + if (wl_resource_get_version(resource) == 1) { + wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT, + "agl_shell has already been bound"); + return; + } + + if (ivi->shell_client_ext.resource && + ivi->shell_client_ext.doas_requested) { + + /* reset status in case client-ext doesn't send an + * explicit agl_shell_destroy request, see + * shell_destroy() */ + if (ivi->shell_client.status == BOUND_FAILED) + ivi->shell_client.status = BOUND_OK; + + 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; + } else { + wl_resource_set_implementation(resource, &agl_shell_implementation, + ivi, NULL); + agl_shell_send_bound_fail(resource); + ivi->shell_client.status = BOUND_FAILED; + return; + } + } + + + if (wl_resource_get_version(resource) >= + AGL_SHELL_BOUND_OK_SINCE_VERSION) { + wl_resource_set_implementation(resource, &agl_shell_implementation, + ivi, unbind_agl_shell); + ivi->shell_client.resource = resource; + /* 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 has already been bound"); + "agl_shell_ext has already been bound"); return; } - wl_resource_set_implementation(resource, &agl_shell_implementation, - ivi, unbind_agl_shell); - ivi->shell_client.resource = resource; + wl_resource_set_implementation(resource, &agl_shell_ext_implementation, + ivi, unbind_agl_shell_ext); + ivi->shell_client_ext.resource = resource; } static void @@ -1476,13 +1785,21 @@ 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, 4, 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, 2, ivi, bind_agl_shell_desktop);