Index: gdm-2.24.0/gui/simple-greeter/Makefile.am
===================================================================
--- gdm-2.24.0.orig/gui/simple-greeter/Makefile.am
+++ gdm-2.24.0/gui/simple-greeter/Makefile.am
@@ -136,6 +136,14 @@ test_greeter_panel_SOURCES = 	\
 	gdm-sessions.c			\
 	gdm-session-option-widget.h	\
 	gdm-session-option-widget.c	\
+	gdm-domain-chooser-dialog.h	\
+	gdm-domain-chooser-dialog.c	\
+	gdm-domain-chooser-widget.h	\
+	gdm-domain-chooser-widget.c	\
+	gdm-domain-option-widget.h	\
+	gdm-domain-option-widget.c	\
+	gdm-domain-provider.h		\
+	gdm-domain-provider.c		\
 	$(NULL)
 
 test_greeter_panel_LDADD =	\
@@ -313,12 +321,20 @@ gdm_simple_greeter_SOURCES =  		\
 	gdm-language-chooser-dialog.c	\
 	gdm-language-option-widget.h	\
 	gdm-language-option-widget.c	\
+	gdm-domain-chooser-dialog.h	\
+	gdm-domain-chooser-dialog.c	\
+	gdm-domain-chooser-widget.h	\
+	gdm-domain-chooser-widget.c	\
+	gdm-domain-option-widget.h	\
+	gdm-domain-option-widget.c	\
 	gdm-sessions.h			\
 	gdm-sessions.c			\
 	gdm-session-option-widget.h	\
 	gdm-session-option-widget.c	\
 	gdm-user-chooser-widget.h	\
 	gdm-user-chooser-widget.c	\
+	gdm-domain-provider.h		\
+	gdm-domain-provider.c		\
 	$(NULL)
 
 gdm_simple_greeter_LDADD = 		\
Index: gdm-2.24.0/gui/simple-greeter/gdm-chooser-widget.c
===================================================================
--- gdm-2.24.0.orig/gui/simple-greeter/gdm-chooser-widget.c
+++ gdm-2.24.0/gui/simple-greeter/gdm-chooser-widget.c
@@ -2105,6 +2105,20 @@ gdm_chooser_widget_remove_item (GdmChoos
         move_cursor_to_top (widget);
 }
 
+void
+gdm_chooser_widget_remove_all_items (GdmChooserWidget *widget)
+{
+        widget->priv->number_of_rows_with_images = 0;
+        widget->priv->number_of_rows_with_status = 0;
+        widget->priv->number_of_separated_rows   = 0;
+        widget->priv->number_of_normal_rows      = 0;
+
+        gtk_list_store_clear (widget->priv->list_store);
+
+        update_separator_visibility (widget);
+        move_cursor_to_top (widget);
+}
+
 gboolean
 gdm_chooser_widget_lookup_item (GdmChooserWidget *widget,
                                 const char       *id,
Index: gdm-2.24.0/gui/simple-greeter/gdm-chooser-widget.h
===================================================================
--- gdm-2.24.0.orig/gui/simple-greeter/gdm-chooser-widget.h
+++ gdm-2.24.0/gui/simple-greeter/gdm-chooser-widget.h
@@ -96,6 +96,8 @@ void         gdm_chooser_widget_update_i
 void          gdm_chooser_widget_remove_item                  (GdmChooserWidget *widget,
                                                                const char       *id);
 
+void          gdm_chooser_widget_remove_all_items             (GdmChooserWidget *widget);
+
 gboolean      gdm_chooser_widget_lookup_item                  (GdmChooserWidget           *widget,
                                                                const char                 *id,
                                                                GdkPixbuf                 **image,
Index: gdm-2.24.0/gui/simple-greeter/gdm-domain-chooser-dialog.c
===================================================================
--- /dev/null
+++ gdm-2.24.0/gui/simple-greeter/gdm-domain-chooser-dialog.c
@@ -0,0 +1,207 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Hans Petter Jansson
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written by: Hans Petter Jansson <hpj@novell.com>
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <locale.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "gdm-domain-chooser-widget.h"
+#include "gdm-domain-chooser-dialog.h"
+
+#define GDM_DOMAIN_CHOOSER_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_DOMAIN_CHOOSER_DIALOG, GdmDomainChooserDialogPrivate))
+
+struct GdmDomainChooserDialogPrivate
+{
+        GtkWidget *chooser_widget;
+};
+
+
+static void     gdm_domain_chooser_dialog_class_init  (GdmDomainChooserDialogClass *klass);
+static void     gdm_domain_chooser_dialog_init        (GdmDomainChooserDialog      *domain_chooser_dialog);
+static void     gdm_domain_chooser_dialog_finalize    (GObject                     *object);
+
+G_DEFINE_TYPE (GdmDomainChooserDialog, gdm_domain_chooser_dialog, GTK_TYPE_DIALOG)
+
+char *
+gdm_domain_chooser_dialog_get_current_domain_name (GdmDomainChooserDialog *dialog)
+{
+        char *domain_name;
+
+        g_return_val_if_fail (GDM_IS_DOMAIN_CHOOSER_DIALOG (dialog), NULL);
+
+        domain_name = gdm_domain_chooser_widget_get_current_domain_name (GDM_DOMAIN_CHOOSER_WIDGET (dialog->priv->chooser_widget));
+
+        return domain_name;
+}
+
+void
+gdm_domain_chooser_dialog_set_current_domain_name (GdmDomainChooserDialog *dialog,
+                                                   const char             *domain_name)
+{
+        g_return_if_fail (GDM_IS_DOMAIN_CHOOSER_DIALOG (dialog));
+
+        gdm_domain_chooser_widget_set_current_domain_name (GDM_DOMAIN_CHOOSER_WIDGET (dialog->priv->chooser_widget), domain_name);
+}
+
+static void
+gdm_domain_chooser_dialog_size_request (GtkWidget      *widget,
+                                       GtkRequisition *requisition)
+{
+        int            screen_w;
+        int            screen_h;
+        GtkRequisition child_requisition;
+
+        if (GTK_WIDGET_CLASS (gdm_domain_chooser_dialog_parent_class)->size_request) {
+                GTK_WIDGET_CLASS (gdm_domain_chooser_dialog_parent_class)->size_request (widget, requisition);
+        }
+
+        screen_w = gdk_screen_get_width (gtk_widget_get_screen (widget));
+        screen_h = gdk_screen_get_height (gtk_widget_get_screen (widget));
+
+        gtk_widget_get_child_requisition (GTK_BIN (widget)->child, &child_requisition);
+        *requisition = child_requisition;
+
+        requisition->width += 2 * GTK_CONTAINER (widget)->border_width;
+        requisition->height += 2 * GTK_CONTAINER (widget)->border_width;
+
+        requisition->width = MIN (requisition->width, .50 * screen_w);
+        requisition->height = MIN (requisition->height, .80 * screen_h);
+}
+
+static void
+gdm_domain_chooser_dialog_realize (GtkWidget *widget)
+{
+        GdmDomainChooserDialog *chooser_dialog;
+        GdkWindow *root_window;
+        GdkCursor *cursor;
+
+        root_window = gdk_screen_get_root_window (gdk_screen_get_default ());
+        cursor = gdk_cursor_new (GDK_WATCH);
+        gdk_window_set_cursor (root_window, cursor);
+        gdk_cursor_unref (cursor);
+
+        chooser_dialog = GDM_DOMAIN_CHOOSER_DIALOG (widget);
+
+        gtk_widget_show (chooser_dialog->priv->chooser_widget);
+
+        GTK_WIDGET_CLASS (gdm_domain_chooser_dialog_parent_class)->realize (widget);
+
+        cursor = gdk_cursor_new (GDK_LEFT_PTR);
+        gdk_window_set_cursor (root_window, cursor);
+        gdk_cursor_unref (cursor);
+}
+
+static void
+gdm_domain_chooser_dialog_class_init (GdmDomainChooserDialogClass *klass)
+{
+        GObjectClass   *object_class = G_OBJECT_CLASS (klass);
+        GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+        object_class->finalize = gdm_domain_chooser_dialog_finalize;
+        widget_class->size_request = gdm_domain_chooser_dialog_size_request;
+        widget_class->realize = gdm_domain_chooser_dialog_realize;
+
+        g_type_class_add_private (klass, sizeof (GdmDomainChooserDialogPrivate));
+}
+
+static gboolean
+respond (GdmDomainChooserDialog *dialog)
+{
+        gtk_dialog_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+        return FALSE;
+}
+
+static void
+queue_response (GdmDomainChooserDialog *dialog)
+{
+        g_idle_add ((GSourceFunc) respond, dialog);
+}
+
+static void
+gdm_domain_chooser_dialog_init (GdmDomainChooserDialog *dialog)
+{
+
+        dialog->priv = GDM_DOMAIN_CHOOSER_DIALOG_GET_PRIVATE (dialog);
+
+        dialog->priv->chooser_widget = gdm_domain_chooser_widget_new ();
+        gdm_chooser_widget_set_hide_inactive_items (GDM_CHOOSER_WIDGET (dialog->priv->chooser_widget),
+                                                    FALSE);
+
+        gdm_domain_chooser_widget_set_current_domain_name (GDM_DOMAIN_CHOOSER_WIDGET (dialog->priv->chooser_widget),
+                                                           "__local");
+        gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), dialog->priv->chooser_widget);
+
+        g_signal_connect_swapped (G_OBJECT (dialog->priv->chooser_widget),
+                                  "activated", G_CALLBACK (queue_response),
+                                  dialog);
+
+        gtk_dialog_add_buttons (GTK_DIALOG (dialog),
+                                GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+                                GTK_STOCK_OK, GTK_RESPONSE_OK,
+                                NULL);
+
+        gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
+        gtk_container_set_border_width (GTK_CONTAINER (dialog), 12);
+        gtk_container_set_border_width (GTK_CONTAINER (dialog->priv->chooser_widget), 5);
+        gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER_ALWAYS);
+        gtk_window_set_default_size (GTK_WINDOW (dialog), 512, 440);
+        gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+}
+
+static void
+gdm_domain_chooser_dialog_finalize (GObject *object)
+{
+        GdmDomainChooserDialog *domain_chooser_dialog;
+
+        g_return_if_fail (object != NULL);
+        g_return_if_fail (GDM_IS_DOMAIN_CHOOSER_DIALOG (object));
+
+        domain_chooser_dialog = GDM_DOMAIN_CHOOSER_DIALOG (object);
+
+        g_return_if_fail (domain_chooser_dialog->priv != NULL);
+
+        G_OBJECT_CLASS (gdm_domain_chooser_dialog_parent_class)->finalize (object);
+}
+
+GtkWidget *
+gdm_domain_chooser_dialog_new (void)
+{
+        GObject *object;
+
+        object = g_object_new (GDM_TYPE_DOMAIN_CHOOSER_DIALOG,
+                               "icon-name", "preferences-system-network",
+                               "title", _("Domains"),
+                               "border-width", 8,
+                               "modal", TRUE,
+                               NULL);
+
+        return GTK_WIDGET (object);
+}
Index: gdm-2.24.0/gui/simple-greeter/gdm-domain-chooser-dialog.h
===================================================================
--- /dev/null
+++ gdm-2.24.0/gui/simple-greeter/gdm-domain-chooser-dialog.h
@@ -0,0 +1,60 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Hans Petter Jansson
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written by: Hans Petter Jansson <hpj@novell.com>
+ */
+
+#ifndef __GDM_DOMAIN_CHOOSER_DIALOG_H
+#define __GDM_DOMAIN_CHOOSER_DIALOG_H
+
+#include <glib-object.h>
+#include <gtk/gtkdialog.h>
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_DOMAIN_CHOOSER_DIALOG         (gdm_domain_chooser_dialog_get_type ())
+#define GDM_DOMAIN_CHOOSER_DIALOG(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_DOMAIN_CHOOSER_DIALOG, GdmDomainChooserDialog))
+#define GDM_DOMAIN_CHOOSER_DIALOG_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_DOMAIN_CHOOSER_DIALOG, GdmDomainChooserDialogClass))
+#define GDM_IS_DOMAIN_CHOOSER_DIALOG(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_DOMAIN_CHOOSER_DIALOG))
+#define GDM_IS_DOMAIN_CHOOSER_DIALOG_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_DOMAIN_CHOOSER_DIALOG))
+#define GDM_DOMAIN_CHOOSER_DIALOG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_DOMAIN_CHOOSER_DIALOG, GdmDomainChooserDialogClass))
+
+typedef struct GdmDomainChooserDialogPrivate GdmDomainChooserDialogPrivate;
+
+typedef struct
+{
+        GtkDialog                      parent;
+        GdmDomainChooserDialogPrivate *priv;
+} GdmDomainChooserDialog;
+
+typedef struct
+{
+        GtkDialogClass   parent_class;
+} GdmDomainChooserDialogClass;
+
+GType                  gdm_domain_chooser_dialog_get_type                     (void);
+
+GtkWidget            * gdm_domain_chooser_dialog_new                          (void);
+
+char *                 gdm_domain_chooser_dialog_get_current_domain_name      (GdmDomainChooserDialog *dialog);
+void                   gdm_domain_chooser_dialog_set_current_domain_name      (GdmDomainChooserDialog *dialog,
+                                                                               const char             *domain_name);
+
+G_END_DECLS
+
+#endif /* __GDM_DOMAIN_CHOOSER_DIALOG_H */
Index: gdm-2.24.0/gui/simple-greeter/gdm-domain-chooser-widget.c
===================================================================
--- /dev/null
+++ gdm-2.24.0/gui/simple-greeter/gdm-domain-chooser-widget.c
@@ -0,0 +1,244 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Hans Petter Jansson
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written by: Hans Petter Jansson <hpj@novell.com>
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <dirent.h>
+#include <locale.h>
+#include <sys/stat.h>
+
+#include <fontconfig/fontconfig.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+
+#include "gdm-domain-chooser-widget.h"
+#include "gdm-chooser-widget.h"
+#include "gdm-domain-provider.h"
+
+#define GDM_DOMAIN_CHOOSER_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_DOMAIN_CHOOSER_WIDGET, GdmDomainChooserWidgetPrivate))
+
+struct GdmDomainChooserWidgetPrivate
+{
+        GdmDomainProvider  *domain_provider;
+};
+
+static void     gdm_domain_chooser_widget_class_init  (GdmDomainChooserWidgetClass *klass);
+static void     gdm_domain_chooser_widget_init        (GdmDomainChooserWidget      *domain_chooser_widget);
+static void     gdm_domain_chooser_widget_finalize    (GObject                     *object);
+
+G_DEFINE_TYPE (GdmDomainChooserWidget, gdm_domain_chooser_widget, GDM_TYPE_CHOOSER_WIDGET)
+
+#if 0
+
+enum {
+        CHOOSER_LIST_TITLE_COLUMN = 0,
+        CHOOSER_LIST_TRANSLATED_COLUMN,
+        CHOOSER_LIST_DOMAIN_COLUMN
+};
+
+#endif
+
+static gchar *
+convert_domain_name_to_display (const gchar *domain_name)
+{
+        gchar *utf8_name;
+        gchar *normalized_name;
+
+        utf8_name = g_locale_to_utf8 (domain_name, -1, NULL, NULL, NULL);
+        return utf8_name;
+}
+
+static gchar *
+maybe_convert_domain_name_to_display (const gchar *domain_name)
+{
+        gchar *display_name;
+
+        if (g_utf8_validate (domain_name, -1, NULL))
+                display_name = g_strdup (domain_name);
+        else
+                display_name = convert_domain_name_to_display (domain_name);
+
+        return display_name;
+}
+
+char *
+gdm_domain_chooser_widget_get_current_domain_name (GdmDomainChooserWidget *widget)
+{
+        char *domain_name;
+
+        g_return_val_if_fail (GDM_IS_DOMAIN_CHOOSER_WIDGET (widget), NULL);
+
+        domain_name = gdm_chooser_widget_get_selected_item (GDM_CHOOSER_WIDGET (widget));
+
+        if (domain_name == NULL) {
+                domain_name = g_strdup ("__local");
+        }
+
+        return domain_name;
+}
+
+void
+gdm_domain_chooser_widget_set_current_domain_name (GdmDomainChooserWidget *widget,
+                                                   const char             *domain_name)
+{
+        g_return_if_fail (GDM_IS_DOMAIN_CHOOSER_WIDGET (widget));
+
+        if (domain_name == NULL) {
+                gdm_chooser_widget_set_selected_item (GDM_CHOOSER_WIDGET (widget),
+                                                      NULL);
+                return;
+        }
+
+        gdm_chooser_widget_set_selected_item (GDM_CHOOSER_WIDGET (widget),
+                                              domain_name);
+}
+
+static void
+populate_widget (GdmDomainChooserWidget *domain_chooser_widget)
+{
+        GdmDomainChooserWidgetPrivate *priv           = domain_chooser_widget->priv;
+        GdmChooserWidget              *chooser_widget = GDM_CHOOSER_WIDGET (domain_chooser_widget);
+        GList                         *domain_list;
+        GList                         *l;
+        gchar                         *current_domain_name;
+
+        domain_list = gdm_domain_provider_peek_domains (priv->domain_provider);
+
+        current_domain_name = gdm_domain_chooser_widget_get_current_domain_name (domain_chooser_widget);
+        gdm_chooser_widget_remove_all_items (chooser_widget);
+
+        for (l = domain_list; l; l = g_list_next (l)) {
+                const gchar *domain_name = l->data;
+                gchar       *display_name;
+
+                display_name = maybe_convert_domain_name_to_display (domain_name);
+                if (!display_name)
+                        continue;
+
+                gdm_chooser_widget_add_item (chooser_widget,
+                                             display_name,
+                                             NULL,
+                                             display_name,
+                                             display_name,
+                                             0,
+                                             FALSE,
+                                             FALSE);
+
+                g_free (display_name);
+        }
+
+        gdm_chooser_widget_add_item (chooser_widget,
+                                     "__local",
+                                     NULL,
+                                     _("Local login"),
+                                     _("Log in to the local computer."),
+                                     0,
+                                     FALSE,
+                                     TRUE);
+
+        gdm_domain_chooser_widget_set_current_domain_name (domain_chooser_widget, current_domain_name);
+}
+
+static void
+gdm_domain_chooser_widget_dispose (GObject *object)
+{
+        G_OBJECT_CLASS (gdm_domain_chooser_widget_parent_class)->dispose (object);
+}
+
+static void
+gdm_domain_chooser_widget_realize (GtkWidget *widget)
+{
+        GdmDomainChooserWidget *chooser;
+
+        chooser = GDM_DOMAIN_CHOOSER_WIDGET (widget);
+
+        GTK_WIDGET_CLASS (gdm_domain_chooser_widget_parent_class)->realize (widget);
+}
+
+static void
+gdm_domain_chooser_widget_class_init (GdmDomainChooserWidgetClass *klass)
+{
+        GObjectClass   *object_class = G_OBJECT_CLASS (klass);
+        GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+        object_class->dispose = gdm_domain_chooser_widget_dispose;
+        object_class->finalize = gdm_domain_chooser_widget_finalize;
+        widget_class->realize = gdm_domain_chooser_widget_realize;
+
+        g_type_class_add_private (klass, sizeof (GdmDomainChooserWidgetPrivate));
+}
+
+static void
+gdm_domain_chooser_widget_init (GdmDomainChooserWidget *domain_chooser_widget)
+{
+        GdmDomainChooserWidgetPrivate *priv;
+
+        priv = domain_chooser_widget->priv = GDM_DOMAIN_CHOOSER_WIDGET_GET_PRIVATE (domain_chooser_widget);
+
+        priv->domain_provider = gdm_get_domain_provider ();
+        g_signal_connect_swapped (priv->domain_provider, "changed",
+                                  (GCallback) populate_widget, domain_chooser_widget);
+
+        populate_widget (domain_chooser_widget);
+
+        gdm_chooser_widget_set_separator_position (GDM_CHOOSER_WIDGET (domain_chooser_widget),
+                                                   GDM_CHOOSER_WIDGET_POSITION_TOP);
+}
+
+static void
+gdm_domain_chooser_widget_finalize (GObject *object)
+{
+        GdmDomainChooserWidgetPrivate *priv;
+        GdmDomainChooserWidget        *domain_chooser_widget;
+
+        g_return_if_fail (object != NULL);
+        g_return_if_fail (GDM_IS_DOMAIN_CHOOSER_WIDGET (object));
+
+        domain_chooser_widget = GDM_DOMAIN_CHOOSER_WIDGET (object);
+        priv = domain_chooser_widget->priv;
+
+        g_return_if_fail (priv != NULL);
+
+        g_signal_handlers_disconnect_by_func (priv->domain_provider, populate_widget, domain_chooser_widget);
+
+        G_OBJECT_CLASS (gdm_domain_chooser_widget_parent_class)->finalize (object);
+}
+
+GtkWidget *
+gdm_domain_chooser_widget_new (void)
+{
+        GObject *object;
+
+        object = g_object_new (GDM_TYPE_DOMAIN_CHOOSER_WIDGET,
+                               "inactive-text", _("_Domains:"),
+                               "active-text", _("_Domain:"),
+                               NULL);
+
+        return GTK_WIDGET (object);
+}
Index: gdm-2.24.0/gui/simple-greeter/gdm-domain-chooser-widget.h
===================================================================
--- /dev/null
+++ gdm-2.24.0/gui/simple-greeter/gdm-domain-chooser-widget.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Hans Petter Jansson
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written by: Hans Petter Jansson
+ */
+
+#ifndef __GDM_DOMAIN_CHOOSER_WIDGET_H
+#define __GDM_DOMAIN_CHOOSER_WIDGET_H
+
+#include <glib-object.h>
+#include "gdm-chooser-widget.h"
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_DOMAIN_CHOOSER_WIDGET         (gdm_domain_chooser_widget_get_type ())
+#define GDM_DOMAIN_CHOOSER_WIDGET(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_DOMAIN_CHOOSER_WIDGET, GdmDomainChooserWidget))
+#define GDM_DOMAIN_CHOOSER_WIDGET_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_DOMAIN_CHOOSER_WIDGET, GdmDomainChooserWidgetClass))
+#define GDM_IS_DOMAIN_CHOOSER_WIDGET(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_DOMAIN_CHOOSER_WIDGET))
+#define GDM_IS_DOMAIN_CHOOSER_WIDGET_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_DOMAIN_CHOOSER_WIDGET))
+#define GDM_DOMAIN_CHOOSER_WIDGET_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_DOMAIN_CHOOSER_WIDGET, GdmDomainChooserWidgetClass))
+
+typedef struct GdmDomainChooserWidgetPrivate GdmDomainChooserWidgetPrivate;
+
+typedef struct
+{
+        GdmChooserWidget                 parent;
+        GdmDomainChooserWidgetPrivate *priv;
+} GdmDomainChooserWidget;
+
+typedef struct
+{
+        GdmChooserWidgetClass   parent_class;
+} GdmDomainChooserWidgetClass;
+
+GType                  gdm_domain_chooser_widget_get_type                       (void);
+GtkWidget *            gdm_domain_chooser_widget_new                            (void);
+
+char *                 gdm_domain_chooser_widget_get_current_domain_name      (GdmDomainChooserWidget *widget);
+void                   gdm_domain_chooser_widget_set_current_domain_name      (GdmDomainChooserWidget *widget,
+                                                                               const char               *lang_name);
+
+G_END_DECLS
+
+#endif /* __GDM_DOMAIN_CHOOSER_WIDGET_H */
Index: gdm-2.24.0/gui/simple-greeter/gdm-domain-option-widget.c
===================================================================
--- /dev/null
+++ gdm-2.24.0/gui/simple-greeter/gdm-domain-option-widget.c
@@ -0,0 +1,381 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Hans Petter Jansson
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written by: Hans Petter Jansson <hpj@novell.com>
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+
+#include "gdm-profile.h"
+#include "gdm-domain-option-widget.h"
+#include "gdm-recent-option-widget.h"
+#include "gdm-domain-chooser-dialog.h"
+#include "gdm-domain-provider.h"
+
+#define GDM_DOMAIN_OPTION_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_DOMAIN_OPTION_WIDGET, GdmDomainOptionWidgetPrivate))
+
+struct GdmDomainOptionWidgetPrivate
+{
+        GtkWidget         *dialog;
+        GdmDomainProvider *domain_provider;
+};
+
+enum {
+        DOMAIN_ACTIVATED,
+        NUMBER_OF_SIGNALS
+};
+
+static guint signals [NUMBER_OF_SIGNALS] = { 0, };
+
+static void     gdm_domain_option_widget_class_init  (GdmDomainOptionWidgetClass *klass);
+static void     gdm_domain_option_widget_init        (GdmDomainOptionWidget      *domain_option_widget);
+static void     gdm_domain_option_widget_finalize    (GObject                    *object);
+static void     gdm_domain_option_widget_hide_dialog (GdmDomainOptionWidget      *widget);
+
+G_DEFINE_TYPE (GdmDomainOptionWidget, gdm_domain_option_widget, GDM_TYPE_RECENT_OPTION_WIDGET)
+
+static gchar *
+convert_domain_name_to_display (const gchar *domain_name)
+{
+        gchar *utf8_name;
+        gchar *normalized_name;
+
+        utf8_name = g_locale_to_utf8 (domain_name, -1, NULL, NULL, NULL);
+        return utf8_name;
+}
+
+static gchar *
+maybe_convert_domain_name_to_display (const gchar *domain_name)
+{
+        gchar *display_name;
+
+        if (g_utf8_validate (domain_name, -1, NULL))
+                display_name = g_strdup (domain_name);
+        else
+                display_name = convert_domain_name_to_display (domain_name);
+
+        return display_name;
+}
+
+static void
+gdm_domain_option_widget_set_domain_from_dialog (GdmDomainOptionWidget *widget)
+{
+        char *domain_name;
+
+        domain_name = gdm_domain_chooser_dialog_get_current_domain_name (GDM_DOMAIN_CHOOSER_DIALOG (widget->priv->dialog));
+        gdm_domain_option_widget_set_current_domain_name (widget, domain_name);
+        g_free (domain_name);
+}
+
+static void
+on_dialog_response (GtkDialog             *dialog,
+                    int                    response_id,
+                    GdmDomainOptionWidget *widget)
+{
+        switch (response_id) {
+                case GTK_RESPONSE_OK:
+                        gdm_domain_option_widget_set_domain_from_dialog (widget);
+                        break;
+
+                default:
+                        break;
+        }
+
+        gdm_domain_option_widget_hide_dialog (widget);
+}
+
+static void
+gdm_domain_option_widget_hide_dialog (GdmDomainOptionWidget *widget)
+{
+        gtk_widget_destroy (widget->priv->dialog);
+        widget->priv->dialog = NULL;
+}
+
+static void
+create_dialog (GdmDomainOptionWidget *widget)
+{
+        gdm_profile_start (NULL);
+
+        g_assert (widget->priv->dialog == NULL);
+
+        widget->priv->dialog = gdm_domain_chooser_dialog_new ();
+
+        gdm_profile_end (NULL);
+}
+
+static void
+gdm_domain_option_widget_show_dialog (GdmDomainOptionWidget *widget,
+                                      const char            *active_item_id)
+{
+        if (widget->priv->dialog == NULL) {
+                create_dialog (widget);
+        }
+
+        g_signal_connect (GTK_DIALOG (widget->priv->dialog),
+                          "response",
+                          G_CALLBACK (on_dialog_response),
+                          widget);
+
+        gtk_widget_show_all (GTK_WIDGET (widget->priv->dialog));
+
+        gdm_domain_chooser_dialog_set_current_domain_name (GDM_DOMAIN_CHOOSER_DIALOG (GDM_DOMAIN_OPTION_WIDGET (widget)->priv->dialog),
+                                                           active_item_id);
+}
+
+static gint
+find_domain_name_in_list (GdmDomainOptionWidget *domain_option_widget, const gchar *domain_name)
+{
+        GdmDomainOptionWidgetPrivate *priv   = domain_option_widget->priv;
+        GList                        *domain_list;
+        GList                        *domain_list_item;
+        gint                          result = -1;
+
+        domain_list = gdm_domain_provider_peek_domains (priv->domain_provider);
+        domain_list_item = g_list_find_custom (domain_list, domain_name, (GCompareFunc) g_utf8_collate);
+
+        if (domain_list_item)
+                result = g_list_position (domain_list, domain_list_item);
+
+        return result;
+}
+
+static gchar *
+gdm_domain_option_widget_lookup_item (GdmRecentOptionWidget *domain_option_widget,
+                                      const char            *id,
+                                      char                 **name,
+                                      char                 **comment)
+{
+        gchar *display_name;
+
+        display_name = maybe_convert_domain_name_to_display (id);
+        if (!display_name)
+                return NULL;
+
+        if (name)
+                *name = g_strdup (display_name);
+        if (comment)
+                *comment = g_strdup (display_name);
+
+        return display_name;
+}
+
+static void
+gdm_domain_option_widget_activated (GdmOptionWidget *widget)
+{
+        char *active_item_id;
+
+        active_item_id = gdm_option_widget_get_active_item (GDM_OPTION_WIDGET (widget));
+        if (active_item_id == NULL) {
+                return;
+        }
+
+        if (strcmp (active_item_id, "__other") == 0) {
+                active_item_id = gdm_option_widget_get_default_item (widget);
+                gdm_domain_option_widget_show_dialog (GDM_DOMAIN_OPTION_WIDGET (widget), active_item_id);
+        }
+
+        gdm_domain_option_widget_set_current_domain_name (GDM_DOMAIN_OPTION_WIDGET (widget), active_item_id);
+        g_signal_emit (G_OBJECT (widget), signals [DOMAIN_ACTIVATED], 0);
+
+        g_free (active_item_id);
+}
+
+static void
+gdm_domain_option_widget_class_init (GdmDomainOptionWidgetClass *klass)
+{
+        GObjectClass         *object_class        = G_OBJECT_CLASS (klass);
+        GdmOptionWidgetClass *option_widget_class = GDM_OPTION_WIDGET_CLASS (klass);
+
+        object_class->finalize = gdm_domain_option_widget_finalize;
+
+        option_widget_class->activated = gdm_domain_option_widget_activated;
+
+        signals [DOMAIN_ACTIVATED] = g_signal_new ("domain-activated",
+                                                   G_TYPE_FROM_CLASS (object_class),
+                                                   G_SIGNAL_RUN_FIRST,
+                                                   G_STRUCT_OFFSET (GdmDomainOptionWidgetClass, domain_activated),
+                                                   NULL,
+                                                   NULL,
+                                                   g_cclosure_marshal_VOID__VOID,
+                                                   G_TYPE_NONE,
+                                                   0);
+
+        g_type_class_add_private (klass, sizeof (GdmDomainOptionWidgetPrivate));
+}
+
+static void
+populate_widget (GdmDomainOptionWidget *domain_option_widget)
+{
+        GdmDomainOptionWidgetPrivate *priv          = domain_option_widget->priv;
+        GdmOptionWidget              *option_widget = GDM_OPTION_WIDGET (domain_option_widget);
+        GList                        *domain_list;
+        GList                        *l;
+        gchar                        *current_domain_name;
+
+        domain_list = gdm_domain_provider_peek_domains (priv->domain_provider);
+
+        current_domain_name = gdm_domain_option_widget_get_current_domain_name (domain_option_widget);
+
+        for (l = domain_list; l; l = g_list_next (l)) {
+                const gchar *domain_name = l->data;
+                gchar       *display_name;
+
+                display_name = maybe_convert_domain_name_to_display (domain_name);
+                if (!display_name)
+                        continue;
+
+                if (!gdm_option_widget_lookup_item (option_widget, display_name,
+                                                    NULL, NULL, NULL))
+                {
+                        gdm_option_widget_add_item (option_widget,
+                                                    display_name,
+                                                    display_name,
+                                                    display_name,
+                                                    GDM_OPTION_WIDGET_POSITION_MIDDLE);
+                }
+
+                g_free (display_name);
+        }
+
+        if (current_domain_name) {
+                gdm_domain_option_widget_set_current_domain_name (domain_option_widget, current_domain_name);
+                g_free (current_domain_name);
+        }
+}
+
+static void
+gdm_domain_option_widget_init (GdmDomainOptionWidget *domain_option_widget)
+{
+        GdmDomainOptionWidgetPrivate *priv;
+        GdmOptionWidget              *option_widget = GDM_OPTION_WIDGET (domain_option_widget);
+        GError                       *error = NULL;
+
+        priv = domain_option_widget->priv = GDM_DOMAIN_OPTION_WIDGET_GET_PRIVATE (domain_option_widget);
+
+        priv->domain_provider = gdm_get_domain_provider ();
+        g_signal_connect_swapped (priv->domain_provider, "changed",
+                                  (GCallback) populate_widget, domain_option_widget);
+
+        gdm_option_widget_add_item (option_widget,
+                                    "__local",
+                                    _("Local login"),
+                                    _("Log in to the local computer."),
+                                    GDM_OPTION_WIDGET_POSITION_TOP);
+
+        gdm_option_widget_add_item (option_widget,
+                                    "__other",
+                                    _("Other..."),
+                                    _("Choose a domain from the "
+                                      "full list of available domains."),
+                                    GDM_OPTION_WIDGET_POSITION_BOTTOM);
+
+        gdm_option_widget_set_default_item (option_widget, "__local");
+
+        populate_widget (domain_option_widget);
+
+        gdm_recent_option_widget_set_gconf_key (GDM_RECENT_OPTION_WIDGET (domain_option_widget),
+                                                "/apps/gdm/simple-greeter/recent-domains",
+                                                (GdmRecentOptionLookupItemFunc) gdm_domain_option_widget_lookup_item,
+                                                &error);
+
+        if (error != NULL) {
+                g_warning ("Could not read recent domains from gconf: %s",
+                           error->message);
+                g_error_free (error);
+        }
+}
+
+static void
+gdm_domain_option_widget_finalize (GObject *object)
+{
+        GdmDomainOptionWidgetPrivate *priv;
+        GdmDomainOptionWidget        *domain_option_widget;
+
+        g_return_if_fail (object != NULL);
+        g_return_if_fail (GDM_IS_DOMAIN_OPTION_WIDGET (object));
+
+        domain_option_widget = GDM_DOMAIN_OPTION_WIDGET (object);
+        priv = domain_option_widget->priv;
+
+        g_return_if_fail (priv != NULL);
+
+        if (priv->dialog != NULL) {
+                gtk_widget_destroy (priv->dialog);
+        }
+
+        g_signal_handlers_disconnect_by_func (priv->domain_provider, populate_widget, domain_option_widget);
+
+        G_OBJECT_CLASS (gdm_domain_option_widget_parent_class)->finalize (object);
+}
+
+GtkWidget *
+gdm_domain_option_widget_new (void)
+{
+        GObject *object;
+
+        object = g_object_new (GDM_TYPE_DOMAIN_OPTION_WIDGET,
+                               "label-text", _("_Domain:"),
+                               "icon-name", "preferences-system-network",
+                               "max-item-count", 16,
+                               NULL);
+
+        return GTK_WIDGET (object);
+}
+
+char *
+gdm_domain_option_widget_get_current_domain_name (GdmDomainOptionWidget *widget)
+{
+        char *active_item_id;
+
+        active_item_id = gdm_option_widget_get_active_item (GDM_OPTION_WIDGET (widget));
+        if (active_item_id == NULL) {
+                return NULL;
+        }
+
+        if (strcmp (active_item_id, "__other") == 0) {
+                g_free (active_item_id);
+                return NULL;
+        }
+
+        return active_item_id;
+}
+
+void
+gdm_domain_option_widget_set_current_domain_name (GdmDomainOptionWidget *widget,
+                                                  const char            *domain_name)
+{
+        g_return_if_fail (GDM_IS_DOMAIN_OPTION_WIDGET (widget));
+
+        gdm_recent_option_widget_add_item (GDM_RECENT_OPTION_WIDGET (widget),
+                                           domain_name);
+        gdm_option_widget_set_active_item (GDM_OPTION_WIDGET (widget), domain_name);
+}
Index: gdm-2.24.0/gui/simple-greeter/gdm-domain-option-widget.h
===================================================================
--- /dev/null
+++ gdm-2.24.0/gui/simple-greeter/gdm-domain-option-widget.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Hans Petter Jansson
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *  Written by: Hans Petter Jansson <hpj@novell.com>
+ */
+
+#ifndef __GDM_DOMAIN_OPTION_WIDGET_H
+#define __GDM_DOMAIN_OPTION_WIDGET_H
+
+#include <glib-object.h>
+#include "gdm-recent-option-widget.h"
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_DOMAIN_OPTION_WIDGET         (gdm_domain_option_widget_get_type ())
+#define GDM_DOMAIN_OPTION_WIDGET(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_DOMAIN_OPTION_WIDGET, GdmDomainOptionWidget))
+#define GDM_DOMAIN_OPTION_WIDGET_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_DOMAIN_OPTION_WIDGET, GdmDomainOptionWidgetClass))
+#define GDM_IS_DOMAIN_OPTION_WIDGET(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_DOMAIN_OPTION_WIDGET))
+#define GDM_IS_DOMAIN_OPTION_WIDGET_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_DOMAIN_OPTION_WIDGET))
+#define GDM_DOMAIN_OPTION_WIDGET_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_DOMAIN_OPTION_WIDGET, GdmDomainOptionWidgetClass))
+
+typedef struct GdmDomainOptionWidgetPrivate GdmDomainOptionWidgetPrivate;
+
+typedef struct
+{
+        GdmRecentOptionWidget          parent;
+        GdmDomainOptionWidgetPrivate *priv;
+} GdmDomainOptionWidget;
+
+typedef struct
+{
+        GdmRecentOptionWidgetClass    parent_class;
+
+        void (* domain_activated)        (GdmDomainOptionWidget *widget);
+} GdmDomainOptionWidgetClass;
+
+GType                  gdm_domain_option_widget_get_type               (void);
+GtkWidget *            gdm_domain_option_widget_new                    (void);
+
+char *                 gdm_domain_option_widget_get_current_domain_name      (GdmDomainOptionWidget *widget);
+void                   gdm_domain_option_widget_set_current_domain_name      (GdmDomainOptionWidget *widget,
+                                                                              const char              *domain_name);
+
+#endif /* __GDM_DOMAIN_OPTION_WIDGET_H */
Index: gdm-2.24.0/gui/simple-greeter/gdm-domain-provider.c
===================================================================
--- /dev/null
+++ gdm-2.24.0/gui/simple-greeter/gdm-domain-provider.c
@@ -0,0 +1,536 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Hans Petter Jansson <hpj@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written by: Hans Petter Jansson <hpj@novell.com>
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <locale.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib-object.h>
+
+#include "gdm-domain-provider.h"
+
+#define QUERY_TIMEOUT_S 30
+#define N_DOMAINS_MAX   128
+#define BUFFER_READ_MAX 256
+
+#define GDM_DOMAIN_PROVIDER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_DOMAIN_PROVIDER, GdmDomainProviderPrivate))
+
+typedef enum
+{
+        STATE_IDLE,                   /* Next state: STATE_QUERY_SEPARATOR */
+        STATE_QUERY_SEPARATOR,        /* Next state: STATE_QUERY_OWN_DOMAIN or STATE_IDLE */
+        STATE_QUERY_OWN_DOMAIN,       /* Next state: STATE_QUERY_TRUSTED_DOMAINS or STATE_IDLE */
+        STATE_QUERY_TRUSTED_DOMAINS,  /* Next state: STATE_IDLE */
+}
+State;
+
+enum
+{
+        CHANGED,
+        NUMBER_OF_SIGNALS
+};
+
+typedef void (*CmdEndFunc) (GdmDomainProvider *domain_provider, gchar *output, gint status);
+
+struct GdmDomainProviderPrivate
+{
+        gchar      *separator;
+        GList      *domains;
+
+        State       state;
+        guint       something_changed : 1;
+
+        GIOChannel *cmd_channel;
+        GString    *cmd_output_string;
+        GPid        cmd_pid;
+        CmdEndFunc  cmd_end_func;
+
+        gchar      *domains_output;
+
+        guint       callback_id;
+};
+
+static void     gdm_domain_provider_class_init  (GdmDomainProviderClass *klass);
+static void     gdm_domain_provider_init        (GdmDomainProvider      *domain_provider);
+static void     gdm_domain_provider_finalize    (GObject                *object);
+
+static void     get_separator_begin             (GdmDomainProvider      *domain_provider);
+
+static GdmDomainProvider *global_domain_provider      = NULL;
+static guint              signals [NUMBER_OF_SIGNALS] = { 0, };
+
+G_DEFINE_TYPE (GdmDomainProvider, gdm_domain_provider, G_TYPE_OBJECT)
+
+static gint
+compare_strings (const gchar *string_a, const gchar *string_b)
+{
+        gint result = 0;
+
+        if (string_a) {
+                if (string_b) {
+                        result = g_ascii_strcasecmp (string_a, string_b);
+                } else {
+                        result = -1;
+                }
+        } else if (string_b) {
+                result = 1;
+        }
+
+        return result;
+}
+
+static gint
+compare_string_lists (GList *list_a, GList *list_b)
+{
+        gint result = 0;
+
+        for (;;) {
+                if (list_a == NULL) {
+                        if (list_b != NULL)
+                                result = 1;
+                        break;
+                } else if (list_b == NULL) {
+                        result = -1;
+                        break;
+                }
+
+                result = g_ascii_strcasecmp (list_a->data, list_b->data);
+                if (result != 0)
+                        break;
+
+                list_a = g_list_next (list_a);
+                list_b = g_list_next (list_b);
+        }
+
+        return result;
+}
+
+static gint
+cmd_cleanup (GdmDomainProvider *domain_provider, gchar **output)
+{
+        GdmDomainProviderPrivate *priv       = domain_provider->priv;
+        gint                      cmd_status = 0;
+        gchar                    *cmd_output;
+
+        /* When idle, callback_id represents the timeout until we run the next
+         * series of queries. Otherwise, it represents the GIOChannel watch. */
+
+        if (priv->callback_id)
+                g_source_remove (priv->callback_id);
+        priv->callback_id = 0;
+        priv->cmd_channel = NULL;
+
+        if (priv->state == STATE_IDLE)
+                return 0;
+
+        priv->state = STATE_IDLE;
+
+	waitpid (priv->cmd_pid, &cmd_status, 0);
+        g_spawn_close_pid (priv->cmd_pid);
+
+        cmd_output = g_string_free (priv->cmd_output_string, FALSE);
+        priv->cmd_output_string = NULL;
+
+        if (output)
+                *output = cmd_output;
+        else
+                g_free (cmd_output);
+
+        return WEXITSTATUS (cmd_status);
+}
+
+static gboolean
+cmd_handle_output (GIOChannel *channel, GIOCondition condition, gpointer data)
+{
+        GdmDomainProvider        *domain_provider = data;
+        GdmDomainProviderPrivate *priv            = domain_provider->priv;
+	gchar                     buf [BUFFER_READ_MAX];
+	gsize                     bytes_read;
+	GIOStatus                 status;
+
+        g_assert (priv->cmd_output_string != NULL);
+        g_assert (priv->state != STATE_IDLE);
+
+	status = g_io_channel_read_chars (channel, buf, BUFFER_READ_MAX, &bytes_read, NULL);
+
+	if (bytes_read < 1) {
+                gchar *cmd_output;
+                gint   cmd_status;
+
+                cmd_status = cmd_cleanup (domain_provider, &cmd_output);
+
+                if (priv->cmd_end_func) {
+                        CmdEndFunc cmd_end_func = priv->cmd_end_func;
+                        priv->cmd_end_func = NULL;
+
+                        cmd_end_func (domain_provider, cmd_output, cmd_status);
+                }
+
+                /* cmd_cleanup () removes the watch for us */
+		return TRUE;
+	}
+
+	g_string_append_len (priv->cmd_output_string, buf, bytes_read);
+	return TRUE;
+}
+
+static gboolean
+async_spawn_and_read (GdmDomainProvider *domain_provider,
+                      const gchar * const *spawn_args,
+                      CmdEndFunc end_func)
+{
+        GdmDomainProviderPrivate *priv         = domain_provider->priv;
+        gint                      cmd_stdout_fd;
+
+        g_assert (priv->state == STATE_IDLE);
+
+        /* NOTE: The GLib docs don't say if g_spawn_async_with_pipes () can actually cause
+         * spawn_args to be written to. We assume that won't happen. */
+
+	if (!g_spawn_async_with_pipes ("/",
+                                       (gchar **) spawn_args,
+                                       NULL,
+                                       G_SPAWN_SEARCH_PATH | G_SPAWN_STDERR_TO_DEV_NULL |
+                                       G_SPAWN_DO_NOT_REAP_CHILD,
+                                       NULL /* child setup function */,
+                                       NULL /* child setup user_data */,
+                                       &priv->cmd_pid,
+                                       NULL /* &cmd_stdin_fd */,
+                                       &cmd_stdout_fd,
+                                       NULL /* &cmd_stderr_fd */,
+                                       NULL))
+                return FALSE;
+
+        priv->cmd_end_func = end_func;
+        priv->cmd_output_string = g_string_new ("");
+
+        fcntl (cmd_stdout_fd, F_SETFL, O_NONBLOCK);
+
+        priv->cmd_channel = g_io_channel_unix_new (cmd_stdout_fd);
+        g_io_channel_set_encoding (priv->cmd_channel, NULL, NULL);
+        g_io_channel_set_close_on_unref (priv->cmd_channel, TRUE);
+        priv->callback_id = g_io_add_watch (priv->cmd_channel,
+                                            G_IO_IN | G_IO_HUP,
+                                            cmd_handle_output,
+                                            domain_provider);
+        g_io_channel_unref (priv->cmd_channel);  /* Watch holds remaining ref */
+
+        return TRUE;
+}
+
+static gboolean
+idle_end (GdmDomainProvider *domain_provider)
+{
+        cmd_cleanup (domain_provider, NULL);
+
+        /* Next state */
+
+        get_separator_begin (domain_provider);
+
+        /* cmd_cleanup () removes the callback for us */
+        return TRUE;
+}
+
+static void
+idle_begin (GdmDomainProvider *domain_provider)
+{
+        GdmDomainProviderPrivate *priv = domain_provider->priv;
+
+        g_free (priv->domains_output);
+        priv->domains_output = NULL;
+
+        if (priv->something_changed) {
+                /* Emit changed signal */
+
+                g_signal_emit (G_OBJECT (domain_provider), signals [CHANGED], 0);
+                priv->something_changed = FALSE;
+        }
+
+        priv->callback_id = g_timeout_add_seconds (QUERY_TIMEOUT_S, (GSourceFunc) idle_end, domain_provider);
+        priv->state = STATE_IDLE;
+}
+
+static void
+get_trusted_domains_end (GdmDomainProvider *domain_provider, gchar *output, gint status)
+{
+        GdmDomainProviderPrivate *priv    = domain_provider->priv;
+        GList                    *domains = NULL;
+        GList                    *old_domains;
+
+        if (status == 0 && output != NULL && *output) {
+                gchar **tokens;
+                gint    i;
+
+                if (priv->domains_output) {
+                        output = g_realloc (output, strlen (output) + strlen (priv->domains_output) + 1);
+                        strcat (output, priv->domains_output);
+                }
+
+                tokens = g_strsplit_set (output, "\n\r", N_DOMAINS_MAX);
+
+		g_qsort_with_data (tokens,
+				   g_strv_length (tokens),
+				   sizeof (gchar *),
+				   (GCompareDataFunc) g_ascii_strcasecmp,
+				   NULL);
+
+                for (i = 0; tokens [i]; i++) {
+                        /* Ensure no blank entries */
+                        if (!strlen (tokens [i]))
+                                continue;
+
+                        /* Ensure no duplicates */
+                        if (i > 0 && !g_ascii_strcasecmp (tokens [i], tokens [i - 1]))
+                                continue;
+
+                        /* Ensure no builtin */
+                        if (!g_ascii_strcasecmp (tokens [i], "BUILTIN"))
+                                continue;
+
+                        /* Ensure no local host name */
+                        if (!g_ascii_strcasecmp (tokens [i], g_get_host_name ()))
+                                continue;
+
+                        domains = g_list_prepend (domains, tokens [i]);
+                }
+
+                g_free (tokens);
+
+                domains = g_list_reverse (domains);
+        }
+
+        g_free (output);
+
+        old_domains = priv->domains;
+        priv->domains = domains;
+
+        if (compare_string_lists (domains, old_domains) != 0)
+                priv->something_changed = TRUE;
+
+        g_list_foreach (old_domains, (GFunc) g_free, NULL);
+        g_list_free (old_domains);
+
+        /* Next state */
+
+        idle_begin (domain_provider);
+}
+
+static void
+get_trusted_domains_begin (GdmDomainProvider *domain_provider)
+{
+        GdmDomainProviderPrivate *priv        = domain_provider->priv;
+        const gchar * const       cmd_args [] = { "wbinfo", "--trusted-domains", NULL };
+
+        if (async_spawn_and_read (domain_provider, cmd_args, get_trusted_domains_end))
+                priv->state = STATE_QUERY_TRUSTED_DOMAINS;
+        else
+                idle_begin (domain_provider);
+}
+
+static void
+get_own_domain_end (GdmDomainProvider *domain_provider, gchar *output, gint status)
+{
+        GdmDomainProviderPrivate *priv = domain_provider->priv;
+
+        if (status == 0 && output != NULL && *output)
+                priv->domains_output = output;
+
+        /* Next state */
+
+        get_trusted_domains_begin (domain_provider);
+}
+
+static void
+get_own_domain_begin (GdmDomainProvider *domain_provider)
+{
+        GdmDomainProviderPrivate *priv        = domain_provider->priv;
+        const gchar * const       cmd_args [] = { "wbinfo", "--own-domain", NULL };
+
+        if (async_spawn_and_read (domain_provider, cmd_args, get_own_domain_end))
+                priv->state = STATE_QUERY_OWN_DOMAIN;
+        else
+                idle_begin (domain_provider);
+}
+
+static void
+get_separator_end (GdmDomainProvider *domain_provider, gchar *output, gint status)
+{
+        GdmDomainProviderPrivate *priv      = domain_provider->priv;
+        gchar                    *separator = NULL;
+        gchar                    *old_separator;
+
+        if (status == 0 && output != NULL) {
+                gchar *p0, *p1;
+
+                p0 = strchr (output, '\n');
+                p1 = strchr (output, '\r');
+                if (!p0 || (p1 && p1 < p0))
+                        p0 = p1;
+
+                if (p0 && p0 != output) {
+                        *p0 = 0;
+                        separator = output;
+                } else {
+                        g_free (output);
+                }
+        }
+
+        old_separator = priv->separator;
+        priv->separator = separator;
+
+        if (compare_strings (separator, old_separator))
+                priv->something_changed = TRUE;
+
+        g_free (old_separator);
+
+        /* Next state */
+
+        if (priv->separator)
+                get_own_domain_begin (domain_provider);
+        else
+                idle_begin (domain_provider);
+}
+
+static void
+get_separator_begin (GdmDomainProvider *domain_provider)
+{
+        GdmDomainProviderPrivate *priv        = domain_provider->priv;
+        const gchar * const       cmd_args [] = { "wbinfo", "--separator", NULL };
+
+        if (async_spawn_and_read (domain_provider, cmd_args, get_separator_end))
+                priv->state = STATE_QUERY_SEPARATOR;
+        else
+                idle_begin (domain_provider);
+}
+
+static void
+gdm_domain_provider_class_init (GdmDomainProviderClass *klass)
+{
+        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+        object_class->finalize = gdm_domain_provider_finalize;
+
+        signals [CHANGED] = g_signal_new ("changed",
+                                          G_TYPE_FROM_CLASS (object_class),
+                                          G_SIGNAL_RUN_FIRST,
+                                          G_STRUCT_OFFSET (GdmDomainProviderClass, changed),
+                                          NULL,
+                                          NULL,
+                                          g_cclosure_marshal_VOID__VOID,
+                                          G_TYPE_NONE,
+                                          0);
+
+        g_type_class_add_private (klass, sizeof (GdmDomainProviderPrivate));
+}
+
+static void
+gdm_domain_provider_init (GdmDomainProvider *domain_provider)
+{
+        GdmDomainProviderPrivate *priv;
+
+        priv = domain_provider->priv = GDM_DOMAIN_PROVIDER_GET_PRIVATE (domain_provider);
+
+        priv->state = STATE_IDLE;
+
+        /* Start the state machine */
+        get_separator_begin (domain_provider);
+}
+
+static void
+gdm_domain_provider_finalize (GObject *object)
+{
+        GdmDomainProvider        *domain_provider;
+        GdmDomainProviderPrivate *priv;
+
+        g_return_if_fail (object != NULL);
+        g_return_if_fail (GDM_IS_DOMAIN_PROVIDER (object));
+
+        domain_provider = GDM_DOMAIN_PROVIDER (object);
+        priv = domain_provider->priv;
+
+        /* Stop the state machine */
+        cmd_cleanup (domain_provider, NULL);
+
+        g_free (priv->domains_output);
+        priv->domains_output = NULL;
+
+        G_OBJECT_CLASS (gdm_domain_provider_parent_class)->finalize (object);
+}
+
+/* --- *
+ * API *
+ * --- */
+
+GdmDomainProvider *
+gdm_get_domain_provider (void)
+{
+        if (!global_domain_provider)
+                global_domain_provider = g_object_new (GDM_TYPE_DOMAIN_PROVIDER, NULL);
+
+        return global_domain_provider;
+}
+
+gboolean
+gdm_domain_provider_is_domain_logon_enabled (GdmDomainProvider *domain_provider)
+{
+        GdmDomainProviderPrivate *priv;
+
+        g_return_val_if_fail (GDM_IS_DOMAIN_PROVIDER (domain_provider), FALSE);
+
+        priv = domain_provider->priv;
+
+        return priv->separator ? TRUE : FALSE;
+}
+
+const gchar *
+gdm_domain_provider_peek_separator (GdmDomainProvider *domain_provider)
+{
+        GdmDomainProviderPrivate *priv;
+
+        g_return_val_if_fail (GDM_IS_DOMAIN_PROVIDER (domain_provider), NULL);
+
+        priv = domain_provider->priv;
+
+        return priv->separator;
+}
+
+GList *
+gdm_domain_provider_peek_domains (GdmDomainProvider *domain_provider)
+{
+        GdmDomainProviderPrivate *priv;
+
+        g_return_val_if_fail (GDM_IS_DOMAIN_PROVIDER (domain_provider), NULL);
+
+        priv = domain_provider->priv;
+
+        return priv->domains;
+}
Index: gdm-2.24.0/gui/simple-greeter/gdm-domain-provider.h
===================================================================
--- /dev/null
+++ gdm-2.24.0/gui/simple-greeter/gdm-domain-provider.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Hans Petter Jansson <hpj@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written by: Hans Petter Jansson <hpj@novell.com>
+ */
+
+#ifndef __GDM_DOMAIN_PROVIDER_H
+#define __GDM_DOMAIN_PROVIDER_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_DOMAIN_PROVIDER         (gdm_domain_provider_get_type ())
+#define GDM_DOMAIN_PROVIDER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_DOMAIN_PROVIDER, GdmDomainProvider))
+#define GDM_DOMAIN_PROVIDER_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_DOMAIN_PROVIDER, GdmDomainProviderClass))
+#define GDM_IS_DOMAIN_PROVIDER(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_DOMAIN_PROVIDER))
+#define GDM_IS_DOMAIN_PROVIDER_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_DOMAIN_PROVIDER))
+#define GDM_DOMAIN_PROVIDER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_DOMAIN_PROVIDER, GdmDomainProviderClass))
+
+typedef struct GdmDomainProviderPrivate GdmDomainProviderPrivate;
+
+typedef struct
+{
+        GObject parent;
+        GdmDomainProviderPrivate *priv;
+} GdmDomainProvider;
+
+typedef struct
+{
+        GObjectClass parent_class;
+
+        void (* changed) (GdmDomainProvider *domain_provider);
+} GdmDomainProviderClass;
+
+GType                  gdm_domain_provider_get_type                (void);
+
+GdmDomainProvider    * gdm_get_domain_provider                     (void);
+
+gboolean               gdm_domain_provider_is_domain_logon_enabled (GdmDomainProvider *domain_provider);
+const gchar          * gdm_domain_provider_peek_separator          (GdmDomainProvider *domain_provider);
+GList                * gdm_domain_provider_peek_domains            (GdmDomainProvider *domain_provider);
+
+G_END_DECLS
+
+#endif /* __GDM_DOMAIN_PROVIDER_H */
Index: gdm-2.24.0/gui/simple-greeter/gdm-greeter-panel.c
===================================================================
--- gdm-2.24.0.orig/gui/simple-greeter/gdm-greeter-panel.c
+++ gdm-2.24.0/gui/simple-greeter/gdm-greeter-panel.c
@@ -40,6 +40,7 @@
 #include "gdm-language-option-widget.h"
 #include "gdm-layout-option-widget.h"
 #include "gdm-session-option-widget.h"
+#include "gdm-domain-option-widget.h"
 #include "gdm-profile.h"
 
 #include "na-tray.h"
@@ -58,6 +59,7 @@ struct GdmGreeterPanelPrivate
         GtkWidget              *language_option_widget;
         GtkWidget              *layout_option_widget;
         GtkWidget              *session_option_widget;
+        GtkWidget              *domain_option_widget;
 
         char                   *default_session_name;
         char                   *default_language_name;
@@ -72,6 +74,7 @@ enum {
         LANGUAGE_SELECTED,
         LAYOUT_SELECTED,
         SESSION_SELECTED,
+        DOMAIN_SELECTED,
         NUMBER_OF_SIGNALS
 };
 
@@ -451,6 +454,17 @@ gdm_greeter_panel_class_init (GdmGreeter
                               G_TYPE_NONE,
                               1, G_TYPE_STRING);
 
+        signals[DOMAIN_SELECTED] =
+                g_signal_new ("domain-selected",
+                              G_TYPE_FROM_CLASS (object_class),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GdmGreeterPanelClass, domain_selected),
+                              NULL,
+                              NULL,
+                              g_cclosure_marshal_VOID__STRING,
+                              G_TYPE_NONE,
+                              1, G_TYPE_STRING);
+
         g_object_class_install_property (object_class,
                                          PROP_MONITOR,
                                          g_param_spec_int ("monitor",
@@ -521,6 +535,24 @@ on_session_activated (GdmSessionOptionWi
 }
 
 static void
+on_domain_activated (GdmDomainOptionWidget *widget,
+                     GdmGreeterPanel        *panel)
+{
+
+        char *domain;
+
+        domain = gdm_domain_option_widget_get_current_domain_name (GDM_DOMAIN_OPTION_WIDGET (panel->priv->domain_option_widget));
+
+        if (domain == NULL) {
+                return;
+        }
+
+        g_signal_emit (panel, signals[DOMAIN_SELECTED], 0, domain);
+
+        g_free (domain);
+}
+
+static void
 gdm_greeter_panel_init (GdmGreeterPanel *panel)
 {
         NaTray    *tray;
@@ -581,6 +613,12 @@ gdm_greeter_panel_init (GdmGreeterPanel
                           G_CALLBACK (on_session_activated), panel);
         gtk_box_pack_start (GTK_BOX (panel->priv->option_hbox), panel->priv->session_option_widget, FALSE, FALSE, 6);
 
+        panel->priv->domain_option_widget = gdm_domain_option_widget_new ();
+        g_signal_connect (G_OBJECT (panel->priv->domain_option_widget),
+                          "domain-activated",
+                          G_CALLBACK (on_domain_activated), panel);
+        gtk_box_pack_start (GTK_BOX (panel->priv->option_hbox), panel->priv->domain_option_widget, FALSE, FALSE, 6);
+
         spacer = gtk_label_new ("");
         gtk_box_pack_start (GTK_BOX (panel->priv->option_hbox), spacer, TRUE, TRUE, 6);
         gtk_widget_show (spacer);
@@ -643,6 +681,7 @@ gdm_greeter_panel_show_user_options (Gdm
         gtk_widget_show (panel->priv->session_option_widget);
         gtk_widget_show (panel->priv->language_option_widget);
         gtk_widget_show (panel->priv->layout_option_widget);
+        gtk_widget_show (panel->priv->domain_option_widget);
 }
 
 void
@@ -651,18 +690,26 @@ gdm_greeter_panel_hide_user_options (Gdm
         gtk_widget_hide (panel->priv->session_option_widget);
         gtk_widget_hide (panel->priv->language_option_widget);
         gtk_widget_hide (panel->priv->layout_option_widget);
+        gtk_widget_show (panel->priv->domain_option_widget);
 
         g_debug ("GdmGreeterPanel: activating default layout");
         gdm_layout_activate (NULL);
 }
 
 void
+gdm_greeter_panel_set_domain_enabled (GdmGreeterPanel *panel, gboolean enabled)
+{
+        gtk_widget_set_sensitive (panel->priv->domain_option_widget, enabled);
+}
+
+void
 gdm_greeter_panel_reset (GdmGreeterPanel *panel)
 {
         gdm_greeter_panel_set_default_language_name (panel, NULL);
         gdm_greeter_panel_set_default_layout_name (panel, NULL);
         gdm_greeter_panel_set_default_session_name (panel, NULL);
         gdm_greeter_panel_hide_user_options (panel);
+        gdm_greeter_panel_set_domain_enabled (panel, TRUE);
 }
 
 void
@@ -730,3 +777,20 @@ gdm_greeter_panel_set_default_session_na
         gdm_option_widget_set_default_item (GDM_OPTION_WIDGET (panel->priv->session_option_widget),
                                             session_name);
 }
+
+void
+gdm_greeter_panel_set_default_domain_name (GdmGreeterPanel *panel,
+                                           const char      *domain_name)
+{
+        g_return_if_fail (GDM_IS_GREETER_PANEL (panel));
+
+        if (domain_name != NULL &&
+            !gdm_option_widget_lookup_item (GDM_OPTION_WIDGET (panel->priv->domain_option_widget),
+                                            domain_name, NULL, NULL, NULL)) {
+                g_warning ("Default domain is not available");
+                return;
+        }
+
+        gdm_option_widget_set_default_item (GDM_OPTION_WIDGET (panel->priv->domain_option_widget),
+                                            domain_name);
+}
Index: gdm-2.24.0/gui/simple-greeter/gdm-greeter-panel.h
===================================================================
--- gdm-2.24.0.orig/gui/simple-greeter/gdm-greeter-panel.h
+++ gdm-2.24.0/gui/simple-greeter/gdm-greeter-panel.h
@@ -53,6 +53,9 @@ typedef struct
 
         void (* session_selected)            (GdmGreeterPanel *panel,
                                               const char      *text);
+
+        void (* domain_selected)             (GdmGreeterPanel *panel,
+                                              const char      *text);
 } GdmGreeterPanelClass;
 
 GType                  gdm_greeter_panel_get_type                       (void);
@@ -64,12 +67,16 @@ void                   gdm_greeter_panel
 void                   gdm_greeter_panel_hide_user_options              (GdmGreeterPanel *panel);
 void                   gdm_greeter_panel_reset                          (GdmGreeterPanel *panel);
 
+void                   gdm_greeter_panel_set_domain_enabled             (GdmGreeterPanel *panel, gboolean enabled);
+
 void                   gdm_greeter_panel_set_default_language_name      (GdmGreeterPanel *panel,
                                                                          const char      *language_name);
 void                   gdm_greeter_panel_set_default_layout_name        (GdmGreeterPanel *panel,
                                                                          const char      *layout_name);
 void                   gdm_greeter_panel_set_default_session_name       (GdmGreeterPanel *panel,
                                                                          const char      *session_name);
+void                   gdm_greeter_panel_set_default_domain_name        (GdmGreeterPanel *panel,
+                                                                         const char      *domain_name);
 G_END_DECLS
 
 #endif /* __GDM_GREETER_PANEL_H */
Index: gdm-2.24.0/gui/simple-greeter/gdm-greeter-session.c
===================================================================
--- gdm-2.24.0.orig/gui/simple-greeter/gdm-greeter-session.c
+++ gdm-2.24.0/gui/simple-greeter/gdm-greeter-session.c
@@ -38,6 +38,8 @@
 #include "gdm-greeter-panel.h"
 #include "gdm-greeter-login-window.h"
 
+#include "gdm-domain-provider.h"
+
 #include "gdm-profile.h"
 
 #define GDM_GREETER_SESSION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_GREETER_SESSION, GdmGreeterSessionPrivate))
@@ -48,6 +50,11 @@ struct GdmGreeterSessionPrivate
 
         GtkWidget             *login_window;
         GtkWidget             *panel;
+
+        char                  *user;
+        char                  *domain;
+
+        gint                   n_info_queries;
 };
 
 enum {
@@ -62,6 +69,33 @@ G_DEFINE_TYPE (GdmGreeterSession, gdm_gr
 
 static gpointer session_object = NULL;
 
+static char *
+maybe_prepend_domain_to_user (GdmGreeterSession *session, const char *user)
+{
+        char       *domain_and_user = NULL;
+        const char *separator;
+
+        separator = gdm_domain_provider_peek_separator (gdm_get_domain_provider ());
+
+        if (user) {
+                if (separator) {
+                        /* The user string may already contain a domain, so we look for the
+                         * separator and prepend ours only if it's not already there. */
+
+                        if (session->priv->domain && !strstr (user, separator))
+                                domain_and_user = g_strconcat (session->priv->domain,
+                                                               separator,
+                                                               user,
+                                                               NULL);
+                }
+
+                if (!domain_and_user)
+                        domain_and_user = g_strdup (user);
+        }
+
+        return domain_and_user;
+}
+
 static void
 on_info (GdmGreeterClient  *client,
          const char        *text,
@@ -99,6 +133,10 @@ on_reset (GdmGreeterClient  *client,
 
         gdm_greeter_panel_reset (GDM_GREETER_PANEL (session->priv->panel));
         gdm_greeter_login_window_reset (GDM_GREETER_LOGIN_WINDOW (session->priv->login_window));
+
+        session->priv->n_info_queries = 0;
+        if (session->priv->panel)
+                gdm_greeter_panel_set_domain_enabled (GDM_GREETER_PANEL (session->priv->panel), TRUE);
 }
 
 static void
@@ -174,6 +212,9 @@ on_secret_info_query (GdmGreeterClient
 {
         g_debug ("GdmGreeterSession: Secret info query: %s", text);
 
+        if (session->priv->panel)
+                gdm_greeter_panel_set_domain_enabled (GDM_GREETER_PANEL (session->priv->panel), FALSE);
+
         gdm_greeter_login_window_secret_info_query (GDM_GREETER_LOGIN_WINDOW (session->priv->login_window), text);
 }
 
@@ -198,8 +239,14 @@ on_begin_verification_for_user (GdmGreet
                                 const char            *username,
                                 GdmGreeterSession     *session)
 {
+        char *domain_and_user;
+
+        domain_and_user = maybe_prepend_domain_to_user (session, username);
+
         gdm_greeter_client_call_begin_verification_for_user (session->priv->client,
-                                                             username);
+                                                             domain_and_user);
+
+        g_free (domain_and_user);
 }
 
 static void
@@ -207,8 +254,21 @@ on_query_answer (GdmGreeterLoginWindow *
                  const char            *text,
                  GdmGreeterSession     *session)
 {
+        gchar *temp;
+
+        if (session->priv->n_info_queries == 0)
+                temp = maybe_prepend_domain_to_user (session, text);
+        else
+                temp = g_strdup (text);
+
+        if (session->priv->panel)
+                gdm_greeter_panel_set_domain_enabled (GDM_GREETER_PANEL (session->priv->panel), FALSE);
+
         gdm_greeter_client_call_answer_query (session->priv->client,
-                                              text);
+                                              temp);
+
+        g_free (temp);
+        session->priv->n_info_queries++;
 }
 
 static void
@@ -236,13 +296,39 @@ on_select_layout (GdmGreeterSession
 }
 
 static void
+on_select_domain (GdmGreeterSession      *session,
+                  const char             *text)
+{
+        char *domain_and_user;
+
+        g_free (session->priv->domain);
+        session->priv->domain = NULL;
+
+        if (text && strcmp (text, "__local") && strcmp (text, "__other"))
+                session->priv->domain = g_strdup (text);
+}
+
+static void
 on_select_user (GdmGreeterLoginWindow *login_window,
                 const char            *text,
                 GdmGreeterSession     *session)
 {
+        char *domain_and_user;
+
         gdm_greeter_panel_show_user_options (GDM_GREETER_PANEL (session->priv->panel));
+
+        g_free (session->priv->user);
+        session->priv->user = NULL;
+
+        if (text)
+                session->priv->user = g_strdup (text);
+
+        domain_and_user = maybe_prepend_domain_to_user (session, text);
+
         gdm_greeter_client_call_select_user (session->priv->client,
-                                             text);
+                                             domain_and_user);
+
+        g_free (domain_and_user);
 }
 
 static void
@@ -251,6 +337,10 @@ on_cancelled (GdmGreeterLoginWindow *log
 {
         gdm_greeter_panel_hide_user_options (GDM_GREETER_PANEL (session->priv->panel));
         gdm_greeter_client_call_cancel (session->priv->client);
+
+        session->priv->n_info_queries = 0;
+        if (session->priv->panel)
+                gdm_greeter_panel_set_domain_enabled (GDM_GREETER_PANEL (session->priv->panel), TRUE);
 }
 
 static void
@@ -300,6 +390,11 @@ toggle_panel (GdmGreeterSession *session
                                           G_CALLBACK (on_select_session),
                                           session);
 
+                g_signal_connect_swapped (session->priv->panel,
+                                          "domain-selected",
+                                          G_CALLBACK (on_select_domain),
+                                          session);
+
                 gtk_widget_show (session->priv->panel);
         } else {
                 gtk_widget_destroy (session->priv->panel);
@@ -542,6 +637,8 @@ gdm_greeter_session_init (GdmGreeterSess
                           G_CALLBACK (on_user_authorized),
                           session);
 
+        session->priv->n_info_queries = 0;
+
         /* We want to listen for panel mnemonics even if the
          * login window is focused, so we intercept them here.
          */
@@ -563,6 +660,9 @@ gdm_greeter_session_finalize (GObject *o
 
         g_return_if_fail (greeter_session->priv != NULL);
 
+        g_free (greeter_session->priv->user);
+        g_free (greeter_session->priv->domain);
+
         G_OBJECT_CLASS (gdm_greeter_session_parent_class)->finalize (object);
 }
 
Index: gdm-2.24.0/gui/simple-greeter/gdm-recent-option-widget.c
===================================================================
--- gdm-2.24.0.orig/gui/simple-greeter/gdm-recent-option-widget.c
+++ gdm-2.24.0/gui/simple-greeter/gdm-recent-option-widget.c
@@ -155,7 +155,6 @@ gdm_recent_option_widget_sync_items_from
                 list = gdm_recent_option_widget_get_list_from_gconf (widget);
         }
 
-        gdm_option_widget_remove_all_items (GDM_OPTION_WIDGET (widget));
         default_is_set = FALSE;
 
         for (tmp = list; tmp != NULL; tmp = tmp->next) {
@@ -163,12 +162,21 @@ gdm_recent_option_widget_sync_items_from
                 char       *name;
                 char       *comment;
 
-                id = (char *) tmp->data;
+                key = (char *) tmp->data;
+
+                id = widget->priv->lookup_item_func (widget, key, &name, &comment);
+
+                if (id != NULL) {
+                        gboolean item_exists;
+
+                        item_exists = gdm_option_widget_lookup_item (GDM_OPTION_WIDGET (widget), id, NULL, NULL, NULL);
+
+                        if (!item_exists) {
+                                gdm_option_widget_add_item (GDM_OPTION_WIDGET (widget),
+                                                            id, name, comment,
+                                                            GDM_OPTION_WIDGET_POSITION_MIDDLE);
+                        }
 
-                if (widget->priv->lookup_item_func (widget, id, &name, &comment)) {
-                        gdm_option_widget_add_item (GDM_OPTION_WIDGET (widget),
-                                                    id, name, comment,
-                                                    GDM_OPTION_WIDGET_POSITION_MIDDLE);
                         if (!default_is_set) {
                                 gdm_option_widget_set_active_item (GDM_OPTION_WIDGET (widget),
                                         id);
@@ -350,10 +358,21 @@ gdm_recent_option_widget_add_item (GdmRe
                                    const char            *id)
 {
         GSList *list;
+        GSList *l;
+        gchar  *id_copy;
+
+        if (!id)
+                return;
 
+        id_copy = g_strdup (id);
         list = gdm_recent_option_widget_get_list_from_gconf (widget);
 
-        list = g_slist_prepend (list, g_strdup (id));
+        while ((l = g_slist_find_custom (list, id_copy, (GCompareFunc) g_utf8_collate))) {
+                g_free (l->data);
+                list = g_slist_delete_link (list, l);
+        }
+
+        list = g_slist_prepend (list, id_copy);
 
         gconf_client_set_list (widget->priv->gconf_client,
                           widget->priv->gconf_key,
Index: gdm-2.24.0/gui/simple-greeter/gdm-simple-greeter.schemas.in
===================================================================
--- gdm-2.24.0.orig/gui/simple-greeter/gdm-simple-greeter.schemas.in
+++ gdm-2.24.0/gui/simple-greeter/gdm-simple-greeter.schemas.in
@@ -92,6 +92,18 @@
       </locale>
     </schema>
     <schema>
+      <key>/schemas/apps/gdm/simple-greeter/recent-domains</key>
+      <applyto>/apps/gdm/simple-greeter/recent-domains</applyto>
+      <owner>gdm-simple-greeter</owner>
+      <type>list</type>
+      <list_type>string</list_type>
+      <default>[]</default>
+      <locale name="C">
+        <short>Recently selected domains</short>
+        <long>Set to a list of login domains to be shown by default at the login window.</long>
+      </locale>
+    </schema>
+    <schema>
       <key>/schemas/apps/gdm/simple-greeter/wm_use_compiz</key>
       <applyto>/apps/gdm/simple-greeter/wm_use_compiz</applyto>
       <owner>gdm-simple-greeter</owner>
Index: gdm-2.24.0/po/POTFILES.in
===================================================================
--- gdm-2.24.0.orig/po/POTFILES.in
+++ gdm-2.24.0/po/POTFILES.in
@@ -63,6 +63,9 @@ gui/simple-chooser/gdm-host-chooser-widg
 gui/simple-greeter/gdm-cell-renderer-timer.c
 gui/simple-greeter/gdm-chooser-widget.c
 gui/simple-greeter/gdm-clock-widget.c
+gui/simple-greeter/gdm-domain-chooser-dialog.c
+gui/simple-greeter/gdm-domain-chooser-widget.c
+gui/simple-greeter/gdm-domain-option-widget.c
 gui/simple-greeter/gdm-greeter-login-window.c
 gui/simple-greeter/gdm-greeter-login-window.c
 gui/simple-greeter/gdm-greeter-login-window.glade
