From 8033c46081b93f19843d140e58b527af7abf7a4f Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Mon, 8 May 2023 19:20:25 +0300 Subject: [PATCH] compositor: Add XWayland basic support This allow starting up XWayland and the ability to display, albeit is incomplete, given that we have no way at this moment to make the window maximized, as we do with xdg-shell/native wayland applications. A further patch, together with libweston changes are needed to make this complete. For now this can provide us with initial smoke testing. Tested with basic X11 clients, including Unigine, which is the reason for having this the moment. Note that this at the moment compiled-out, requiring the pass -Dxwayland=true when building the compositor. Bug-AGL: SPEC-4782 Signed-off-by: Marius Vlad Change-Id: I87d2a852165cb3f03482bea1e04931bdd6d4c115 --- meson.build | 7 ++ meson_options.txt | 14 +++- src/compositor.c | 53 +++++++----- src/desktop.c | 43 +++++++++- src/ivi-compositor.h | 8 ++ src/layout.c | 9 ++- src/xwayland.c | 224 +++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 333 insertions(+), 25 deletions(-) create mode 100644 src/xwayland.c diff --git a/meson.build b/meson.build index 4d846d2..2f8b11f 100644 --- a/meson.build +++ b/meson.build @@ -205,6 +205,13 @@ if deps_remoting.length() == depnames.length() message('Found remoting depends, enabling remoting') endif +if get_option('xwayland') + config_h.set('BUILD_XWAYLAND', '1') + + srcs_agl_compositor += 'src/xwayland.c' + config_h.set_quoted('XSERVER_PATH', get_option('xwayland-path')) +endif + dir_module_agl_compositor = join_paths(join_paths(prefix_path, get_option('libdir')), 'agl-compositor') libexec_compositor = shared_library( diff --git a/meson_options.txt b/meson_options.txt index 1e54773..7c0e103 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -5,10 +5,22 @@ option( value: 'allow-all', description: 'Default policy when no specific policy was set' ) - option( 'grpc-proxy', type: 'boolean', value: true, description: 'Build gRPC proxy which exposes some of the agl-shell protocol over a gRPC API' ) +option( + 'xwayland', + type: 'boolean', + value: false, + description: 'Build compositor with XWayland support. Disabled by default' +) + +option( + 'xwayland-path', + type: 'string', + value: '/usr/bin/Xwayland', + description: 'Xwayland: path to installed Xwayland binary' +) diff --git a/src/compositor.c b/src/compositor.c index c0a66c8..6cb822b 100644 --- a/src/compositor.c +++ b/src/compositor.c @@ -1234,27 +1234,18 @@ load_modules(struct ivi_compositor *ivi, const char *modules, snprintf(buffer, sizeof buffer, "%.*s", (int) (end - p), p); if (strstr(buffer, "xwayland.so")) { - weston_log("Xwayland plug-in not supported!\n"); - p = end; - while (*p == ',') - p++; - continue; - } - - if (strstr(buffer, "systemd-notify.so")) { + *xwayland = true; + } else if (strstr(buffer, "systemd-notify.so")) { weston_log("systemd-notify plug-in already loaded!\n"); - p = end; - while (*p == ',') - p++; - continue; - } + } else { + module_init = weston_load_module(buffer, "wet_module_init"); + if (!module_init) + return -1; - module_init = weston_load_module(buffer, "wet_module_init"); - if (!module_init) - return -1; + if (module_init(ivi->compositor, *argc, argv) < 0) + return -1; - if (module_init(ivi->compositor, *argc, argv) < 0) - return -1; + } p = end; while (*p == ',') @@ -1649,6 +1640,16 @@ copy_command_line(int argc, char * const argv[]) return str; } +#if !defined(BUILD_XWAYLAND) +int +wet_load_xwayland(struct weston_compositor *comp) +{ + weston_log("Attempted to load xwayland library but compositor " + "was *not* built with xwayland support!\n"); + return -1; +} +#endif + static void weston_log_setup_scopes(struct weston_log_context *log_ctx, struct weston_log_subscriber *subscriber, @@ -1677,7 +1678,6 @@ weston_log_subscribe_to_scopes(struct weston_log_context *log_ctx, weston_log_subscribe(log_ctx, logger, "log"); } - WL_EXPORT int wet_main(int argc, char *argv[], const struct weston_testsuite_data *test_data) { @@ -1840,12 +1840,23 @@ int wet_main(int argc, char *argv[], const struct weston_testsuite_data *test_da if (load_modules(&ivi, option_modules, &argc, argv, &xwayland) < 0) goto error_compositor; - if (ivi_policy_init(&ivi) < 0) - goto error_compositor; + if (!xwayland) { + weston_config_section_get_bool(section, "xwayland", &xwayland, + false); + } if (ivi_shell_init(&ivi) < 0) goto error_compositor; + if (xwayland) { + if (wet_load_xwayland(ivi.compositor) < 0) + goto error_compositor; + } + + if (ivi_policy_init(&ivi) < 0) + goto error_compositor; + + if (list_debug_scopes) { struct weston_log_scope *nscope = NULL; diff --git a/src/desktop.c b/src/desktop.c index ed648c2..9f472c0 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -30,6 +30,7 @@ #include "shared/helpers.h" #include #include +#include #include "agl-shell-desktop-server-protocol.h" @@ -512,7 +513,12 @@ static void desktop_set_xwayland_position(struct weston_desktop_surface *dsurface, int32_t x, int32_t y, void *userdata) { - /* not supported */ + struct ivi_surface *ivisurf = + weston_desktop_surface_get_user_data(dsurface); + + ivisurf->xwayland.x = x; + ivisurf->xwayland.y = y; + ivisurf->xwayland.is_set = true; } static const struct weston_desktop_api desktop_api = { @@ -543,9 +549,39 @@ ivi_shell_destroy(struct wl_listener *listener, void *data) ivi_layout_destroy_saved_outputs(ivi); weston_desktop_destroy(ivi->desktop); + wl_list_remove(&ivi->transform_listener.link); wl_list_remove(&listener->link); } +static void +transform_handler(struct wl_listener *listener, void *data) +{ + struct weston_surface *surface = data; + struct ivi_surface *ivisurf = get_ivi_shell_surface(surface); + const struct weston_xwayland_surface_api *api; + int x, y; + + if (!ivisurf) + return; + + api = ivisurf->ivi->xwayland_surface_api; + if (!api) { + api = weston_xwayland_surface_get_api(ivisurf->ivi->compositor); + ivisurf->ivi->xwayland_surface_api = api; + } + + if (!api || !api->is_xwayland_surface(surface)) + return; + + if (!weston_view_is_mapped(ivisurf->view)) + return; + + x = ivisurf->view->geometry.x; + y = ivisurf->view->geometry.y; + + api->send_position(surface, x, y); +} + int ivi_desktop_init(struct ivi_compositor *ivi) { @@ -560,5 +596,10 @@ ivi_desktop_init(struct ivi_compositor *ivi) return -1; } + ivi->transform_listener.notify = transform_handler; + wl_signal_add(&ivi->compositor->transform_signal, + &ivi->transform_listener); + + return 0; } diff --git a/src/ivi-compositor.h b/src/ivi-compositor.h index 207fd82..c5d9fc3 100644 --- a/src/ivi-compositor.h +++ b/src/ivi-compositor.h @@ -117,6 +117,8 @@ struct ivi_compositor { struct wl_list remote_pending_apps; struct wl_listener destroy_listener; + struct wl_listener transform_listener; + const struct weston_xwayland_surface_api *xwayland_surface_api; struct weston_layer hidden; struct weston_layer background; @@ -304,6 +306,12 @@ struct ivi_surface { struct wl_listener listener_advertise_app; struct wl_signal signal_advertise_app; + + struct { + bool is_set; + int32_t x; + int32_t y; + } xwayland; }; struct ivi_shell_seat { diff --git a/src/layout.c b/src/layout.c index 082c36e..89c2097 100644 --- a/src/layout.c +++ b/src/layout.c @@ -581,7 +581,7 @@ ivi_layout_desktop_committed(struct ivi_surface *surf) return; } - if (!surf->ivi->activate_by_default) { + if (!surf->ivi->activate_by_default && !surf->xwayland.is_set) { weston_log("Refusing to activate surface role %d, app_id %s\n", surf->role, app_id); @@ -611,7 +611,12 @@ ivi_layout_desktop_committed(struct ivi_surface *surf) */ weston_log("Surface no app_id, role %s activating by default\n", ivi_layout_get_surface_role_name(surf)); - ivi_layout_activate_by_surf(r_output, surf); + if (surf->xwayland.is_set) { + ivi_layout_activate_by_surf(r_output, surf); + ivi_layout_activate_complete(r_output, surf); + } else { + ivi_layout_activate_by_surf(r_output, surf); + } } } diff --git a/src/xwayland.c b/src/xwayland.c new file mode 100644 index 0000000..8ac5edc --- /dev/null +++ b/src/xwayland.c @@ -0,0 +1,224 @@ +/* + * Copyright © 2011 Intel Corporation + * Copyright © 2016 Giulio Camuffo + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "config.h" +#include "ivi-compositor.h" + +#include +#include +#include +#include + +#include +#include +#include +#include "shared/helpers.h" + +struct wet_xwayland { + struct ivi_compositor *ivi_compositor; + const struct weston_xwayland_api *api; + struct weston_xwayland *xwayland; + struct wl_event_source *sigusr1_source; + struct wl_client *client; + int wm_fd; + struct weston_process process; +}; + +static int +handle_sigusr1(int signal_number, void *data) +{ + struct wet_xwayland *wxw = data; + + /* We'd be safer if we actually had the struct + * signalfd_siginfo from the signalfd data and could verify + * this came from Xwayland.*/ + wxw->api->xserver_loaded(wxw->xwayland, wxw->client, wxw->wm_fd); + wl_event_source_remove(wxw->sigusr1_source); + + return 1; +} + +static pid_t +spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd) +{ + struct wet_xwayland *wxw = user_data; + pid_t pid; + char s[12], abstract_fd_str[12], unix_fd_str[12], wm_fd_str[12]; + int sv[2], wm[2], fd; + char *xserver = NULL; + struct weston_config *config = wxw->ivi_compositor->config; + struct weston_config_section *section; + + if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) { + weston_log("wl connection socketpair failed\n"); + return 1; + } + + if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, wm) < 0) { + weston_log("X wm connection socketpair failed\n"); + return 1; + } + + pid = fork(); + switch (pid) { + case 0: + /* SOCK_CLOEXEC closes both ends, so we need to unset + * the flag on the client fd. */ + fd = dup(sv[1]); + if (fd < 0) + goto fail; + snprintf(s, sizeof s, "%d", fd); + setenv("WAYLAND_SOCKET", s, 1); + + fd = dup(abstract_fd); + if (fd < 0) + goto fail; + snprintf(abstract_fd_str, sizeof abstract_fd_str, "%d", fd); + fd = dup(unix_fd); + if (fd < 0) + goto fail; + snprintf(unix_fd_str, sizeof unix_fd_str, "%d", fd); + fd = dup(wm[1]); + if (fd < 0) + goto fail; + snprintf(wm_fd_str, sizeof wm_fd_str, "%d", fd); + + section = weston_config_get_section(config, + "xwayland", NULL, NULL); + weston_config_section_get_string(section, "path", + &xserver, XSERVER_PATH); + + /* Ignore SIGUSR1 in the child, which will make the X + * server send SIGUSR1 to the parent (weston) when + * it's done with initialization. During + * initialization the X server will round trip and + * block on the wayland compositor, so avoid making + * blocking requests (like xcb_connect_to_fd) until + * it's done with that. */ + signal(SIGUSR1, SIG_IGN); + + if (execl(xserver, + xserver, + display, + "-rootless", +#ifdef HAVE_XWAYLAND_LISTENFD + "-listenfd", abstract_fd_str, + "-listenfd", unix_fd_str, +#else + "-listen", abstract_fd_str, + "-listen", unix_fd_str, +#endif + "-wm", wm_fd_str, + "-terminate", + NULL) < 0) + weston_log("exec of '%s %s -rootless " +#ifdef HAVE_XWAYLAND_LISTENFD + "-listenfd %s -listenfd %s " +#else + "-listen %s -listen %s " +#endif + "-wm %s -terminate' failed: %s\n", + xserver, display, + abstract_fd_str, unix_fd_str, wm_fd_str, + strerror(errno)); + fail: + _exit(EXIT_FAILURE); + + default: + close(sv[1]); + wxw->client = wl_client_create(wxw->ivi_compositor->compositor->wl_display, sv[0]); + + close(wm[1]); + wxw->wm_fd = wm[0]; + + wxw->process.pid = pid; + wl_list_insert(&wxw->ivi_compositor->child_process_list, + &wxw->process.link); + break; + + case -1: + weston_log("Failed to fork to spawn xserver process\n"); + break; + } + + return pid; +} + +static void +xserver_cleanup(struct weston_process *process, int status) +{ + struct wet_xwayland *wxw = + container_of(process, struct wet_xwayland, process); + struct wl_event_loop *loop = + wl_display_get_event_loop(wxw->ivi_compositor->compositor->wl_display); + + wxw->api->xserver_exited(wxw->xwayland, status); + wxw->sigusr1_source = wl_event_loop_add_signal(loop, SIGUSR1, + handle_sigusr1, wxw); + wxw->client = NULL; +} + +int +wet_load_xwayland(struct weston_compositor *comp) +{ + const struct weston_xwayland_api *api; + struct weston_xwayland *xwayland; + struct wet_xwayland *wxw; + struct wl_event_loop *loop; + struct ivi_compositor *ivi = to_ivi_compositor(comp); + + if (weston_compositor_load_xwayland(comp) < 0) + return -1; + + api = weston_xwayland_get_api(comp); + if (!api) { + weston_log("Failed to get the xwayland module API.\n"); + return -1; + } + + xwayland = api->get(comp); + if (!xwayland) { + weston_log("Failed to get the xwayland object.\n"); + return -1; + } + + wxw = zalloc(sizeof *wxw); + if (!wxw) + return -1; + + wxw->ivi_compositor = ivi; + wxw->api = api; + wxw->xwayland = xwayland; + wxw->process.cleanup = xserver_cleanup; + if (api->listen(xwayland, wxw, spawn_xserver) < 0) + return -1; + + loop = wl_display_get_event_loop(comp->wl_display); + wxw->sigusr1_source = wl_event_loop_add_signal(loop, SIGUSR1, + handle_sigusr1, wxw); + + return 0; +} -- 2.16.6