xwayland: Do not block on Xwayland initialization

We artificially made Xwayland initialization synchronous, as we used
to rely on MetaX11Display and other bits during meta_display_open().
With support for Xwayland on demand and --no-x11, this is certainly
not the case.

So drop the main loop surrounding Xwayland initialization, and turn
it into an async operation called from meta_display_init_x11(). This
function is turned then into the high-level entry point that will
get you from no X server to having a MetaX11Display.

The role of meta_init() in Xwayland initialization is thus reduced
to setting up the sockets. Notably no processes are spawned from here,
deferring that till there is a MetaDisplay to poke.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/944
This commit is contained in:
Carlos Garnacho 2019-11-18 14:04:24 +01:00 committed by Jonas Ådahl
parent 649911b6b3
commit 25c9e66c73
4 changed files with 73 additions and 49 deletions

View File

@ -716,19 +716,40 @@ meta_display_init_x11_finish (MetaDisplay *display,
return TRUE; return TRUE;
} }
static void
on_xserver_started (MetaXWaylandManager *manager,
GAsyncResult *result,
gpointer user_data)
{
g_autoptr (GTask) task = user_data;
GError *error = NULL;
gboolean retval;
retval = meta_xwayland_start_xserver_finish (manager, result, &error);
if (error)
g_task_return_error (task, error);
else
g_task_return_boolean (task, retval);
}
void void
meta_display_init_x11 (MetaDisplay *display, meta_display_init_x11 (MetaDisplay *display,
GCancellable *cancellable, GCancellable *cancellable,
GAsyncReadyCallback callback, GAsyncReadyCallback callback,
gpointer user_data) gpointer user_data)
{ {
GTask *task; MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
g_autoptr (GTask) task = NULL;
task = g_task_new (display, cancellable, callback, user_data); task = g_task_new (display, cancellable, callback, user_data);
g_task_set_source_tag (task, meta_display_init_x11); g_task_set_source_tag (task, meta_display_init_x11);
g_task_return_boolean (task, TRUE); meta_xwayland_start_xserver (&compositor->xwayland_manager,
g_object_unref (task); cancellable,
(GAsyncReadyCallback) on_xserver_started,
g_steal_pointer (&task));
} }
static void static void

View File

@ -57,7 +57,6 @@ typedef struct
GCancellable *xserver_died_cancellable; GCancellable *xserver_died_cancellable;
GSubprocess *proc; GSubprocess *proc;
GMainLoop *init_loop;
GList *x11_windows; GList *x11_windows;

View File

@ -42,4 +42,12 @@ gboolean meta_xwayland_dnd_handle_event (XEvent *xevent);
const MetaWaylandDragDestFuncs * meta_xwayland_selection_get_drag_dest_funcs (void); const MetaWaylandDragDestFuncs * meta_xwayland_selection_get_drag_dest_funcs (void);
void meta_xwayland_start_xserver (MetaXWaylandManager *manager,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean meta_xwayland_start_xserver_finish (MetaXWaylandManager *manager,
GAsyncResult *result,
GError **error);
#endif /* META_XWAYLAND_PRIVATE_H */ #endif /* META_XWAYLAND_PRIVATE_H */

View File

@ -534,17 +534,6 @@ add_local_user_to_xhost (Display *xdisplay)
XAddHost (xdisplay, &host_entry); XAddHost (xdisplay, &host_entry);
} }
static void
xserver_finished_init (MetaXWaylandManager *manager)
{
/* At this point xwayland is all setup to start accepting
* connections so we can quit the transient initialization mainloop
* and unblock meta_wayland_init() to continue initializing mutter.
* */
g_main_loop_quit (manager->init_loop);
g_clear_pointer (&manager->init_loop, g_main_loop_unref);
}
static void static void
on_init_x11_cb (MetaDisplay *display, on_init_x11_cb (MetaDisplay *display,
GAsyncResult *result, GAsyncResult *result,
@ -561,45 +550,53 @@ on_displayfd_ready (int fd,
GIOCondition condition, GIOCondition condition,
gpointer user_data) gpointer user_data)
{ {
MetaXWaylandManager *manager = user_data; GTask *task = user_data;
MetaDisplay *display = meta_get_display ();
/* The server writes its display name to the displayfd /* The server writes its display name to the displayfd
* socket when it's ready. We don't care about the data * socket when it's ready. We don't care about the data
* in the socket, just that it wrote something, since * in the socket, just that it wrote something, since
* that means it's ready. */ * that means it's ready. */
xserver_finished_init (manager); g_task_return_boolean (task, TRUE);
g_object_unref (task);
if (meta_get_x11_display_policy () == META_DISPLAY_POLICY_ON_DEMAND)
{
meta_display_init_x11 (display, NULL,
(GAsyncReadyCallback) on_init_x11_cb, NULL);
}
return G_SOURCE_REMOVE; return G_SOURCE_REMOVE;
} }
static gboolean void
meta_xwayland_start_xserver (MetaXWaylandManager *manager) meta_xwayland_start_xserver (MetaXWaylandManager *manager,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{ {
int xwayland_client_fd[2]; int xwayland_client_fd[2];
int displayfd[2]; int displayfd[2];
g_autoptr(GSubprocessLauncher) launcher = NULL; g_autoptr(GSubprocessLauncher) launcher = NULL;
GSubprocessFlags flags; GSubprocessFlags flags;
GError *error = NULL; GError *error = NULL;
g_autoptr (GTask) task = NULL;
task = g_task_new (NULL, cancellable, callback, user_data);
g_task_set_source_tag (task, meta_xwayland_start_xserver);
g_task_set_task_data (task, manager, NULL);
/* We want xwayland to be a wayland client so we make a socketpair to setup a /* We want xwayland to be a wayland client so we make a socketpair to setup a
* wayland protocol connection. */ * wayland protocol connection. */
if (socketpair (AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, xwayland_client_fd) < 0) if (socketpair (AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, xwayland_client_fd) < 0)
{ {
g_warning ("xwayland_client_fd socketpair failed\n"); g_task_return_new_error (task,
return FALSE; G_IO_ERROR,
g_io_error_from_errno (errno),
"xwayland_client_fd socketpair failed");
return;
} }
if (socketpair (AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, displayfd) < 0) if (socketpair (AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, displayfd) < 0)
{ {
g_warning ("displayfd socketpair failed\n"); g_task_return_new_error (task,
return FALSE; G_IO_ERROR,
g_io_error_from_errno (errno),
"displayfd socketpair failed");
return;
} }
/* xwayland, please. */ /* xwayland, please. */
@ -633,24 +630,28 @@ meta_xwayland_start_xserver (MetaXWaylandManager *manager)
NULL); NULL);
if (!manager->proc) if (!manager->proc)
{ {
g_error ("Failed to spawn Xwayland: %s", error->message); g_task_return_error (task, error);
return FALSE; return;
} }
manager->xserver_died_cancellable = g_cancellable_new (); manager->xserver_died_cancellable = g_cancellable_new ();
g_subprocess_wait_async (manager->proc, manager->xserver_died_cancellable, g_subprocess_wait_async (manager->proc, manager->xserver_died_cancellable,
xserver_died, NULL); xserver_died, NULL);
g_unix_fd_add (displayfd[0], G_IO_IN, on_displayfd_ready, manager); g_unix_fd_add (displayfd[0], G_IO_IN, on_displayfd_ready,
g_steal_pointer (&task));
manager->client = wl_client_create (manager->wayland_display, manager->client = wl_client_create (manager->wayland_display,
xwayland_client_fd[0]); xwayland_client_fd[0]);
}
/* We need to run a mainloop until we know xwayland has a binding gboolean
* for our xserver interface at which point we can assume it's meta_xwayland_start_xserver_finish (MetaXWaylandManager *manager,
* ready to start accepting connections. */ GAsyncResult *result,
manager->init_loop = g_main_loop_new (NULL, FALSE); GError **error)
g_main_loop_run (manager->init_loop); {
g_assert (g_task_get_source_tag (G_TASK (result)) ==
meta_xwayland_start_xserver);
return TRUE; return g_task_propagate_boolean (G_TASK (result), error);
} }
static gboolean static gboolean
@ -658,10 +659,10 @@ xdisplay_connection_activity_cb (gint fd,
GIOCondition cond, GIOCondition cond,
gpointer user_data) gpointer user_data)
{ {
MetaXWaylandManager *manager = user_data; MetaDisplay *display = meta_get_display ();
if (!meta_xwayland_start_xserver (manager)) meta_display_init_x11 (display, NULL,
g_critical ("Could not start Xserver"); (GAsyncReadyCallback) on_init_x11_cb, NULL);
return G_SOURCE_REMOVE; return G_SOURCE_REMOVE;
} }
@ -744,18 +745,13 @@ meta_xwayland_init (MetaXWaylandManager *manager,
manager->wayland_display = wl_display; manager->wayland_display = wl_display;
policy = meta_get_x11_display_policy (); policy = meta_get_x11_display_policy ();
if (policy == META_DISPLAY_POLICY_MANDATORY) if (policy == META_DISPLAY_POLICY_ON_DEMAND)
{
return meta_xwayland_start_xserver (manager);
}
else if (policy == META_DISPLAY_POLICY_ON_DEMAND)
{ {
g_unix_fd_add (manager->abstract_fd, G_IO_IN, g_unix_fd_add (manager->abstract_fd, G_IO_IN,
xdisplay_connection_activity_cb, manager); xdisplay_connection_activity_cb, manager);
return TRUE;
} }
return FALSE; return TRUE;
} }
static void static void