[mutter/wip/carlosg/xwayland-on-demand] wayland: Launch Xwayland on demand



commit 74db0632ebf8c2c48b056ec6fc024c8307675877
Author: Carlos Garnacho <carlosg gnome org>
Date:   Mon Jan 7 13:46:33 2019 +0100

    wayland: Launch Xwayland on demand

 data/org.gnome.mutter.gschema.xml.in |   2 +
 src/backends/meta-backend-private.h  |   3 +
 src/backends/meta-backend.c          |  16 ++++
 src/backends/meta-settings-private.h |   1 +
 src/backends/meta-settings.c         |   2 +
 src/compositor/compositor-private.h  |   2 +
 src/compositor/compositor.c          |   9 ++
 src/core/display-private.h           |   7 --
 src/core/display.c                   |  14 ++-
 src/core/keybindings.c               |  13 ++-
 src/core/main-private.h              |   9 +-
 src/core/main.c                      |  22 +++--
 src/core/meta-launch-context.c       |   3 +-
 src/core/stack-tracker.c             |  41 +++++++++
 src/meson.build                      |   7 ++
 src/meta/display.h                   |   7 ++
 src/meta/meta-x11-display.h          |  21 -----
 src/org.gnome.Mutter.Windowing.xml   |  47 ++++++++++
 src/wayland/meta-wayland-private.h   |   2 +
 src/wayland/meta-wayland.c           |  16 ++--
 src/wayland/meta-xwayland-private.h  |   3 +
 src/wayland/meta-xwayland.c          | 169 ++++++++++++++++++++++++++++++++---
 src/x11/meta-x11-display-private.h   |   2 +
 src/x11/meta-x11-display.c           |  32 ++++++-
 24 files changed, 388 insertions(+), 62 deletions(-)
---
diff --git a/data/org.gnome.mutter.gschema.xml.in b/data/org.gnome.mutter.gschema.xml.in
index 6cbd9c1b5..e6a3719cf 100644
--- a/data/org.gnome.mutter.gschema.xml.in
+++ b/data/org.gnome.mutter.gschema.xml.in
@@ -120,6 +120,8 @@
                                         framebuffers instead of window content,
                                         to manage HiDPI monitors. Does not
                                         require a restart.
+        • “autostart-xwayland”        — initializes Xwayland lazily if there are
+                                        X11 clients. Requires restart.
       </description>
     </key>
 
diff --git a/src/backends/meta-backend-private.h b/src/backends/meta-backend-private.h
index 7c3d912be..b8573faba 100644
--- a/src/backends/meta-backend-private.h
+++ b/src/backends/meta-backend-private.h
@@ -31,6 +31,7 @@
 
 #include "meta/meta-backend.h"
 #include "meta/meta-idle-monitor.h"
+#include "meta-dbus-windowing.h"
 #include "backends/meta-backend-types.h"
 #include "backends/meta-cursor-renderer.h"
 #include "backends/meta-egl.h"
@@ -180,4 +181,6 @@ void meta_backend_notify_keymap_layout_group_changed (MetaBackend *backend,
 
 void meta_backend_notify_ui_scaling_factor_changed (MetaBackend *backend);
 
+MetaDBusWindowing * meta_backend_get_windowing (MetaBackend *backend);
+
 #endif /* META_BACKEND_PRIVATE_H */
diff --git a/src/backends/meta-backend.c b/src/backends/meta-backend.c
index 709d4194c..cdd5d95c8 100644
--- a/src/backends/meta-backend.c
+++ b/src/backends/meta-backend.c
@@ -40,6 +40,7 @@
 #include "meta/main.h"
 #include "meta/meta-backend.h"
 #include "meta/util.h"
+#include "meta-dbus-windowing.h"
 
 #ifdef HAVE_REMOTE_DESKTOP
 #include "backends/meta-dbus-session-watcher.h"
@@ -101,6 +102,7 @@ struct _MetaBackendPrivate
   MetaScreenCast *screen_cast;
   MetaRemoteDesktop *remote_desktop;
 #endif
+  MetaDBusWindowing *windowing;
 
   ClutterBackend *clutter_backend;
   ClutterActor *stage;
@@ -483,6 +485,12 @@ meta_backend_real_post_init (MetaBackend *backend)
   priv->remote_desktop = meta_remote_desktop_new (priv->dbus_session_watcher);
 #endif /* HAVE_REMOTE_DESKTOP */
 
+  priv->windowing = meta_dbus_windowing_skeleton_new ();
+  g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (priv->windowing),
+                                    g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL),
+                                    "/org/gnome/Mutter/Windowing",
+                                    NULL);
+
   if (!meta_monitor_manager_is_headless (priv->monitor_manager))
     {
       reset_pointer_position (backend);
@@ -1322,3 +1330,11 @@ meta_backend_notify_keymap_layout_group_changed (MetaBackend *backend,
   g_signal_emit (backend, signals[KEYMAP_LAYOUT_GROUP_CHANGED], 0,
                  locked_group);
 }
+
+MetaDBusWindowing *
+meta_backend_get_windowing (MetaBackend *backend)
+{
+  MetaBackendPrivate *priv = meta_backend_get_instance_private (backend);
+
+  return priv->windowing;
+}
diff --git a/src/backends/meta-settings-private.h b/src/backends/meta-settings-private.h
index 90bfb439f..eb640e07e 100644
--- a/src/backends/meta-settings-private.h
+++ b/src/backends/meta-settings-private.h
@@ -32,6 +32,7 @@ typedef enum _MetaExperimentalFeature
   META_EXPERIMENTAL_FEATURE_NONE = 0,
   META_EXPERIMENTAL_FEATURE_SCALE_MONITOR_FRAMEBUFFER = (1 << 0),
   META_EXPERIMENTAL_FEATURE_KMS_MODIFIERS  = (1 << 1),
+  META_EXPERIMENTAL_FEATURE_AUTOSTART_XWAYLAND  = (1 << 2),
 } MetaExperimentalFeature;
 
 #define META_TYPE_SETTINGS (meta_settings_get_type ())
diff --git a/src/backends/meta-settings.c b/src/backends/meta-settings.c
index 34d49b3ed..5c720b2db 100644
--- a/src/backends/meta-settings.c
+++ b/src/backends/meta-settings.c
@@ -264,6 +264,8 @@ experimental_features_handler (GVariant *features_variant,
         features |= META_EXPERIMENTAL_FEATURE_SCALE_MONITOR_FRAMEBUFFER;
       else if (g_str_equal (feature, "kms-modifiers"))
         features |= META_EXPERIMENTAL_FEATURE_KMS_MODIFIERS;
+      else if (g_str_equal (feature, "autostart-xwayland"))
+        features |= META_EXPERIMENTAL_FEATURE_AUTOSTART_XWAYLAND;
       else
         g_info ("Unknown experimental feature '%s'\n", feature);
     }
diff --git a/src/compositor/compositor-private.h b/src/compositor/compositor-private.h
index 0f92d42da..2038a1228 100644
--- a/src/compositor/compositor-private.h
+++ b/src/compositor/compositor-private.h
@@ -69,4 +69,6 @@ MetaCloseDialog * meta_compositor_create_close_dialog (MetaCompositor *composito
 MetaInhibitShortcutsDialog * meta_compositor_create_inhibit_shortcuts_dialog (MetaCompositor *compositor,
                                                                               MetaWindow     *window);
 
+void meta_compositor_redirect_x11_windows (MetaCompositor *compositor);
+
 #endif /* META_COMPOSITOR_PRIVATE_H */
diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c
index a378ed805..7a8cfc9e3 100644
--- a/src/compositor/compositor.c
+++ b/src/compositor/compositor.c
@@ -486,6 +486,15 @@ redirect_windows (MetaX11Display *x11_display)
     }
 }
 
+void
+meta_compositor_redirect_x11_windows (MetaCompositor *compositor)
+{
+  MetaDisplay *display = compositor->display;
+
+  if (display->x11_display)
+    redirect_windows (display->x11_display);
+}
+
 void
 meta_compositor_manage (MetaCompositor *compositor)
 {
diff --git a/src/core/display-private.h b/src/core/display-private.h
index 8b158d440..bfa781b2f 100644
--- a/src/core/display-private.h
+++ b/src/core/display-private.h
@@ -357,13 +357,6 @@ void meta_display_sync_wayland_input_focus (MetaDisplay *display);
 void meta_display_update_focus_window (MetaDisplay *display,
                                        MetaWindow  *window);
 
-void meta_display_set_input_focus   (MetaDisplay *display,
-                                     MetaWindow  *window,
-                                     gboolean     focus_frame,
-                                     guint32      timestamp);
-void meta_display_unset_input_focus (MetaDisplay *display,
-                                     guint32      timestamp);
-
 void meta_display_sanity_check_timestamps (MetaDisplay *display,
                                            guint32      timestamp);
 gboolean meta_display_timestamp_too_old (MetaDisplay *display,
diff --git a/src/core/display.c b/src/core/display.c
index 0412a9841..92209a5d4 100644
--- a/src/core/display.c
+++ b/src/core/display.c
@@ -50,6 +50,7 @@
 #include "backends/meta-stage-private.h"
 #include "backends/x11/meta-backend-x11.h"
 #include "clutter/x11/clutter-x11.h"
+#include "compositor/compositor-private.h"
 #include "core/bell.h"
 #include "core/boxes-private.h"
 #include "core/display-private.h"
@@ -643,8 +644,14 @@ meta_display_init_x11 (MetaDisplay  *display,
 
   display->x11_display = x11_display;
   g_signal_emit (display, display_signals[X11_DISPLAY_OPENED], 0);
-  meta_x11_display_create_guard_window (x11_display);
-  meta_display_manage_all_windows (display);
+
+  if (meta_get_x11_display_policy () == META_DISPLAY_POLICY_ON_DEMAND)
+    {
+      meta_x11_display_create_guard_window (x11_display);
+      meta_display_manage_all_windows (display);
+      meta_compositor_redirect_x11_windows (display->compositor);
+    }
+
   return TRUE;
 }
 
@@ -674,7 +681,6 @@ meta_display_open (void)
 {
   GError *error = NULL;
   MetaDisplay *display;
-  MetaX11Display *x11_display;
   int i;
   guint32 timestamp;
   Window old_active_xwindow = None;
@@ -756,7 +762,7 @@ meta_display_open (void)
   display->selection = meta_selection_new (display);
   meta_clipboard_manager_init (display);
 
-  if (meta_should_autostart_x11_display ())
+  if (meta_get_x11_display_policy () == META_DISPLAY_POLICY_MANDATORY)
     {
       if (!meta_display_init_x11 (display, &error))
         g_error ("Failed to start Xwayland: %s", error->message);
diff --git a/src/core/keybindings.c b/src/core/keybindings.c
index 3830492a9..1fda18003 100644
--- a/src/core/keybindings.c
+++ b/src/core/keybindings.c
@@ -1622,7 +1622,11 @@ meta_display_grab_accelerator (MetaDisplay *display,
       return META_KEYBINDING_ACTION_NONE;
     }
 
-  meta_change_keygrab (keys, display->x11_display->xroot, TRUE, &resolved_combo);
+  if (display->x11_display)
+    {
+      meta_change_keygrab (keys, display->x11_display->xroot,
+                           TRUE, &resolved_combo);
+    }
 
   grab = g_new0 (MetaKeyGrab, 1);
   grab->action = next_dynamic_keybinding_action ();
@@ -1666,8 +1670,11 @@ meta_display_ungrab_accelerator (MetaDisplay *display,
     {
       int i;
 
-      meta_change_keygrab (keys, display->x11_display->xroot,
-                           FALSE, &binding->resolved_combo);
+      if (display->x11_display)
+        {
+          meta_change_keygrab (keys, display->x11_display->xroot,
+                               FALSE, &binding->resolved_combo);
+        }
 
       for (i = 0; i < binding->resolved_combo.len; i++)
         {
diff --git a/src/core/main-private.h b/src/core/main-private.h
index e59b76608..f6626b16f 100644
--- a/src/core/main-private.h
+++ b/src/core/main-private.h
@@ -28,9 +28,16 @@ typedef enum _MetaCompositorType
   META_COMPOSITOR_TYPE_X11,
 } MetaCompositorType;
 
+typedef enum _MetaDisplayPolicy
+{
+  META_DISPLAY_POLICY_MANDATORY,
+  META_DISPLAY_POLICY_ON_DEMAND,
+  META_DISPLAY_POLICY_DISABLED,
+} MetaDisplayPolicy;
+
 void meta_override_compositor_configuration (MetaCompositorType compositor_type,
                                              GType              backend_gtype);
 
-gboolean meta_should_autostart_x11_display (void);
+MetaDisplayPolicy meta_get_x11_display_policy (void);
 
 #endif /* META_MAIN_PRIVATE_H */
diff --git a/src/core/main.c b/src/core/main.c
index 492c74a9d..b0d50d47b 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -717,15 +717,27 @@ prefs_changed_callback (MetaPreference pref,
     }
 }
 
-gboolean
-meta_should_autostart_x11_display (void)
+MetaDisplayPolicy
+meta_get_x11_display_policy (void)
 {
   MetaBackend *backend = meta_get_backend ();
-  gboolean wants_x11 = TRUE;
+
+  if (META_IS_BACKEND_X11_CM (backend))
+    return META_DISPLAY_POLICY_MANDATORY;
 
 #ifdef HAVE_WAYLAND
-  wants_x11 = !opt_no_x11;
+  if (meta_is_wayland_compositor ())
+    {
+      MetaSettings *settings = meta_backend_get_settings (backend);
+
+      if (opt_no_x11)
+        return META_DISPLAY_POLICY_DISABLED;
+
+      if (meta_settings_is_experimental_feature_enabled (settings,
+                                                         META_EXPERIMENTAL_FEATURE_AUTOSTART_XWAYLAND))
+        return META_DISPLAY_POLICY_ON_DEMAND;
+    }
 #endif
 
-  return META_IS_BACKEND_X11_CM (backend) || wants_x11;
+  return META_DISPLAY_POLICY_MANDATORY;
 }
diff --git a/src/core/meta-launch-context.c b/src/core/meta-launch-context.c
index 05fc7152f..3be850f7b 100644
--- a/src/core/meta-launch-context.c
+++ b/src/core/meta-launch-context.c
@@ -113,7 +113,8 @@ meta_launch_context_constructed (GObject *object)
   G_OBJECT_CLASS (meta_launch_context_parent_class)->constructed (object);
 
   g_app_launch_context_setenv (G_APP_LAUNCH_CONTEXT (context),
-                               "DISPLAY", getenv ("DISPLAY"));
+                               "DISPLAY",
+                               meta_x11_get_display_name ());
   g_app_launch_context_setenv (G_APP_LAUNCH_CONTEXT (context),
                                "WAYLAND_DISPLAY", getenv ("WAYLAND_DISPLAY"));
 }
diff --git a/src/core/stack-tracker.c b/src/core/stack-tracker.c
index 34e4698cd..4c4465a2d 100644
--- a/src/core/stack-tracker.c
+++ b/src/core/stack-tracker.c
@@ -506,6 +506,43 @@ query_xserver_stack (MetaDisplay      *display,
   XFree (children);
 }
 
+static void
+drop_x11_windows (MetaDisplay      *display,
+                  MetaStackTracker *tracker)
+{
+  GArray *new_stack;
+  GList *l;
+  int i;
+
+  tracker->xserver_serial = 0;
+
+  new_stack = g_array_new (FALSE, FALSE, sizeof (guint64));
+
+  for (i = 0; i < tracker->verified_stack->len; i++)
+    {
+      guint64 window = g_array_index (tracker->verified_stack, guint64, i);
+
+      if (!META_STACK_ID_IS_X11 (window))
+        g_array_append_val (new_stack, window);
+    }
+
+  g_array_unref (tracker->verified_stack);
+  tracker->verified_stack = new_stack;
+
+  l = tracker->unverified_predictions->head;
+
+  while (l)
+    {
+      MetaStackOp *op = l->data;
+      GList *next = l->next;
+
+      if (META_STACK_ID_IS_X11 (op->any.window))
+        g_queue_remove (tracker->unverified_predictions, op);
+
+      l = next;
+    }
+}
+
 MetaStackTracker *
 meta_stack_tracker_new (MetaDisplay *display)
 {
@@ -521,6 +558,10 @@ meta_stack_tracker_new (MetaDisplay *display)
                     "x11-display-opened",
                     G_CALLBACK (query_xserver_stack),
                     tracker);
+  g_signal_connect (display,
+                    "x11-display-closing",
+                    G_CALLBACK (drop_x11_windows),
+                    tracker);
 
   meta_stack_tracker_dump (tracker);
 
diff --git a/src/meson.build b/src/meson.build
index c835b06f8..4fa327a21 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -601,6 +601,13 @@ dbus_idle_monitor_built_sources = gnome.gdbus_codegen('meta-dbus-idle-monitor',
   )
 mutter_built_sources += dbus_idle_monitor_built_sources
 
+dbus_windowing_built_sources = gnome.gdbus_codegen('meta-dbus-windowing',
+    'org.gnome.Mutter.Windowing.xml',
+    interface_prefix: 'org.gnome.Mutter.',
+    namespace: 'MetaDBus',
+  )
+mutter_built_sources += dbus_windowing_built_sources
+
 if have_native_backend
   cvt = find_program('cvt')
 
diff --git a/src/meta/display.h b/src/meta/display.h
index 6f80567bd..daa716959 100644
--- a/src/meta/display.h
+++ b/src/meta/display.h
@@ -86,6 +86,13 @@ MetaX11Display *meta_display_get_x11_display (MetaDisplay *display);
 
 MetaWindow *meta_display_get_focus_window (MetaDisplay *display);
 
+void meta_display_set_input_focus   (MetaDisplay *display,
+                                     MetaWindow  *window,
+                                     gboolean     focus_frame,
+                                     guint32      timestamp);
+void meta_display_unset_input_focus (MetaDisplay *display,
+                                     guint32      timestamp);
+
 gboolean meta_display_xserver_time_is_before (MetaDisplay *display,
                                               guint32      time1,
                                               guint32      time2);
diff --git a/src/meta/meta-x11-display.h b/src/meta/meta-x11-display.h
index 352ceb675..6c4a60943 100644
--- a/src/meta/meta-x11-display.h
+++ b/src/meta/meta-x11-display.h
@@ -46,25 +46,4 @@ void meta_x11_display_set_cm_selection (MetaX11Display *x11_display);
 gboolean meta_x11_display_xwindow_is_a_no_focus_window (MetaX11Display *x11_display,
                                                         Window xwindow);
 
-/* meta_x11_display_set_input_focus_window is like XSetInputFocus, except
- * that (a) it can't detect timestamps later than the current time,
- * since Mutter isn't part of the XServer, and thus gives erroneous
- * behavior in this circumstance (so don't do it), (b) it uses
- * display->last_focus_time since we don't have access to the true
- * Xserver one, (c) it makes use of display->user_time since checking
- * whether a window should be allowed to be focused should depend
- * on user_time events (see bug 167358, comment 15 in particular)
- */
-void meta_x11_display_set_input_focus_window (MetaX11Display *x11_display,
-                                              MetaWindow     *window,
-                                              gboolean        focus_frame,
-                                              guint32         timestamp);
-
-/* meta_x11_display_focus_the_no_focus_window is called when the
- * designated no_focus_window should be focused, but is otherwise the
- * same as meta_display_set_input_focus_window
- */
-void meta_x11_display_focus_the_no_focus_window (MetaX11Display *x11_display,
-                                                 guint32         timestamp);
-
 #endif /* META_X11_DISPLAY_H */
diff --git a/src/org.gnome.Mutter.Windowing.xml b/src/org.gnome.Mutter.Windowing.xml
new file mode 100644
index 000000000..1ea4c48df
--- /dev/null
+++ b/src/org.gnome.Mutter.Windowing.xml
@@ -0,0 +1,47 @@
+<!DOCTYPE node PUBLIC
+'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
+'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
+<node>
+  <!--
+      org.gnome.Mutter.Windowing:
+      @short_description: Windowing availability
+
+      This interface announces availability of the windowing
+      systems and their features.
+  -->
+  <interface name="org.gnome.Mutter.Windowing">
+    <!--
+        X11Available:
+
+        Says whether there is a X display server running for this
+       session.
+    -->
+    <property name="X11Available" type="b" access="read" />
+
+    <!--
+        X11Started:
+
+        The signal is emitted after a X11 display server has been
+       started for this session.
+    -->
+    <signal name="X11Started" />
+
+    <!--
+        X11PreShutdown:
+
+        The signal is emitted before the compositor intends to
+       shut the X11 display server down. Normally a grace period
+       would be given, applications might want to react to this
+       as quickly as possible.
+    -->
+    <signal name="X11PreShutdown" />
+
+    <!--
+        X11Shutdown:
+
+        The signal is emitted after the compositor did shut down
+       the X11 display server.
+    -->
+    <signal name="X11Shutdown" />
+  </interface>
+</node>
diff --git a/src/wayland/meta-wayland-private.h b/src/wayland/meta-wayland-private.h
index f2410be4c..85b127e75 100644
--- a/src/wayland/meta-wayland-private.h
+++ b/src/wayland/meta-wayland-private.h
@@ -57,6 +57,8 @@ typedef struct
   GSubprocess *proc;
   GMainLoop *init_loop;
 
+  GList *x11_windows;
+
   MetaXWaylandDnd *dnd;
 } MetaXWaylandManager;
 
diff --git a/src/wayland/meta-wayland.c b/src/wayland/meta-wayland.c
index a06d2d7d8..4f69497d5 100644
--- a/src/wayland/meta-wayland.c
+++ b/src/wayland/meta-wayland.c
@@ -263,12 +263,14 @@ meta_wayland_compositor_destroy_frame_callbacks (MetaWaylandCompositor *composit
 
 static void
 set_gnome_env (const char *name,
-              const char *value)
+              const char *value,
+              gboolean    set_env_on_self)
 {
   GDBusConnection *session_bus;
   GError *error = NULL;
 
-  setenv (name, value, TRUE);
+  if (set_env_on_self)
+    setenv (name, value, TRUE);
 
   session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
   g_assert (session_bus);
@@ -413,7 +415,7 @@ meta_wayland_init (void)
   meta_wayland_eglstream_controller_init (compositor);
 #endif
 
-  if (meta_should_autostart_x11_display ())
+  if (meta_get_x11_display_policy () != META_DISPLAY_POLICY_DISABLED)
     {
       if (!meta_xwayland_start (&compositor->xwayland_manager, compositor->wayland_display))
         g_error ("Failed to start X Wayland");
@@ -438,10 +440,10 @@ meta_wayland_init (void)
       compositor->display_name = g_strdup (display_name);
     }
 
-  if (meta_should_autostart_x11_display ())
-    set_gnome_env ("DISPLAY", meta_wayland_get_xwayland_display_name (compositor));
+  if (meta_get_x11_display_policy () != META_DISPLAY_POLICY_DISABLED)
+    set_gnome_env ("DISPLAY", meta_wayland_get_xwayland_display_name (compositor), FALSE);
 
-  set_gnome_env ("WAYLAND_DISPLAY", meta_wayland_get_wayland_display_name (compositor));
+  set_gnome_env ("WAYLAND_DISPLAY", meta_wayland_get_wayland_display_name (compositor), TRUE);
 }
 
 const char *
@@ -463,7 +465,7 @@ meta_wayland_finalize (void)
 
   compositor = meta_wayland_compositor_get_default ();
 
-  meta_xwayland_stop (&compositor->xwayland_manager);
+  meta_xwayland_shutdown (&compositor->xwayland_manager);
   g_clear_pointer (&compositor->display_name, g_free);
 }
 
diff --git a/src/wayland/meta-xwayland-private.h b/src/wayland/meta-xwayland-private.h
index 9a109da66..29e481b17 100644
--- a/src/wayland/meta-xwayland-private.h
+++ b/src/wayland/meta-xwayland-private.h
@@ -34,6 +34,9 @@ meta_xwayland_complete_init (MetaDisplay *display);
 void
 meta_xwayland_stop (MetaXWaylandManager *manager);
 
+void
+meta_xwayland_shutdown (MetaXWaylandManager *manager);
+
 /* wl_data_device/X11 selection interoperation */
 void     meta_xwayland_init_dnd         (void);
 void     meta_xwayland_shutdown_dnd     (void);
diff --git a/src/wayland/meta-xwayland.c b/src/wayland/meta-xwayland.c
index 4432a05af..c66f6cf33 100644
--- a/src/wayland/meta-xwayland.c
+++ b/src/wayland/meta-xwayland.c
@@ -33,6 +33,7 @@
 #include <sys/un.h>
 
 #include "compositor/meta-surface-actor-wayland.h"
+#include "core/main-private.h"
 #include "meta/main.h"
 #include "wayland/meta-wayland-actor-surface.h"
 
@@ -351,24 +352,33 @@ xserver_died (GObject      *source,
       g_warning ("Failed to finish waiting for Xwayland: %s", error->message);
     }
   else if (!g_subprocess_get_successful (proc))
-    g_warning ("X Wayland crashed; exiting");
-  else
+    g_warning ("X Wayland process exited");
+
+  if (meta_get_x11_display_policy () == META_DISPLAY_POLICY_MANDATORY)
     {
-      /* For now we simply abort if we see the server exit.
-       *
-       * In the future X will only be loaded lazily for legacy X support
-       * but for now it's a hard requirement. */
-      g_warning ("Spurious exit of X Wayland server");
+      meta_exit (META_EXIT_ERROR);
     }
+  else if (meta_get_x11_display_policy () == META_DISPLAY_POLICY_ON_DEMAND)
+    {
+      MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
+      MetaDisplay *display = meta_get_display ();
 
-  meta_exit (META_EXIT_ERROR);
+      if (display->x11_display)
+        meta_display_shutdown_x11 (display);
+
+      if (!meta_xwayland_start (&compositor->xwayland_manager,
+                                compositor->wayland_display))
+        g_warning ("Failed to init X sockets");
+    }
 }
 
 static int
 x_io_error (Display *display)
 {
   g_warning ("Connection to xwayland lost");
-  meta_exit (META_EXIT_ERROR);
+
+  if (meta_get_x11_display_policy () == META_DISPLAY_POLICY_MANDATORY)
+    meta_exit (META_EXIT_ERROR);
 
   return 0;
 }
@@ -437,6 +447,29 @@ choose_xdisplay (MetaXWaylandManager *manager)
   return TRUE;
 }
 
+static gboolean
+reopen_display_sockets (MetaXWaylandManager *manager)
+{
+  gboolean fatal;
+
+  manager->abstract_fd = bind_to_abstract_socket (manager->display_index,
+                                                  &fatal);
+  if (manager->abstract_fd < 0)
+    {
+      g_warning ("Failed to bind abstract socket");
+      return FALSE;
+    }
+
+  manager->unix_fd = bind_to_unix_socket (manager->display_index);
+  if (manager->unix_fd < 0)
+    {
+      close (manager->abstract_fd);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
 static void
 xserver_finished_init (MetaXWaylandManager *manager)
 {
@@ -454,6 +487,7 @@ on_displayfd_ready (int          fd,
                     gpointer     user_data)
 {
   MetaXWaylandManager *manager = user_data;
+  MetaDisplay *display = meta_get_display ();
 
   /* The server writes its display name to the displayfd
    * socket when it's ready. We don't care about the data
@@ -461,6 +495,8 @@ on_displayfd_ready (int          fd,
    * that means it's ready. */
   xserver_finished_init (manager);
 
+  meta_display_init_x11 (display, NULL);
+
   return G_SOURCE_REMOVE;
 }
 
@@ -474,6 +510,9 @@ meta_xwayland_init_xserver (MetaXWaylandManager *manager)
   GSubprocessFlags flags;
   GError *error = NULL;
 
+  g_clear_object (&manager->xserver_died_cancellable);
+  g_clear_object (&manager->proc);
+
   /* We want xwayland to be a wayland client so we make a socketpair to setup a
    * wayland protocol connection. */
   if (socketpair (AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, xwayland_client_fd) < 0)
@@ -552,27 +591,121 @@ out:
   return started;
 }
 
+static gboolean
+xdisplay_connection_activity_cb (gint         fd,
+                                 GIOCondition cond,
+                                 gpointer     user_data)
+{
+  MetaXWaylandManager *manager = user_data;
+
+  if (!meta_xwayland_init_xserver (manager))
+    g_critical ("Could not start Xserver");
+
+  return G_SOURCE_REMOVE;
+}
+
+static gboolean
+shutdown_xwayland_cb (gpointer data)
+{
+  MetaXWaylandManager *manager = data;
+
+  g_debug ("Shutting down Xwayland");
+  meta_display_shutdown_x11 (meta_get_display ());
+  meta_xwayland_stop (manager);
+  return G_SOURCE_REMOVE;
+}
+
+static gboolean
+shutdown_xwayland_timeout_cb (gpointer data)
+{
+  MetaDBusWindowing *windowing;
+
+  windowing = meta_backend_get_windowing (meta_get_backend ());
+  meta_dbus_windowing_emit_x11_pre_shutdown (windowing);
+
+  /* Have a grace period for clients to handle shutdown */
+  g_timeout_add_seconds (2, shutdown_xwayland_cb, data);
+}
+
+static void
+window_unmanaged_cb (MetaWindow          *window,
+                     MetaXWaylandManager *manager)
+{
+  manager->x11_windows = g_list_remove (manager->x11_windows, window);
+  g_signal_handlers_disconnect_by_func (window,
+                                        window_unmanaged_cb,
+                                        manager);
+  if (!manager->x11_windows)
+    {
+      g_debug ("All X11 windows gone, setting shutdown timeout");
+      g_timeout_add_seconds (10, shutdown_xwayland_timeout_cb, manager);
+    }
+}
+
+static void
+window_created_cb (MetaDisplay         *display,
+                   MetaWindow          *window,
+                   MetaXWaylandManager *manager)
+{
+  if (window->xwindow &&
+      meta_window_get_client_pid (window) != getpid ())
+    {
+      manager->x11_windows = g_list_prepend (manager->x11_windows, window);
+      g_signal_connect (window, "unmanaged",
+                        G_CALLBACK (window_unmanaged_cb), manager);
+    }
+}
+
 gboolean
 meta_xwayland_start (MetaXWaylandManager *manager,
                      struct wl_display   *wl_display)
 {
-  if (!choose_xdisplay (manager))
-    return FALSE;
+  MetaDisplayPolicy policy;
+
+  if (!manager->display_name)
+    {
+      if (!choose_xdisplay (manager))
+        return FALSE;
+    }
+  else
+    {
+      if (!reopen_display_sockets (manager))
+        return FALSE;
+    }
 
   manager->wayland_display = wl_display;
-  return meta_xwayland_init_xserver (manager);
+  policy = meta_get_x11_display_policy ();
+
+  if (policy == META_DISPLAY_POLICY_MANDATORY)
+    {
+      return meta_xwayland_init_xserver (manager);
+    }
+  else if (policy == META_DISPLAY_POLICY_ON_DEMAND)
+    {
+      g_unix_fd_add (manager->abstract_fd, G_IO_IN,
+                     xdisplay_connection_activity_cb, manager);
+      return TRUE;
+    }
+
+  return FALSE;
 }
 
 static void
 on_x11_display_closing (MetaDisplay *display)
 {
   meta_xwayland_shutdown_dnd ();
+  g_signal_handlers_disconnect_by_func (display,
+                                        on_x11_display_closing,
+                                        NULL);
 }
 
 /* To be called right after connecting */
 void
 meta_xwayland_complete_init (MetaDisplay *display)
 {
+  MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
+  MetaXWaylandManager *manager = &compositor->xwayland_manager;
+
   /* We install an X IO error handler in addition to the child watch,
      because after Xlib connects our child watch may not be called soon
      enough, and therefore we won't crash when X exits (and most important
@@ -583,10 +716,22 @@ meta_xwayland_complete_init (MetaDisplay *display)
   g_signal_connect (display, "x11-display-closing",
                     G_CALLBACK (on_x11_display_closing), NULL);
   meta_xwayland_init_dnd ();
+
+  g_signal_connect (meta_get_display (), "window-created",
+                    G_CALLBACK (window_created_cb), manager);
 }
 
 void
 meta_xwayland_stop (MetaXWaylandManager *manager)
+{
+  g_subprocess_send_signal (manager->proc, SIGTERM);
+  g_signal_handlers_disconnect_by_func (meta_get_display (),
+                                        window_created_cb,
+                                        manager);
+}
+
+void
+meta_xwayland_shutdown (MetaXWaylandManager *manager)
 {
   char path[256];
 
diff --git a/src/x11/meta-x11-display-private.h b/src/x11/meta-x11-display-private.h
index d4bbf53a7..e2e0da08a 100644
--- a/src/x11/meta-x11-display-private.h
+++ b/src/x11/meta-x11-display-private.h
@@ -243,4 +243,6 @@ void meta_x11_display_set_input_focus (MetaX11Display *x11_display,
                                        Window          xwindow,
                                        guint32         timestamp);
 
+const gchar * meta_x11_get_display_name (void);
+
 #endif /* META_X11_DISPLAY_PRIVATE_H */
diff --git a/src/x11/meta-x11-display.c b/src/x11/meta-x11-display.c
index e035a8138..9efc4cea5 100644
--- a/src/x11/meta-x11-display.c
+++ b/src/x11/meta-x11-display.c
@@ -236,8 +236,14 @@ meta_x11_display_dispose (GObject *object)
 
   if (x11_display->gdk_display)
     {
+      MetaDBusWindowing *windowing;
+
       gdk_display_close (x11_display->gdk_display);
       x11_display->gdk_display = NULL;
+
+      windowing = meta_backend_get_windowing (meta_get_backend ());
+      meta_dbus_windowing_set_x11_available (windowing, FALSE);
+      meta_dbus_windowing_emit_x11_shutdown (windowing);
     }
 
   if (x11_display->display_close_idle)
@@ -1001,6 +1007,25 @@ meta_set_gnome_wm_keybindings (const char *wm_keybindings)
   gnome_wm_keybindings = wm_keybindings;
 }
 
+const gchar *
+meta_x11_get_display_name (void)
+{
+#ifdef HAVE_WAYLAND
+  if (meta_is_wayland_compositor ())
+    {
+      MetaWaylandCompositor *compositor;
+
+      compositor = meta_wayland_compositor_get_default ();
+
+      return meta_wayland_get_xwayland_display_name (compositor);
+    }
+  else
+#endif
+    {
+      return g_getenv ("DISPLAY");
+    }
+}
+
 gboolean
 meta_x11_init_gdk_display (GError **error)
 {
@@ -1009,7 +1034,7 @@ meta_x11_init_gdk_display (GError **error)
   const char *gdk_gl_env = NULL;
   Display *xdisplay;
 
-  xdisplay_name = g_getenv ("DISPLAY");
+  xdisplay_name = meta_x11_get_display_name ();
   if (!xdisplay_name)
     {
       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
@@ -1105,6 +1130,7 @@ meta_x11_display_new (MetaDisplay *display, GError **error)
   MetaBackend *backend = meta_get_backend ();
   MetaMonitorManager *monitor_manager =
     meta_backend_get_monitor_manager (backend);
+  MetaDBusWindowing *windowing;
 
   /* A list of all atom names, so that we can intern them in one go. */
   const char *atom_names[] = {
@@ -1386,6 +1412,10 @@ meta_x11_display_new (MetaDisplay *display, GError **error)
   meta_x11_startup_notification_init (x11_display);
   meta_x11_selection_init (x11_display);
 
+  windowing = meta_backend_get_windowing (meta_get_backend ());
+  meta_dbus_windowing_emit_x11_started (windowing);
+  meta_dbus_windowing_set_x11_available (windowing, TRUE);
+
   return x11_display;
 }
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]