diff options
Diffstat (limited to 'chooser')
-rw-r--r-- | chooser/chooser-main.c | 250 | ||||
-rw-r--r-- | chooser/gdm-chooser-host.c | 252 | ||||
-rw-r--r-- | chooser/gdm-chooser-host.h | 44 | ||||
-rw-r--r-- | chooser/gdm-chooser-session.c | 337 | ||||
-rw-r--r-- | chooser/gdm-chooser-session.h | 39 | ||||
-rw-r--r-- | chooser/gdm-host-chooser-dialog.c | 203 | ||||
-rw-r--r-- | chooser/gdm-host-chooser-dialog.h | 41 | ||||
-rw-r--r-- | chooser/gdm-host-chooser-widget.c | 879 | ||||
-rw-r--r-- | chooser/gdm-host-chooser-widget.h | 44 | ||||
-rw-r--r-- | chooser/gdm-host-chooser.c | 252 | ||||
-rw-r--r-- | chooser/meson.build | 50 |
11 files changed, 2391 insertions, 0 deletions
diff --git a/chooser/chooser-main.c b/chooser/chooser-main.c new file mode 100644 index 0000000..6f94c62 --- /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 (EXIT_FAILURE); + } + + 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 (EXIT_FAILURE); + } + + 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 0000000..2491af1 --- /dev/null +++ b/chooser/gdm-chooser-host.c @@ -0,0 +1,252 @@ +/* -*- 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" + +struct _GdmChooserHost +{ + GObject parent; + + 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->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->description; +} + +GdmChooserHostKind +gdm_chooser_host_get_kind (GdmChooserHost *host) +{ + g_return_val_if_fail (GDM_IS_CHOOSER_HOST (host), 0); + + return host->kind; +} + +gboolean +gdm_chooser_host_get_willing (GdmChooserHost *host) +{ + g_return_val_if_fail (GDM_IS_CHOOSER_HOST (host), FALSE); + + return host->willing; +} + +static void +_gdm_chooser_host_set_address (GdmChooserHost *host, + GdmAddress *address) +{ + if (host->address != NULL) { + gdm_address_free (host->address); + } + + g_assert (address != NULL); + + gdm_address_debug (address); + host->address = gdm_address_copy (address); +} + +static void +_gdm_chooser_host_set_description (GdmChooserHost *host, + const char *description) +{ + g_free (host->description); + host->description = g_strdup (description); +} + +static void +_gdm_chooser_host_set_kind (GdmChooserHost *host, + int kind) +{ + if (host->kind != kind) { + host->kind = kind; + } +} + +static void +_gdm_chooser_host_set_willing (GdmChooserHost *host, + gboolean willing) +{ + if (host->willing != willing) { + host->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->address); + break; + case PROP_DESCRIPTION: + g_value_set_string (value, host->description); + break; + case PROP_KIND: + g_value_set_int (value, host->kind); + break; + case PROP_WILLING: + g_value_set_boolean (value, host->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_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, + PROP_DESCRIPTION, + g_param_spec_string ("description", + "description", + "description", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); + + 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_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, + PROP_WILLING, + g_param_spec_boolean ("willing", + "willing", + "willing", + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); +} + +static void +gdm_chooser_host_init (GdmChooserHost *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_free (host->description); + gdm_address_free (host->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 0000000..028cccc --- /dev/null +++ b/chooser/gdm-chooser-host.h @@ -0,0 +1,44 @@ +/* -*- 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 ()) +G_DECLARE_FINAL_TYPE (GdmChooserHost, gdm_chooser_host, GDM, CHOOSER_HOST, GObject) + +typedef enum { + GDM_CHOOSER_HOST_KIND_XDMCP = 1 << 0, +} GdmChooserHostKind; + +#define GDM_CHOOSER_HOST_KIND_MASK_ALL (GDM_CHOOSER_HOST_KIND_XDMCP) + +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 0000000..c90403b --- /dev/null +++ b/chooser/gdm-chooser-session.c @@ -0,0 +1,337 @@ +/* -*- 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" + +struct _GdmChooserSession +{ + GObject parent; + + 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->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->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->remote_greeter = gdm_client_get_remote_greeter_sync (session->client, + NULL, + error); + if (session->remote_greeter == NULL) { + return FALSE; + } + + session->chooser = gdm_client_get_chooser_sync (session->client, + NULL, + error); + if (session->chooser == NULL) { + return FALSE; + } + + start_settings_daemon (session); + start_window_manager (session); + + /* Only support XDMCP on remote choosers */ + session->chooser_dialog = gdm_host_chooser_dialog_new (GDM_CHOOSER_HOST_KIND_XDMCP); + g_signal_connect (session->chooser_dialog, + "response", + G_CALLBACK (on_dialog_response), + session); + gtk_widget_show (session->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; +} + +static void +gdm_chooser_session_init (GdmChooserSession *session) +{ + session->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 != NULL); + + g_clear_object (&chooser_session->chooser); + g_clear_object (&chooser_session->remote_greeter); + g_clear_object (&chooser_session->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 0000000..e86380b --- /dev/null +++ b/chooser/gdm-chooser-session.h @@ -0,0 +1,39 @@ +/* -*- 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 ()) +G_DECLARE_FINAL_TYPE (GdmChooserSession, gdm_chooser_session, GDM, CHOOSER_SESSION, GObject) + +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 0000000..3f7e2b7 --- /dev/null +++ b/chooser/gdm-host-chooser-dialog.c @@ -0,0 +1,203 @@ +/* -*- 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" + +struct _GdmHostChooserDialog +{ + GtkDialog parent; + + 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); + +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->chooser_widget)); + + return host; +} + +static void +_gdm_host_chooser_dialog_set_kind_mask (GdmHostChooserDialog *dialog, + int kind_mask) +{ + if (dialog->kind_mask != kind_mask) { + dialog->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->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->chooser_widget = gdm_host_chooser_widget_new (dialog->kind_mask); + gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), dialog->chooser_widget, TRUE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (dialog->chooser_widget), 5); + + gtk_dialog_add_buttons (GTK_DIALOG (dialog), + _("_Refresh"), GTK_RESPONSE_APPLY, + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("C_onnect"), 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; + + 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_PARAM_STATIC_STRINGS)); +} + +static void +gdm_host_chooser_dialog_init (GdmHostChooserDialog *dialog) +{ +} + +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 0000000..1c82ff1 --- /dev/null +++ b/chooser/gdm-host-chooser-dialog.h @@ -0,0 +1,41 @@ +/* -*- 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 ()) +G_DECLARE_FINAL_TYPE (GdmHostChooserDialog, gdm_host_chooser_dialog, GDM, HOST_CHOOSER_DIALOG, GtkDialog) + +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 0000000..0b67c58 --- /dev/null +++ b/chooser/gdm-host-chooser-widget.c @@ -0,0 +1,879 @@ +/* -*- 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" + +struct _GdmHostChooserWidget +{ + GtkBox parent; + + 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); + +G_DEFINE_TYPE (GdmHostChooserWidget, gdm_host_chooser_widget, GTK_TYPE_BOX) + +#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->chooser_hosts = g_slist_prepend (widget->chooser_hosts, host); +} + +#if 0 +static void +chooser_host_remove (GdmHostChooserWidget *widget, + GdmChooserHost *host) +{ + widget->chooser_hosts = g_slist_remove (widget->chooser_hosts, host); +} +#endif + +static gboolean +address_hostnames_equal (GdmAddress *address, + GdmAddress *other_address) +{ + char *hostname, *other_hostname; + gboolean are_equal; + + if (gdm_address_equal (address, other_address)) { + return TRUE; + } + + if (!gdm_address_get_hostname (address, &hostname)) { + gdm_address_get_numeric_info (address, &hostname, NULL); + } + + if (!gdm_address_get_hostname (other_address, &other_hostname)) { + gdm_address_get_numeric_info (other_address, &other_hostname, NULL); + } + + are_equal = g_strcmp0 (hostname, other_hostname) == 0; + + g_free (hostname); + g_free (other_hostname); + + return are_equal; +} + +static GdmChooserHost * +find_known_host (GdmHostChooserWidget *widget, + GdmAddress *address) +{ + GSList *li; + GdmChooserHost *host; + + for (li = widget->chooser_hosts; li != NULL; li = li->next) { + GdmAddress *other_address; + + host = li->data; + + other_address = gdm_chooser_host_get_address (host); + + if (address_hostnames_equal (address, other_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; + + GtkTreeSelection *selection; + + 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->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); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget->treeview)); + if (!gtk_tree_selection_get_selected (selection, NULL, NULL)) { + gtk_tree_selection_select_iter (selection, &iter); + } + +} + +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->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)) { + browser_add_host (widget, chooser_host); + g_object_set (chooser_host, "willing", TRUE, NULL); + } + /* 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->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->socket_fd); + res = XdmcpFlush (widget->socket_fd, + &widget->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->query_addresses; l != NULL; l = l->next) { + GdmAddress *address; + int res; + + address = l->data; + + gdm_address_debug (address); + res = XdmcpFlush (widget->socket_fd, + &widget->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->ping_tries --; + + if (widget->ping_tries <= 0) { + widget->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->scan_time_id > 0) { + g_source_remove (widget->scan_time_id); + } + + widget->scan_time_id = g_timeout_add_seconds (SCAN_TIMEOUT, + chooser_scan_time_update, + widget); +#endif + /* Note we already used up one try */ + widget->ping_tries = PING_TRIES - 1; + if (widget->ping_try_id > 0) { + g_source_remove (widget->ping_try_id); + } + + widget->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->broadcast_addresses = g_slist_append (widget->broadcast_addresses, address); + } + } + } + out: + g_free (buf); + close (sock); +} + +static void +add_hosts (GdmHostChooserWidget *widget) +{ + int i; + + for (i = 0; widget->hosts != NULL && widget->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->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->query_addresses = g_slist_append (widget->query_addresses, address); + } + } + + freeaddrinfo (result); + } + + if (widget->broadcast_addresses == NULL && widget->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->socket_fd = -1; + + /* Open socket for communication */ +#ifdef ENABLE_IPV6 + widget->socket_fd = socket (AF_INET6, SOCK_DGRAM, 0); + if (widget->socket_fd != -1) { + widget->have_ipv6 = TRUE; +#ifdef IPV6_V6ONLY + { + int zero = 0; + if (setsockopt(widget->socket_fd, IPPROTO_IPV6, IPV6_V6ONLY, &zero, sizeof(zero)) < 0) + g_warning("setsockopt(IPV6_V6ONLY): %s", g_strerror(errno)); + } +#endif + } +#endif + if (! widget->have_ipv6) { + widget->socket_fd = socket (AF_INET, SOCK_DGRAM, 0); + if (widget->socket_fd == -1) { + g_critical ("Could not create socket!"); + } + } + + res = setsockopt (widget->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->broadcast_buf, &header); + XdmcpWriteARRAYofARRAY8 (&widget->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->query_buf, 0, sizeof (XdmcpBuffer)); + XdmcpWriteHeader (&widget->query_buf, &header); + XdmcpWriteARRAYofARRAY8 (&widget->query_buf, &aanames); + + add_hosts (widget); + + ioc = g_io_channel_unix_new (widget->socket_fd); + g_io_channel_set_encoding (ioc, NULL, NULL); + g_io_channel_set_buffered (ioc, FALSE); + widget->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->current_host != NULL) { + host = g_object_ref (widget->current_host); + } + + return host; +} + +static void +_gdm_host_chooser_widget_set_kind_mask (GdmHostChooserWidget *widget, + int kind_mask) +{ + if (widget->kind_mask != kind_mask) { + widget->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->broadcast_addresses != NULL) { + g_slist_foreach (widget->broadcast_addresses, + (GFunc)gdm_address_free, + NULL); + g_slist_free (widget->broadcast_addresses); + widget->broadcast_addresses = NULL; + } + if (widget->query_addresses != NULL) { + g_slist_foreach (widget->query_addresses, + (GFunc)gdm_address_free, + NULL); + g_slist_free (widget->query_addresses); + widget->query_addresses = NULL; + } + if (widget->chooser_hosts != NULL) { + g_slist_foreach (widget->chooser_hosts, + (GFunc)g_object_unref, + NULL); + g_slist_free (widget->chooser_hosts); + widget->chooser_hosts = NULL; + } + + widget->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; + + 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_PARAM_STATIC_STRINGS)); + + signals [HOST_ACTIVATED] = g_signal_new ("host-activated", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); +} + +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->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; + + 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->treeview = gtk_tree_view_new (); + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (widget->treeview), FALSE); + g_signal_connect (widget->treeview, + "row-activated", + G_CALLBACK (on_row_activated), + widget); + gtk_container_add (GTK_CONTAINER (scrolled), widget->treeview); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget->treeview)); + gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE); + 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->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->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->treeview), column); + + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model), + CHOOSER_LIST_LABEL_COLUMN, + GTK_SORT_ASCENDING); +} + +GtkWidget * +gdm_host_chooser_widget_new (int kind_mask) +{ + GObject *object; + + object = g_object_new (GDM_TYPE_HOST_CHOOSER_WIDGET, + "orientation", GTK_ORIENTATION_VERTICAL, + "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 0000000..48ce419 --- /dev/null +++ b/chooser/gdm-host-chooser-widget.h @@ -0,0 +1,44 @@ +/* -*- 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 ()) +G_DECLARE_FINAL_TYPE (GdmHostChooserWidget, gdm_host_chooser_widget, GDM, HOST_CHOOSER_WIDGET, GtkBox) + +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 0000000..47f37d4 --- /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; +} diff --git a/chooser/meson.build b/chooser/meson.build new file mode 100644 index 0000000..f3b948d --- /dev/null +++ b/chooser/meson.build @@ -0,0 +1,50 @@ +# Simple chooser +gdm_simple_chooser_src = [ + 'chooser-main.c', + 'gdm-chooser-host.c', + 'gdm-chooser-session.c', + 'gdm-host-chooser-dialog.c', + 'gdm-host-chooser-widget.c', +] + +gdm_simple_chooser_deps = [ + glib_dep, + gtk_dep, + libgdmcommon_dep, + libgdm_dep, + x_deps, + xdmcp_dep, +] + +gdm_simple_chooser = executable('gdm-simple-chooser', + gdm_simple_chooser_src, + dependencies: gdm_simple_chooser_deps, + include_directories: config_h_dir, + install: true, + install_dir: get_option('libexecdir'), +) + +# Host chooser +gdm_host_chooser_src = [ + 'gdm-host-chooser.c', + 'gdm-chooser-host.c', + 'gdm-host-chooser-dialog.c', + 'gdm-host-chooser-widget.c', +] + +gdm_host_chooser_deps = [ + glib_dep, + gtk_dep, + libgdmcommon_dep, + libgdm_dep, + x_deps, + xdmcp_dep, +] + +gdm_host_chooser = executable('gdm-host-chooser', + gdm_host_chooser_src, + dependencies: gdm_host_chooser_deps, + include_directories: config_h_dir, + install: true, + install_dir: get_option('libexecdir'), +) |