Blob Blame History Raw
From d85448e2e523deb1487e7e405a480e1c4e6a5f6f Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Fri, 31 Aug 2018 15:33:00 -0400
Subject: [PATCH 29/51] local-display-factory: defer killing greeter until new
 session registers

At the moment we kill the greeter the second the VT change to the new
session happens.

That can cause flicker if the new session doesn't take over the display
quickly enough.

This commit defers killing the greeter until the new display registers.

Closes https://gitlab.gnome.org/GNOME/gdm/issues/413
---
 daemon/gdm-display.h               |  1 +
 daemon/gdm-local-display-factory.c | 43 +++++++++++++++++++++++++-----
 2 files changed, 38 insertions(+), 6 deletions(-)

diff --git a/daemon/gdm-display.h b/daemon/gdm-display.h
index 6d5e88df9..33dc3be41 100644
--- a/daemon/gdm-display.h
+++ b/daemon/gdm-display.h
@@ -13,60 +13,61 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  *
  */
 
 
 #ifndef __GDM_DISPLAY_H
 #define __GDM_DISPLAY_H
 
 #include <glib-object.h>
 #include <gio/gio.h>
 
 G_BEGIN_DECLS
 
 #define GDM_TYPE_DISPLAY         (gdm_display_get_type ())
 #define GDM_DISPLAY(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_DISPLAY, GdmDisplay))
 #define GDM_DISPLAY_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_DISPLAY, GdmDisplayClass))
 #define GDM_IS_DISPLAY(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_DISPLAY))
 #define GDM_IS_DISPLAY_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_DISPLAY))
 #define GDM_DISPLAY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_DISPLAY, GdmDisplayClass))
 
 typedef struct GdmDisplayPrivate GdmDisplayPrivate;
 
 typedef enum {
         GDM_DISPLAY_UNMANAGED = 0,
         GDM_DISPLAY_PREPARED,
         GDM_DISPLAY_MANAGED,
+        GDM_DISPLAY_WAITING_TO_FINISH,
         GDM_DISPLAY_FINISHED,
         GDM_DISPLAY_FAILED,
 } GdmDisplayStatus;
 
 typedef struct
 {
         GObject            parent;
         GdmDisplayPrivate *priv;
 } GdmDisplay;
 
 typedef struct
 {
         GObjectClass   parent_class;
 
         /* methods */
         gboolean (*prepare) (GdmDisplay *display);
         void     (*manage)  (GdmDisplay *self);
 } GdmDisplayClass;
 
 typedef enum
 {
          GDM_DISPLAY_ERROR_GENERAL,
          GDM_DISPLAY_ERROR_GETTING_USER_INFO,
          GDM_DISPLAY_ERROR_GETTING_SESSION_INFO,
 } GdmDisplayError;
 
 #define GDM_DISPLAY_ERROR gdm_display_error_quark ()
 
 GQuark              gdm_display_error_quark                    (void);
 GType               gdm_display_get_type                       (void);
diff --git a/daemon/gdm-local-display-factory.c b/daemon/gdm-local-display-factory.c
index 127127005..bc6ac6855 100644
--- a/daemon/gdm-local-display-factory.c
+++ b/daemon/gdm-local-display-factory.c
@@ -241,60 +241,90 @@ gdm_local_display_factory_create_transient_display (GdmLocalDisplayFactory *fact
 
                 display = gdm_legacy_display_new (num);
         }
 #endif
 
         g_object_set (display,
                       "seat-id", "seat0",
                       "allow-timed-login", FALSE,
                       NULL);
 
         store_display (factory, display);
 
         if (! gdm_display_manage (display)) {
                 display = NULL;
                 goto out;
         }
 
         if (! gdm_display_get_id (display, id, NULL)) {
                 display = NULL;
                 goto out;
         }
 
         ret = TRUE;
  out:
         /* ref either held by store or not at all */
         g_object_unref (display);
 
         return ret;
 }
 
+static gboolean
+finish_display_on_seat_if_waiting (GdmDisplayStore *display_store,
+                                   GdmDisplay      *display,
+                                   const char      *seat_id)
+{
+        if (gdm_display_get_status (display) != GDM_DISPLAY_WAITING_TO_FINISH)
+                return FALSE;
+
+        g_debug ("GdmLocalDisplayFactory: finish background display\n");
+        gdm_display_stop_greeter_session (display);
+        gdm_display_unmanage (display);
+        gdm_display_finish (display);
+
+        return FALSE;
+}
+
+static void
+finish_waiting_displays_on_seat (GdmLocalDisplayFactory *factory,
+                                 const char             *seat_id)
+{
+        GdmDisplayStore *store;
+
+        store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory));
+
+        gdm_display_store_foreach (store,
+                                   (GdmDisplayStoreFunc) finish_display_on_seat_if_waiting,
+                                   (gpointer)
+                                   seat_id);
+}
+
 static void
 on_display_status_changed (GdmDisplay             *display,
                            GParamSpec             *arg1,
                            GdmLocalDisplayFactory *factory)
 {
         int              status;
         int              num;
         char            *seat_id = NULL;
         char            *session_type = NULL;
         char            *session_class = NULL;
         gboolean         is_initial = TRUE;
         gboolean         is_local = TRUE;
 
         num = -1;
         gdm_display_get_x11_display_number (display, &num, NULL);
 
         g_object_get (display,
                       "seat-id", &seat_id,
                       "is-initial", &is_initial,
                       "is-local", &is_local,
                       "session-type", &session_type,
                       "session-class", &session_class,
                       NULL);
 
         status = gdm_display_get_status (display);
 
         g_debug ("GdmLocalDisplayFactory: display status changed: %d", status);
         switch (status) {
         case GDM_DISPLAY_FINISHED:
                 /* remove the display number from factory->priv->used_display_numbers
@@ -324,60 +354,63 @@ on_display_status_changed (GdmDisplay             *display,
                 /* Create a new equivalent display if it was static */
                 if (is_local) {
 
                         factory->priv->num_failures++;
 
                         if (factory->priv->num_failures > MAX_DISPLAY_FAILURES) {
                                 /* oh shit */
                                 g_warning ("GdmLocalDisplayFactory: maximum number of X display failures reached: check X server log for errors");
                         } else {
 #ifdef ENABLE_WAYLAND_SUPPORT
                                 if (g_strcmp0 (session_type, "wayland") == 0) {
                                         g_free (session_type);
                                         session_type = NULL;
 
                                         /* workaround logind race for now
                                          * bug 1643874
                                          */
                                         g_usleep (2 * G_USEC_PER_SEC);
                                 }
 
 #endif
                                 create_display (factory, seat_id, session_type, is_initial);
                         }
                 }
                 break;
         case GDM_DISPLAY_UNMANAGED:
                 break;
         case GDM_DISPLAY_PREPARED:
                 break;
         case GDM_DISPLAY_MANAGED:
+                finish_waiting_displays_on_seat (factory, seat_id);
+                break;
+        case GDM_DISPLAY_WAITING_TO_FINISH:
                 break;
         default:
                 g_assert_not_reached ();
                 break;
         }
 
         g_free (seat_id);
         g_free (session_type);
         g_free (session_class);
 }
 
 static gboolean
 lookup_by_seat_id (const char *id,
                    GdmDisplay *display,
                    gpointer    user_data)
 {
         const char *looking_for = user_data;
         char *current;
         gboolean res;
 
         g_object_get (G_OBJECT (display), "seat-id", &current, NULL);
 
         res = g_strcmp0 (current, looking_for) == 0;
 
         g_free(current);
 
         return res;
 }
 
 static gboolean
@@ -561,84 +594,82 @@ on_seat_new (GDBusConnection *connection,
 
 static void
 on_seat_removed (GDBusConnection *connection,
                  const gchar     *sender_name,
                  const gchar     *object_path,
                  const gchar     *interface_name,
                  const gchar     *signal_name,
                  GVariant        *parameters,
                  gpointer         user_data)
 {
         const char *seat;
 
         g_variant_get (parameters, "(&s&o)", &seat, NULL);
         delete_display (GDM_LOCAL_DISPLAY_FACTORY (user_data), seat);
 }
 
 #if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER)
 static gboolean
 lookup_by_session_id (const char *id,
                       GdmDisplay *display,
                       gpointer    user_data)
 {
         const char *looking_for = user_data;
         const char *current;
 
         current = gdm_display_get_session_id (display);
         return g_strcmp0 (current, looking_for) == 0;
 }
 
 static void
-maybe_stop_greeter_display (GdmDisplay *display)
+maybe_stop_greeter_in_background (GdmDisplay *display)
 {
         g_autofree char *display_session_type = NULL;
 
         if (gdm_display_get_status (display) != GDM_DISPLAY_MANAGED) {
                 g_debug ("GdmLocalDisplayFactory: login window not in managed state, so ignoring");
                 return;
         }
 
         g_object_get (G_OBJECT (display),
                       "session-type", &display_session_type,
                       NULL);
 
         /* we can only stop greeter for wayland sessions, since
          * X server would jump back on exit */
         if (g_strcmp0 (display_session_type, "wayland") != 0) {
                 g_debug ("GdmLocalDisplayFactory: login window is running on Xorg, so ignoring");
                 return;
         }
 
-        g_debug ("GdmLocalDisplayFactory: killing login window since its now unused");
-        gdm_display_stop_greeter_session (display);
-        gdm_display_unmanage (display);
-        gdm_display_finish (display);
+        g_debug ("GdmLocalDisplayFactory: killing login window once its unused");
+        g_object_set (G_OBJECT (display), "status", GDM_DISPLAY_WAITING_TO_FINISH, NULL);
 }
 
 static gboolean
 on_vt_changed (GIOChannel    *source,
                GIOCondition   condition,
                GdmLocalDisplayFactory *factory)
 {
         GIOStatus status;
         static const char *tty_of_initial_vt = "tty" GDM_INITIAL_VT;
         g_autofree char *tty_of_previous_vt = NULL;
         g_autofree char *tty_of_active_vt = NULL;
         g_autofree char *login_session_id = NULL;
         g_autofree char *active_session_id = NULL;
         const char *session_type = NULL;
         int ret;
 
         g_debug ("GdmLocalDisplayFactory: received VT change event");
         g_io_channel_seek_position (source, 0, G_SEEK_SET, NULL);
 
         if (condition & G_IO_PRI) {
                 g_autoptr (GError) error = NULL;
                 status = g_io_channel_read_line (source, &tty_of_active_vt, NULL, NULL, &error);
 
                 if (error != NULL) {
                         g_warning ("could not read active VT from kernel: %s", error->message);
                 }
                 switch (status) {
                         case G_IO_STATUS_ERROR:
                             return G_SOURCE_REMOVE;
                         case G_IO_STATUS_EOF:
@@ -678,61 +709,61 @@ on_vt_changed (GIOChannel    *source,
                 return G_SOURCE_CONTINUE;
         }
 
         g_debug ("GdmLocalDisplayFactory: VT changed from %s to %s",
                  tty_of_previous_vt, factory->priv->tty_of_active_vt);
 
         /* if the old VT was running a wayland login screen kill it
          */
         if (gdm_get_login_window_session_id ("seat0", &login_session_id)) {
                 unsigned int vt;
 
                 ret = sd_session_get_vt (login_session_id, &vt);
                 if (ret == 0 && vt != 0) {
                         g_autofree char *tty_of_login_window_vt = NULL;
 
                         tty_of_login_window_vt = g_strdup_printf ("tty%u", vt);
 
                         g_debug ("GdmLocalDisplayFactory: tty of login window is %s", tty_of_login_window_vt);
                         if (g_strcmp0 (tty_of_login_window_vt, tty_of_previous_vt) == 0) {
                                 GdmDisplayStore *store;
                                 GdmDisplay *display;
 
                                 g_debug ("GdmLocalDisplayFactory: VT switched from login window");
 
                                 store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory));
                                 display = gdm_display_store_find (store,
                                                                   lookup_by_session_id,
                                                                   (gpointer) login_session_id);
 
                                 if (display != NULL)
-                                        maybe_stop_greeter_display (display);
+                                        maybe_stop_greeter_in_background (display);
                         } else {
                                 g_debug ("GdmLocalDisplayFactory: VT not switched from login window");
                         }
                 }
         }
 
         /* if user jumped back to initial vt and it's empty put a login screen
          * on it (unless a login screen is already running elsewhere, then
          * jump to that login screen)
          */
         if (strcmp (factory->priv->tty_of_active_vt, tty_of_initial_vt) != 0) {
                 g_debug ("GdmLocalDisplayFactory: active VT is not initial VT, so ignoring");
                 return G_SOURCE_CONTINUE;
         }
 
         ret = sd_seat_get_active ("seat0", &active_session_id, NULL);
 
         if (ret == 0) {
                 g_autofree char *state = NULL;
                 ret = sd_session_get_state (active_session_id, &state);
 
                 /* if there's something already running on the active VT then bail */
                 if (ret == 0 && g_strcmp0 (state, "closing") != 0) {
                         g_debug ("GdmLocalDisplayFactory: initial VT is in use, so ignoring");
                         return G_SOURCE_CONTINUE;
                 }
         }
 
         if (gdm_local_display_factory_use_wayland ())
                 session_type = "wayland";
-- 
2.27.0