summaryrefslogtreecommitdiff
path: root/chooser
diff options
context:
space:
mode:
Diffstat (limited to 'chooser')
-rw-r--r--chooser/Makefile.am71
-rw-r--r--chooser/chooser-main.c250
-rw-r--r--chooser/gdm-chooser-host.c258
-rw-r--r--chooser/gdm-chooser-host.h63
-rw-r--r--chooser/gdm-chooser-session.c342
-rw-r--r--chooser/gdm-chooser-session.h58
-rw-r--r--chooser/gdm-host-chooser-dialog.c223
-rw-r--r--chooser/gdm-host-chooser-dialog.h60
-rw-r--r--chooser/gdm-host-chooser-widget.c860
-rw-r--r--chooser/gdm-host-chooser-widget.h65
-rw-r--r--chooser/gdm-host-chooser.c252
11 files changed, 2502 insertions, 0 deletions
diff --git a/chooser/Makefile.am b/chooser/Makefile.am
new file mode 100644
index 00000000..b649d38b
--- /dev/null
+++ b/chooser/Makefile.am
@@ -0,0 +1,71 @@
+NULL =
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/common \
+ -I$(top_srcdir)/libgdm \
+ -I$(top_builddir)/libgdm \
+ -DDATADIR=\""$(datadir)"\" \
+ -DGDMCONFDIR=\"$(gdmconfdir)\" \
+ -DDMCONFDIR=\""$(dmconfdir)"\" \
+ -DGDMLOCALEDIR=\""$(gdmlocaledir)"\" \
+ -DGNOMELOCALEDIR=\""$(datadir)/locale"\" \
+ -DLIBEXECDIR=\""$(libexecdir)"\" \
+ -DSBINDIR=\""$(sbindir)"\" \
+ -DPIXMAPDIR=\""$(pixmapdir)"\" \
+ -DAT_SPI_REGISTRYD_DIR="\"$(AT_SPI_REGISTRYD_DIR)\"" \
+ -DGNOME_SETTINGS_DAEMON_DIR="\"$(GNOME_SETTINGS_DAEMON_DIR)\"" \
+ $(GTK_CFLAGS) \
+ $(XLIB_CFLAGS) \
+ $(XDMCP_CFLAGS) \
+ $(EXTRA_CHOOSER_CFLAGS) \
+ $(SIMPLE_CHOOSER_CFLAGS) \
+ $(NULL)
+
+libexec_PROGRAMS = \
+ gdm-simple-chooser \
+ gdm-host-chooser \
+ $(NULL)
+
+gdm_simple_chooser_SOURCES = \
+ chooser-main.c \
+ gdm-chooser-host.c \
+ gdm-chooser-host.h \
+ gdm-host-chooser-widget.c \
+ gdm-host-chooser-widget.h \
+ gdm-host-chooser-dialog.c \
+ gdm-host-chooser-dialog.h \
+ gdm-chooser-session.c \
+ gdm-chooser-session.h \
+ $(NULL)
+
+gdm_simple_chooser_LDADD = \
+ $(top_builddir)/common/libgdmcommon.la \
+ $(top_builddir)/libgdm/libgdm.la \
+ $(GTK_LIBS) \
+ $(EXTRA_CHOOSER_LIBS) \
+ $(SIMPLE_CHOOSER_LIBS) \
+ $(XLIB_LIBS) \
+ $(XDMCP_LIBS) \
+ $(NULL)
+
+gdm_host_chooser_SOURCES = \
+ gdm-host-chooser.c \
+ gdm-chooser-host.c \
+ gdm-chooser-host.h \
+ gdm-host-chooser-widget.c \
+ gdm-host-chooser-widget.h \
+ gdm-host-chooser-dialog.c \
+ gdm-host-chooser-dialog.h \
+ $(NULL)
+
+gdm_host_chooser_LDADD = \
+ $(top_builddir)/common/libgdmcommon.la \
+ $(GTK_LIBS) \
+ $(EXTRA_CHOOSER_LIBS) \
+ $(SIMPLE_CHOOSER_LIBS) \
+ $(XLIB_LIBS) \
+ $(XDMCP_LIBS) \
+ $(NULL)
+
+EXTRA_DIST = \
+ $(NULL)
diff --git a/chooser/chooser-main.c b/chooser/chooser-main.c
new file mode 100644
index 00000000..54aebf17
--- /dev/null
+++ b/chooser/chooser-main.c
@@ -0,0 +1,250 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <libintl.h>
+#include <locale.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+
+#include <glib/gi18n.h>
+#include <gdk/gdkx.h>
+#include <gtk/gtk.h>
+
+#include "gdm-common.h"
+#include "gdm-log.h"
+
+#include "gdm-chooser-session.h"
+
+#define ACCESSIBILITY_KEY "/desktop/gnome/interface/accessibility"
+
+static Atom AT_SPI_IOR;
+
+
+static gboolean
+assistive_registry_launch (void)
+{
+ GPid pid;
+ GError *error;
+ const char *command;
+ char **argv;
+ gboolean res;
+
+ command = AT_SPI_REGISTRYD_DIR "/at-spi-registryd";
+
+ argv = NULL;
+ error = NULL;
+ res = g_shell_parse_argv (command, NULL, &argv, &error);
+ if (! res) {
+ g_warning ("Unable to parse command: %s", error->message);
+ return FALSE;
+ }
+
+ error = NULL;
+ res = g_spawn_async (NULL,
+ argv,
+ NULL,
+ G_SPAWN_SEARCH_PATH
+ | G_SPAWN_STDOUT_TO_DEV_NULL
+ | G_SPAWN_STDERR_TO_DEV_NULL,
+ NULL,
+ NULL,
+ &pid,
+ &error);
+ g_strfreev (argv);
+
+ if (! res) {
+ g_warning ("Unable to run command %s: %s", command, error->message);
+ return FALSE;
+ }
+
+ if (kill (pid, 0) < 0) {
+ g_warning ("at-spi-registryd not running");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static GdkFilterReturn
+filter_watch (GdkXEvent *xevent,
+ GdkEvent *event,
+ gpointer data)
+{
+ XEvent *xev = (XEvent *)xevent;
+
+ if (xev->xany.type == PropertyNotify
+ && xev->xproperty.atom == AT_SPI_IOR) {
+ gtk_main_quit ();
+
+ return GDK_FILTER_REMOVE;
+ }
+
+ return GDK_FILTER_CONTINUE;
+}
+
+static gboolean
+filter_timeout (gpointer data)
+{
+ g_warning ("The accessibility registry was not found.");
+
+ gtk_main_quit ();
+
+ return FALSE;
+}
+
+static void
+assistive_registry_start (void)
+{
+ GdkWindow *root;
+ guint tid;
+
+ root = gdk_get_default_root_window ();
+
+ if ( ! AT_SPI_IOR) {
+ AT_SPI_IOR = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "AT_SPI_IOR", False);
+ }
+
+ gdk_window_set_events (root, GDK_PROPERTY_CHANGE_MASK);
+
+ if ( ! assistive_registry_launch ()) {
+ g_warning ("The accessibility registry could not be started.");
+ return;
+ }
+
+ gdk_window_add_filter (root, filter_watch, NULL);
+ tid = g_timeout_add_seconds (5, filter_timeout, NULL);
+
+ gtk_main ();
+
+ gdk_window_remove_filter (root, filter_watch, NULL);
+ g_source_remove (tid);
+}
+
+static void
+at_set_gtk_modules (void)
+{
+ GSList *modules_list;
+ GSList *l;
+ const char *old;
+ char **modules;
+ gboolean found_gail;
+ gboolean found_atk_bridge;
+ int n;
+
+ n = 0;
+ modules_list = NULL;
+ found_gail = FALSE;
+ found_atk_bridge = FALSE;
+
+ if ((old = g_getenv ("GTK_MODULES")) != NULL) {
+ modules = g_strsplit (old, ":", -1);
+ for (n = 0; modules[n]; n++) {
+ if (!strcmp (modules[n], "gail")) {
+ found_gail = TRUE;
+ } else if (!strcmp (modules[n], "atk-bridge")) {
+ found_atk_bridge = TRUE;
+ }
+
+ modules_list = g_slist_prepend (modules_list, modules[n]);
+ modules[n] = NULL;
+ }
+ g_free (modules);
+ }
+
+ if (!found_gail) {
+ modules_list = g_slist_prepend (modules_list, "gail");
+ ++n;
+ }
+
+ if (!found_atk_bridge) {
+ modules_list = g_slist_prepend (modules_list, "atk-bridge");
+ ++n;
+ }
+
+ modules = g_new (char *, n + 1);
+ modules[n--] = NULL;
+ for (l = modules_list; l; l = l->next) {
+ modules[n--] = g_strdup (l->data);
+ }
+
+ g_setenv ("GTK_MODULES", g_strjoinv (":", modules), TRUE);
+ g_strfreev (modules);
+ g_slist_free (modules_list);
+}
+
+static void
+load_a11y (void)
+{
+ assistive_registry_start ();
+ at_set_gtk_modules ();
+}
+
+int
+main (int argc, char *argv[])
+{
+ GdmChooserSession *session;
+ gboolean res;
+ GError *error;
+
+ bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+ textdomain (GETTEXT_PACKAGE);
+
+ setlocale (LC_ALL, "");
+
+ gdm_log_init ();
+ gdm_log_set_debug (TRUE);
+
+ g_debug ("Chooser for display %s xauthority:%s",
+ g_getenv ("DISPLAY"),
+ g_getenv ("XAUTHORITY"));
+
+ gdk_init (&argc, &argv);
+
+ load_a11y ();
+
+ gtk_init (&argc, &argv);
+
+ session = gdm_chooser_session_new ();
+ if (session == NULL) {
+ g_critical ("Unable to create chooser session");
+ exit (1);
+ }
+
+ error = NULL;
+ res = gdm_chooser_session_start (session, &error);
+ if (! res) {
+ g_warning ("Unable to start chooser session: %s", error->message);
+ g_error_free (error);
+ exit (1);
+ }
+
+ gtk_main ();
+
+ if (session != NULL) {
+ g_object_unref (session);
+ }
+
+ return 0;
+}
diff --git a/chooser/gdm-chooser-host.c b/chooser/gdm-chooser-host.c
new file mode 100644
index 00000000..5b76f652
--- /dev/null
+++ b/chooser/gdm-chooser-host.c
@@ -0,0 +1,258 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib-object.h>
+
+#include "gdm-address.h"
+#include "gdm-chooser-host.h"
+
+#define GDM_CHOOSER_HOST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_CHOOSER_HOST, GdmChooserHostPrivate))
+
+struct GdmChooserHostPrivate
+{
+ GdmAddress *address;
+ char *description;
+ GdmChooserHostKind kind;
+ gboolean willing;
+};
+
+enum {
+ PROP_0,
+ PROP_ADDRESS,
+ PROP_DESCRIPTION,
+ PROP_KIND,
+ PROP_WILLING,
+};
+
+static void gdm_chooser_host_class_init (GdmChooserHostClass *klass);
+static void gdm_chooser_host_init (GdmChooserHost *chooser_host);
+static void gdm_chooser_host_finalize (GObject *object);
+
+G_DEFINE_TYPE (GdmChooserHost, gdm_chooser_host, G_TYPE_OBJECT)
+
+GdmAddress *
+gdm_chooser_host_get_address (GdmChooserHost *host)
+{
+ g_return_val_if_fail (GDM_IS_CHOOSER_HOST (host), NULL);
+
+ return host->priv->address;
+}
+
+G_CONST_RETURN char *
+gdm_chooser_host_get_description (GdmChooserHost *host)
+{
+ g_return_val_if_fail (GDM_IS_CHOOSER_HOST (host), NULL);
+
+ return host->priv->description;
+}
+
+GdmChooserHostKind
+gdm_chooser_host_get_kind (GdmChooserHost *host)
+{
+ g_return_val_if_fail (GDM_IS_CHOOSER_HOST (host), 0);
+
+ return host->priv->kind;
+}
+
+gboolean
+gdm_chooser_host_get_willing (GdmChooserHost *host)
+{
+ g_return_val_if_fail (GDM_IS_CHOOSER_HOST (host), FALSE);
+
+ return host->priv->willing;
+}
+
+static void
+_gdm_chooser_host_set_address (GdmChooserHost *host,
+ GdmAddress *address)
+{
+ if (host->priv->address != NULL) {
+ gdm_address_free (host->priv->address);
+ }
+
+ g_assert (address != NULL);
+
+ gdm_address_debug (address);
+ host->priv->address = gdm_address_copy (address);
+}
+
+static void
+_gdm_chooser_host_set_description (GdmChooserHost *host,
+ const char *description)
+{
+ g_free (host->priv->description);
+ host->priv->description = g_strdup (description);
+}
+
+static void
+_gdm_chooser_host_set_kind (GdmChooserHost *host,
+ int kind)
+{
+ if (host->priv->kind != kind) {
+ host->priv->kind = kind;
+ }
+}
+
+static void
+_gdm_chooser_host_set_willing (GdmChooserHost *host,
+ gboolean willing)
+{
+ if (host->priv->willing != willing) {
+ host->priv->willing = willing;
+ }
+}
+
+static void
+gdm_chooser_host_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GdmChooserHost *host;
+
+ host = GDM_CHOOSER_HOST (object);
+
+ switch (param_id) {
+ case PROP_ADDRESS:
+ _gdm_chooser_host_set_address (host, g_value_get_boxed (value));
+ break;
+ case PROP_DESCRIPTION:
+ _gdm_chooser_host_set_description (host, g_value_get_string (value));
+ break;
+ case PROP_KIND:
+ _gdm_chooser_host_set_kind (host, g_value_get_int (value));
+ break;
+ case PROP_WILLING:
+ _gdm_chooser_host_set_willing (host, g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gdm_chooser_host_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GdmChooserHost *host;
+
+ host = GDM_CHOOSER_HOST (object);
+
+ switch (param_id) {
+ case PROP_ADDRESS:
+ g_value_set_boxed (value, host->priv->address);
+ break;
+ case PROP_DESCRIPTION:
+ g_value_set_string (value, host->priv->description);
+ break;
+ case PROP_KIND:
+ g_value_set_int (value, host->priv->kind);
+ break;
+ case PROP_WILLING:
+ g_value_set_boolean (value, host->priv->willing);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gdm_chooser_host_class_init (GdmChooserHostClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->set_property = gdm_chooser_host_set_property;
+ object_class->get_property = gdm_chooser_host_get_property;
+ object_class->finalize = gdm_chooser_host_finalize;
+
+
+ g_object_class_install_property (object_class,
+ PROP_ADDRESS,
+ g_param_spec_boxed ("address",
+ "address",
+ "address",
+ GDM_TYPE_ADDRESS,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ g_object_class_install_property (object_class,
+ PROP_DESCRIPTION,
+ g_param_spec_string ("description",
+ "description",
+ "description",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class,
+ PROP_KIND,
+ g_param_spec_int ("kind",
+ "kind",
+ "kind",
+ 0,
+ G_MAXINT,
+ 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ g_object_class_install_property (object_class,
+ PROP_WILLING,
+ g_param_spec_boolean ("willing",
+ "willing",
+ "willing",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+
+ g_type_class_add_private (klass, sizeof (GdmChooserHostPrivate));
+}
+
+static void
+gdm_chooser_host_init (GdmChooserHost *widget)
+{
+ widget->priv = GDM_CHOOSER_HOST_GET_PRIVATE (widget);
+}
+
+static void
+gdm_chooser_host_finalize (GObject *object)
+{
+ GdmChooserHost *host;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GDM_IS_CHOOSER_HOST (object));
+
+ host = GDM_CHOOSER_HOST (object);
+
+ g_return_if_fail (host->priv != NULL);
+
+ g_free (host->priv->description);
+ gdm_address_free (host->priv->address);
+
+ G_OBJECT_CLASS (gdm_chooser_host_parent_class)->finalize (object);
+}
diff --git a/chooser/gdm-chooser-host.h b/chooser/gdm-chooser-host.h
new file mode 100644
index 00000000..fda6e9f9
--- /dev/null
+++ b/chooser/gdm-chooser-host.h
@@ -0,0 +1,63 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __GDM_CHOOSER_HOST__
+#define __GDM_CHOOSER_HOST__
+
+#include <glib-object.h>
+#include "gdm-address.h"
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_CHOOSER_HOST (gdm_chooser_host_get_type ())
+#define GDM_CHOOSER_HOST(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_CHOOSER_HOST, GdmChooserHost))
+#define GDM_CHOOSER_HOST_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_CHOOSER_HOST, GdmChooserHostClass))
+#define GDM_IS_CHOOSER_HOST(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_CHOOSER_HOST))
+#define GDM_IS_CHOOSER_HOST_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_CHOOSER_HOST))
+#define GDM_CHOOSER_HOST_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_CHOOSER_HOST, GdmChooserHostClass))
+
+typedef enum {
+ GDM_CHOOSER_HOST_KIND_XDMCP = 1 << 0,
+} GdmChooserHostKind;
+
+#define GDM_CHOOSER_HOST_KIND_MASK_ALL (GDM_CHOOSER_HOST_KIND_XDMCP)
+
+typedef struct GdmChooserHostPrivate GdmChooserHostPrivate;
+
+typedef struct
+{
+ GObject parent;
+ GdmChooserHostPrivate *priv;
+} GdmChooserHost;
+
+typedef struct
+{
+ GObjectClass parent_class;
+} GdmChooserHostClass;
+
+GType gdm_chooser_host_get_type (void) G_GNUC_CONST;
+
+G_CONST_RETURN char *gdm_chooser_host_get_description (GdmChooserHost *chooser_host);
+GdmAddress * gdm_chooser_host_get_address (GdmChooserHost *chooser_host);
+gboolean gdm_chooser_host_get_willing (GdmChooserHost *chooser_host);
+GdmChooserHostKind gdm_chooser_host_get_kind (GdmChooserHost *chooser_host);
+
+G_END_DECLS
+
+#endif
diff --git a/chooser/gdm-chooser-session.c b/chooser/gdm-chooser-session.c
new file mode 100644
index 00000000..dd618b8a
--- /dev/null
+++ b/chooser/gdm-chooser-session.c
@@ -0,0 +1,342 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib-object.h>
+
+#include "gdm-chooser-session.h"
+#include "gdm-client.h"
+
+#include "gdm-host-chooser-dialog.h"
+
+#define GDM_CHOOSER_SESSION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_CHOOSER_SESSION, GdmChooserSessionPrivate))
+
+struct GdmChooserSessionPrivate
+{
+ GdmClient *client;
+ GdmRemoteGreeter *remote_greeter;
+ GdmChooser *chooser;
+ GtkWidget *chooser_dialog;
+};
+
+enum {
+ PROP_0,
+};
+
+static void gdm_chooser_session_class_init (GdmChooserSessionClass *klass);
+static void gdm_chooser_session_init (GdmChooserSession *chooser_session);
+static void gdm_chooser_session_finalize (GObject *object);
+
+G_DEFINE_TYPE (GdmChooserSession, gdm_chooser_session, G_TYPE_OBJECT)
+
+static gpointer session_object = NULL;
+
+static gboolean
+launch_compiz (GdmChooserSession *session)
+{
+ GError *error;
+ gboolean ret;
+
+ g_debug ("GdmChooserSession: Launching compiz");
+
+ ret = FALSE;
+
+ error = NULL;
+ g_spawn_command_line_async ("gtk-window-decorator --replace", &error);
+ if (error != NULL) {
+ g_warning ("Error starting WM: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ error = NULL;
+ g_spawn_command_line_async ("compiz --replace", &error);
+ if (error != NULL) {
+ g_warning ("Error starting WM: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ ret = TRUE;
+
+ /* FIXME: should try to detect if it actually works */
+
+ out:
+ return ret;
+}
+
+static gboolean
+launch_metacity (GdmChooserSession *session)
+{
+ GError *error;
+ gboolean ret;
+
+ g_debug ("GdmChooserSession: Launching metacity");
+
+ ret = FALSE;
+
+ error = NULL;
+ g_spawn_command_line_async ("metacity --replace", &error);
+ if (error != NULL) {
+ g_warning ("Error starting WM: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ ret = TRUE;
+
+ out:
+ return ret;
+}
+
+static void
+start_window_manager (GdmChooserSession *session)
+{
+ if (! launch_metacity (session)) {
+ launch_compiz (session);
+ }
+}
+
+static gboolean
+start_settings_daemon (GdmChooserSession *session)
+{
+ GError *error;
+ gboolean ret;
+
+ g_debug ("GdmChooserSession: Launching settings daemon");
+
+ ret = FALSE;
+
+ error = NULL;
+ g_spawn_command_line_async (GNOME_SETTINGS_DAEMON_DIR "/gnome-settings-daemon", &error);
+ if (error != NULL) {
+ g_warning ("Error starting settings daemon: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ ret = TRUE;
+
+ out:
+ return ret;
+}
+
+static void
+on_dialog_response (GtkDialog *dialog,
+ int response_id,
+ GdmChooserSession *session)
+{
+ GdmChooserHost *host;
+ GError *error = NULL;
+
+ host = NULL;
+ switch (response_id) {
+ case GTK_RESPONSE_OK:
+ host = gdm_host_chooser_dialog_get_host (GDM_HOST_CHOOSER_DIALOG (dialog));
+ case GTK_RESPONSE_NONE:
+ /* delete event */
+ default:
+ break;
+ }
+
+ if (host != NULL) {
+ char *hostname;
+
+ /* only support XDMCP hosts in remote chooser */
+ g_assert (gdm_chooser_host_get_kind (host) == GDM_CHOOSER_HOST_KIND_XDMCP);
+
+ hostname = NULL;
+ gdm_address_get_hostname (gdm_chooser_host_get_address (host), &hostname);
+ /* FIXME: fall back to numerical address? */
+ if (hostname != NULL) {
+ g_debug ("GdmChooserSession: Selected hostname '%s'", hostname);
+ gdm_chooser_call_select_hostname_sync (session->priv->chooser,
+ hostname,
+ NULL,
+ &error);
+
+ if (error != NULL) {
+ g_debug ("GdmChooserSession: %s", error->message);
+ g_clear_error (&error);
+ }
+ g_free (hostname);
+ }
+ }
+
+ gdm_remote_greeter_call_disconnect_sync (session->priv->remote_greeter,
+ NULL,
+ &error);
+ if (error != NULL) {
+ g_debug ("GdmChooserSession: disconnect failed: %s", error->message);
+ }
+}
+
+gboolean
+gdm_chooser_session_start (GdmChooserSession *session,
+ GError **error)
+{
+ g_return_val_if_fail (GDM_IS_CHOOSER_SESSION (session), FALSE);
+
+ session->priv->remote_greeter = gdm_client_get_remote_greeter_sync (session->priv->client,
+ NULL,
+ error);
+ if (session->priv->remote_greeter == NULL) {
+ return FALSE;
+ }
+
+ session->priv->chooser = gdm_client_get_chooser_sync (session->priv->client,
+ NULL,
+ error);
+ if (session->priv->chooser == NULL) {
+ return FALSE;
+ }
+
+ start_settings_daemon (session);
+ start_window_manager (session);
+
+ /* Only support XDMCP on remote choosers */
+ session->priv->chooser_dialog = gdm_host_chooser_dialog_new (GDM_CHOOSER_HOST_KIND_XDMCP);
+ g_signal_connect (session->priv->chooser_dialog,
+ "response",
+ G_CALLBACK (on_dialog_response),
+ session);
+ gtk_widget_show (session->priv->chooser_dialog);
+
+ return TRUE;
+}
+
+void
+gdm_chooser_session_stop (GdmChooserSession *session)
+{
+ g_return_if_fail (GDM_IS_CHOOSER_SESSION (session));
+
+}
+
+static void
+gdm_chooser_session_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gdm_chooser_session_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static GObject *
+gdm_chooser_session_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ GdmChooserSession *chooser_session;
+
+ chooser_session = GDM_CHOOSER_SESSION (G_OBJECT_CLASS (gdm_chooser_session_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_properties));
+
+ return G_OBJECT (chooser_session);
+}
+
+static void
+gdm_chooser_session_dispose (GObject *object)
+{
+ g_debug ("GdmChooserSession: Disposing chooser_session");
+
+ G_OBJECT_CLASS (gdm_chooser_session_parent_class)->dispose (object);
+}
+
+static void
+gdm_chooser_session_class_init (GdmChooserSessionClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = gdm_chooser_session_get_property;
+ object_class->set_property = gdm_chooser_session_set_property;
+ object_class->constructor = gdm_chooser_session_constructor;
+ object_class->dispose = gdm_chooser_session_dispose;
+ object_class->finalize = gdm_chooser_session_finalize;
+
+ g_type_class_add_private (klass, sizeof (GdmChooserSessionPrivate));
+}
+
+static void
+gdm_chooser_session_init (GdmChooserSession *session)
+{
+
+ session->priv = GDM_CHOOSER_SESSION_GET_PRIVATE (session);
+
+ session->priv->client = gdm_client_new ();
+}
+
+static void
+gdm_chooser_session_finalize (GObject *object)
+{
+ GdmChooserSession *chooser_session;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GDM_IS_CHOOSER_SESSION (object));
+
+ chooser_session = GDM_CHOOSER_SESSION (object);
+
+ g_return_if_fail (chooser_session->priv != NULL);
+
+ g_clear_object (&chooser_session->priv->chooser);
+ g_clear_object (&chooser_session->priv->remote_greeter);
+ g_clear_object (&chooser_session->priv->client);
+
+ G_OBJECT_CLASS (gdm_chooser_session_parent_class)->finalize (object);
+}
+
+GdmChooserSession *
+gdm_chooser_session_new (void)
+{
+ if (session_object != NULL) {
+ g_object_ref (session_object);
+ } else {
+ session_object = g_object_new (GDM_TYPE_CHOOSER_SESSION, NULL);
+ g_object_add_weak_pointer (session_object,
+ (gpointer *) &session_object);
+ }
+
+ return GDM_CHOOSER_SESSION (session_object);
+}
diff --git a/chooser/gdm-chooser-session.h b/chooser/gdm-chooser-session.h
new file mode 100644
index 00000000..b6d8ccdf
--- /dev/null
+++ b/chooser/gdm-chooser-session.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef __GDM_CHOOSER_SESSION_H
+#define __GDM_CHOOSER_SESSION_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_CHOOSER_SESSION (gdm_chooser_session_get_type ())
+#define GDM_CHOOSER_SESSION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_CHOOSER_SESSION, GdmChooserSession))
+#define GDM_CHOOSER_SESSION_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_CHOOSER_SESSION, GdmChooserSessionClass))
+#define GDM_IS_CHOOSER_SESSION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_CHOOSER_SESSION))
+#define GDM_IS_CHOOSER_SESSION_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_CHOOSER_SESSION))
+#define GDM_CHOOSER_SESSION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_CHOOSER_SESSION, GdmChooserSessionClass))
+
+typedef struct GdmChooserSessionPrivate GdmChooserSessionPrivate;
+
+typedef struct
+{
+ GObject parent;
+ GdmChooserSessionPrivate *priv;
+} GdmChooserSession;
+
+typedef struct
+{
+ GObjectClass parent_class;
+} GdmChooserSessionClass;
+
+GType gdm_chooser_session_get_type (void);
+
+GdmChooserSession * gdm_chooser_session_new (void);
+
+gboolean gdm_chooser_session_start (GdmChooserSession *session,
+ GError **error);
+void gdm_chooser_session_stop (GdmChooserSession *session);
+
+G_END_DECLS
+
+#endif /* __GDM_CHOOSER_SESSION_H */
diff --git a/chooser/gdm-host-chooser-dialog.c b/chooser/gdm-host-chooser-dialog.c
new file mode 100644
index 00000000..02169502
--- /dev/null
+++ b/chooser/gdm-host-chooser-dialog.c
@@ -0,0 +1,223 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "gdm-host-chooser-dialog.h"
+#include "gdm-host-chooser-widget.h"
+
+#define GDM_HOST_CHOOSER_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_HOST_CHOOSER_DIALOG, GdmHostChooserDialogPrivate))
+
+struct GdmHostChooserDialogPrivate
+{
+ GtkWidget *chooser_widget;
+ int kind_mask;
+};
+
+enum {
+ PROP_0,
+ PROP_KIND_MASK,
+};
+
+static void gdm_host_chooser_dialog_class_init (GdmHostChooserDialogClass *klass);
+static void gdm_host_chooser_dialog_init (GdmHostChooserDialog *host_chooser_dialog);
+static void gdm_host_chooser_dialog_finalize (GObject *object);
+
+G_DEFINE_TYPE (GdmHostChooserDialog, gdm_host_chooser_dialog, GTK_TYPE_DIALOG)
+
+GdmChooserHost *
+gdm_host_chooser_dialog_get_host (GdmHostChooserDialog *dialog)
+{
+ GdmChooserHost *host;
+
+ g_return_val_if_fail (GDM_IS_HOST_CHOOSER_DIALOG (dialog), NULL);
+
+ host = gdm_host_chooser_widget_get_host (GDM_HOST_CHOOSER_WIDGET (dialog->priv->chooser_widget));
+
+ return host;
+}
+
+static void
+_gdm_host_chooser_dialog_set_kind_mask (GdmHostChooserDialog *dialog,
+ int kind_mask)
+{
+ if (dialog->priv->kind_mask != kind_mask) {
+ dialog->priv->kind_mask = kind_mask;
+ }
+}
+
+static void
+gdm_host_chooser_dialog_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GdmHostChooserDialog *self;
+
+ self = GDM_HOST_CHOOSER_DIALOG (object);
+
+ switch (prop_id) {
+ case PROP_KIND_MASK:
+ _gdm_host_chooser_dialog_set_kind_mask (self, g_value_get_int (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gdm_host_chooser_dialog_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+on_response (GdmHostChooserDialog *dialog,
+ gint response_id)
+{
+ switch (response_id) {
+ case GTK_RESPONSE_APPLY:
+ gdm_host_chooser_widget_refresh (GDM_HOST_CHOOSER_WIDGET (dialog->priv->chooser_widget));
+ g_signal_stop_emission_by_name (dialog, "response");
+ break;
+ default:
+ break;
+ }
+}
+
+static GObject *
+gdm_host_chooser_dialog_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ GdmHostChooserDialog *dialog;
+
+ dialog = GDM_HOST_CHOOSER_DIALOG (G_OBJECT_CLASS (gdm_host_chooser_dialog_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_properties));
+
+
+ dialog->priv->chooser_widget = gdm_host_chooser_widget_new (dialog->priv->kind_mask);
+ gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), dialog->priv->chooser_widget);
+ gtk_container_set_border_width (GTK_CONTAINER (dialog->priv->chooser_widget), 5);
+
+ gtk_dialog_add_buttons (GTK_DIALOG (dialog),
+ GTK_STOCK_REFRESH, GTK_RESPONSE_APPLY,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_CONNECT, GTK_RESPONSE_OK,
+ NULL);
+
+ gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER_ALWAYS);
+ gtk_container_set_border_width (GTK_CONTAINER (dialog), 12);
+ gtk_window_set_title (GTK_WINDOW (dialog), _("Select System"));
+ gtk_window_set_icon_name (GTK_WINDOW (dialog), "computer");
+
+ g_signal_connect (dialog,
+ "response",
+ G_CALLBACK (on_response),
+ dialog);
+
+ gtk_widget_show_all (GTK_WIDGET (dialog));
+
+ return G_OBJECT (dialog);
+}
+
+static void
+gdm_host_chooser_dialog_dispose (GObject *object)
+{
+ g_debug ("Disposing host_chooser_dialog");
+
+ G_OBJECT_CLASS (gdm_host_chooser_dialog_parent_class)->dispose (object);
+}
+
+static void
+gdm_host_chooser_dialog_class_init (GdmHostChooserDialogClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = gdm_host_chooser_dialog_get_property;
+ object_class->set_property = gdm_host_chooser_dialog_set_property;
+ object_class->constructor = gdm_host_chooser_dialog_constructor;
+ object_class->dispose = gdm_host_chooser_dialog_dispose;
+ object_class->finalize = gdm_host_chooser_dialog_finalize;
+
+ g_object_class_install_property (object_class,
+ PROP_KIND_MASK,
+ g_param_spec_int ("kind-mask",
+ "kind mask",
+ "kind mask",
+ 0,
+ G_MAXINT,
+ 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ g_type_class_add_private (klass, sizeof (GdmHostChooserDialogPrivate));
+}
+
+static void
+gdm_host_chooser_dialog_init (GdmHostChooserDialog *dialog)
+{
+ dialog->priv = GDM_HOST_CHOOSER_DIALOG_GET_PRIVATE (dialog);
+}
+
+static void
+gdm_host_chooser_dialog_finalize (GObject *object)
+{
+ GdmHostChooserDialog *host_chooser_dialog;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GDM_IS_HOST_CHOOSER_DIALOG (object));
+
+ host_chooser_dialog = GDM_HOST_CHOOSER_DIALOG (object);
+
+ g_return_if_fail (host_chooser_dialog->priv != NULL);
+
+ G_OBJECT_CLASS (gdm_host_chooser_dialog_parent_class)->finalize (object);
+}
+
+GtkWidget *
+gdm_host_chooser_dialog_new (int kind_mask)
+{
+ GObject *object;
+
+ object = g_object_new (GDM_TYPE_HOST_CHOOSER_DIALOG,
+ "kind-mask", kind_mask,
+ NULL);
+
+ return GTK_WIDGET (object);
+}
diff --git a/chooser/gdm-host-chooser-dialog.h b/chooser/gdm-host-chooser-dialog.h
new file mode 100644
index 00000000..f413091b
--- /dev/null
+++ b/chooser/gdm-host-chooser-dialog.h
@@ -0,0 +1,60 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef __GDM_HOST_CHOOSER_DIALOG_H
+#define __GDM_HOST_CHOOSER_DIALOG_H
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include "gdm-chooser-host.h"
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_HOST_CHOOSER_DIALOG (gdm_host_chooser_dialog_get_type ())
+#define GDM_HOST_CHOOSER_DIALOG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_HOST_CHOOSER_DIALOG, GdmHostChooserDialog))
+#define GDM_HOST_CHOOSER_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_HOST_CHOOSER_DIALOG, GdmHostChooserDialogClass))
+#define GDM_IS_HOST_CHOOSER_DIALOG(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_HOST_CHOOSER_DIALOG))
+#define GDM_IS_HOST_CHOOSER_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_HOST_CHOOSER_DIALOG))
+#define GDM_HOST_CHOOSER_DIALOG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_HOST_CHOOSER_DIALOG, GdmHostChooserDialogClass))
+
+typedef struct GdmHostChooserDialogPrivate GdmHostChooserDialogPrivate;
+
+typedef struct
+{
+ GtkDialog parent;
+ GdmHostChooserDialogPrivate *priv;
+} GdmHostChooserDialog;
+
+typedef struct
+{
+ GtkDialogClass parent_class;
+} GdmHostChooserDialogClass;
+
+GType gdm_host_chooser_dialog_get_type (void);
+
+GtkWidget * gdm_host_chooser_dialog_new (int kind_mask);
+void gdm_host_chooser_dialog_set_kind_mask (GdmHostChooserDialog *dialog,
+ int kind_mask);
+
+GdmChooserHost * gdm_host_chooser_dialog_get_host (GdmHostChooserDialog *dialog);
+
+G_END_DECLS
+
+#endif /* __GDM_HOST_CHOOSER_DIALOG_H */
diff --git a/chooser/gdm-host-chooser-widget.c b/chooser/gdm-host-chooser-widget.c
new file mode 100644
index 00000000..f9200aa0
--- /dev/null
+++ b/chooser/gdm-host-chooser-widget.c
@@ -0,0 +1,860 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 1998, 1999, 2000 Martin K, Petersen <mkp@mkp.net>
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#ifdef HAVE_SYS_SOCKIO_H
+#include <sys/sockio.h>
+#endif
+
+#include <X11/Xmd.h>
+#include <X11/Xdmcp.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "gdm-address.h"
+#include "gdm-chooser-host.h"
+#include "gdm-host-chooser-widget.h"
+
+#define GDM_HOST_CHOOSER_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_HOST_CHOOSER_WIDGET, GdmHostChooserWidgetPrivate))
+
+struct GdmHostChooserWidgetPrivate
+{
+ GtkWidget *treeview;
+
+ int kind_mask;
+
+ char **hosts;
+
+ XdmcpBuffer broadcast_buf;
+ XdmcpBuffer query_buf;
+ gboolean have_ipv6;
+ int socket_fd;
+ guint io_watch_id;
+ guint scan_time_id;
+ guint ping_try_id;
+
+ int ping_tries;
+
+ GSList *broadcast_addresses;
+ GSList *query_addresses;
+ GSList *chooser_hosts;
+
+ GdmChooserHost *current_host;
+};
+
+enum {
+ PROP_0,
+ PROP_KIND_MASK,
+};
+
+enum {
+ HOST_ACTIVATED,
+ LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+static void gdm_host_chooser_widget_class_init (GdmHostChooserWidgetClass *klass);
+static void gdm_host_chooser_widget_init (GdmHostChooserWidget *host_chooser_widget);
+static void gdm_host_chooser_widget_finalize (GObject *object);
+
+G_DEFINE_TYPE (GdmHostChooserWidget, gdm_host_chooser_widget, GTK_TYPE_VBOX)
+
+#define GDM_XDMCP_PROTOCOL_VERSION 1001
+#define SCAN_TIMEOUT 30
+#define PING_TIMEOUT 2
+#define PING_TRIES 3
+
+enum {
+ CHOOSER_LIST_ICON_COLUMN = 0,
+ CHOOSER_LIST_LABEL_COLUMN,
+ CHOOSER_LIST_HOST_COLUMN
+};
+
+static void
+chooser_host_add (GdmHostChooserWidget *widget,
+ GdmChooserHost *host)
+{
+ widget->priv->chooser_hosts = g_slist_prepend (widget->priv->chooser_hosts, host);
+}
+
+#if 0
+static void
+chooser_host_remove (GdmHostChooserWidget *widget,
+ GdmChooserHost *host)
+{
+ widget->priv->chooser_hosts = g_slist_remove (widget->priv->chooser_hosts, host);
+}
+#endif
+
+static GdmChooserHost *
+find_known_host (GdmHostChooserWidget *widget,
+ GdmAddress *address)
+{
+ GSList *li;
+ GdmChooserHost *host;
+
+ for (li = widget->priv->chooser_hosts; li != NULL; li = li->next) {
+ host = li->data;
+ if (gdm_address_equal (gdm_chooser_host_get_address (host), address)) {
+ goto out;
+ }
+ }
+
+ host = NULL;
+ out:
+
+ return host;
+}
+
+static void
+browser_add_host (GdmHostChooserWidget *widget,
+ GdmChooserHost *host)
+{
+ char *hostname;
+ char *name;
+ char *desc;
+ char *label;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gboolean res;
+
+ g_assert (host != NULL);
+
+ if (! gdm_chooser_host_get_willing (host)) {
+ gtk_widget_set_sensitive (GTK_WIDGET (widget), TRUE);
+ return;
+ }
+
+ res = gdm_address_get_hostname (gdm_chooser_host_get_address (host), &hostname);
+ if (! res) {
+ gdm_address_get_numeric_info (gdm_chooser_host_get_address (host), &hostname, NULL);
+ }
+
+ name = g_markup_escape_text (hostname, -1);
+ desc = g_markup_escape_text (gdm_chooser_host_get_description (host), -1);
+ label = g_strdup_printf ("<b>%s</b>\n%s", name, desc);
+ g_free (name);
+ g_free (desc);
+
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget->priv->treeview));
+
+ gtk_list_store_append (GTK_LIST_STORE (model), &iter);
+ gtk_list_store_set (GTK_LIST_STORE (model),
+ &iter,
+ CHOOSER_LIST_ICON_COLUMN, NULL,
+ CHOOSER_LIST_LABEL_COLUMN, label,
+ CHOOSER_LIST_HOST_COLUMN, host,
+ -1);
+ g_free (label);
+
+}
+
+static gboolean
+decode_packet (GIOChannel *source,
+ GIOCondition condition,
+ GdmHostChooserWidget *widget)
+{
+ struct sockaddr_storage clnt_ss;
+ GdmAddress *address;
+ int ss_len;
+ XdmcpHeader header;
+ int res;
+ static XdmcpBuffer buf;
+ ARRAY8 auth = {0};
+ ARRAY8 host = {0};
+ ARRAY8 stat = {0};
+ char *status;
+ GdmChooserHost *chooser_host;
+
+ status = NULL;
+ address = NULL;
+
+ g_debug ("decode_packet: GIOCondition %d", (int)condition);
+
+ if ( ! (condition & G_IO_IN)) {
+ return TRUE;
+ }
+
+ ss_len = (int) sizeof (clnt_ss);
+
+ res = XdmcpFill (widget->priv->socket_fd, &buf, (XdmcpNetaddr)&clnt_ss, &ss_len);
+ if G_UNLIKELY (! res) {
+ g_debug (_("XDMCP: Could not create XDMCP buffer!"));
+ return TRUE;
+ }
+
+ res = XdmcpReadHeader (&buf, &header);
+ if G_UNLIKELY (! res) {
+ g_warning (_("XDMCP: Could not read XDMCP header!"));
+ return TRUE;
+ }
+
+ if G_UNLIKELY (header.version != XDM_PROTOCOL_VERSION &&
+ header.version != GDM_XDMCP_PROTOCOL_VERSION) {
+ g_warning (_("XDMCP: Incorrect XDMCP version!"));
+ return TRUE;
+ }
+
+ address = gdm_address_new_from_sockaddr ((struct sockaddr *) &clnt_ss, ss_len);
+ if (address == NULL) {
+ g_warning (_("XDMCP: Unable to parse address"));
+ return TRUE;
+ }
+
+ gdm_address_debug (address);
+
+ if (header.opcode == WILLING) {
+ if (! XdmcpReadARRAY8 (&buf, &auth)) {
+ goto done;
+ }
+
+ if (! XdmcpReadARRAY8 (&buf, &host)) {
+ goto done;
+ }
+
+ if (! XdmcpReadARRAY8 (&buf, &stat)) {
+ goto done;
+ }
+
+ status = g_strndup ((char *) stat.data, MIN (stat.length, 256));
+ } else if (header.opcode == UNWILLING) {
+ /* immaterial, will not be shown */
+ status = NULL;
+ } else {
+ goto done;
+ }
+
+ g_debug ("STATUS: %s", status);
+
+ chooser_host = find_known_host (widget, address);
+ if (chooser_host == NULL) {
+ chooser_host = g_object_new (GDM_TYPE_CHOOSER_HOST,
+ "address", address,
+ "description", status,
+ "willing", (header.opcode == WILLING),
+ "kind", GDM_CHOOSER_HOST_KIND_XDMCP,
+ NULL);
+ chooser_host_add (widget, chooser_host);
+ browser_add_host (widget, chooser_host);
+ } else {
+ /* server changed it's mind */
+ if (header.opcode == WILLING
+ && ! gdm_chooser_host_get_willing (chooser_host)) {
+ g_object_set (chooser_host, "willing", TRUE, NULL);
+ browser_add_host (widget, chooser_host);
+ }
+ /* FIXME: handle unwilling? */
+ }
+
+ done:
+ if (header.opcode == WILLING) {
+ XdmcpDisposeARRAY8 (&auth);
+ XdmcpDisposeARRAY8 (&host);
+ XdmcpDisposeARRAY8 (&stat);
+ }
+
+ g_free (status);
+ gdm_address_free (address);
+
+ return TRUE;
+}
+
+static void
+do_ping (GdmHostChooserWidget *widget,
+ gboolean full)
+{
+ GSList *l;
+
+ g_debug ("do ping full:%d", full);
+
+ for (l = widget->priv->broadcast_addresses; l != NULL; l = l->next) {
+ GdmAddress *address;
+ int res;
+
+ address = l->data;
+
+ gdm_address_debug (address);
+ errno = 0;
+ g_debug ("fd:%d", widget->priv->socket_fd);
+ res = XdmcpFlush (widget->priv->socket_fd,
+ &widget->priv->broadcast_buf,
+ (XdmcpNetaddr)gdm_address_peek_sockaddr_storage (address),
+ (int)gdm_sockaddr_len (gdm_address_peek_sockaddr_storage (address)));
+
+ if (! res) {
+ g_warning ("Unable to flush the XDMCP broadcast packet: %s", g_strerror (errno));
+ }
+ }
+
+ if (full) {
+ for (l = widget->priv->query_addresses; l != NULL; l = l->next) {
+ GdmAddress *address;
+ int res;
+
+ address = l->data;
+
+ gdm_address_debug (address);
+ res = XdmcpFlush (widget->priv->socket_fd,
+ &widget->priv->query_buf,
+ (XdmcpNetaddr)gdm_address_peek_sockaddr_storage (address),
+ (int)gdm_sockaddr_len (gdm_address_peek_sockaddr_storage (address)));
+ if (! res) {
+ g_warning ("Unable to flush the XDMCP query packet");
+ }
+ }
+ }
+}
+
+static gboolean
+ping_try (GdmHostChooserWidget *widget)
+{
+ do_ping (widget, FALSE);
+
+ widget->priv->ping_tries --;
+
+ if (widget->priv->ping_tries <= 0) {
+ widget->priv->ping_try_id = 0;
+ return FALSE;
+ } else {
+ return TRUE;
+ }
+}
+
+static void
+xdmcp_discover (GdmHostChooserWidget *widget)
+{
+#if 0
+ gtk_widget_set_sensitive (GTK_WIDGET (manage), FALSE);
+ gtk_widget_set_sensitive (GTK_WIDGET (rescan), FALSE);
+ gtk_list_store_clear (GTK_LIST_STORE (browser_model));
+ gtk_widget_set_sensitive (GTK_WIDGET (browser), FALSE);
+ gtk_label_set_label (GTK_LABEL (status_label),
+ _(scanning_message));
+
+ while (hl) {
+ gdm_chooser_host_dispose ((GdmChooserHost *) hl->data);
+ hl = hl->next;
+ }
+
+ g_list_free (chooser_hosts);
+ chooser_hosts = NULL;
+#endif
+
+ do_ping (widget, TRUE);
+
+#if 0
+ if (widget->priv->scan_time_id > 0) {
+ g_source_remove (widget->priv->scan_time_id);
+ }
+
+ widget->priv->scan_time_id = g_timeout_add_seconds (SCAN_TIMEOUT,
+ chooser_scan_time_update,
+ widget);
+#endif
+ /* Note we already used up one try */
+ widget->priv->ping_tries = PING_TRIES - 1;
+ if (widget->priv->ping_try_id > 0) {
+ g_source_remove (widget->priv->ping_try_id);
+ }
+
+ widget->priv->ping_try_id = g_timeout_add_seconds (PING_TIMEOUT,
+ (GSourceFunc)ping_try,
+ widget);
+}
+
+/* Find broadcast address for all active, non pointopoint interfaces */
+static void
+find_broadcast_addresses (GdmHostChooserWidget *widget)
+{
+ int i;
+ int num;
+ int sock;
+ struct ifconf ifc;
+ char *buf;
+ struct ifreq *ifr;
+
+ g_debug ("Finding broadcast addresses");
+
+ sock = socket (AF_INET, SOCK_DGRAM, 0);
+#ifdef SIOCGIFNUM
+ if (ioctl (sock, SIOCGIFNUM, &num) < 0) {
+ num = 64;
+ }
+#else
+ num = 64;
+#endif
+
+ ifc.ifc_len = sizeof (struct ifreq) * num;
+ ifc.ifc_buf = buf = g_malloc0 (ifc.ifc_len);
+ if (ioctl (sock, SIOCGIFCONF, &ifc) < 0) {
+ g_warning ("Could not get local addresses!");
+ goto out;
+ }
+
+ ifr = ifc.ifc_req;
+ num = ifc.ifc_len / sizeof (struct ifreq);
+ for (i = 0 ; i < num ; i++) {
+ const char *name;
+
+ name = ifr[i].ifr_name;
+ g_debug ("Checking if %s", name);
+ if (name != NULL && name[0] != '\0') {
+ struct ifreq ifreq;
+ GdmAddress *address;
+ struct sockaddr_in sin;
+
+ memset (&ifreq, 0, sizeof (ifreq));
+
+ strncpy (ifreq.ifr_name,
+ ifr[i].ifr_name,
+ sizeof (ifreq.ifr_name));
+ /* paranoia */
+ ifreq.ifr_name[sizeof (ifreq.ifr_name) - 1] = '\0';
+
+ if ((ioctl (sock, SIOCGIFFLAGS, &ifreq) < 0) && (errno != ENXIO)) {
+ g_warning ("Could not get SIOCGIFFLAGS for %s", ifr[i].ifr_name);
+ }
+
+ if ((ifreq.ifr_flags & IFF_UP) == 0 ||
+ (ifreq.ifr_flags & IFF_BROADCAST) == 0 ||
+ ioctl (sock, SIOCGIFBRDADDR, &ifreq) < 0) {
+ g_debug ("Skipping if %s", name);
+ continue;
+ }
+
+ g_memmove (&sin, &ifreq.ifr_broadaddr, sizeof (struct sockaddr_in));
+ sin.sin_port = htons (XDM_UDP_PORT);
+ address = gdm_address_new_from_sockaddr ((struct sockaddr *) &sin, sizeof (sin));
+ if (address != NULL) {
+ g_debug ("Adding if %s", name);
+ gdm_address_debug (address);
+
+ widget->priv->broadcast_addresses = g_slist_append (widget->priv->broadcast_addresses, address);
+ }
+ }
+ }
+ out:
+ g_free (buf);
+ close (sock);
+}
+
+static void
+add_hosts (GdmHostChooserWidget *widget)
+{
+ int i;
+
+ for (i = 0; widget->priv->hosts != NULL && widget->priv->hosts[i] != NULL; i++) {
+ struct addrinfo hints;
+ struct addrinfo *result;
+ struct addrinfo *ai;
+ int gaierr;
+ const char *name;
+ char serv_buf [NI_MAXSERV];
+ const char *serv;
+
+ name = widget->priv->hosts[i];
+
+ if (strcmp (name, "BROADCAST") == 0) {
+ find_broadcast_addresses (widget);
+ continue;
+ }
+
+ if (strcmp (name, "MULTICAST") == 0) {
+ /*gdm_chooser_find_mcaddr ();*/
+ continue;
+ }
+
+ result = NULL;
+ memset (&hints, 0, sizeof (hints));
+ hints.ai_socktype = SOCK_STREAM;
+
+ snprintf (serv_buf, sizeof (serv_buf), "%u", XDM_UDP_PORT);
+ serv = serv_buf;
+
+ gaierr = getaddrinfo (name, serv, &hints, &result);
+ if (gaierr != 0) {
+ g_warning ("Unable to get address info for name %s: %s", name, gai_strerror (gaierr));
+ continue;
+ }
+
+ for (ai = result; ai != NULL; ai = ai->ai_next) {
+ GdmAddress *address;
+
+ address = gdm_address_new_from_sockaddr (ai->ai_addr, ai->ai_addrlen);
+ if (address != NULL) {
+ widget->priv->query_addresses = g_slist_append (widget->priv->query_addresses, address);
+ }
+ }
+
+ freeaddrinfo (result);
+ }
+
+ if (widget->priv->broadcast_addresses == NULL && widget->priv->query_addresses == NULL) {
+ find_broadcast_addresses (widget);
+ }
+}
+
+static void
+xdmcp_init (GdmHostChooserWidget *widget)
+{
+ XdmcpHeader header;
+ int sockopts;
+ int res;
+ GIOChannel *ioc;
+ ARRAYofARRAY8 aanames;
+
+ sockopts = 1;
+
+ widget->priv->socket_fd = -1;
+
+ /* Open socket for communication */
+#ifdef ENABLE_IPV6
+ widget->priv->socket_fd = socket (AF_INET6, SOCK_DGRAM, 0);
+ if (widget->priv->socket_fd != -1) {
+ widget->priv->have_ipv6 = TRUE;
+#ifdef IPV6_V6ONLY
+ {
+ int zero = 0;
+ if (setsockopt(widget->priv->socket_fd, IPPROTO_IPV6, IPV6_V6ONLY, &zero, sizeof(zero)) < 0)
+ g_warning("setsockopt(IPV6_V6ONLY): %s", g_strerror(errno));
+ }
+#endif
+ }
+#endif
+ if (! widget->priv->have_ipv6) {
+ widget->priv->socket_fd = socket (AF_INET, SOCK_DGRAM, 0);
+ if (widget->priv->socket_fd == -1) {
+ g_critical ("Could not create socket!");
+ }
+ }
+
+ res = setsockopt (widget->priv->socket_fd,
+ SOL_SOCKET,
+ SO_BROADCAST,
+ (char *) &sockopts,
+ sizeof (sockopts));
+ if (res < 0) {
+ g_critical ("Could not set socket options!");
+ }
+
+ /* Assemble XDMCP BROADCAST_QUERY packet in static buffer */
+ memset (&header, 0, sizeof (XdmcpHeader));
+ header.opcode = (CARD16) BROADCAST_QUERY;
+ header.length = 1;
+ header.version = XDM_PROTOCOL_VERSION;
+ aanames.length = 0;
+ XdmcpWriteHeader (&widget->priv->broadcast_buf, &header);
+ XdmcpWriteARRAYofARRAY8 (&widget->priv->broadcast_buf, &aanames);
+
+ /* Assemble XDMCP QUERY packet in static buffer */
+ memset (&header, 0, sizeof (XdmcpHeader));
+ header.opcode = (CARD16) QUERY;
+ header.length = 1;
+ header.version = XDM_PROTOCOL_VERSION;
+ memset (&widget->priv->query_buf, 0, sizeof (XdmcpBuffer));
+ XdmcpWriteHeader (&widget->priv->query_buf, &header);
+ XdmcpWriteARRAYofARRAY8 (&widget->priv->query_buf, &aanames);
+
+ add_hosts (widget);
+
+ ioc = g_io_channel_unix_new (widget->priv->socket_fd);
+ g_io_channel_set_encoding (ioc, NULL, NULL);
+ g_io_channel_set_buffered (ioc, FALSE);
+ widget->priv->io_watch_id = g_io_add_watch (ioc,
+ G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ (GIOFunc)decode_packet,
+ widget);
+ g_io_channel_unref (ioc);
+}
+
+void
+gdm_host_chooser_widget_refresh (GdmHostChooserWidget *widget)
+{
+ g_return_if_fail (GDM_IS_HOST_CHOOSER_WIDGET (widget));
+
+ xdmcp_discover (widget);
+}
+
+GdmChooserHost *
+gdm_host_chooser_widget_get_host (GdmHostChooserWidget *widget)
+{
+ GdmChooserHost *host;
+
+ g_return_val_if_fail (GDM_IS_HOST_CHOOSER_WIDGET (widget), NULL);
+
+ host = NULL;
+ if (widget->priv->current_host != NULL) {
+ host = g_object_ref (widget->priv->current_host);
+ }
+
+ return host;
+}
+
+static void
+_gdm_host_chooser_widget_set_kind_mask (GdmHostChooserWidget *widget,
+ int kind_mask)
+{
+ if (widget->priv->kind_mask != kind_mask) {
+ widget->priv->kind_mask = kind_mask;
+ }
+}
+
+static void
+gdm_host_chooser_widget_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GdmHostChooserWidget *self;
+
+ self = GDM_HOST_CHOOSER_WIDGET (object);
+
+ switch (prop_id) {
+ case PROP_KIND_MASK:
+ _gdm_host_chooser_widget_set_kind_mask (self, g_value_get_int (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gdm_host_chooser_widget_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static GObject *
+gdm_host_chooser_widget_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ GdmHostChooserWidget *widget;
+
+ widget = GDM_HOST_CHOOSER_WIDGET (G_OBJECT_CLASS (gdm_host_chooser_widget_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_properties));
+
+ xdmcp_init (widget);
+ xdmcp_discover (widget);
+
+ return G_OBJECT (widget);
+}
+
+static void
+gdm_host_chooser_widget_dispose (GObject *object)
+{
+ GdmHostChooserWidget *widget;
+
+ widget = GDM_HOST_CHOOSER_WIDGET (object);
+
+ g_debug ("Disposing host_chooser_widget");
+
+ if (widget->priv->broadcast_addresses != NULL) {
+ g_slist_foreach (widget->priv->broadcast_addresses,
+ (GFunc)gdm_address_free,
+ NULL);
+ g_slist_free (widget->priv->broadcast_addresses);
+ widget->priv->broadcast_addresses = NULL;
+ }
+ if (widget->priv->query_addresses != NULL) {
+ g_slist_foreach (widget->priv->query_addresses,
+ (GFunc)gdm_address_free,
+ NULL);
+ g_slist_free (widget->priv->query_addresses);
+ widget->priv->query_addresses = NULL;
+ }
+ if (widget->priv->chooser_hosts != NULL) {
+ g_slist_foreach (widget->priv->chooser_hosts,
+ (GFunc)g_object_unref,
+ NULL);
+ g_slist_free (widget->priv->chooser_hosts);
+ widget->priv->chooser_hosts = NULL;
+ }
+
+ widget->priv->current_host = NULL;
+
+ G_OBJECT_CLASS (gdm_host_chooser_widget_parent_class)->dispose (object);
+}
+
+static void
+gdm_host_chooser_widget_class_init (GdmHostChooserWidgetClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = gdm_host_chooser_widget_get_property;
+ object_class->set_property = gdm_host_chooser_widget_set_property;
+ object_class->constructor = gdm_host_chooser_widget_constructor;
+ object_class->dispose = gdm_host_chooser_widget_dispose;
+ object_class->finalize = gdm_host_chooser_widget_finalize;
+
+ g_object_class_install_property (object_class,
+ PROP_KIND_MASK,
+ g_param_spec_int ("kind-mask",
+ "kind mask",
+ "kind mask",
+ 0,
+ G_MAXINT,
+ 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ signals [HOST_ACTIVATED] = g_signal_new ("host-activated",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GdmHostChooserWidgetClass, host_activated),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+ g_type_class_add_private (klass, sizeof (GdmHostChooserWidgetPrivate));
+}
+
+static void
+on_host_selected (GtkTreeSelection *selection,
+ GdmHostChooserWidget *widget)
+{
+ GtkTreeModel *model = NULL;
+ GtkTreeIter iter = {0};
+ GdmChooserHost *curhost;
+
+ curhost = NULL;
+
+ if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+ gtk_tree_model_get (model, &iter, CHOOSER_LIST_HOST_COLUMN, &curhost, -1);
+ }
+
+ widget->priv->current_host = curhost;
+}
+
+static void
+on_row_activated (GtkTreeView *tree_view,
+ GtkTreePath *tree_path,
+ GtkTreeViewColumn *tree_column,
+ GdmHostChooserWidget *widget)
+{
+ g_signal_emit (widget, signals[HOST_ACTIVATED], 0);
+}
+
+static void
+gdm_host_chooser_widget_init (GdmHostChooserWidget *widget)
+{
+ GtkWidget *scrolled;
+ GtkTreeSelection *selection;
+ GtkTreeViewColumn *column;
+ GtkTreeModel *model;
+
+ widget->priv = GDM_HOST_CHOOSER_WIDGET_GET_PRIVATE (widget);
+
+ scrolled = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled),
+ GTK_SHADOW_IN);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
+ GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+ gtk_box_pack_start (GTK_BOX (widget), scrolled, TRUE, TRUE, 0);
+
+ widget->priv->treeview = gtk_tree_view_new ();
+ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (widget->priv->treeview), FALSE);
+ g_signal_connect (widget->priv->treeview,
+ "row-activated",
+ G_CALLBACK (on_row_activated),
+ widget);
+ gtk_container_add (GTK_CONTAINER (scrolled), widget->priv->treeview);
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget->priv->treeview));
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
+ g_signal_connect (selection, "changed",
+ G_CALLBACK (on_host_selected),
+ widget);
+
+ model = (GtkTreeModel *)gtk_list_store_new (3,
+ GDK_TYPE_PIXBUF,
+ G_TYPE_STRING,
+ G_TYPE_POINTER);
+ gtk_tree_view_set_model (GTK_TREE_VIEW (widget->priv->treeview), model);
+
+ column = gtk_tree_view_column_new_with_attributes ("Icon",
+ gtk_cell_renderer_pixbuf_new (),
+ "pixbuf", CHOOSER_LIST_ICON_COLUMN,
+ NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (widget->priv->treeview), column);
+
+ column = gtk_tree_view_column_new_with_attributes ("Hostname",
+ gtk_cell_renderer_text_new (),
+ "markup", CHOOSER_LIST_LABEL_COLUMN,
+ NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (widget->priv->treeview), column);
+
+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model),
+ CHOOSER_LIST_LABEL_COLUMN,
+ GTK_SORT_ASCENDING);
+}
+
+static void
+gdm_host_chooser_widget_finalize (GObject *object)
+{
+ GdmHostChooserWidget *host_chooser_widget;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GDM_IS_HOST_CHOOSER_WIDGET (object));
+
+ host_chooser_widget = GDM_HOST_CHOOSER_WIDGET (object);
+
+ g_return_if_fail (host_chooser_widget->priv != NULL);
+
+ G_OBJECT_CLASS (gdm_host_chooser_widget_parent_class)->finalize (object);
+}
+
+GtkWidget *
+gdm_host_chooser_widget_new (int kind_mask)
+{
+ GObject *object;
+
+ object = g_object_new (GDM_TYPE_HOST_CHOOSER_WIDGET,
+ "kind-mask", kind_mask,
+ NULL);
+
+ return GTK_WIDGET (object);
+}
diff --git a/chooser/gdm-host-chooser-widget.h b/chooser/gdm-host-chooser-widget.h
new file mode 100644
index 00000000..739eac01
--- /dev/null
+++ b/chooser/gdm-host-chooser-widget.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef __GDM_HOST_CHOOSER_WIDGET_H
+#define __GDM_HOST_CHOOSER_WIDGET_H
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include "gdm-chooser-host.h"
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_HOST_CHOOSER_WIDGET (gdm_host_chooser_widget_get_type ())
+#define GDM_HOST_CHOOSER_WIDGET(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_HOST_CHOOSER_WIDGET, GdmHostChooserWidget))
+#define GDM_HOST_CHOOSER_WIDGET_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_HOST_CHOOSER_WIDGET, GdmHostChooserWidgetClass))
+#define GDM_IS_HOST_CHOOSER_WIDGET(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_HOST_CHOOSER_WIDGET))
+#define GDM_IS_HOST_CHOOSER_WIDGET_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_HOST_CHOOSER_WIDGET))
+#define GDM_HOST_CHOOSER_WIDGET_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_HOST_CHOOSER_WIDGET, GdmHostChooserWidgetClass))
+
+typedef struct GdmHostChooserWidgetPrivate GdmHostChooserWidgetPrivate;
+
+typedef struct
+{
+ GtkVBox parent;
+ GdmHostChooserWidgetPrivate *priv;
+} GdmHostChooserWidget;
+
+typedef struct
+{
+ GtkVBoxClass parent_class;
+
+ /* signals */
+ void (* host_activated) (GdmHostChooserWidget *widget);
+} GdmHostChooserWidgetClass;
+
+GType gdm_host_chooser_widget_get_type (void);
+GtkWidget * gdm_host_chooser_widget_new (int kind_mask);
+
+void gdm_host_chooser_widget_set_kind_mask (GdmHostChooserWidget *widget,
+ int kind_mask);
+
+void gdm_host_chooser_widget_refresh (GdmHostChooserWidget *widget);
+
+GdmChooserHost * gdm_host_chooser_widget_get_host (GdmHostChooserWidget *widget);
+
+G_END_DECLS
+
+#endif /* __GDM_HOST_CHOOSER_WIDGET_H */
diff --git a/chooser/gdm-host-chooser.c b/chooser/gdm-host-chooser.c
new file mode 100644
index 00000000..47f37d47
--- /dev/null
+++ b/chooser/gdm-host-chooser.c
@@ -0,0 +1,252 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <libintl.h>
+#include <locale.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+
+#include <glib/gi18n.h>
+#include <gdk/gdkx.h>
+#include <gtk/gtk.h>
+
+#include "gdm-common.h"
+#include "gdm-log.h"
+
+#include "gdm-chooser-host.h"
+#include "gdm-host-chooser-dialog.h"
+
+#define ACCESSIBILITY_KEY "/desktop/gnome/interface/accessibility"
+
+static Atom AT_SPI_IOR;
+
+
+static gboolean
+assistive_registry_launch (void)
+{
+ GPid pid;
+ GError *error;
+ const char *command;
+ char **argv;
+ gboolean res;
+
+ command = AT_SPI_REGISTRYD_DIR "/at-spi-registryd";
+
+ argv = NULL;
+ error = NULL;
+ res = g_shell_parse_argv (command, NULL, &argv, &error);
+ if (! res) {
+ g_warning ("Unable to parse command: %s", error->message);
+ return FALSE;
+ }
+
+ error = NULL;
+ res = g_spawn_async (NULL,
+ argv,
+ NULL,
+ G_SPAWN_SEARCH_PATH
+ | G_SPAWN_STDOUT_TO_DEV_NULL
+ | G_SPAWN_STDERR_TO_DEV_NULL,
+ NULL,
+ NULL,
+ &pid,
+ &error);
+ g_strfreev (argv);
+
+ if (! res) {
+ g_warning ("Unable to run command %s: %s", command, error->message);
+ return FALSE;
+ }
+
+ if (kill (pid, 0) < 0) {
+ g_warning ("at-spi-registryd not running");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static GdkFilterReturn
+filter_watch (GdkXEvent *xevent,
+ GdkEvent *event,
+ gpointer data)
+{
+ XEvent *xev = (XEvent *)xevent;
+
+ if (xev->xany.type == PropertyNotify
+ && xev->xproperty.atom == AT_SPI_IOR) {
+ gtk_main_quit ();
+
+ return GDK_FILTER_REMOVE;
+ }
+
+ return GDK_FILTER_CONTINUE;
+}
+
+static gboolean
+filter_timeout (gpointer data)
+{
+ g_warning ("The accessibility registry was not found.");
+
+ gtk_main_quit ();
+
+ return FALSE;
+}
+
+static void
+assistive_registry_start (void)
+{
+ GdkWindow *root;
+ guint tid;
+
+ root = gdk_get_default_root_window ();
+
+ if ( ! AT_SPI_IOR) {
+ AT_SPI_IOR = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "AT_SPI_IOR", False);
+ }
+
+ gdk_window_set_events (root, GDK_PROPERTY_CHANGE_MASK);
+
+ if ( ! assistive_registry_launch ()) {
+ g_warning ("The accessibility registry could not be started.");
+ return;
+ }
+
+ gdk_window_add_filter (root, filter_watch, NULL);
+ tid = g_timeout_add_seconds (5, filter_timeout, NULL);
+
+ gtk_main ();
+
+ gdk_window_remove_filter (root, filter_watch, NULL);
+ g_source_remove (tid);
+}
+
+static void
+at_set_gtk_modules (void)
+{
+ GSList *modules_list;
+ GSList *l;
+ const char *old;
+ char **modules;
+ gboolean found_gail;
+ gboolean found_atk_bridge;
+ int n;
+
+ n = 0;
+ modules_list = NULL;
+ found_gail = FALSE;
+ found_atk_bridge = FALSE;
+
+ if ((old = g_getenv ("GTK_MODULES")) != NULL) {
+ modules = g_strsplit (old, ":", -1);
+ for (n = 0; modules[n]; n++) {
+ if (!strcmp (modules[n], "gail")) {
+ found_gail = TRUE;
+ } else if (!strcmp (modules[n], "atk-bridge")) {
+ found_atk_bridge = TRUE;
+ }
+
+ modules_list = g_slist_prepend (modules_list, modules[n]);
+ modules[n] = NULL;
+ }
+ g_free (modules);
+ }
+
+ if (!found_gail) {
+ modules_list = g_slist_prepend (modules_list, "gail");
+ ++n;
+ }
+
+ if (!found_atk_bridge) {
+ modules_list = g_slist_prepend (modules_list, "atk-bridge");
+ ++n;
+ }
+
+ modules = g_new (char *, n + 1);
+ modules[n--] = NULL;
+ for (l = modules_list; l; l = l->next) {
+ modules[n--] = g_strdup (l->data);
+ }
+
+ g_setenv ("GTK_MODULES", g_strjoinv (":", modules), TRUE);
+ g_strfreev (modules);
+ g_slist_free (modules_list);
+}
+
+static void
+load_a11y (void)
+{
+ assistive_registry_start ();
+ at_set_gtk_modules ();
+}
+
+int
+main (int argc, char *argv[])
+{
+ GtkWidget *chooser;
+
+ bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+ textdomain (GETTEXT_PACKAGE);
+
+ setlocale (LC_ALL, "");
+
+ gdm_log_init ();
+ gdm_log_set_debug (TRUE);
+
+ g_debug ("Chooser for display %s xauthority:%s",
+ g_getenv ("DISPLAY"),
+ g_getenv ("XAUTHORITY"));
+
+ gdk_init (&argc, &argv);
+
+ load_a11y ();
+
+ gtk_init (&argc, &argv);
+
+ chooser = gdm_host_chooser_dialog_new (GDM_CHOOSER_HOST_KIND_MASK_ALL);
+ gtk_widget_set_size_request (chooser, 480, 600);
+
+ if (gtk_dialog_run (GTK_DIALOG (chooser)) == GTK_RESPONSE_OK) {
+ GdmChooserHost *host;
+
+ host = gdm_host_chooser_dialog_get_host (GDM_HOST_CHOOSER_DIALOG (chooser));
+ if (host != NULL) {
+ char *hostname;
+ /* FIXME: handle different host types here? */
+
+ hostname = NULL;
+ gdm_address_get_hostname (gdm_chooser_host_get_address (host), &hostname);
+ /* FIXME: fall back to numerical address? */
+ if (hostname != NULL) {
+ g_print ("hostname: %s\n", hostname);
+ g_free (hostname);
+ }
+ }
+ }
+
+ gtk_widget_destroy (chooser);
+
+ return 0;
+}