diff options
-rw-r--r-- | daemon/gdm-local-display-factory.c | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/daemon/gdm-local-display-factory.c b/daemon/gdm-local-display-factory.c index 8c898215..344a074d 100644 --- a/daemon/gdm-local-display-factory.c +++ b/daemon/gdm-local-display-factory.c @@ -62,6 +62,10 @@ struct _GdmLocalDisplayFactory guint seat_new_id; guint seat_removed_id; + guint seat_prop_changed_id; + + gboolean udev_settle_started; + gboolean udev_settled; #if defined(ENABLE_USER_DISPLAY_SERVER) unsigned int active_vt; @@ -450,17 +454,74 @@ lookup_prepared_display_by_seat_id (const char *id, return lookup_by_seat_id (id, display, user_data); } +static void +udevadm_settle_cb (GPid pid, + gint status, + gpointer user_data) +{ + GdmLocalDisplayFactory *factory = user_data; + + if (status != 0) + g_warning ("GdmLocalDisplayFactory: udevadm settle finished with status %d", status); + g_debug ("GdmLocalDisplayFactory: udevadm settle finished.", + status); + factory->udev_settled = TRUE; + + /* Simply try to re-add seat0. If it is there already (i.e. CanGraphical + * turned TRUE, then we'll find it and it will not be created again. + */ + create_display (factory, "seat0", 0); +} + static GdmDisplay * create_display (GdmLocalDisplayFactory *factory, const char *seat_id, int failures) { gboolean is_initial; + int can_graphical; const char *session_type = NULL; GdmDisplayStore *store; GdmDisplay *display = NULL; g_autofree char *login_session_id = NULL; + can_graphical = sd_seat_can_graphical (seat_id); + if (can_graphical < 0) + return NULL; + + /* If seat0 is not graphical, then start udevadm settle if needed. */ + if (!can_graphical && !factory->udev_settle_started && g_strcmp0 (seat_id, "seat0") == 0) { + g_autoptr(GError) error = NULL; + gchar * argv[] = { "udevadm", "settle", NULL }; + GPid pid; + + factory->udev_settle_started = TRUE; + + g_debug ("GdmLocalDisplayFactory: Starting udevadm settle to wait for all devices."); + + if (!g_spawn_async (NULL, argv, NULL, + G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH | G_SPAWN_LEAVE_DESCRIPTORS_OPEN, + NULL, NULL, + &pid, + &error)) { + g_warning ("GdmLocalDisplayFactory: Failed to spawn udevadm settle: %s", error->message); + return NULL; + } + + g_child_watch_add (pid, udevadm_settle_cb, factory); + + return NULL; + } + + /* If udev is settled and this is seat0 then assume we can use it */ + if (!can_graphical && factory->udev_settled && g_strcmp0 (seat_id, "seat0") == 0) { + g_warning ("GdmLocalDisplayFactory: Assuming we can use seat0 even though it is not graphical!"); + can_graphical = TRUE; + } + + if (!can_graphical) + return NULL; + if (g_strcmp0 (seat_id, "seat0") == 0) { is_initial = TRUE; if (failures == 0 && gdm_local_display_factory_use_wayland ()) @@ -599,6 +660,7 @@ on_seat_new (GDBusConnection *connection, const char *seat; g_variant_get (parameters, "(&s&o)", &seat, NULL); + create_display (GDM_LOCAL_DISPLAY_FACTORY (user_data), seat, 0); } @@ -617,6 +679,49 @@ on_seat_removed (GDBusConnection *connection, delete_display (GDM_LOCAL_DISPLAY_FACTORY (user_data), seat); } +static void +on_seat_prop_changed (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + const gchar *seat = NULL; + g_autoptr(GVariant) changed_props = NULL; + g_autoptr(GVariant) changed_prop = NULL; + g_autofree const gchar * const *invalidated_props = NULL; + gboolean changed = FALSE; + int res; + + /* Extract seat id, i.e. the last element of the object path. */ + seat = strrchr (object_path, '/'); + if (seat == NULL) + return; + seat += 1; + + g_variant_get (parameters, "(s@a{sv}^a&s)", NULL, &changed_props, &invalidated_props); + + changed_prop = g_variant_lookup_value (changed_props, "CanGraphical", NULL); + if (changed_prop) + changed = TRUE; + if (!changed && g_strv_contains (invalidated_props, "CanGraphical")) + changed = TRUE; + + if (!changed) + return; + + res = sd_seat_can_graphical (seat); + if (res < 0) + return; + + if (res) + create_display (GDM_LOCAL_DISPLAY_FACTORY (user_data), seat, 0); + else + delete_display (GDM_LOCAL_DISPLAY_FACTORY (user_data), seat); +} + static gboolean lookup_by_session_id (const char *id, GdmDisplay *display, @@ -851,6 +956,16 @@ gdm_local_display_factory_start_monitor (GdmLocalDisplayFactory *factory) on_seat_removed, g_object_ref (factory), g_object_unref); + factory->seat_prop_changed_id = g_dbus_connection_signal_subscribe (factory->connection, + "org.freedesktop.login1", + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + NULL, + "org.freedesktop.login1.Seat", + G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE, + on_seat_prop_changed, + g_object_ref (factory), + g_object_unref); #if defined(ENABLE_USER_DISPLAY_SERVER) io_channel = g_io_channel_new_file ("/sys/class/tty/tty0/active", "r", NULL); @@ -879,6 +994,11 @@ gdm_local_display_factory_stop_monitor (GdmLocalDisplayFactory *factory) factory->seat_removed_id); factory->seat_removed_id = 0; } + if (factory->seat_prop_changed_id) { + g_dbus_connection_signal_unsubscribe (factory->connection, + factory->seat_prop_changed_id); + factory->seat_prop_changed_id = 0; + } #if defined(ENABLE_USER_DISPLAY_SERVER) if (factory->active_vt_watch_id) { g_source_remove (factory->active_vt_watch_id); |