Blob Blame History Raw
From 8451325378ef2981304d96bb27844174dba930c5 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Fri, 16 Jul 2021 12:34:57 -0400
Subject: [PATCH 1/4] local-display-factory: Provide more flexibility for
 configuring display server

There's currently a way to disable wayland, but no way to disable Xorg.
We currently prefer wayland if it's not disabled, but have no way to
prefer Xorg without disabling wayland entirely.

There's currently no way use legacy Xorg support at all if user display
server support is enabled at a build time.

This commit adds more flexibility to display server selection. It adds
two new keys: XorgEnable and and PreferredDisplayServer.

XorgEnable=false disables Xorg support entirely on seat 0.

PreferredDisplayServer can be set to "wayland", "xorg", "legacy-xorg" or
"none" to select which display server is used by default. If it's set to
"wayland", it will fall back to "xorg". If it's set to "xorg" it will
fall back to "wayland".
---
 common/gdm-settings-keys.h         |   2 +
 daemon/gdm-display.c               |  36 ++++
 daemon/gdm-launch-environment.c    |   9 +
 daemon/gdm-local-display-factory.c | 267 ++++++++++++++++++++++++-----
 daemon/gdm-manager.c               |  20 +--
 daemon/gdm-session.c               | 169 ++++++++++--------
 data/gdm.schemas.in                |  10 ++
 libgdm/gdm-sessions.c              |  72 +++++---
 8 files changed, 437 insertions(+), 148 deletions(-)

diff --git a/common/gdm-settings-keys.h b/common/gdm-settings-keys.h
index f0059b5c..87685d3c 100644
--- a/common/gdm-settings-keys.h
+++ b/common/gdm-settings-keys.h
@@ -6,59 +6,61 @@
  * modify it under the terms of the GNU Library General Public
  * License as published by the Free Software Foundation; either
  * version 2 of the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Library General Public License for more details.
  *
  * You should have received a copy of the GNU Library General Public
  * License along with this library; if not, write to the
  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  * Boston, MA 02110-1301, USA.
  */
 
 #ifndef _GDM_SETTINGS_KEYS_H
 #define _GDM_SETTINGS_KEYS_H
 
 #include <glib.h>
 
 G_BEGIN_DECLS
 
 #define GDM_KEY_USER "daemon/User"
 #define GDM_KEY_GROUP "daemon/Group"
 #define GDM_KEY_AUTO_LOGIN_ENABLE "daemon/AutomaticLoginEnable"
 #define GDM_KEY_AUTO_LOGIN_USER "daemon/AutomaticLogin"
 #define GDM_KEY_TIMED_LOGIN_ENABLE "daemon/TimedLoginEnable"
 #define GDM_KEY_TIMED_LOGIN_USER "daemon/TimedLogin"
 #define GDM_KEY_TIMED_LOGIN_DELAY "daemon/TimedLoginDelay"
 #define GDM_KEY_INITIAL_SETUP_ENABLE "daemon/InitialSetupEnable"
+#define GDM_KEY_PREFERRED_DISPLAY_SERVER "daemon/PreferredDisplayServer"
 #define GDM_KEY_WAYLAND_ENABLE "daemon/WaylandEnable"
+#define GDM_KEY_XORG_ENABLE "daemon/XorgEnable"
 
 #define GDM_KEY_DEBUG "debug/Enable"
 
 #define GDM_KEY_INCLUDE "greeter/Include"
 #define GDM_KEY_EXCLUDE "greeter/Exclude"
 #define GDM_KEY_INCLUDE_ALL "greeter/IncludeAll"
 
 #define GDM_KEY_DISALLOW_TCP "security/DisallowTCP"
 #define GDM_KEY_ALLOW_REMOTE_AUTOLOGIN "security/AllowRemoteAutoLogin"
 
 #define GDM_KEY_XDMCP_ENABLE "xdmcp/Enable"
 #define GDM_KEY_SHOW_LOCAL_GREETER "xdmcp/ShowLocalGreeter"
 #define GDM_KEY_MAX_PENDING "xdmcp/MaxPending"
 #define GDM_KEY_MAX_SESSIONS "xdmcp/MaxSessions"
 #define GDM_KEY_MAX_WAIT "xdmcp/MaxWait"
 #define GDM_KEY_DISPLAYS_PER_HOST "xdmcp/DisplaysPerHost"
 #define GDM_KEY_UDP_PORT "xdmcp/Port"
 #define GDM_KEY_INDIRECT "xdmcp/HonorIndirect"
 #define GDM_KEY_MAX_WAIT_INDIRECT "xdmcp/MaxWaitIndirect"
 #define GDM_KEY_PING_INTERVAL "xdmcp/PingIntervalSeconds"
 #define GDM_KEY_WILLING "xdmcp/Willing"
 
 #define GDM_KEY_MULTICAST "chooser/Multicast"
 #define GDM_KEY_MULTICAST_ADDR "chooser/MulticastAddr"
 
 G_END_DECLS
 
 #endif /* _GDM_SETTINGS_KEYS_H */
diff --git a/daemon/gdm-display.c b/daemon/gdm-display.c
index 7c954ad2..bd96dd03 100644
--- a/daemon/gdm-display.c
+++ b/daemon/gdm-display.c
@@ -66,83 +66,86 @@ typedef struct _GdmDisplayPrivate
         char                 *x11_display_name;
         int                   status;
         time_t                creation_time;
 
         char                 *x11_cookie;
         gsize                 x11_cookie_size;
         GdmDisplayAccessFile *access_file;
 
         guint                 finish_idle_id;
 
         xcb_connection_t     *xcb_connection;
         int                   xcb_screen_number;
 
         GDBusConnection      *connection;
         GdmDisplayAccessFile *user_access_file;
 
         GdmDBusDisplay       *display_skeleton;
         GDBusObjectSkeleton  *object_skeleton;
 
         GDBusProxy           *accountsservice_proxy;
 
         /* this spawns and controls the greeter session */
         GdmLaunchEnvironment *launch_environment;
 
         guint                 is_local : 1;
         guint                 is_initial : 1;
         guint                 allow_timed_login : 1;
         guint                 have_existing_user_accounts : 1;
         guint                 doing_initial_setup : 1;
         guint                 session_registered : 1;
+
+        GStrv                 supported_session_types;
 } GdmDisplayPrivate;
 
 enum {
         PROP_0,
         PROP_ID,
         PROP_STATUS,
         PROP_SEAT_ID,
         PROP_SESSION_ID,
         PROP_SESSION_CLASS,
         PROP_SESSION_TYPE,
         PROP_REMOTE_HOSTNAME,
         PROP_X11_DISPLAY_NUMBER,
         PROP_X11_DISPLAY_NAME,
         PROP_X11_COOKIE,
         PROP_X11_AUTHORITY_FILE,
         PROP_IS_CONNECTED,
         PROP_IS_LOCAL,
         PROP_LAUNCH_ENVIRONMENT,
         PROP_IS_INITIAL,
         PROP_ALLOW_TIMED_LOGIN,
         PROP_HAVE_EXISTING_USER_ACCOUNTS,
         PROP_DOING_INITIAL_SETUP,
         PROP_SESSION_REGISTERED,
+        PROP_SUPPORTED_SESSION_TYPES,
 };
 
 static void     gdm_display_class_init  (GdmDisplayClass *klass);
 static void     gdm_display_init        (GdmDisplay      *self);
 static void     gdm_display_finalize    (GObject         *object);
 static void     queue_finish            (GdmDisplay      *self);
 static void     _gdm_display_set_status (GdmDisplay *self,
                                          int         status);
 static gboolean wants_initial_setup (GdmDisplay *self);
 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GdmDisplay, gdm_display, G_TYPE_OBJECT)
 
 GQuark
 gdm_display_error_quark (void)
 {
         static GQuark ret = 0;
         if (ret == 0) {
                 ret = g_quark_from_static_string ("gdm_display_error");
         }
 
         return ret;
 }
 
 time_t
 gdm_display_get_creation_time (GdmDisplay *self)
 {
         GdmDisplayPrivate *priv;
 
         g_return_val_if_fail (GDM_IS_DISPLAY (self), 0);
 
         priv = gdm_display_get_instance_private (self);
@@ -885,116 +888,136 @@ _gdm_display_set_launch_environment (GdmDisplay           *self,
 
         priv = gdm_display_get_instance_private (self);
 
         g_clear_object (&priv->launch_environment);
 
         priv->launch_environment = g_object_ref (launch_environment);
 }
 
 static void
 _gdm_display_set_is_initial (GdmDisplay     *self,
                              gboolean        initial)
 {
         GdmDisplayPrivate *priv;
 
         priv = gdm_display_get_instance_private (self);
         g_debug ("GdmDisplay: initial: %s", initial? "yes" : "no");
         priv->is_initial = initial;
 }
 
 static void
 _gdm_display_set_allow_timed_login (GdmDisplay     *self,
                                     gboolean        allow_timed_login)
 {
         GdmDisplayPrivate *priv;
 
         priv = gdm_display_get_instance_private (self);
         g_debug ("GdmDisplay: allow timed login: %s", allow_timed_login? "yes" : "no");
         priv->allow_timed_login = allow_timed_login;
 }
 
+static void
+_gdm_display_set_supported_session_types (GdmDisplay         *self,
+                                          const char * const *supported_session_types)
+
+{
+        GdmDisplayPrivate *priv;
+        g_autofree char *supported_session_types_string = NULL;
+
+        if (supported_session_types != NULL)
+                supported_session_types_string = g_strjoinv (":", (GStrv) supported_session_types);
+
+        priv = gdm_display_get_instance_private (self);
+        g_debug ("GdmDisplay: supported session types: %s", supported_session_types_string);
+        g_strfreev (priv->supported_session_types);
+        priv->supported_session_types = g_strdupv ((GStrv) supported_session_types);
+}
+
 static void
 gdm_display_set_property (GObject        *object,
                           guint           prop_id,
                           const GValue   *value,
                           GParamSpec     *pspec)
 {
         GdmDisplay *self;
 
         self = GDM_DISPLAY (object);
 
         switch (prop_id) {
         case PROP_ID:
                 _gdm_display_set_id (self, g_value_get_string (value));
                 break;
         case PROP_STATUS:
                 _gdm_display_set_status (self, g_value_get_int (value));
                 break;
         case PROP_SEAT_ID:
                 _gdm_display_set_seat_id (self, g_value_get_string (value));
                 break;
         case PROP_SESSION_ID:
                 _gdm_display_set_session_id (self, g_value_get_string (value));
                 break;
         case PROP_SESSION_CLASS:
                 _gdm_display_set_session_class (self, g_value_get_string (value));
                 break;
         case PROP_SESSION_TYPE:
                 _gdm_display_set_session_type (self, g_value_get_string (value));
                 break;
         case PROP_REMOTE_HOSTNAME:
                 _gdm_display_set_remote_hostname (self, g_value_get_string (value));
                 break;
         case PROP_X11_DISPLAY_NUMBER:
                 _gdm_display_set_x11_display_number (self, g_value_get_int (value));
                 break;
         case PROP_X11_DISPLAY_NAME:
                 _gdm_display_set_x11_display_name (self, g_value_get_string (value));
                 break;
         case PROP_X11_COOKIE:
                 _gdm_display_set_x11_cookie (self, g_value_get_string (value));
                 break;
         case PROP_IS_LOCAL:
                 _gdm_display_set_is_local (self, g_value_get_boolean (value));
                 break;
         case PROP_ALLOW_TIMED_LOGIN:
                 _gdm_display_set_allow_timed_login (self, g_value_get_boolean (value));
                 break;
         case PROP_LAUNCH_ENVIRONMENT:
                 _gdm_display_set_launch_environment (self, g_value_get_object (value));
                 break;
         case PROP_IS_INITIAL:
                 _gdm_display_set_is_initial (self, g_value_get_boolean (value));
                 break;
         case PROP_SESSION_REGISTERED:
                 _gdm_display_set_session_registered (self, g_value_get_boolean (value));
                 break;
+        case PROP_SUPPORTED_SESSION_TYPES:
+                _gdm_display_set_supported_session_types (self, g_value_get_boxed (value));
+                break;
         default:
                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                 break;
         }
 }
 
 static void
 gdm_display_get_property (GObject        *object,
                           guint           prop_id,
                           GValue         *value,
                           GParamSpec     *pspec)
 {
         GdmDisplay *self;
         GdmDisplayPrivate *priv;
 
         self = GDM_DISPLAY (object);
         priv = gdm_display_get_instance_private (self);
 
         switch (prop_id) {
         case PROP_ID:
                 g_value_set_string (value, priv->id);
                 break;
         case PROP_STATUS:
                 g_value_set_int (value, priv->status);
                 break;
         case PROP_SEAT_ID:
                 g_value_set_string (value, priv->seat_id);
                 break;
         case PROP_SESSION_ID:
                 g_value_set_string (value, priv->session_id);
@@ -1019,60 +1042,63 @@ gdm_display_get_property (GObject        *object,
                 break;
         case PROP_X11_AUTHORITY_FILE:
                 g_value_take_string (value,
                                      priv->access_file?
                                      gdm_display_access_file_get_path (priv->access_file) : NULL);
                 break;
         case PROP_IS_LOCAL:
                 g_value_set_boolean (value, priv->is_local);
                 break;
         case PROP_IS_CONNECTED:
                 g_value_set_boolean (value, priv->xcb_connection != NULL);
                 break;
         case PROP_LAUNCH_ENVIRONMENT:
                 g_value_set_object (value, priv->launch_environment);
                 break;
         case PROP_IS_INITIAL:
                 g_value_set_boolean (value, priv->is_initial);
                 break;
         case PROP_HAVE_EXISTING_USER_ACCOUNTS:
                 g_value_set_boolean (value, priv->have_existing_user_accounts);
                 break;
         case PROP_DOING_INITIAL_SETUP:
                 g_value_set_boolean (value, priv->doing_initial_setup);
                 break;
         case PROP_SESSION_REGISTERED:
                 g_value_set_boolean (value, priv->session_registered);
                 break;
         case PROP_ALLOW_TIMED_LOGIN:
                 g_value_set_boolean (value, priv->allow_timed_login);
                 break;
+        case PROP_SUPPORTED_SESSION_TYPES:
+                g_value_set_boxed (value, priv->supported_session_types);
+                break;
         default:
                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                 break;
         }
 }
 
 static gboolean
 handle_get_id (GdmDBusDisplay        *skeleton,
                GDBusMethodInvocation *invocation,
                GdmDisplay            *self)
 {
         char *id;
 
         gdm_display_get_id (self, &id, NULL);
 
         gdm_dbus_display_complete_get_id (skeleton, invocation, id);
 
         g_free (id);
         return TRUE;
 }
 
 static gboolean
 handle_get_remote_hostname (GdmDBusDisplay        *skeleton,
                             GDBusMethodInvocation *invocation,
                             GdmDisplay            *self)
 {
         char *hostname;
 
         gdm_display_get_remote_hostname (self, &hostname, NULL);
 
@@ -1204,60 +1230,61 @@ gdm_display_constructor (GType                  type,
         priv = gdm_display_get_instance_private (self);
 
         g_free (priv->id);
         priv->id = g_strdup_printf ("/org/gnome/DisplayManager/Displays/%lu",
                                           (gulong) self);
 
         res = register_display (self);
         if (! res) {
                 g_warning ("Unable to register display with system bus");
         }
 
         return G_OBJECT (self);
 }
 
 static void
 gdm_display_dispose (GObject *object)
 {
         GdmDisplay *self;
         GdmDisplayPrivate *priv;
 
         self = GDM_DISPLAY (object);
         priv = gdm_display_get_instance_private (self);
 
         g_debug ("GdmDisplay: Disposing display");
 
         if (priv->finish_idle_id != 0) {
                 g_source_remove (priv->finish_idle_id);
                 priv->finish_idle_id = 0;
         }
         g_clear_object (&priv->launch_environment);
+        g_clear_pointer (&priv->supported_session_types, g_strfreev);
 
         g_warn_if_fail (priv->status != GDM_DISPLAY_MANAGED);
         g_warn_if_fail (priv->user_access_file == NULL);
         g_warn_if_fail (priv->access_file == NULL);
 
         G_OBJECT_CLASS (gdm_display_parent_class)->dispose (object);
 }
 
 static void
 gdm_display_class_init (GdmDisplayClass *klass)
 {
         GObjectClass   *object_class = G_OBJECT_CLASS (klass);
 
         object_class->get_property = gdm_display_get_property;
         object_class->set_property = gdm_display_set_property;
         object_class->constructor = gdm_display_constructor;
         object_class->dispose = gdm_display_dispose;
         object_class->finalize = gdm_display_finalize;
 
         klass->prepare = gdm_display_real_prepare;
 
         g_object_class_install_property (object_class,
                                          PROP_ID,
                                          g_param_spec_string ("id",
                                                               "id",
                                                               "id",
                                                               NULL,
                                                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
         g_object_class_install_property (object_class,
                                          PROP_REMOTE_HOSTNAME,
@@ -1364,60 +1391,68 @@ gdm_display_class_init (GdmDisplayClass *klass)
                                          PROP_DOING_INITIAL_SETUP,
                                          g_param_spec_boolean ("doing-initial-setup",
                                                                NULL,
                                                                NULL,
                                                                FALSE,
                                                                G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
         g_object_class_install_property (object_class,
                                          PROP_SESSION_REGISTERED,
                                          g_param_spec_boolean ("session-registered",
                                                                NULL,
                                                                NULL,
                                                                FALSE,
                                                                G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
         g_object_class_install_property (object_class,
                                          PROP_LAUNCH_ENVIRONMENT,
                                          g_param_spec_object ("launch-environment",
                                                               NULL,
                                                               NULL,
                                                               GDM_TYPE_LAUNCH_ENVIRONMENT,
                                                               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
         g_object_class_install_property (object_class,
                                          PROP_STATUS,
                                          g_param_spec_int ("status",
                                                            "status",
                                                            "status",
                                                            -1,
                                                            G_MAXINT,
                                                            GDM_DISPLAY_UNMANAGED,
                                                            G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+
+        g_object_class_install_property (object_class,
+                                         PROP_SUPPORTED_SESSION_TYPES,
+                                         g_param_spec_boxed ("supported-session-types",
+                                                             "supported session types",
+                                                             "supported session types",
+                                                             G_TYPE_STRV,
+                                                             G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
 }
 
 static void
 gdm_display_init (GdmDisplay *self)
 {
         GdmDisplayPrivate *priv;
 
         priv = gdm_display_get_instance_private (self);
 
         priv->creation_time = time (NULL);
 }
 
 static void
 gdm_display_finalize (GObject *object)
 {
         GdmDisplay *self;
         GdmDisplayPrivate *priv;
 
         g_return_if_fail (object != NULL);
         g_return_if_fail (GDM_IS_DISPLAY (object));
 
         self = GDM_DISPLAY (object);
         priv = gdm_display_get_instance_private (self);
 
         g_return_if_fail (priv != NULL);
 
         g_debug ("GdmDisplay: Finalizing display: %s", priv->id);
         g_free (priv->id);
         g_free (priv->seat_id);
         g_free (priv->session_class);
@@ -1696,60 +1731,61 @@ gdm_display_start_greeter_session (GdmDisplay *self)
                                  G_CALLBACK (on_launch_environment_session_opened),
                                  self, 0);
         g_signal_connect_object (priv->launch_environment,
                                  "started",
                                  G_CALLBACK (on_launch_environment_session_started),
                                  self, 0);
         g_signal_connect_object (priv->launch_environment,
                                  "stopped",
                                  G_CALLBACK (on_launch_environment_session_stopped),
                                  self, 0);
         g_signal_connect_object (priv->launch_environment,
                                  "exited",
                                  G_CALLBACK (on_launch_environment_session_exited),
                                  self, 0);
         g_signal_connect_object (priv->launch_environment,
                                  "died",
                                  G_CALLBACK (on_launch_environment_session_died),
                                  self, 0);
 
         if (auth_file != NULL) {
                 g_object_set (priv->launch_environment,
                               "x11-authority-file", auth_file,
                               NULL);
         }
 
         gdm_launch_environment_start (priv->launch_environment);
 
         session = gdm_launch_environment_get_session (priv->launch_environment);
         g_object_set (G_OBJECT (session),
                       "display-is-initial", priv->is_initial,
+                      "supported-session-types", priv->supported_session_types,
                       NULL);
 
         g_free (display_name);
         g_free (seat_id);
         g_free (hostname);
         g_free (auth_file);
 }
 
 void
 gdm_display_stop_greeter_session (GdmDisplay *self)
 {
         GdmDisplayPrivate *priv;
 
         priv = gdm_display_get_instance_private (self);
 
         if (priv->launch_environment != NULL) {
 
                 g_signal_handlers_disconnect_by_func (priv->launch_environment,
                                                       G_CALLBACK (on_launch_environment_session_opened),
                                                       self);
                 g_signal_handlers_disconnect_by_func (priv->launch_environment,
                                                       G_CALLBACK (on_launch_environment_session_started),
                                                       self);
                 g_signal_handlers_disconnect_by_func (priv->launch_environment,
                                                       G_CALLBACK (on_launch_environment_session_stopped),
                                                       self);
                 g_signal_handlers_disconnect_by_func (priv->launch_environment,
                                                       G_CALLBACK (on_launch_environment_session_exited),
                                                       self);
                 g_signal_handlers_disconnect_by_func (priv->launch_environment,
diff --git a/daemon/gdm-launch-environment.c b/daemon/gdm-launch-environment.c
index feccf057..5044290c 100644
--- a/daemon/gdm-launch-environment.c
+++ b/daemon/gdm-launch-environment.c
@@ -117,60 +117,61 @@ static GHashTable *
 build_launch_environment (GdmLaunchEnvironment *launch_environment,
                           gboolean              start_session)
 {
         GHashTable    *hash;
         struct passwd *pwent;
         static const char *const optional_environment[] = {
                 "GI_TYPELIB_PATH",
                 "LANG",
                 "LANGUAGE",
                 "LC_ADDRESS",
                 "LC_ALL",
                 "LC_COLLATE",
                 "LC_CTYPE",
                 "LC_IDENTIFICATION",
                 "LC_MEASUREMENT",
                 "LC_MESSAGES",
                 "LC_MONETARY",
                 "LC_NAME",
                 "LC_NUMERIC",
                 "LC_PAPER",
                 "LC_TELEPHONE",
                 "LC_TIME",
                 "LD_LIBRARY_PATH",
                 "PATH",
                 "WINDOWPATH",
                 "XCURSOR_PATH",
                 "XDG_CONFIG_DIRS",
                 NULL
         };
         char *system_data_dirs;
+        g_auto (GStrv) supported_session_types = NULL;
         int i;
 
         /* create a hash table of current environment, then update keys has necessary */
         hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
 
         for (i = 0; optional_environment[i] != NULL; i++) {
                 if (g_getenv (optional_environment[i]) == NULL) {
                         continue;
                 }
 
                 g_hash_table_insert (hash,
                                      g_strdup (optional_environment[i]),
                                      g_strdup (g_getenv (optional_environment[i])));
         }
 
         system_data_dirs = g_strjoinv (":", (char **) g_get_system_data_dirs ());
 
         g_hash_table_insert (hash,
                              g_strdup ("XDG_DATA_DIRS"),
                              g_strdup_printf ("%s:%s",
                                               DATADIR "/gdm/greeter",
                                               system_data_dirs));
         g_free (system_data_dirs);
 
         if (launch_environment->priv->x11_authority_file != NULL)
                 g_hash_table_insert (hash, g_strdup ("XAUTHORITY"), g_strdup (launch_environment->priv->x11_authority_file));
 
         if (launch_environment->priv->session_mode != NULL) {
                 g_hash_table_insert (hash, g_strdup ("GNOME_SHELL_SESSION_MODE"), g_strdup (launch_environment->priv->session_mode));
 
@@ -191,60 +192,68 @@ build_launch_environment (GdmLaunchEnvironment *launch_environment,
         g_hash_table_insert (hash, g_strdup ("USER"), g_strdup (launch_environment->priv->user_name));
         g_hash_table_insert (hash, g_strdup ("USERNAME"), g_strdup (launch_environment->priv->user_name));
 
         g_hash_table_insert (hash, g_strdup ("GDM_VERSION"), g_strdup (VERSION));
         g_hash_table_remove (hash, "MAIL");
 
         g_hash_table_insert (hash, g_strdup ("HOME"), g_strdup ("/"));
         g_hash_table_insert (hash, g_strdup ("PWD"), g_strdup ("/"));
         g_hash_table_insert (hash, g_strdup ("SHELL"), g_strdup ("/bin/sh"));
 
         gdm_get_pwent_for_name (launch_environment->priv->user_name, &pwent);
         if (pwent != NULL) {
                 if (pwent->pw_dir != NULL && pwent->pw_dir[0] != '\0') {
                         g_hash_table_insert (hash, g_strdup ("HOME"), g_strdup (pwent->pw_dir));
                         g_hash_table_insert (hash, g_strdup ("PWD"), g_strdup (pwent->pw_dir));
                 }
 
                 g_hash_table_insert (hash, g_strdup ("SHELL"), g_strdup (pwent->pw_shell));
         }
 
         if (start_session && launch_environment->priv->x11_display_seat_id != NULL) {
                 char *seat_id;
 
                 seat_id = launch_environment->priv->x11_display_seat_id;
 
                 g_hash_table_insert (hash, g_strdup ("GDM_SEAT_ID"), g_strdup (seat_id));
         }
 
         g_hash_table_insert (hash, g_strdup ("RUNNING_UNDER_GDM"), g_strdup ("true"));
 
+        g_object_get (launch_environment->priv->session,
+                      "supported-session-types",
+                      &supported_session_types,
+                      NULL);
+        g_hash_table_insert (hash,
+                             g_strdup ("GDM_SUPPORTED_SESSION_TYPES"),
+                             g_strjoinv (":", supported_session_types));
+
         return hash;
 }
 
 static void
 on_session_setup_complete (GdmSession        *session,
                            const char        *service_name,
                            GdmLaunchEnvironment *launch_environment)
 {
         GHashTable       *hash;
         GHashTableIter    iter;
         gpointer          key, value;
 
         hash = build_launch_environment (launch_environment, TRUE);
 
         g_hash_table_iter_init (&iter, hash);
         while (g_hash_table_iter_next (&iter, &key, &value)) {
                 gdm_session_set_environment_variable (launch_environment->priv->session, key, value);
         }
         g_hash_table_destroy (hash);
 }
 
 static void
 on_session_opened (GdmSession           *session,
                    const char           *service_name,
                    const char           *session_id,
                    GdmLaunchEnvironment *launch_environment)
 {
         launch_environment->priv->session_id = g_strdup (session_id);
 
         g_signal_emit (G_OBJECT (launch_environment), signals [OPENED], 0);
diff --git a/daemon/gdm-local-display-factory.c b/daemon/gdm-local-display-factory.c
index 8a4ef06c..eba38671 100644
--- a/daemon/gdm-local-display-factory.c
+++ b/daemon/gdm-local-display-factory.c
@@ -156,126 +156,287 @@ take_next_display_number (GdmLocalDisplayFactory *factory)
 
         g_debug ("GdmLocalDisplayFactory: Found the following X displays:");
         for (l = list; l != NULL; l = l->next) {
                 g_debug ("GdmLocalDisplayFactory: %u", GPOINTER_TO_UINT (l->data));
         }
 
         for (l = list; l != NULL; l = l->next) {
                 guint32 num;
                 num = GPOINTER_TO_UINT (l->data);
 
                 /* always fill zero */
                 if (l->prev == NULL && num != 0) {
                         ret = 0;
                         break;
                 }
                 /* now find the first hole */
                 if (l->next == NULL || GPOINTER_TO_UINT (l->next->data) != (num + 1)) {
                         ret = num + 1;
                         break;
                 }
         }
  out:
 
         /* now reserve this number */
         g_debug ("GdmLocalDisplayFactory: Reserving X display: %u", ret);
         g_hash_table_insert (factory->used_display_numbers, GUINT_TO_POINTER (ret), NULL);
 
         return ret;
 }
 
+static char *
+get_preferred_display_server (GdmLocalDisplayFactory *factory)
+{
+        g_autofree gchar *preferred_display_server = NULL;
+        gboolean wayland_enabled = FALSE, xorg_enabled = FALSE;
+
+        gdm_settings_direct_get_boolean (GDM_KEY_WAYLAND_ENABLE, &wayland_enabled);
+        gdm_settings_direct_get_boolean (GDM_KEY_XORG_ENABLE, &xorg_enabled);
+
+        if (wayland_enabled && !xorg_enabled) {
+                return g_strdup ("wayland");
+        }
+
+        if (!wayland_enabled && !xorg_enabled) {
+                return g_strdup ("none");
+        }
+
+        gdm_settings_direct_get_string (GDM_KEY_PREFERRED_DISPLAY_SERVER, &preferred_display_server);
+
+        if (g_strcmp0 (preferred_display_server, "wayland") == 0) {
+                if (wayland_enabled)
+                        return g_strdup (preferred_display_server);
+                else
+                        return g_strdup ("xorg");
+        }
+
+        if (g_strcmp0 (preferred_display_server, "xorg") == 0) {
+                if (xorg_enabled)
+                        return g_strdup (preferred_display_server);
+                else
+                        return g_strdup ("wayland");
+        }
+
+        if (g_strcmp0 (preferred_display_server, "legacy-xorg") == 0) {
+                if (xorg_enabled)
+                        return g_strdup (preferred_display_server);
+        }
+
+        return g_strdup ("none");
+}
+
+struct GdmDisplayServerConfiguration {
+        const char *display_server;
+        const char *key;
+        const char *binary;
+        const char *session_type;
+} display_server_configuration[] = {
+#ifdef ENABLE_WAYLAND_SUPPORT
+        { "wayland", GDM_KEY_WAYLAND_ENABLE, "/usr/bin/Xwayland", "wayland" },
+#endif
+        { "xorg", GDM_KEY_XORG_ENABLE, "/usr/bin/Xorg", "x11" },
+        { NULL, NULL, NULL },
+};
+
+static gboolean
+display_server_enabled (GdmLocalDisplayFactory *factory,
+                        const char             *display_server)
+{
+        size_t i;
+
+        for (i = 0; display_server_configuration[i].display_server != NULL; i++) {
+                const char *key = display_server_configuration[i].key;
+                const char *binary = display_server_configuration[i].binary;
+                gboolean enabled = FALSE;
+
+                if (!g_str_equal (display_server_configuration[i].display_server,
+                                  display_server))
+                        continue;
+
+                if (!gdm_settings_direct_get_boolean (key, &enabled) || !enabled)
+                        return FALSE;
+
+                if (!g_file_test (binary, G_FILE_TEST_IS_EXECUTABLE))
+                        return FALSE;
+
+                return TRUE;
+        }
+
+        return FALSE;
+}
+
+static const char *
+get_session_type_for_display_server (GdmLocalDisplayFactory *factory,
+                                     const char             *display_server)
+{
+        size_t i;
+
+        for (i = 0; display_server_configuration[i].display_server != NULL; i++) {
+                if (!g_str_equal (display_server_configuration[i].display_server,
+                                  display_server))
+                        continue;
+
+                return display_server_configuration[i].session_type;
+        }
+
+        return NULL;
+}
+
+static char **
+gdm_local_display_factory_get_session_types (GdmLocalDisplayFactory *factory,
+                                             gboolean                should_fall_back)
+{
+        g_autofree gchar *preferred_display_server = NULL;
+        const char *fallback_display_server = NULL;
+        gboolean wayland_preferred = FALSE;
+        gboolean xorg_preferred = FALSE;
+        g_autoptr (GPtrArray) session_types_array = NULL;
+        char **session_types;
+
+        session_types_array = g_ptr_array_new ();
+
+        preferred_display_server = get_preferred_display_server (factory);
+
+        g_debug ("GdmLocalDisplayFactory: Getting session type (prefers %s, falling back: %s)",
+                 preferred_display_server, should_fall_back? "yes" : "no");
+
+        wayland_preferred = g_str_equal (preferred_display_server, "wayland");
+        xorg_preferred = g_str_equal (preferred_display_server, "xorg");
+
+        if (wayland_preferred)
+                fallback_display_server = "xorg";
+        else if (xorg_preferred)
+                fallback_display_server = "wayland";
+        else
+                return NULL;
+
+        if (!should_fall_back) {
+                if (display_server_enabled (factory, preferred_display_server))
+                      g_ptr_array_add (session_types_array, (gpointer) get_session_type_for_display_server (factory, preferred_display_server));
+        }
+
+        if (display_server_enabled (factory, fallback_display_server))
+                g_ptr_array_add (session_types_array, (gpointer) get_session_type_for_display_server (factory, fallback_display_server));
+
+        if (session_types_array->len == 0)
+                return NULL;
+
+        g_ptr_array_add (session_types_array, NULL);
+
+        session_types = g_strdupv ((char **) session_types_array->pdata);
+
+        return session_types;
+}
+
 static void
 on_display_disposed (GdmLocalDisplayFactory *factory,
                      GdmDisplay             *display)
 {
         g_debug ("GdmLocalDisplayFactory: Display %p disposed", display);
 }
 
 static void
 store_display (GdmLocalDisplayFactory *factory,
                GdmDisplay             *display)
 {
         GdmDisplayStore *store;
 
         store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory));
         gdm_display_store_add (store, display);
 }
 
-static gboolean
-gdm_local_display_factory_use_wayland (void)
-{
-#ifdef ENABLE_WAYLAND_SUPPORT
-        gboolean wayland_enabled = FALSE;
-        if (gdm_settings_direct_get_boolean (GDM_KEY_WAYLAND_ENABLE, &wayland_enabled)) {
-                if (wayland_enabled && g_file_test ("/usr/bin/Xwayland", G_FILE_TEST_IS_EXECUTABLE) )
-                        return TRUE;
-        }
-#endif
-        return FALSE;
-}
-
 /*
   Example:
   dbus-send --system --dest=org.gnome.DisplayManager \
   --type=method_call --print-reply --reply-timeout=2000 \
   /org/gnome/DisplayManager/Manager \
   org.gnome.DisplayManager.Manager.GetDisplays
 */
 gboolean
 gdm_local_display_factory_create_transient_display (GdmLocalDisplayFactory *factory,
                                                     char                  **id,
                                                     GError                **error)
 {
         gboolean         ret;
         GdmDisplay      *display = NULL;
         gboolean         is_initial = FALSE;
+        const char      *session_type;
+        g_autofree gchar *preferred_display_server = NULL;
 
         g_return_val_if_fail (GDM_IS_LOCAL_DISPLAY_FACTORY (factory), FALSE);
 
         ret = FALSE;
 
         g_debug ("GdmLocalDisplayFactory: Creating transient display");
 
-#ifdef ENABLE_USER_DISPLAY_SERVER
-        display = gdm_local_display_new ();
-        if (gdm_local_display_factory_use_wayland ())
-                g_object_set (G_OBJECT (display), "session-type", "wayland", NULL);
-        is_initial = TRUE;
-#else
-        if (display == NULL) {
-                guint32 num;
+        preferred_display_server = get_preferred_display_server (factory);
 
-                num = take_next_display_number (factory);
+#ifdef ENABLE_USER_DISPLAY_SERVER
+        if (g_strcmp0 (preferred_display_server, "wayland") == 0 ||
+            g_strcmp0 (preferred_display_server, "xorg") == 0) {
+                g_auto(GStrv) session_types = NULL;
+
+                session_types = gdm_local_display_factory_get_session_types (factory, FALSE);
+
+                if (session_types == NULL) {
+                        g_set_error_literal (error,
+                                             GDM_DISPLAY_ERROR,
+                                             GDM_DISPLAY_ERROR_GENERAL,
+                                             "Both Wayland and Xorg are unavailable");
+                        return FALSE;
+                }
 
-                display = gdm_legacy_display_new (num);
+                display = gdm_local_display_new ();
+                g_object_set (G_OBJECT (display),
+                              "session-type", session_types[0],
+                              "supported-session-types", session_types,
+                              NULL);
+                is_initial = TRUE;
         }
 #endif
+        if (g_strcmp0 (preferred_display_server, "legacy-xorg") == 0) {
+                if (display == NULL) {
+                        guint32 num;
+
+                        num = take_next_display_number (factory);
+
+                        display = gdm_legacy_display_new (num);
+                }
+        }
+
+        if (display == NULL) {
+                g_set_error_literal (error,
+                                     GDM_DISPLAY_ERROR,
+                                     GDM_DISPLAY_ERROR_GENERAL,
+                                     "Invalid preferred display server configured");
+                return FALSE;
+        }
 
         g_object_set (display,
                       "seat-id", "seat0",
                       "allow-timed-login", FALSE,
                       "is-initial", is_initial,
                       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 void
 finish_display_on_seat_if_waiting (GdmDisplayStore *display_store,
                                    GdmDisplay      *display,
@@ -450,194 +611,220 @@ lookup_prepared_display_by_seat_id (const char *id,
 
         if (status != GDM_DISPLAY_PREPARED)
                 return FALSE;
 
         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)
 {
         int ret;
         gboolean seat_supports_graphics;
         gboolean is_seat0;
-        const char *session_type = "wayland";
+        g_auto (GStrv) session_types = NULL;
+        const char *legacy_session_types[] = { "x11", NULL };
         GdmDisplayStore *store;
         GdmDisplay      *display = NULL;
         g_autofree char *login_session_id = NULL;
+        gboolean wayland_enabled = FALSE, xorg_enabled = FALSE;
+        g_autofree gchar *preferred_display_server = NULL;
+        gboolean falling_back = FALSE;
+
+        gdm_settings_direct_get_boolean (GDM_KEY_WAYLAND_ENABLE, &wayland_enabled);
+        gdm_settings_direct_get_boolean (GDM_KEY_XORG_ENABLE, &xorg_enabled);
+
+        preferred_display_server = get_preferred_display_server (factory);
+
+        if (g_strcmp0 (preferred_display_server, "none") == 0) {
+               g_debug ("GdmLocalDisplayFactory: Preferred display server is none, so not creating display");
+               return;
+        }
 
         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_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");
-                }
+                falling_back = factory->num_failures > 0;
+                session_types = gdm_local_display_factory_get_session_types (factory, falling_back);
+
+                g_debug ("GdmLocalDisplayFactory: New displays on seat0 will use %s%s",
+                         session_types[0], falling_back? " fallback" : "");
         } else {
                 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;
+                session_types = g_strdupv ((char **) legacy_session_types);
         }
 
         /* 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;
+                        wayland_enabled = FALSE;
+                        g_strfreev (session_types);
+                        session_types = g_strdupv ((char **) legacy_session_types);
                 } 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);
+        if (session_types != NULL)
+                g_debug ("GdmLocalDisplayFactory: %s login display for seat %s requested",
+                         session_types[0], seat_id);
+        else if (g_strcmp0 (preferred_display_server, "legacy-xorg") == 0)
+                g_debug ("GdmLocalDisplayFactory: Legacy Xorg login display for seat %s requested",
+                         seat_id);
+
         store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory));
 
         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);
 
         /* Ensure we don't create the same display more than once */
         if (display != NULL) {
                 g_debug ("GdmLocalDisplayFactory: display already created");
                 return;
         }
 
         /* If we already have a login window, switch to it */
         if (gdm_get_login_window_session_id (seat_id, &login_session_id)) {
                 GdmDisplay *display;
 
                 display = gdm_display_store_find (store,
                                                   lookup_by_session_id,
                                                   (gpointer) login_session_id);
                 if (display != NULL &&
                     (gdm_display_get_status (display) == GDM_DISPLAY_MANAGED ||
                      gdm_display_get_status (display) == GDM_DISPLAY_WAITING_TO_FINISH)) {
                         g_object_set (G_OBJECT (display), "status", GDM_DISPLAY_MANAGED, NULL);
                         g_debug ("GdmLocalDisplayFactory: session %s found, activating.",
                                  login_session_id);
                         gdm_activate_session_by_id (factory->connection, seat_id, login_session_id);
                         return;
                 }
         }
 
         g_debug ("GdmLocalDisplayFactory: Adding display on seat %s", seat_id);
 
 #ifdef ENABLE_USER_DISPLAY_SERVER
-        if (is_seat0) {
-                display = gdm_local_display_new ();
-                if (session_type != NULL) {
-                        g_object_set (G_OBJECT (display), "session-type", session_type, NULL);
+        if (g_strcmp0 (preferred_display_server, "wayland") == 0 ||
+            g_strcmp0 (preferred_display_server, "xorg") == 0) {
+                if (is_seat0) {
+                        display = gdm_local_display_new ();
+                        g_object_set (G_OBJECT (display),
+                                      "session-type", session_types[0],
+                                      "supported-session-types", session_types,
+                                      NULL);
                 }
         }
 #endif
 
         if (display == NULL) {
                 guint32 num;
 
                 num = take_next_display_number (factory);
 
                 display = gdm_legacy_display_new (num);
+                g_object_set (G_OBJECT (display),
+                              "session-type", legacy_session_types[0],
+                              "supported-session-types", legacy_session_types,
+                              NULL);
         }
 
         g_object_set (display, "seat-id", seat_id, NULL);
         g_object_set (display, "is-initial", is_seat0, NULL);
 
         store_display (factory, display);
 
         /* let store own the ref */
         g_object_unref (display);
 
         if (! gdm_display_manage (display)) {
                 gdm_display_unmanage (display);
         }
 
         return;
 }
 
 static void
 delete_display (GdmLocalDisplayFactory *factory,
                 const char             *seat_id) {
 
         GdmDisplayStore *store;
 
         g_debug ("GdmLocalDisplayFactory: Removing used_display_numbers on seat %s", seat_id);
 
         store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory));
         gdm_display_store_foreach_remove (store, lookup_by_seat_id, (gpointer) seat_id);
 }
 
 static gboolean
diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c
index 9c10adff..24219691 100644
--- a/daemon/gdm-manager.c
+++ b/daemon/gdm-manager.c
@@ -1306,74 +1306,75 @@ get_automatic_login_details (GdmManager *manager,
                 *usernamep = username;
         } else {
                 g_free (username);
         }
 
         return enabled;
 }
 
 static const char *
 get_username_for_greeter_display (GdmManager *manager,
                                   GdmDisplay *display)
 {
         gboolean doing_initial_setup = FALSE;
 
         g_object_get (G_OBJECT (display),
                       "doing-initial-setup", &doing_initial_setup,
                       NULL);
 
         if (doing_initial_setup) {
                 return INITIAL_SETUP_USERNAME;
         } else {
                 return GDM_USERNAME;
         }
 }
 
 static void
 set_up_automatic_login_session (GdmManager *manager,
                                 GdmDisplay *display)
 {
         GdmSession *session;
-        char       *display_session_type = NULL;
+        g_auto (GStrv) supported_session_types = NULL;
 
         /* 0 is root user; since the daemon talks to the session object
          * directly, itself, for automatic login
          */
         create_user_session_for_display (manager, display, 0);
         session = get_user_session_for_display (display);
 
         g_object_get (G_OBJECT (display),
-                      "session-type", &display_session_type,
+                      "supported-session-types", &supported_session_types,
                       NULL);
 
         g_object_set (G_OBJECT (session),
                       "display-is-initial", FALSE,
+                      "supported-session-types", supported_session_types,
                       NULL);
 
         g_debug ("GdmManager: Starting automatic login conversation");
         gdm_session_start_conversation (session, "gdm-autologin");
 }
 
 static void
 set_up_chooser_session (GdmManager *manager,
                         GdmDisplay *display)
 {
         const char *allowed_user;
         struct passwd *passwd_entry;
 
         allowed_user = get_username_for_greeter_display (manager, display);
 
         if (!gdm_get_pwent_for_name (allowed_user, &passwd_entry)) {
                 g_warning ("GdmManager: couldn't look up username %s",
                            allowed_user);
                 gdm_display_unmanage (display);
                 gdm_display_finish (display);
                 return;
         }
 
         gdm_display_start_greeter_session (display);
 }
 
 static void
 set_up_greeter_session (GdmManager *manager,
                         GdmDisplay *display)
 {
@@ -2278,87 +2279,83 @@ on_session_reauthentication_started (GdmSession *session,
 
         if (invocation != NULL) {
                 g_hash_table_steal (manager->priv->open_reauthentication_requests,
                                     source_tag);
                 gdm_dbus_manager_complete_open_reauthentication_channel (GDM_DBUS_MANAGER (manager),
                                                                          invocation,
                                                                          address);
         }
 }
 
 static void
 clean_user_session (GdmSession *session)
 {
         g_object_set_data (G_OBJECT (session), "gdm-display", NULL);
         g_object_unref (session);
 }
 
 static void
 create_user_session_for_display (GdmManager *manager,
                                  GdmDisplay *display,
                                  uid_t       allowed_user)
 {
         GdmSession *session;
         gboolean    display_is_local = FALSE;
         char       *display_name = NULL;
         char       *display_device = NULL;
         char       *remote_hostname = NULL;
         char       *display_auth_file = NULL;
         char       *display_seat_id = NULL;
         char       *display_id = NULL;
-#if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER)
-        g_autofree char *display_session_type = NULL;
-        gboolean    greeter_is_wayland;
-#endif
+        g_auto (GStrv) supported_session_types = NULL;
 
         g_object_get (G_OBJECT (display),
                       "id", &display_id,
                       "x11-display-name", &display_name,
                       "is-local", &display_is_local,
                       "remote-hostname", &remote_hostname,
                       "x11-authority-file", &display_auth_file,
                       "seat-id", &display_seat_id,
-#if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER)
-                      "session-type", &display_session_type,
-#endif
+                      "supported-session-types", &supported_session_types,
                       NULL);
         display_device = get_display_device (manager, display);
 
         session = gdm_session_new (GDM_SESSION_VERIFICATION_MODE_LOGIN,
                                    allowed_user,
                                    display_name,
                                    remote_hostname,
                                    display_device,
                                    display_seat_id,
                                    display_auth_file,
                                    display_is_local,
                                    NULL);
+        g_object_set (G_OBJECT (session), "supported-session-types", supported_session_types, NULL);
 
         g_debug ("GdmSession: Created user session for user %d on display %s (seat %s)",
                  (int) allowed_user,
                  display_id,
                  display_seat_id);
 
         g_free (display_name);
         g_free (remote_hostname);
         g_free (display_auth_file);
         g_free (display_seat_id);
 
         g_signal_connect (session,
                           "reauthentication-started",
                           G_CALLBACK (on_session_reauthentication_started),
                           manager);
         g_signal_connect (session,
                           "reauthenticated",
                           G_CALLBACK (on_session_reauthenticated),
                           manager);
         g_signal_connect (session,
                           "client-ready-for-session-to-start",
                           G_CALLBACK (on_session_client_ready_for_session_to_start),
                           manager);
         g_signal_connect (session,
                           "client-connected",
                           G_CALLBACK (on_session_client_connected),
                           manager);
         g_signal_connect (session,
                           "client-disconnected",
                           G_CALLBACK (on_session_client_disconnected),
@@ -2378,65 +2375,60 @@ create_user_session_for_display (GdmManager *manager,
         g_signal_connect (session,
                           "authentication-failed",
                           G_CALLBACK (on_session_authentication_failed),
                           manager);
         g_signal_connect (session,
                           "session-opened",
                           G_CALLBACK (on_user_session_opened),
                           manager);
         g_signal_connect (session,
                           "session-started",
                           G_CALLBACK (on_user_session_started),
                           manager);
         g_signal_connect (session,
                           "session-start-failed",
                           G_CALLBACK (on_session_start_failed),
                           manager);
         g_signal_connect (session,
                           "session-exited",
                           G_CALLBACK (on_user_session_exited),
                           manager);
         g_signal_connect (session,
                           "session-died",
                           G_CALLBACK (on_user_session_died),
                           manager);
         g_object_set_data (G_OBJECT (session), "gdm-display", display);
         g_object_set_data_full (G_OBJECT (display),
                                 "gdm-user-session",
                                 session,
                                 (GDestroyNotify)
                                 clean_user_session);
-
-#if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER)
-        greeter_is_wayland = g_strcmp0 (display_session_type, "wayland") == 0;
-        g_object_set (G_OBJECT (session), "ignore-wayland", !greeter_is_wayland, NULL);
-#endif
 }
 
 static void
 on_display_added (GdmDisplayStore *display_store,
                   const char      *id,
                   GdmManager      *manager)
 {
         GdmDisplay *display;
 
         display = gdm_display_store_lookup (display_store, id);
 
         if (display != NULL) {
                 g_dbus_object_manager_server_export (manager->priv->object_manager,
                                                      gdm_display_get_object_skeleton (display));
 
                 g_signal_connect (display, "notify::status",
                                   G_CALLBACK (on_display_status_changed),
                                   manager);
                 g_signal_emit (manager, signals[DISPLAY_ADDED], 0, id);
         }
 }
 
 GQuark
 gdm_manager_error_quark (void)
 {
         static GQuark ret = 0;
         if (ret == 0) {
                 ret = g_quark_from_static_string ("gdm_manager_error");
         }
 
diff --git a/daemon/gdm-session.c b/daemon/gdm-session.c
index 5c5903a4..7b0ade1e 100644
--- a/daemon/gdm-session.c
+++ b/daemon/gdm-session.c
@@ -105,100 +105,98 @@ struct _GdmSession
         GdmDBusUserVerifier   *user_verifier_interface;
         GHashTable            *user_verifier_extensions;
         GdmDBusGreeter        *greeter_interface;
         GdmDBusRemoteGreeter  *remote_greeter_interface;
         GdmDBusChooser        *chooser_interface;
 
         GList               *pending_worker_connections;
         GList               *outside_connections;
 
         GPid                 session_pid;
 
         /* object lifetime scope */
         char                *session_type;
         char                *display_name;
         char                *display_hostname;
         char                *display_device;
         char                *display_seat_id;
         char                *display_x11_authority_file;
         gboolean             display_is_local;
 
         GdmSessionVerificationMode verification_mode;
 
         uid_t                allowed_user;
 
         char                *fallback_session_name;
 
         GDBusServer         *worker_server;
         GDBusServer         *outside_server;
         GHashTable          *environment;
 
+        GStrv                supported_session_types;
+
         guint32              is_program_session : 1;
         guint32              display_is_initial : 1;
-#ifdef ENABLE_WAYLAND_SUPPORT
-        guint32              ignore_wayland : 1;
-#endif
 };
 
 enum {
         PROP_0,
         PROP_VERIFICATION_MODE,
         PROP_ALLOWED_USER,
         PROP_DISPLAY_NAME,
         PROP_DISPLAY_HOSTNAME,
         PROP_DISPLAY_IS_LOCAL,
         PROP_DISPLAY_IS_INITIAL,
         PROP_SESSION_TYPE,
         PROP_DISPLAY_DEVICE,
         PROP_DISPLAY_SEAT_ID,
         PROP_DISPLAY_X11_AUTHORITY_FILE,
         PROP_USER_X11_AUTHORITY_FILE,
         PROP_CONVERSATION_ENVIRONMENT,
-#ifdef ENABLE_WAYLAND_SUPPORT
-        PROP_IGNORE_WAYLAND,
-#endif
+        PROP_SUPPORTED_SESSION_TYPES,
 };
 
 enum {
         CONVERSATION_STARTED = 0,
         CONVERSATION_STOPPED,
         SETUP_COMPLETE,
         CANCELLED,
         HOSTNAME_SELECTED,
         CLIENT_REJECTED,
         CLIENT_CONNECTED,
         CLIENT_DISCONNECTED,
         CLIENT_READY_FOR_SESSION_TO_START,
         DISCONNECTED,
         AUTHENTICATION_FAILED,
         VERIFICATION_COMPLETE,
         SESSION_OPENED,
+        SESSION_OPENED_FAILED,
         SESSION_STARTED,
         SESSION_START_FAILED,
         SESSION_EXITED,
         SESSION_DIED,
         REAUTHENTICATION_STARTED,
         REAUTHENTICATED,
         LAST_SIGNAL
 };
 
 #ifdef ENABLE_WAYLAND_SUPPORT
 static gboolean gdm_session_is_wayland_session (GdmSession *self);
 #endif
 static void update_session_type (GdmSession *self);
 static void set_session_type (GdmSession *self,
                               const char *session_type);
 static void close_conversation (GdmSessionConversation *conversation);
 
 static guint signals [LAST_SIGNAL] = { 0, };
 
 G_DEFINE_TYPE (GdmSession,
                gdm_session,
                G_TYPE_OBJECT);
 
 static GdmSessionConversation *
 find_conversation_by_name (GdmSession *self,
                            const char *service_name)
 {
         GdmSessionConversation *conversation;
 
         conversation = g_hash_table_lookup (self->conversations, service_name);
@@ -295,128 +293,136 @@ on_authorize_cb (GdmDBusWorker *proxy,
 
         if (worked) {
                 gdm_session_accredit (self, service_name);
         } else {
                 report_and_stop_conversation (self, service_name, error);
         }
 }
 
 static void
 on_establish_credentials_cb (GdmDBusWorker *proxy,
                              GAsyncResult  *res,
                              gpointer       user_data)
 {
         GdmSessionConversation *conversation = user_data;
         GdmSession *self;
         char *service_name;
 
         GError *error = NULL;
         gboolean worked;
 
         worked = gdm_dbus_worker_call_establish_credentials_finish (proxy, res, &error);
 
         if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CLOSED) ||
             g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
                 return;
 
         self = g_object_ref (conversation->session);
         service_name = g_strdup (conversation->service_name);
 
         if (worked) {
-                if (self->user_verifier_interface != NULL) {
-                        gdm_dbus_user_verifier_emit_verification_complete (self->user_verifier_interface,
-                                                                           service_name);
-                        g_signal_emit (self, signals[VERIFICATION_COMPLETE], 0, service_name);
-                }
-
                 switch (self->verification_mode) {
                 case GDM_SESSION_VERIFICATION_MODE_LOGIN:
                 case GDM_SESSION_VERIFICATION_MODE_CHOOSER:
                         gdm_session_open_session (self, service_name);
                         break;
                 case GDM_SESSION_VERIFICATION_MODE_REAUTHENTICATE:
+                        if (self->user_verifier_interface != NULL) {
+                                gdm_dbus_user_verifier_emit_verification_complete (self->user_verifier_interface,
+                                                                                   service_name);
+                                g_signal_emit (self, signals[VERIFICATION_COMPLETE], 0, service_name);
+                        }
+                        break;
                 default:
                         break;
                 }
         } else {
                 report_and_stop_conversation (self, service_name, error);
         }
 
         g_free (service_name);
         g_object_unref (self);
 }
 
+static gboolean
+supports_session_type (GdmSession *self,
+                       const char *session_type)
+{
+        if (session_type == NULL)
+                return TRUE;
+
+        return g_strv_contains ((const char * const *) self->supported_session_types,
+                                session_type);
+}
+
 static char **
 get_system_session_dirs (GdmSession *self)
 {
         GArray *search_array = NULL;
         char **search_dirs;
-        int i;
+        int i, j;
         const gchar * const *system_data_dirs = g_get_system_data_dirs ();
 
         static const char *x_search_dirs[] = {
                 "/etc/X11/sessions/",
                 DMCONFDIR "/Sessions/",
                 DATADIR "/gdm/BuiltInSessions/",
                 DATADIR "/xsessions/",
         };
 
         static const char *wayland_search_dir = DATADIR "/wayland-sessions/";
 
         search_array = g_array_new (TRUE, TRUE, sizeof (char *));
 
-        for (i = 0; system_data_dirs[i]; i++) {
-                gchar *dir = g_build_filename (system_data_dirs[i], "xsessions", NULL);
-                g_array_append_val (search_array, dir);
-        }
+        for (j = 0; self->supported_session_types[j] != NULL; j++) {
+                const char *supported_type = self->supported_session_types[j];
 
-        g_array_append_vals (search_array, x_search_dirs, G_N_ELEMENTS (x_search_dirs));
+                if (g_str_equal (supported_type, "x11")) {
+                        for (i = 0; system_data_dirs[i]; i++) {
+                                gchar *dir = g_build_filename (system_data_dirs[i], "xsessions", NULL);
+                                g_array_append_val (search_array, dir);
+                        }
+
+                        g_array_append_vals (search_array, x_search_dirs, G_N_ELEMENTS (x_search_dirs));
+                }
 
 #ifdef ENABLE_WAYLAND_SUPPORT
-        if (!self->ignore_wayland) {
-#ifdef ENABLE_USER_DISPLAY_SERVER
-                g_array_prepend_val (search_array, wayland_search_dir);
+                if (g_str_equal (supported_type, "wayland")) {
+                        for (i = 0; system_data_dirs[i]; i++) {
+                                gchar *dir = g_build_filename (system_data_dirs[i], "wayland-sessions", NULL);
+                                g_array_append_val (search_array, dir);
+                        }
 
-                for (i = 0; system_data_dirs[i]; i++) {
-                        gchar *dir = g_build_filename (system_data_dirs[i], "wayland-sessions", NULL);
-                        g_array_insert_val (search_array, i, dir);
-                }
-#else
-                for (i = 0; system_data_dirs[i]; i++) {
-                        gchar *dir = g_build_filename (system_data_dirs[i], "wayland-sessions", NULL);
-                        g_array_append_val (search_array, dir);
+                        g_array_append_val (search_array, wayland_search_dir);
                 }
-
-                g_array_append_val (search_array, wayland_search_dir);
 #endif
         }
-#endif
 
         search_dirs = g_strdupv ((char **) search_array->data);
 
         g_array_free (search_array, TRUE);
 
         return search_dirs;
 }
 
 static gboolean
 is_prog_in_path (const char *prog)
 {
         char    *f;
         gboolean ret;
 
         f = g_find_program_in_path (prog);
         ret = (f != NULL);
         g_free (f);
         return ret;
 }
 
 static GKeyFile *
 load_key_file_for_file (GdmSession   *self,
                         const char   *file,
                         char        **full_path)
 {
         GKeyFile   *key_file;
         GError     *error;
         gboolean    res;
         char      **search_dirs;
 
@@ -839,78 +845,78 @@ static void
 on_opened (GdmDBusWorker *worker,
            GAsyncResult  *res,
            gpointer       user_data)
 {
         GdmSessionConversation *conversation = user_data;
         GdmSession *self;
         char *service_name;
 
         GError *error = NULL;
         gboolean worked;
         char *session_id;
 
         worked = gdm_dbus_worker_call_open_finish (worker,
                                                    &session_id,
                                                    res,
                                                    &error);
 
         if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CLOSED) ||
             g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
                 return;
 
         self = conversation->session;
         service_name = conversation->service_name;
 
         if (worked) {
                 g_clear_pointer (&conversation->session_id,
                                  (GDestroyNotify) g_free);
 
                 conversation->session_id = g_strdup (session_id);
 
-                if (self->greeter_interface != NULL) {
-                        gdm_dbus_greeter_emit_session_opened (self->greeter_interface,
-                                                              service_name);
-                }
-
                 if (self->user_verifier_interface != NULL) {
                         gdm_dbus_user_verifier_emit_verification_complete (self->user_verifier_interface,
                                                                            service_name);
                         g_signal_emit (self, signals[VERIFICATION_COMPLETE], 0, service_name);
                 }
 
+                if (self->greeter_interface != NULL) {
+                        gdm_dbus_greeter_emit_session_opened (self->greeter_interface,
+                                                              service_name);
+                }
+
                 g_debug ("GdmSession: Emitting 'session-opened' signal");
                 g_signal_emit (self, signals[SESSION_OPENED], 0, service_name, session_id);
         } else {
                 report_and_stop_conversation (self, service_name, error);
 
                 g_debug ("GdmSession: Emitting 'session-start-failed' signal");
-                g_signal_emit (self, signals[SESSION_START_FAILED], 0, service_name, error->message);
+                g_signal_emit (self, signals[SESSION_OPENED_FAILED], 0, service_name, error->message);
         }
 }
 
 static void
 worker_on_username_changed (GdmDBusWorker          *worker,
                             const char             *username,
                             GdmSessionConversation *conversation)
 {
         GdmSession *self = conversation->session;
 
         g_debug ("GdmSession: changing username from '%s' to '%s'",
                  self->selected_user != NULL ? self->selected_user : "<unset>",
                  (strlen (username)) ? username : "<unset>");
 
         gdm_session_select_user (self, (strlen (username) > 0) ? g_strdup (username) : NULL);
         gdm_session_defaults_changed (self);
 }
 
 static void
 worker_on_session_exited (GdmDBusWorker          *worker,
                           const char             *service_name,
                           int                     status,
                           GdmSessionConversation *conversation)
 {
         GdmSession *self = conversation->session;
 
         self->session_conversation = NULL;
 
         if (WIFEXITED (status)) {
                 g_debug ("GdmSession: Emitting 'session-exited' signal with exit code '%d'",
@@ -968,73 +974,76 @@ worker_on_reauthenticated (GdmDBusWorker          *worker,
 static void
 worker_on_saved_language_name_read (GdmDBusWorker          *worker,
                                     const char             *language_name,
                                     GdmSessionConversation *conversation)
 {
         GdmSession *self = conversation->session;
 
         if (strlen (language_name) > 0) {
                 g_free (self->saved_language);
                 self->saved_language = g_strdup (language_name);
 
                 if (self->greeter_interface != NULL) {
                         gdm_dbus_greeter_emit_default_language_name_changed (self->greeter_interface,
                                                                              language_name);
                 }
         }
 }
 
 static void
 worker_on_saved_session_name_read (GdmDBusWorker          *worker,
                                    const char             *session_name,
                                    GdmSessionConversation *conversation)
 {
         GdmSession *self = conversation->session;
 
         if (! get_session_command_for_name (self, session_name, NULL)) {
                 /* ignore sessions that don't exist */
                 g_debug ("GdmSession: not using invalid .dmrc session: %s", session_name);
                 g_free (self->saved_session);
                 self->saved_session = NULL;
-        } else if (strcmp (session_name,
-                   get_default_session_name (self)) != 0) {
-                g_free (self->saved_session);
-                self->saved_session = g_strdup (session_name);
-
-                if (self->greeter_interface != NULL) {
-                        gdm_dbus_greeter_emit_default_session_name_changed (self->greeter_interface,
-                                                                            session_name);
+                update_session_type (self);
+        } else {
+                if (strcmp (session_name,
+                            get_default_session_name (self)) != 0) {
+                        g_free (self->saved_session);
+                        self->saved_session = g_strdup (session_name);
+
+                        if (self->greeter_interface != NULL) {
+                                gdm_dbus_greeter_emit_default_session_name_changed (self->greeter_interface,
+                                                                                    session_name);
+                        }
                 }
+                if (self->saved_session_type != NULL)
+                        set_session_type (self, self->saved_session_type);
         }
 
-        update_session_type (self);
-
 }
 
 static GdmSessionConversation *
 find_conversation_by_pid (GdmSession *self,
                           GPid        pid)
 {
         GHashTableIter iter;
         gpointer key, value;
 
         g_hash_table_iter_init (&iter, self->conversations);
         while (g_hash_table_iter_next (&iter, &key, &value)) {
                 GdmSessionConversation *conversation;
 
                 conversation = (GdmSessionConversation *) value;
 
                 if (conversation->worker_pid == pid) {
                         return conversation;
                 }
         }
 
         return NULL;
 }
 
 static gboolean
 allow_worker_function (GDBusAuthObserver *observer,
                        GIOStream         *stream,
                        GCredentials      *credentials,
                        GdmSession        *self)
 {
         uid_t connecting_user;
@@ -2197,68 +2206,72 @@ close_conversation (GdmSessionConversation *conversation)
 
         if (conversation->worker_manager_interface != NULL) {
                 unexport_worker_manager_interface (self, conversation->worker_manager_interface);
                 g_clear_object (&conversation->worker_manager_interface);
         }
 
         if (conversation->worker_proxy != NULL) {
                 GDBusConnection *connection = g_dbus_proxy_get_connection (G_DBUS_PROXY (conversation->worker_proxy));
                 g_dbus_connection_close_sync (connection, NULL, NULL);
         }
 }
 
 static void
 stop_conversation (GdmSessionConversation *conversation)
 {
         close_conversation (conversation);
 
         conversation->is_stopping = TRUE;
         gdm_session_worker_job_stop (conversation->job);
 }
 
 static void
 stop_conversation_now (GdmSessionConversation *conversation)
 {
         close_conversation (conversation);
 
         gdm_session_worker_job_stop_now (conversation->job);
         g_clear_object (&conversation->job);
 }
 
-#ifdef ENABLE_WAYLAND_SUPPORT
 void
-gdm_session_set_ignore_wayland (GdmSession *self,
-                                gboolean    ignore_wayland)
+gdm_session_set_supported_session_types (GdmSession         *self,
+                                         const char * const *supported_session_types)
 {
-        self->ignore_wayland = ignore_wayland;
+        const char * const session_types[] = { "wayland", "x11", NULL };
+        g_strfreev (self->supported_session_types);
+
+        if (supported_session_types == NULL)
+                self->supported_session_types = g_strdupv ((GStrv) session_types);
+        else
+                self->supported_session_types = g_strdupv ((GStrv) supported_session_types);
 }
-#endif
 
 gboolean
 gdm_session_start_conversation (GdmSession *self,
                                 const char *service_name)
 {
         GdmSessionConversation *conversation;
 
         g_return_val_if_fail (GDM_IS_SESSION (self), FALSE);
 
         conversation = g_hash_table_lookup (self->conversations,
                                             service_name);
 
         if (conversation != NULL) {
                 if (!conversation->is_stopping) {
                         g_warning ("GdmSession: conversation %s started more than once", service_name);
                         return FALSE;
                 }
                 g_debug ("GdmSession: stopping old conversation %s", service_name);
                 gdm_session_worker_job_stop_now (conversation->job);
                 g_object_unref (conversation->job);
                 conversation->job = NULL;
         }
 
         g_debug ("GdmSession: starting conversation %s for session (%p)", service_name, self);
 
         conversation = start_conversation (self, service_name);
 
         g_hash_table_insert (self->conversations,
                              g_strdup (service_name), conversation);
         return TRUE;
@@ -3137,64 +3150,66 @@ gdm_session_get_conversation_session_id (GdmSession *self,
 
         conversation = find_conversation_by_name (self, service_name);
 
         if (conversation == NULL) {
                 return NULL;
         }
 
         return conversation->session_id;
 }
 
 static char *
 get_session_filename (GdmSession *self)
 {
         return g_strdup_printf ("%s.desktop", get_session_name (self));
 }
 
 #ifdef ENABLE_WAYLAND_SUPPORT
 static gboolean
 gdm_session_is_wayland_session (GdmSession *self)
 {
         GKeyFile   *key_file;
         gboolean    is_wayland_session = FALSE;
         char       *filename;
         char       *full_path = NULL;
 
         g_return_val_if_fail (self != NULL, FALSE);
         g_return_val_if_fail (GDM_IS_SESSION (self), FALSE);
 
         filename = get_session_filename (self);
 
-        key_file = load_key_file_for_file (self, filename, &full_path);
+        if (supports_session_type (self, "wayland")) {
+            key_file = load_key_file_for_file (self, filename, &full_path);
 
-        if (key_file == NULL) {
-                goto out;
+            if (key_file == NULL) {
+                    goto out;
+            }
         }
 
         if (full_path != NULL && strstr (full_path, "/wayland-sessions/") != NULL) {
                 is_wayland_session = TRUE;
         }
         g_debug ("GdmSession: checking if file '%s' is wayland session: %s", filename, is_wayland_session? "yes" : "no");
 
 out:
         g_clear_pointer (&key_file, g_key_file_free);
         g_free (filename);
         return is_wayland_session;
 }
 #endif
 
 static void
 update_session_type (GdmSession *self)
 {
 #ifdef ENABLE_WAYLAND_SUPPORT
         gboolean is_wayland_session;
 
         is_wayland_session = gdm_session_is_wayland_session (self);
         if (is_wayland_session) {
                 set_session_type (self, "wayland");
         } else {
                 set_session_type (self, NULL);
         }
 #endif
 }
 
 gboolean
@@ -3424,195 +3439,192 @@ set_display_is_initial (GdmSession *self,
 {
         self->display_is_initial = is_initial;
 }
 
 static void
 set_verification_mode (GdmSession                 *self,
                        GdmSessionVerificationMode  verification_mode)
 {
         self->verification_mode = verification_mode;
 }
 
 static void
 set_allowed_user (GdmSession *self,
                   uid_t       allowed_user)
 {
         self->allowed_user = allowed_user;
 }
 
 static void
 set_conversation_environment (GdmSession  *self,
                               char       **environment)
 {
         g_strfreev (self->conversation_environment);
         self->conversation_environment = g_strdupv (environment);
 }
 
 static void
 set_session_type (GdmSession *self,
                   const char *session_type)
 {
-
+        g_debug ("GdmSession: setting session to type '%s'", session_type? session_type : "");
         if (g_strcmp0 (self->session_type, session_type) != 0) {
-                g_debug ("GdmSession: setting session to type '%s'", session_type? session_type : "");
                 g_free (self->session_type);
                 self->session_type = g_strdup (session_type);
         }
 }
 
 static void
 gdm_session_set_property (GObject      *object,
                           guint         prop_id,
                           const GValue *value,
                           GParamSpec   *pspec)
 {
         GdmSession *self;
 
         self = GDM_SESSION (object);
 
         switch (prop_id) {
         case PROP_SESSION_TYPE:
                 set_session_type (self, g_value_get_string (value));
                 break;
         case PROP_DISPLAY_NAME:
                 set_display_name (self, g_value_get_string (value));
                 break;
         case PROP_DISPLAY_HOSTNAME:
                 set_display_hostname (self, g_value_get_string (value));
                 break;
         case PROP_DISPLAY_DEVICE:
                 set_display_device (self, g_value_get_string (value));
                 break;
         case PROP_DISPLAY_SEAT_ID:
                 set_display_seat_id (self, g_value_get_string (value));
                 break;
         case PROP_USER_X11_AUTHORITY_FILE:
                 set_user_x11_authority_file (self, g_value_get_string (value));
                 break;
         case PROP_DISPLAY_X11_AUTHORITY_FILE:
                 set_display_x11_authority_file (self, g_value_get_string (value));
                 break;
         case PROP_DISPLAY_IS_LOCAL:
                 set_display_is_local (self, g_value_get_boolean (value));
                 break;
         case PROP_DISPLAY_IS_INITIAL:
                 set_display_is_initial (self, g_value_get_boolean (value));
                 break;
         case PROP_VERIFICATION_MODE:
                 set_verification_mode (self, g_value_get_enum (value));
                 break;
         case PROP_ALLOWED_USER:
                 set_allowed_user (self, g_value_get_uint (value));
                 break;
         case PROP_CONVERSATION_ENVIRONMENT:
                 set_conversation_environment (self, g_value_get_pointer (value));
                 break;
-#ifdef ENABLE_WAYLAND_SUPPORT
-        case PROP_IGNORE_WAYLAND:
-                gdm_session_set_ignore_wayland (self, g_value_get_boolean (value));
+        case PROP_SUPPORTED_SESSION_TYPES:
+                gdm_session_set_supported_session_types (self, g_value_get_boxed (value));
                 break;
-#endif
         default:
                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                 break;
         }
 }
 
 static void
 gdm_session_get_property (GObject    *object,
                           guint       prop_id,
                           GValue     *value,
                           GParamSpec *pspec)
 {
         GdmSession *self;
 
         self = GDM_SESSION (object);
 
         switch (prop_id) {
         case PROP_SESSION_TYPE:
                 g_value_set_string (value, self->session_type);
                 break;
         case PROP_DISPLAY_NAME:
                 g_value_set_string (value, self->display_name);
                 break;
         case PROP_DISPLAY_HOSTNAME:
                 g_value_set_string (value, self->display_hostname);
                 break;
         case PROP_DISPLAY_DEVICE:
                 g_value_set_string (value, self->display_device);
                 break;
         case PROP_DISPLAY_SEAT_ID:
                 g_value_set_string (value, self->display_seat_id);
                 break;
         case PROP_USER_X11_AUTHORITY_FILE:
                 g_value_set_string (value, self->user_x11_authority_file);
                 break;
         case PROP_DISPLAY_X11_AUTHORITY_FILE:
                 g_value_set_string (value, self->display_x11_authority_file);
                 break;
         case PROP_DISPLAY_IS_LOCAL:
                 g_value_set_boolean (value, self->display_is_local);
                 break;
         case PROP_DISPLAY_IS_INITIAL:
                 g_value_set_boolean (value, self->display_is_initial);
                 break;
         case PROP_VERIFICATION_MODE:
                 g_value_set_enum (value, self->verification_mode);
                 break;
         case PROP_ALLOWED_USER:
                 g_value_set_uint (value, self->allowed_user);
                 break;
         case PROP_CONVERSATION_ENVIRONMENT:
                 g_value_set_pointer (value, self->environment);
                 break;
-#ifdef ENABLE_WAYLAND_SUPPORT
-        case PROP_IGNORE_WAYLAND:
-                g_value_set_boolean (value, self->ignore_wayland);
+        case PROP_SUPPORTED_SESSION_TYPES:
+                g_value_set_boxed (value, self->supported_session_types);
                 break;
-#endif
         default:
                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                 break;
         }
 }
 
 static void
 gdm_session_dispose (GObject *object)
 {
         GdmSession *self;
 
         self = GDM_SESSION (object);
 
         g_debug ("GdmSession: Disposing session");
 
         gdm_session_close (self);
 
+        g_clear_pointer (&self->supported_session_types,
+                         g_strfreev);
         g_clear_pointer (&self->conversations,
                          g_hash_table_unref);
 
         g_clear_object (&self->user_verifier_interface);
         g_clear_pointer (&self->user_verifier_extensions,
                          g_hash_table_unref);
         g_clear_object (&self->greeter_interface);
         g_clear_object (&self->remote_greeter_interface);
         g_clear_object (&self->chooser_interface);
 
         g_free (self->display_name);
         self->display_name = NULL;
 
         g_free (self->display_hostname);
         self->display_hostname = NULL;
 
         g_free (self->display_device);
         self->display_device = NULL;
 
         g_free (self->display_seat_id);
         self->display_seat_id = NULL;
 
         g_free (self->display_x11_authority_file);
         self->display_x11_authority_file = NULL;
 
         g_strfreev (self->conversation_environment);
         self->conversation_environment = NULL;
 
         if (self->worker_server != NULL) {
                 g_dbus_server_stop (self->worker_server);
@@ -3719,60 +3731,71 @@ gdm_session_class_init (GdmSessionClass *session_class)
                               NULL,
                               NULL,
                               NULL,
                               G_TYPE_NONE,
                               2,
                               G_TYPE_STRING,
                               G_TYPE_INT);
         signals [VERIFICATION_COMPLETE] =
                 g_signal_new ("verification-complete",
                               GDM_TYPE_SESSION,
                               G_SIGNAL_RUN_FIRST,
                               0,
                               NULL,
                               NULL,
                               NULL,
                               G_TYPE_NONE,
                               1,
                               G_TYPE_STRING);
         signals [SESSION_OPENED] =
                 g_signal_new ("session-opened",
                               GDM_TYPE_SESSION,
                               G_SIGNAL_RUN_FIRST,
                               0,
                               NULL,
                               NULL,
                               NULL,
                               G_TYPE_NONE,
                               2,
                               G_TYPE_STRING,
                               G_TYPE_STRING);
+        signals [SESSION_OPENED_FAILED] =
+                g_signal_new ("session-opened-failed",
+                              GDM_TYPE_SESSION,
+                              G_SIGNAL_RUN_FIRST,
+                              0,
+                              NULL,
+                              NULL,
+                              g_cclosure_marshal_generic,
+                              G_TYPE_NONE,
+                              2,
+                              G_TYPE_STRING, G_TYPE_STRING);
         signals [SESSION_STARTED] =
                 g_signal_new ("session-started",
                               GDM_TYPE_SESSION,
                               G_SIGNAL_RUN_FIRST,
                               0,
                               NULL,
                               NULL,
                               g_cclosure_marshal_generic,
                               G_TYPE_NONE,
                               2,
                               G_TYPE_STRING,
                               G_TYPE_INT);
         signals [SESSION_START_FAILED] =
                 g_signal_new ("session-start-failed",
                               GDM_TYPE_SESSION,
                               G_SIGNAL_RUN_FIRST,
                               0,
                               NULL,
                               NULL,
                               g_cclosure_marshal_generic,
                               G_TYPE_NONE,
                               2,
                               G_TYPE_STRING, G_TYPE_STRING);
         signals [SESSION_EXITED] =
                 g_signal_new ("session-exited",
                               GDM_TYPE_SESSION,
                               G_SIGNAL_RUN_FIRST,
                               0,
                               NULL,
                               NULL,
@@ -3962,69 +3985,67 @@ gdm_session_class_init (GdmSessionClass *session_class)
                                          PROP_DISPLAY_X11_AUTHORITY_FILE,
                                          g_param_spec_string ("display-x11-authority-file",
                                                               "display x11 authority file",
                                                               "display x11 authority file",
                                                               NULL,
                                                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
         /* not construct only */
         g_object_class_install_property (object_class,
                                          PROP_USER_X11_AUTHORITY_FILE,
                                          g_param_spec_string ("user-x11-authority-file",
                                                               "",
                                                               "",
                                                               NULL,
                                                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
         g_object_class_install_property (object_class,
                                          PROP_DISPLAY_DEVICE,
                                          g_param_spec_string ("display-device",
                                                               "display device",
                                                               "display device",
                                                               NULL,
                                                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
 
         g_object_class_install_property (object_class,
                                          PROP_DISPLAY_SEAT_ID,
                                          g_param_spec_string ("display-seat-id",
                                                               "display seat id",
                                                               "display seat id",
                                                               NULL,
                                                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
 
-#ifdef ENABLE_WAYLAND_SUPPORT
         g_object_class_install_property (object_class,
-                                         PROP_IGNORE_WAYLAND,
-                                         g_param_spec_boolean ("ignore-wayland",
-                                                               "ignore wayland",
-                                                               "ignore wayland",
-                                                               FALSE,
-                                                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
-#endif
+                                         PROP_SUPPORTED_SESSION_TYPES,
+                                         g_param_spec_boxed ("supported-session-types",
+                                                             "supported session types",
+                                                             "supported session types",
+                                                             G_TYPE_STRV,
+                                                             G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
 
         /* Ensure we can resolve errors */
         gdm_dbus_error_ensure (GDM_SESSION_WORKER_ERROR);
 }
 
 GdmSession *
 gdm_session_new (GdmSessionVerificationMode  verification_mode,
                  uid_t                       allowed_user,
                  const char                 *display_name,
                  const char                 *display_hostname,
                  const char                 *display_device,
                  const char                 *display_seat_id,
                  const char                 *display_x11_authority_file,
                  gboolean                    display_is_local,
                  const char * const         *environment)
 {
         GdmSession *self;
 
         self = g_object_new (GDM_TYPE_SESSION,
                              "verification-mode", verification_mode,
                              "allowed-user", (guint) allowed_user,
                              "display-name", display_name,
                              "display-hostname", display_hostname,
                              "display-device", display_device,
                              "display-seat-id", display_seat_id,
                              "display-x11-authority-file", display_x11_authority_file,
                              "display-is-local", display_is_local,
                              "conversation-environment", environment,
                              NULL);
 
diff --git a/data/gdm.schemas.in b/data/gdm.schemas.in
index 255bff02..a1035f95 100644
--- a/data/gdm.schemas.in
+++ b/data/gdm.schemas.in
@@ -25,65 +25,75 @@
     <schema>
       <key>daemon/AutomaticLoginEnable</key>
       <signature>b</signature>
       <default>false</default>
     </schema>
     <schema>
       <key>daemon/AutomaticLogin</key>
       <signature>s</signature>
       <default></default>
     </schema>
     <schema>
       <key>daemon/TimedLoginEnable</key>
       <signature>b</signature>
       <default>false</default>
     </schema>
     <schema>
       <key>daemon/TimedLogin</key>
       <signature>s</signature>
       <default></default>
     </schema>
     <schema>
       <key>daemon/TimedLoginDelay</key>
       <signature>i</signature>
       <default>30</default>
     </schema>
     <schema>
       <key>daemon/InitialSetupEnable</key>
       <signature>b</signature>
       <default>true</default>
     </schema>
+    <schema>
+      <key>daemon/PreferredDisplayServer</key>
+      <signature>s</signature>
+      <default>wayland</default>
+    </schema>
     <schema>
       <key>daemon/WaylandEnable</key>
       <signature>b</signature>
       <default>true</default>
     </schema>
+    <schema>
+      <key>daemon/XorgEnable</key>
+      <signature>b</signature>
+      <default>true</default>
+    </schema>
     <schema>
       <key>security/AllowRemoteAutoLogin</key>
       <signature>b</signature>
       <default>false</default>
     </schema>
 
     <schema>
       <key>debug/Enable</key>
       <signature>b</signature>
       <default>false</default>
     </schema>
 
     <schema>
       <key>security/DisallowTCP</key>
       <signature>b</signature>
       <default>true</default>
     </schema>
     <schema>
       <key>xdmcp/Enable</key>
       <signature>b</signature>
       <default>false</default>
     </schema>
     <schema>
       <key>xdmcp/ShowLocalGreeter</key>
       <signature>b</signature>
       <default>true</default>
     </schema>
     <schema>
       <key>xdmcp/MaxPending</key>
       <signature>i</signature>
diff --git a/libgdm/gdm-sessions.c b/libgdm/gdm-sessions.c
index 75d442ee..97ed5ef3 100644
--- a/libgdm/gdm-sessions.c
+++ b/libgdm/gdm-sessions.c
@@ -163,171 +163,203 @@ load_session_file (const char              *id,
 
 static gboolean
 remove_duplicate_sessions (gpointer key,
                            gpointer value,
                            gpointer user_data)
 {
         gboolean already_known;
         GHashTable *names_seen_before;
         GdmSessionFile *session;
 
         names_seen_before = (GHashTable *) user_data;
         session = (GdmSessionFile *) value;
         already_known = !g_hash_table_add (names_seen_before, session->translated_name);
 
         if (already_known)
                 g_debug ("GdmSession: Removing %s (%s) as we already have a session by this name",
                          session->id,
                          session->path);
 
         return already_known;
 }
 
 static void
 collect_sessions_from_directory (const char *dirname)
 {
         GDir       *dir;
         const char *filename;
 
         gboolean is_x11 = g_getenv ("WAYLAND_DISPLAY") == NULL &&
                           g_getenv ("RUNNING_UNDER_GDM") != NULL;
+        gboolean is_wayland = g_getenv ("WAYLAND_DISPLAY") != NULL &&
+                              g_getenv ("RUNNING_UNDER_GDM") != NULL;
 
         /* FIXME: add file monitor to directory */
 
         dir = g_dir_open (dirname, 0, NULL);
         if (dir == NULL) {
                 return;
         }
 
         while ((filename = g_dir_read_name (dir))) {
                 char *id;
                 char *full_path;
 
                 if (! g_str_has_suffix (filename, ".desktop")) {
                         continue;
                 }
 
-                if (is_x11 && g_str_has_suffix (filename, "-xorg.desktop")) {
-                        char *base_name = g_strndup (filename, strlen (filename) - strlen ("-xorg.desktop"));
-                        char *fallback_name = g_strconcat (base_name, ".desktop", NULL);
-                        g_free (base_name);
-                        char *fallback_path = g_build_filename (dirname, fallback_name, NULL);
-                        g_free (fallback_name);
-                        if (g_file_test (fallback_path, G_FILE_TEST_EXISTS)) {
-                                g_free (fallback_path);
-                                g_debug ("Running under X11, ignoring %s", filename);
-                                continue;
+                if (is_wayland) {
+                        if (g_str_has_suffix (filename, "-wayland.desktop")) {
+                                g_autofree char *base_name = g_strndup (filename, strlen (filename) - strlen ("-wayland.desktop"));
+                                g_autofree char *other_name = g_strconcat (base_name, ".desktop", NULL);
+                                g_autofree char *other_path = g_build_filename (dirname, other_name, NULL);
+
+                                if (g_file_test (other_path, G_FILE_TEST_EXISTS)) {
+                                        g_debug ("Running under Wayland, ignoring %s", filename);
+                                        continue;
+                                }
+                        } else {
+                                g_autofree char *base_name = g_strndup (filename, strlen (filename) - strlen (".desktop"));
+                                g_autofree char *other_name = g_strdup_printf ("%s-xorg.desktop", base_name);
+                                g_autofree char *other_path = g_build_filename (dirname, other_name, NULL);
+
+                                if (g_file_test (other_path, G_FILE_TEST_EXISTS)) {
+                                        g_debug ("Running under Wayland, ignoring %s", filename);
+                                        continue;
+                                }
+                        }
+                } else if (is_x11) {
+                        if (g_str_has_suffix (filename, "-xorg.desktop")) {
+                                g_autofree char *base_name = g_strndup (filename, strlen (filename) - strlen ("-xorg.desktop"));
+                                g_autofree char *other_name = g_strconcat (base_name, ".desktop", NULL);
+                                g_autofree char *other_path = g_build_filename (dirname, other_name, NULL);
+
+                                if (g_file_test (other_path, G_FILE_TEST_EXISTS)) {
+                                        g_debug ("Running under X11, ignoring %s", filename);
+                                        continue;
+                                }
+                        } else {
+                                g_autofree char *base_name = g_strndup (filename, strlen (filename) - strlen (".desktop"));
+                                g_autofree char *other_name = g_strdup_printf ("%s-wayland.desktop", base_name);
+                                g_autofree char *other_path = g_build_filename (dirname, other_name, NULL);
+
+                                if (g_file_test (other_path, G_FILE_TEST_EXISTS)) {
+                                        g_debug ("Running under X11, ignoring %s", filename);
+                                        continue;
+                                }
                         }
-                        g_free (fallback_path);
                 }
 
                 id = g_strndup (filename, strlen (filename) - strlen (".desktop"));
 
                 full_path = g_build_filename (dirname, filename, NULL);
 
                 load_session_file (id, full_path);
 
                 g_free (id);
                 g_free (full_path);
         }
 
         g_dir_close (dir);
 }
 
 static void
 collect_sessions (void)
 {
         g_autoptr(GHashTable) names_seen_before = NULL;
         g_autoptr(GPtrArray) xorg_search_array = NULL;
         g_autoptr(GPtrArray) wayland_search_array = NULL;
         gchar      *session_dir = NULL;
         int         i;
         const char *xorg_search_dirs[] = {
                 "/etc/X11/sessions/",
                 DMCONFDIR "/Sessions/",
                 DATADIR "/gdm/BuiltInSessions/",
                 DATADIR "/xsessions/",
         };
+        g_auto (GStrv) supported_session_types = NULL;
+
+        supported_session_types = g_strsplit (g_getenv ("GDM_SUPPORTED_SESSION_TYPES"), ":", -1);
 
         names_seen_before = g_hash_table_new (g_str_hash, g_str_equal);
         xorg_search_array = g_ptr_array_new_with_free_func (g_free);
 
         const gchar * const *system_data_dirs = g_get_system_data_dirs ();
 
         for (i = 0; system_data_dirs[i]; i++) {
                 session_dir = g_build_filename (system_data_dirs[i], "xsessions", NULL);
                 g_ptr_array_add (xorg_search_array, session_dir);
         }
 
         for (i = 0; i < G_N_ELEMENTS (xorg_search_dirs); i++) {
                 g_ptr_array_add (xorg_search_array, g_strdup (xorg_search_dirs[i]));
         }
 
 #ifdef ENABLE_WAYLAND_SUPPORT
         const char *wayland_search_dirs[] = {
                 DATADIR "/wayland-sessions/",
         };
 
         wayland_search_array = g_ptr_array_new_with_free_func (g_free);
 
         for (i = 0; system_data_dirs[i]; i++) {
                 session_dir = g_build_filename (system_data_dirs[i], "wayland-sessions", NULL);
                 g_ptr_array_add (wayland_search_array, session_dir);
         }
 
         for (i = 0; i < G_N_ELEMENTS (wayland_search_dirs); i++) {
                 g_ptr_array_add (wayland_search_array, g_strdup (wayland_search_dirs[i]));
         }
 #endif
 
         if (gdm_available_sessions_map == NULL) {
                 gdm_available_sessions_map = g_hash_table_new_full (g_str_hash, g_str_equal,
                                                                     g_free, (GDestroyNotify)gdm_session_file_free);
         }
 
-        for (i = 0; i < xorg_search_array->len; i++) {
-                collect_sessions_from_directory (g_ptr_array_index (xorg_search_array, i));
+        if (!supported_session_types || g_strv_contains ((const char * const *) supported_session_types, "x11")) {
+                for (i = 0; i < xorg_search_array->len; i++) {
+                        collect_sessions_from_directory (g_ptr_array_index (xorg_search_array, i));
+                }
         }
 
 #ifdef ENABLE_WAYLAND_SUPPORT
 #ifdef ENABLE_USER_DISPLAY_SERVER
-        if (g_getenv ("WAYLAND_DISPLAY") == NULL && g_getenv ("RUNNING_UNDER_GDM") != NULL) {
-                goto out;
+        if (!supported_session_types  || g_strv_contains ((const char * const *) supported_session_types, "wayland")) {
+                for (i = 0; i < wayland_search_array->len; i++) {
+                        collect_sessions_from_directory (g_ptr_array_index (wayland_search_array, i));
+                }
         }
 #endif
-
-        for (i = 0; i < wayland_search_array->len; i++) {
-                collect_sessions_from_directory (g_ptr_array_index (wayland_search_array, i));
-        }
 #endif
 
-out:
         g_hash_table_foreach_remove (gdm_available_sessions_map,
                                      remove_duplicate_sessions,
                                      names_seen_before);
 }
 
 /**
  * gdm_get_session_ids:
  *
  * Reads /usr/share/xsessions and other relevant places for possible sessions
  * to log into and returns the complete list.
  *
  * Returns: (transfer full): a %NULL terminated list of session ids
  */
 char **
 gdm_get_session_ids (void)
 {
         GHashTableIter iter;
         gpointer key, value;
         GPtrArray *array;
 
         if (!gdm_sessions_map_is_initialized) {
                 collect_sessions ();
 
                 gdm_sessions_map_is_initialized = TRUE;
         }
 
         array = g_ptr_array_new ();
         g_hash_table_iter_init (&iter, gdm_available_sessions_map);
         while (g_hash_table_iter_next (&iter, &key, &value)) {
                 GdmSessionFile *session;
-- 
2.34.1