diff --git a/gdm-domain-logon.patch b/gdm-domain-logon.patch
new file mode 100644
index 0000000..6bdf7c2
--- /dev/null
+++ b/gdm-domain-logon.patch
@@ -0,0 +1,1996 @@
+Index: gui/simple-greeter/gdm-simple-greeter.schemas.in
+===================================================================
+--- gui/simple-greeter/gdm-simple-greeter.schemas.in (revision 6548)
++++ gui/simple-greeter/gdm-simple-greeter.schemas.in (working copy)
+@@ -92,6 +92,18 @@
+
+
+
++ /schemas/apps/gdm/simple-greeter/recent-domains
++ /apps/gdm/simple-greeter/recent-domains
++ gdm-simple-greeter
++ list
++ string
++ []
++
++ Recently selected domains
++ Set to a list of login domains to be shown by default at the login window.
++
++
++
+ /schemas/apps/gdm/simple-greeter/wm_use_compiz
+ /apps/gdm/simple-greeter/wm_use_compiz
+ gdm-simple-greeter
+Index: gui/simple-greeter/gdm-domain-option-widget.c
+===================================================================
+--- gui/simple-greeter/gdm-domain-option-widget.c (revision 0)
++++ gui/simple-greeter/gdm-domain-option-widget.c (revision 0)
+@@ -0,0 +1,379 @@
++/* -*- 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
++ *
++ */
++
++#include "config.h"
++
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++
++#include
++#include
++#include
++#include
++
++#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);
++ if (!utf8_name)
++ return NULL;
++
++ normalized_name = g_utf8_strdown (utf8_name, -1);
++ g_free (utf8_name);
++
++ return normalized_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_strcasecmp);
++
++ if (domain_list_item)
++ result = g_list_position (domain_list, domain_list_item);
++
++ return result;
++}
++
++static gboolean
++gdm_domain_option_widget_lookup_item (GdmRecentOptionWidget *domain_option_widget,
++ const char *id,
++ char **name,
++ char **comment)
++{
++ gchar *display_name;
++
++ if (!find_domain_name_in_list (GDM_DOMAIN_OPTION_WIDGET (domain_option_widget), id))
++ return FALSE;
++
++ display_name = convert_domain_name_to_display (id);
++ if (!display_name)
++ return FALSE;
++
++ if (name)
++ *name = g_strdup (display_name);
++ if (comment)
++ *comment = g_strdup (display_name);
++
++ g_free (display_name);
++ return TRUE;
++}
++
++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_set_current_domain_name (GDM_DOMAIN_OPTION_WIDGET (widget), active_item_id);
++ gdm_domain_option_widget_show_dialog (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);
++ gdm_option_widget_remove_all_items (option_widget);
++
++ for (l = domain_list; l; l = g_list_next (l)) {
++ const gchar *domain_name = l->data;
++ gchar *display_name;
++
++ display_name = convert_domain_name_to_display (domain_name);
++ if (!display_name)
++ continue;
++
++ gdm_option_widget_add_item (option_widget,
++ domain_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",
++ 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));
++
++ if (domain_name != NULL &&
++ !gdm_option_widget_lookup_item (GDM_OPTION_WIDGET (widget),
++ domain_name, NULL, NULL, NULL)) {
++ 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: gui/simple-greeter/gdm-domain-option-widget.h
+===================================================================
+--- gui/simple-greeter/gdm-domain-option-widget.h (revision 0)
++++ gui/simple-greeter/gdm-domain-option-widget.h (revision 0)
+@@ -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_OPTION_WIDGET_H
++#define __GDM_DOMAIN_OPTION_WIDGET_H
++
++#include
++#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: gui/simple-greeter/gdm-domain-chooser-dialog.c
+===================================================================
+--- gui/simple-greeter/gdm-domain-chooser-dialog.c (revision 0)
++++ gui/simple-greeter/gdm-domain-chooser-dialog.c (revision 0)
+@@ -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
++ */
++
++#include "config.h"
++
++#include
++#include
++#include
++#include
++
++#include
++
++#include
++#include
++#include
++#include
++
++#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: gui/simple-greeter/gdm-domain-chooser-dialog.h
+===================================================================
+--- gui/simple-greeter/gdm-domain-chooser-dialog.h (revision 0)
++++ gui/simple-greeter/gdm-domain-chooser-dialog.h (revision 0)
+@@ -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
++ */
++
++#ifndef __GDM_DOMAIN_CHOOSER_DIALOG_H
++#define __GDM_DOMAIN_CHOOSER_DIALOG_H
++
++#include
++#include
++
++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: gui/simple-greeter/gdm-domain-chooser-widget.c
+===================================================================
+--- gui/simple-greeter/gdm-domain-chooser-widget.c (revision 0)
++++ gui/simple-greeter/gdm-domain-chooser-widget.c (revision 0)
+@@ -0,0 +1,237 @@
++/* -*- 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
++ */
++
++#include "config.h"
++
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++
++#include
++
++#include
++#include
++#include
++#include
++
++#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);
++ if (!utf8_name)
++ return NULL;
++
++ normalized_name = g_utf8_strdown (utf8_name, -1);
++ g_free (utf8_name);
++
++ return normalized_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 = convert_domain_name_to_display (domain_name);
++ if (!display_name)
++ continue;
++
++ gdm_chooser_widget_add_item (chooser_widget,
++ domain_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: gui/simple-greeter/gdm-domain-chooser-widget.h
+===================================================================
+--- gui/simple-greeter/gdm-domain-chooser-widget.h (revision 0)
++++ gui/simple-greeter/gdm-domain-chooser-widget.h (revision 0)
+@@ -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
++#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: gui/simple-greeter/gdm-domain-provider.c
+===================================================================
+--- gui/simple-greeter/gdm-domain-provider.c (revision 0)
++++ gui/simple-greeter/gdm-domain-provider.c (revision 0)
+@@ -0,0 +1,478 @@
++/* -*- 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
++ */
++
++#include "config.h"
++
++#include
++#include
++#include
++
++#include
++#include
++#include
++#include
++
++#include
++
++#include
++#include
++#include
++
++#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,
++ STATE_QUERY_SEPARATOR,
++ STATE_QUERY_DOMAINS
++}
++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;
++
++ 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;
++
++ if (priv->something_changed) {
++ /* Emit changed signal */
++
++ g_signal_emit (G_OBJECT (domain_provider), signals [CHANGED], 0);
++ priv->something_changed = FALSE;
++ }
++
++ g_timeout_add_seconds (QUERY_TIMEOUT_S, (GSourceFunc) idle_end, domain_provider);
++ priv->state = STATE_IDLE;
++}
++
++static void
++get_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;
++
++ 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;
++
++ domains = g_list_prepend (domains, tokens [i]);
++ }
++
++ g_free (tokens);
++
++ domains = g_list_reverse (domains);
++ }
++
++ 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_domains_begin (GdmDomainProvider *domain_provider)
++{
++ GdmDomainProviderPrivate *priv = domain_provider->priv;
++ const gchar * const cmd_args [] = { "wbinfo", "--all-domains", NULL };
++
++ if (async_spawn_and_read (domain_provider, cmd_args, get_domains_end))
++ priv->state = STATE_QUERY_DOMAINS;
++ 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;
++
++ p0 = strchr (output, '\n');
++ if (!p0)
++ p0 = strchr (output, '\r');
++
++ if (p0 && p0 != output) {
++ *p0 = 0;
++ separator = g_strdup (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_domains_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;
++
++ g_return_if_fail (object != NULL);
++ g_return_if_fail (GDM_IS_DOMAIN_PROVIDER (object));
++
++ domain_provider = GDM_DOMAIN_PROVIDER (object);
++
++ /* Stop the state machine */
++ cmd_cleanup (domain_provider, 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: gui/simple-greeter/gdm-domain-provider.h
+===================================================================
+--- gui/simple-greeter/gdm-domain-provider.h (revision 0)
++++ gui/simple-greeter/gdm-domain-provider.h (revision 0)
+@@ -0,0 +1,61 @@
++/* -*- 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_PROVIDER_H
++#define __GDM_DOMAIN_PROVIDER_H
++
++#include
++
++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: gui/simple-greeter/gdm-greeter-panel.c
+===================================================================
+--- gui/simple-greeter/gdm-greeter-panel.c (revision 6548)
++++ gui/simple-greeter/gdm-greeter-panel.c (working copy)
+@@ -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_hide (panel->priv->domain_option_widget);
+ }
+
+ void
+@@ -651,6 +690,7 @@ 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);
+@@ -730,3 +770,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: gui/simple-greeter/gdm-greeter-panel.h
+===================================================================
+--- gui/simple-greeter/gdm-greeter-panel.h (revision 6548)
++++ gui/simple-greeter/gdm-greeter-panel.h (working copy)
+@@ -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);
+@@ -70,6 +73,8 @@ void gdm_greeter_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: gui/simple-greeter/gdm-greeter-session.c
+===================================================================
+--- gui/simple-greeter/gdm-greeter-session.c (revision 6548)
++++ gui/simple-greeter/gdm-greeter-session.c (working copy)
+@@ -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,9 @@ struct GdmGreeterSessionPrivate
+
+ GtkWidget *login_window;
+ GtkWidget *panel;
++
++ char *user;
++ char *domain;
+ };
+
+ enum {
+@@ -62,6 +67,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,
+@@ -140,6 +172,16 @@ on_default_session_name_changed (GdmGree
+ }
+
+ static void
++on_selected_domain_changed (GdmGreeterClient *client,
++ const char *text,
++ GdmGreeterSession *session)
++{
++ g_debug ("GdmGreeterSession: selected domain changed: %s", text);
++ gdm_greeter_panel_set_default_domain_name (GDM_GREETER_PANEL (session->priv->panel),
++ text);
++}
++
++static void
+ on_timed_login_requested (GdmGreeterClient *client,
+ const char *text,
+ int delay,
+@@ -198,8 +240,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
+@@ -236,13 +284,46 @@ 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);
++
++ domain_and_user = maybe_prepend_domain_to_user (session, session->priv->user);
++
++ gdm_greeter_client_call_select_user (session->priv->client,
++ domain_and_user);
++
++ g_free (domain_and_user);
++}
++
++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
+@@ -300,6 +381,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);
+@@ -522,6 +608,10 @@ gdm_greeter_session_init (GdmGreeterSess
+ G_CALLBACK (on_selected_user_changed),
+ session);
+ g_signal_connect (session->priv->client,
++ "selected-domain-changed",
++ G_CALLBACK (on_selected_domain_changed),
++ session);
++ g_signal_connect (session->priv->client,
+ "default-language-name-changed",
+ G_CALLBACK (on_default_language_name_changed),
+ session);
+@@ -563,6 +653,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: gui/simple-greeter/gdm-chooser-widget.c
+===================================================================
+--- gui/simple-greeter/gdm-chooser-widget.c (revision 6548)
++++ gui/simple-greeter/gdm-chooser-widget.c (working copy)
+@@ -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: gui/simple-greeter/Makefile.am
+===================================================================
+--- gui/simple-greeter/Makefile.am (revision 6548)
++++ gui/simple-greeter/Makefile.am (working copy)
+@@ -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: gui/simple-greeter/gdm-chooser-widget.h
+===================================================================
+--- gui/simple-greeter/gdm-chooser-widget.h (revision 6548)
++++ gui/simple-greeter/gdm-chooser-widget.h (working copy)
+@@ -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,
diff --git a/gdm.changes b/gdm.changes
index 354dd36..9c7a199 100644
--- a/gdm.changes
+++ b/gdm.changes
@@ -1,3 +1,14 @@
+-------------------------------------------------------------------
+Mon Oct 6 15:45:07 CEST 2008 - sbrabec@suse.cz
+
+- Conflict with other branding providers (FATE#304881).
+
+-------------------------------------------------------------------
+Fri Sep 26 01:52:16 CDT 2008 - hpj@novell.com
+
+- Add gdm-domain-logon.patch, which implements an Active Directory
+ domain logon selector using wbinfo.
+
-------------------------------------------------------------------
Sat Sep 20 14:46:06 CEST 2008 - vuntz@novell.com
diff --git a/gdm.spec b/gdm.spec
index 0a28810..a9952ba 100644
--- a/gdm.spec
+++ b/gdm.spec
@@ -56,7 +56,7 @@ PreReq: %insserv_prereq
License: GPL v2 or later
Group: System/GUI/GNOME
Version: 2.23.92
-Release: 5
+Release: 6
Summary: The GNOME 2.x Display Manager
Source: %{name}-%{version}.tar.bz2
Source1: gdm.pamd
@@ -73,6 +73,7 @@ Patch5: gdm-2.23.92-gsd-path.patch
Patch6: gdm-desktop-session-env-pam.patch
# PATCH-FIX-OPENSUSE gdm-suse-xsession.patch vuntz@novell.com -- Use the /etc/X11/xdm/* scripts
Patch7: gdm-suse-xsession.patch
+Patch8: gdm-domain-logon.patch
Patch28: gdm-X_SERVER.patch
# PATCH-SUSE: enable SELinux
Patch60: gdm-selinux.patch
@@ -106,6 +107,7 @@ License: GPL v2 or later
Summary: The GNOME 2.x Display Manager
Group: System/GUI/GNOME
Provides: %{name}-branding = %{version}
+Conflicts: otherproviders(%{name}-branding)
Supplements: packageand(branding-upstream:%{name})
#BRAND: Provide two equal files - /usr/share/gdm/defaults.conf and
#BRAND: factory-defaults.conf with default configuration of gdm.
@@ -136,6 +138,7 @@ Authors:
%patch5 -p1
%patch6 -p1
%patch7 -p1
+%patch8
%patch28
%patch60
@@ -243,6 +246,11 @@ fi
%files lang -f %{name}.lang
%changelog
+* Mon Oct 06 2008 sbrabec@suse.cz
+- Conflict with other branding providers (FATE#304881).
+* Fri Sep 26 2008 hpj@novell.com
+- Add gdm-domain-logon.patch, which implements an Active Directory
+ domain logon selector using wbinfo.
* Sat Sep 20 2008 vuntz@novell.com
- Add gdm-suse-xsession.patch, which is similar to the
gdm-xdm-sessions.patch we have with GDM <= 2.20: it makes GDM