summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--daemon/gdm-local-display-factory.c120
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);