summaryrefslogtreecommitdiff
path: root/daemon/gdm-local-display-factory.c
diff options
context:
space:
mode:
Diffstat (limited to 'daemon/gdm-local-display-factory.c')
-rw-r--r--daemon/gdm-local-display-factory.c177
1 files changed, 168 insertions, 9 deletions
diff --git a/daemon/gdm-local-display-factory.c b/daemon/gdm-local-display-factory.c
index 9ecc0395..bc55f0c4 100644
--- a/daemon/gdm-local-display-factory.c
+++ b/daemon/gdm-local-display-factory.c
@@ -48,6 +48,7 @@
#define MAX_DISPLAY_FAILURES 5
#define WAIT_TO_FINISH_TIMEOUT 10 /* seconds */
+#define SEAT0_GRAPHICS_CHECK_TIMEOUT 10 /* seconds */
struct _GdmLocalDisplayFactory
{
@@ -62,6 +63,10 @@ struct _GdmLocalDisplayFactory
guint seat_new_id;
guint seat_removed_id;
+ guint seat_properties_changed_id;
+
+ gboolean seat0_graphics_check_timed_out;
+ guint seat0_graphics_check_timeout_id;
#if defined(ENABLE_USER_DISPLAY_SERVER)
unsigned int active_vt;
@@ -449,29 +454,117 @@ lookup_prepared_display_by_seat_id (const char *id,
return lookup_by_seat_id (id, display, user_data);
}
+static int
+on_seat0_graphics_check_timeout (gpointer user_data)
+{
+ GdmLocalDisplayFactory *factory = user_data;
+
+ factory->seat0_graphics_check_timeout_id = 0;
+
+ /* 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).
+ */
+ factory->seat0_graphics_check_timed_out = TRUE;
+ ensure_display_for_seat (factory, "seat0");
+
+ return G_SOURCE_REMOVE;
+}
+
static void
ensure_display_for_seat (GdmLocalDisplayFactory *factory,
const char *seat_id)
{
- gboolean is_initial;
- const char *session_type = NULL;
+ int ret;
+ gboolean seat_supports_graphics;
+ gboolean is_seat0;
+ const char *session_type = "wayland";
GdmDisplayStore *store;
GdmDisplay *display = NULL;
g_autofree char *login_session_id = NULL;
+ ret = sd_seat_can_graphical (seat_id);
+
+ if (ret < 0) {
+ g_critical ("Failed to query CanGraphical information for seat %s", seat_id);
+ return;
+ }
+
+ if (ret == 0) {
+ g_debug ("GdmLocalDisplayFactory: System doesn't currently support graphics");
+ seat_supports_graphics = FALSE;
+ } else {
+ g_debug ("GdmLocalDisplayFactory: System supports graphics");
+ seat_supports_graphics = TRUE;
+ }
+
if (g_strcmp0 (seat_id, "seat0") == 0) {
- is_initial = TRUE;
- if (factory->num_failures == 0 && gdm_local_display_factory_use_wayland ())
- session_type = "wayland";
+ is_seat0 = TRUE;
+
+ /* If we've failed, or are explicitly told to, fall back to legacy X11 support
+ */
+ if (factory->num_failures > 0 || !gdm_local_display_factory_use_wayland ()) {
+ session_type = NULL;
+ g_debug ("GdmLocalDisplayFactory: New displays on seat0 will use X11 fallback");
+ } else {
+ g_debug ("GdmLocalDisplayFactory: New displays on seat0 will use wayland");
+ }
} else {
- is_initial = FALSE;
+ is_seat0 = FALSE;
+
+ g_debug ("GdmLocalDisplayFactory: New displays on seat %s will use X11 fallback", seat_id);
+ /* Force legacy X11 for all auxiliary seats */
+ seat_supports_graphics = TRUE;
+ session_type = NULL;
+ }
+
+ /* For seat0, we have a fallback logic to still try starting it after
+ * SEAT0_GRAPHICS_CHECK_TIMEOUT seconds. i.e. we simply continue even if
+ * CanGraphical is unset.
+ * This is ugly, but it means we'll come up eventually in some
+ * scenarios where no master device is present.
+ * Note that we'll force an X11 fallback even though there might be
+ * cases where an wayland capable device is present and simply not marked as
+ * master-of-seat. In these cases, this should likely be fixed in the
+ * udev rules.
+ *
+ * At the moment, systemd always sets CanGraphical for non-seat0 seats.
+ * This is because non-seat0 seats are defined by having master-of-seat
+ * set. This means we can avoid the fallback check for non-seat0 seats,
+ * which simplifies the code.
+ */
+ if (is_seat0) {
+ if (!seat_supports_graphics) {
+ if (!factory->seat0_graphics_check_timed_out) {
+ if (factory->seat0_graphics_check_timeout_id == 0) {
+ g_debug ("GdmLocalDisplayFactory: seat0 doesn't yet support graphics. Waiting %d seconds to try again.", SEAT0_GRAPHICS_CHECK_TIMEOUT);
+ factory->seat0_graphics_check_timeout_id = g_timeout_add_seconds (SEAT0_GRAPHICS_CHECK_TIMEOUT,
+ on_seat0_graphics_check_timeout,
+ factory);
+
+ } else {
+ /* It is not yet time to force X11 fallback. */
+ g_debug ("GdmLocalDisplayFactory: seat0 display requested when there is no graphics support before graphics check timeout.");
+ return;
+ }
+ }
+
+ g_debug ("GdmLocalDisplayFactory: Assuming we can use seat0 for X11 even though system says it doesn't support graphics!");
+ g_debug ("GdmLocalDisplayFactory: This might indicate an issue where the framebuffer device is not tagged as master-of-seat in udev.");
+ seat_supports_graphics = TRUE;
+ session_type = NULL;
+ } else {
+ g_clear_handle_id (&factory->seat0_graphics_check_timeout_id, g_source_remove);
+ }
}
+ if (!seat_supports_graphics)
+ return;
+
g_debug ("GdmLocalDisplayFactory: %s login display for seat %s requested",
session_type? : "X11", seat_id);
store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory));
- if (g_strcmp0 (seat_id, "seat0") == 0)
+ if (is_seat0)
display = gdm_display_store_find (store, lookup_prepared_display_by_seat_id, (gpointer) seat_id);
else
display = gdm_display_store_find (store, lookup_by_seat_id, (gpointer) seat_id);
@@ -503,7 +596,7 @@ ensure_display_for_seat (GdmLocalDisplayFactory *factory,
g_debug ("GdmLocalDisplayFactory: Adding display on seat %s", seat_id);
#ifdef ENABLE_USER_DISPLAY_SERVER
- if (g_strcmp0 (seat_id, "seat0") == 0) {
+ if (is_seat0) {
display = gdm_local_display_new ();
if (session_type != NULL) {
g_object_set (G_OBJECT (display), "session-type", session_type, NULL);
@@ -520,7 +613,7 @@ ensure_display_for_seat (GdmLocalDisplayFactory *factory,
}
g_object_set (display, "seat-id", seat_id, NULL);
- g_object_set (display, "is-initial", is_initial, NULL);
+ g_object_set (display, "is-initial", is_seat0, NULL);
store_display (factory, display);
@@ -615,6 +708,53 @@ on_seat_removed (GDBusConnection *connection,
delete_display (GDM_LOCAL_DISPLAY_FACTORY (user_data), seat);
}
+static void
+on_seat_properties_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 **invalidated_props = NULL;
+ gboolean changed = FALSE;
+ int ret;
+
+ /* Extract seat id, i.e. the last element of the object path. */
+ seat = strrchr (object_path, '/');
+ if (seat == NULL)
+ return;
+ seat += 1;
+
+ /* Valid seat IDs must start with seat, i.e. ignore "auto" */
+ if (!g_str_has_prefix (seat, "seat"))
+ return;
+
+ 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;
+
+ ret = sd_seat_can_graphical (seat);
+ if (ret < 0)
+ return;
+
+ if (ret != 0)
+ ensure_display_for_seat (GDM_LOCAL_DISPLAY_FACTORY (user_data), seat);
+ else
+ delete_display (GDM_LOCAL_DISPLAY_FACTORY (user_data), seat);
+}
+
static gboolean
lookup_by_session_id (const char *id,
GdmDisplay *display,
@@ -782,6 +922,8 @@ on_vt_changed (GIOChannel *source,
if (factory->active_vt != login_window_vt) {
GdmDisplay *display;
+ g_clear_handle_id (&factory->seat0_graphics_check_timeout_id, g_source_remove);
+
display = gdm_display_store_find (store,
lookup_by_tty,
(gpointer) tty_of_active_vt);
@@ -849,6 +991,16 @@ gdm_local_display_factory_start_monitor (GdmLocalDisplayFactory *factory)
on_seat_removed,
g_object_ref (factory),
g_object_unref);
+ factory->seat_properties_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_properties_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);
@@ -877,6 +1029,11 @@ gdm_local_display_factory_stop_monitor (GdmLocalDisplayFactory *factory)
factory->seat_removed_id);
factory->seat_removed_id = 0;
}
+ if (factory->seat_properties_changed_id) {
+ g_dbus_connection_signal_unsubscribe (factory->connection,
+ factory->seat_properties_changed_id);
+ factory->seat_properties_changed_id = 0;
+ }
#if defined(ENABLE_USER_DISPLAY_SERVER)
if (factory->active_vt_watch_id) {
g_source_remove (factory->active_vt_watch_id);
@@ -962,6 +1119,8 @@ gdm_local_display_factory_stop (GdmDisplayFactory *base_factory)
G_CALLBACK (on_display_removed),
factory);
+ g_clear_handle_id (&factory->seat0_graphics_check_timeout_id, g_source_remove);
+
return TRUE;
}