gnome-shell r183 - in trunk: . js/ui src src/gdmuser
- From: walters svn gnome org
- To: svn-commits-list gnome org
- Subject: gnome-shell r183 - in trunk: . js/ui src src/gdmuser
- Date: Wed, 4 Feb 2009 18:45:38 +0000 (UTC)
Author: walters
Date: Wed Feb 4 18:45:38 2009
New Revision: 183
URL: http://svn.gnome.org/viewvc/gnome-shell?rev=183&view=rev
Log:
Merge branch 'statusmenu'
Added:
trunk/src/gdmuser/
trunk/src/gdmuser/gdm-user-chooser-dialog.c
trunk/src/gdmuser/gdm-user-chooser-dialog.h
trunk/src/gdmuser/gdm-user-chooser-widget.c
trunk/src/gdmuser/gdm-user-chooser-widget.h
trunk/src/gdmuser/gdm-user-manager.c
trunk/src/gdmuser/gdm-user-manager.h
trunk/src/gdmuser/gdm-user-private.h
trunk/src/gdmuser/gdm-user.c
trunk/src/gdmuser/gdm-user.h
trunk/src/shell-status-menu.c
trunk/src/shell-status-menu.h
Modified:
trunk/configure.ac
trunk/js/ui/button.js
trunk/js/ui/panel.js
trunk/src/Makefile.am
Modified: trunk/configure.ac
==============================================================================
--- trunk/configure.ac (original)
+++ trunk/configure.ac Wed Feb 4 18:45:38 2009
@@ -21,6 +21,7 @@
PKG_CHECK_MODULES(MUTTER_PLUGIN, gtk+-2.0 dbus-glib-1 metacity-plugins gjs-gi-1.0)
PKG_CHECK_MODULES(TIDY, clutter-0.8)
PKG_CHECK_MODULES(BIG, clutter-cairo-0.8 gtk+-2.0 librsvg-2.0)
+PKG_CHECK_MODULES(GDMUSER, dbus-glib-1 gtk+-2.0)
PKG_CHECK_MODULES(TRAY, gtk+-2.0)
PKG_CHECK_MODULES(TASKPANEL, libwnck-1.0 dbus-glib-1)
# We require libgnomeui for generating thumbnails for recent files with GnomeThumbnailFactory.
Modified: trunk/js/ui/button.js
==============================================================================
--- trunk/js/ui/button.js (original)
+++ trunk/js/ui/button.js Wed Feb 4 18:45:38 2009
@@ -1,29 +1,21 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Clutter = imports.gi.Clutter;
+const Big = imports.gi.Big;
const Tweener = imports.tweener.tweener;
const DEFAULT_BUTTON_COLOR = new Clutter.Color();
-DEFAULT_BUTTON_COLOR.from_pixel(0xeeddccff);
+DEFAULT_BUTTON_COLOR.from_pixel(0xeeddcc66);
const DEFAULT_PRESSED_BUTTON_COLOR = new Clutter.Color();
-DEFAULT_PRESSED_BUTTON_COLOR.from_pixel(0xccbbaaff);
+DEFAULT_PRESSED_BUTTON_COLOR.from_pixel(0xccbbaa66);
-// Time for animation making the button darker
-const ANIMATION_TIME = 0.3;
-
-const NO_OPACITY = 0;
-
-const PARTIAL_OPACITY = 0.4 * 255;
-
-const FULL_OPACITY = 255;
-
-function Button(text, buttonColor, pressedButtonColor, staysPressed, minWidth, minHeight) {
- this._init(text, buttonColor, pressedButtonColor, staysPressed, minWidth, minHeight);
+function Button(widget, buttonColor, pressedButtonColor, staysPressed, minWidth, minHeight) {
+ this._init(widget, buttonColor, pressedButtonColor, staysPressed, minWidth, minHeight);
}
Button.prototype = {
- _init : function(text, buttonColor, pressedButtonColor, staysPressed, minWidth, minHeight) {
+ _init : function(widgetOrText, buttonColor, pressedButtonColor, staysPressed, minWidth, minHeight) {
let me = this;
this._buttonColor = buttonColor
@@ -47,29 +39,29 @@
this._isBetweenPressAndRelease = false;
this._mouseIsOverButton = false;
- this.button = new Clutter.Group({reactive: true});
- this._background = new Clutter.Rectangle({ color: this._buttonColor});
- this._pressedBackground = new Clutter.Rectangle({ color: this._pressedButtonColor, opacity: NO_OPACITY});
- this._label = new Clutter.Label({ font_name: "Sans Bold 16px",
- text: text});
- this._label.set_position(5, 5);
- let backgroundWidth = Math.max(this._label.get_width()+10, minWidth);
- let backgroundHeight = Math.max(this._label.get_height()+10, minHeight);
- this._background.set_width(backgroundWidth)
- this._background.set_height(backgroundHeight)
- this._pressedBackground.set_width(backgroundWidth)
- this._pressedBackground.set_height(backgroundHeight)
- this.button.add_actor(this._background);
- this.button.add_actor(this._pressedBackground);
- this.button.add_actor(this._label);
+ this.button = new Big.Box({ reactive: true,
+ corner_radius: 5,
+ padding_left: 4,
+ padding_right: 4,
+ orientation: Big.BoxOrientation.HORIZONTAL,
+ y_align: Big.BoxAlignment.CENTER
+ });
+ if (typeof widgetOrText == 'string') {
+ this._widget = new Clutter.Label({ font_name: "Sans Bold 16px",
+ text: widgetOrText });
+ } else {
+ this._widget = widgetOrText;
+ }
+
+ this.button.append(this._widget, Big.BoxPackFlags.EXPAND);
+
+ this._minWidth = minWidth;
+ this._minHeight = minHeight;
+
this.button.connect('button-press-event',
function(o, event) {
me._isBetweenPressAndRelease = true;
- Tweener.addTween(me._pressedBackground,
- { time: ANIMATION_TIME,
- opacity: FULL_OPACITY,
- transition: "linear"
- });
+ me.button.backgroundColor = me._pressedButtonColor;
return false;
});
this.button.connect('button-release-event',
@@ -86,8 +78,7 @@
function(o, event) {
me._mouseIsOverButton = true;
if (!me._active) {
- Tweener.removeTweens(me._pressedBackground);
- me._pressedBackground.set_opacity(PARTIAL_OPACITY);
+ me.button.backgroundColor = me._buttonColor;
}
return false;
});
@@ -96,8 +87,7 @@
me._isBetweenPressAndRelease = false;
me._mouseIsOverButton = false;
if (!me._active) {
- Tweener.removeTweens(me._pressedBackground);
- me._pressedBackground.set_opacity(NO_OPACITY);
+ me.button.backgroundColor = null;
}
return false;
});
@@ -106,11 +96,10 @@
release : function() {
if (!this._isBetweenPressAndRelease) {
this._active = false;
- Tweener.removeTweens(this._pressedBackground);
if (this._mouseIsOverButton) {
- this._pressedBackground.set_opacity(PARTIAL_OPACITY);
+ this.button.backgroundColor = this._buttonColor;
} else {
- this._pressedBackground.set_opacity(NO_OPACITY);
+ this.button.backgroundColor = null;
}
}
}
Modified: trunk/js/ui/panel.js
==============================================================================
--- trunk/js/ui/panel.js (original)
+++ trunk/js/ui/panel.js Wed Feb 4 18:45:38 2009
@@ -13,6 +13,8 @@
const TRAY_HEIGHT = 24;
const PANEL_BACKGROUND_COLOR = new Clutter.Color();
PANEL_BACKGROUND_COLOR.from_pixel(0xeeddccff);
+const PANEL_BUTTON_COLOR = new Clutter.Color();
+PANEL_BUTTON_COLOR.from_pixel(0xccbbaa66);
const PANEL_BORDER_COLOR = new Clutter.Color();
PANEL_BORDER_COLOR.from_pixel(0x000000ff);
const PRESSED_BUTTON_BACKGROUND_COLOR = new Clutter.Color();
@@ -37,10 +39,25 @@
border_bottom: 1,
border_color: PANEL_BORDER_COLOR });
- this.button = new Button.Button("Activities", PANEL_BACKGROUND_COLOR, PRESSED_BUTTON_BACKGROUND_COLOR, true, null, PANEL_HEIGHT);
+ this.button = new Button.Button("Activities", PANEL_BUTTON_COLOR, PRESSED_BUTTON_BACKGROUND_COLOR, true, null, PANEL_HEIGHT);
this._box.append(this.button.button, Big.BoxPackFlags.NONE);
+ let statusbox = new Big.Box();
+ this._statusmenu = new Shell.StatusMenu();
+ statusbox.append(this._statusmenu, Big.BoxPackFlags.NONE);
+ let statusbutton = new Button.Button(statusbox, PANEL_BUTTON_COLOR, PRESSED_BUTTON_BACKGROUND_COLOR,
+ true, null, PANEL_HEIGHT);
+ statusbutton.button.connect('button-press-event', function (b, e) {
+ me._statusmenu.toggle(e);
+ return false;
+ });
+ this._box.append(statusbutton.button, Big.BoxPackFlags.END);
+ // We get a deactivated event when the popup disappears
+ this._statusmenu.connect('deactivated', function (sm) {
+ statusbutton.release();
+ });
+
this._clock = new Clutter.Label({ font_name: "Sans Bold 16px",
text: "" });
let pad = (PANEL_HEIGHT - this._clock.height) / 2;
Modified: trunk/src/Makefile.am
==============================================================================
--- trunk/src/Makefile.am (original)
+++ trunk/src/Makefile.am Wed Feb 4 18:45:38 2009
@@ -7,6 +7,7 @@
include Makefile-tidy.am
include Makefile-big.am
+include Makefile-gdmuser.am
include Makefile-tray.am
include Makefile-taskpanel.am
@@ -42,6 +43,8 @@
shell-process.h \
shell-global.c \
shell-global.h \
+ shell-status-menu.c \
+ shell-status-menu.h \
shell-tray-manager.c \
shell-tray-manager.h \
shell-wm.c \
@@ -77,6 +80,7 @@
$(MUTTER_PLUGIN_LIBS) \
$(LIBGNOMEUI_LIBS) \
libbig-1.0.la \
+ libgdmuser-1.0.la \
libtidy-1.0.la \
libtray.la
libgnome_shell_la_CPPFLAGS = $(gnome_shell_cflags)
@@ -84,13 +88,15 @@
typelibdir = $(pkglibdir)
typelib_DATA = Shell-0.1.typelib Tidy-1.0.typelib Big-1.0.typelib
-Shell-0.1.gir: $(metacity) $(G_IR_SCANNER) libgnome-shell.la Makefile
+Shell-0.1.gir: $(metacity) $(G_IR_SCANNER) Big-1.0.gir libgnome-shell.la Makefile
$(G_IR_SCANNER) \
--namespace=Shell \
--nsversion=0.1 \
--add-include-path=$(libdir)/metacity/ \
--include=Clutter-0.8 \
--include=Meta-2.25 \
+ --add-include-path=$(builddir) \
+ --include=Big-1.0 \
--program=metacity \
--program-arg=--mutter-plugins=$$(pwd)/libgnome-shell.la \
$(libgnome_shell_la_gir_sources) \
@@ -101,7 +107,7 @@
# The dependency on libgnome-shell.la here is because g-ir-compiler opens it
# (not the fake library, since we've already done the rewriting)
Shell-0.1.typelib: libgnome-shell.la Shell-0.1.gir
- LD_LIBRARY_PATH=$${LD_LIBRARY_PATH:+$$LD_LIBRARY_PATH:}. g-ir-compiler --includedir=$(libdir)/metacity/ Shell-0.1.gir -o $@
+ LD_LIBRARY_PATH=$${LD_LIBRARY_PATH:+$$LD_LIBRARY_PATH:}. g-ir-compiler --includedir=$(builddir) --includedir=$(libdir)/metacity/ Shell-0.1.gir -o $@
CLEANFILES += Shell-1.0.typelib
Tidy-1.0.gir: $(metacity) $(G_IR_SCANNER) libgnome-shell.la libtidy-1.0.la Makefile
Added: trunk/src/gdmuser/gdm-user-chooser-dialog.c
==============================================================================
--- (empty file)
+++ trunk/src/gdmuser/gdm-user-chooser-dialog.c Wed Feb 4 18:45:38 2009
@@ -0,0 +1,208 @@
+/* -*- 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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-user-chooser-widget.h"
+#include "gdm-user-chooser-dialog.h"
+
+#define GDM_USER_CHOOSER_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_USER_CHOOSER_DIALOG, GdmUserChooserDialogPrivate))
+
+struct GdmUserChooserDialogPrivate
+{
+ GtkWidget *chooser_widget;
+};
+
+enum {
+ PROP_0,
+};
+
+static void gdm_user_chooser_dialog_class_init (GdmUserChooserDialogClass *klass);
+static void gdm_user_chooser_dialog_init (GdmUserChooserDialog *user_chooser_dialog);
+static void gdm_user_chooser_dialog_finalize (GObject *object);
+
+G_DEFINE_TYPE (GdmUserChooserDialog, gdm_user_chooser_dialog, GTK_TYPE_DIALOG)
+
+char *
+gdm_user_chooser_dialog_get_chosen_user_name (GdmUserChooserDialog *dialog)
+{
+ char *user_name;
+
+ g_return_val_if_fail (GDM_IS_USER_CHOOSER_DIALOG (dialog), NULL);
+
+ user_name = gdm_user_chooser_widget_get_chosen_user_name (GDM_USER_CHOOSER_WIDGET (dialog->priv->chooser_widget));
+
+ return user_name;
+}
+
+void
+gdm_user_chooser_dialog_set_show_user_other (GdmUserChooserDialog *dialog,
+ gboolean show_user)
+{
+ g_return_if_fail (GDM_IS_USER_CHOOSER_DIALOG (dialog));
+
+ gdm_user_chooser_widget_set_show_user_other (GDM_USER_CHOOSER_WIDGET (dialog->priv->chooser_widget), show_user);
+}
+
+void
+gdm_user_chooser_dialog_set_show_user_guest (GdmUserChooserDialog *dialog,
+ gboolean show_user)
+{
+ g_return_if_fail (GDM_IS_USER_CHOOSER_DIALOG (dialog));
+
+ gdm_user_chooser_widget_set_show_user_guest (GDM_USER_CHOOSER_WIDGET (dialog->priv->chooser_widget), show_user);
+}
+
+void
+gdm_user_chooser_dialog_set_show_user_auto (GdmUserChooserDialog *dialog,
+ gboolean show_user)
+{
+ g_return_if_fail (GDM_IS_USER_CHOOSER_DIALOG (dialog));
+
+ gdm_user_chooser_widget_set_show_user_auto (GDM_USER_CHOOSER_WIDGET (dialog->priv->chooser_widget), show_user);
+}
+
+static void
+gdm_user_chooser_dialog_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_user_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 GObject *
+gdm_user_chooser_dialog_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ GdmUserChooserDialog *user_chooser_dialog;
+
+ user_chooser_dialog = GDM_USER_CHOOSER_DIALOG (G_OBJECT_CLASS (gdm_user_chooser_dialog_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_properties));
+
+ return G_OBJECT (user_chooser_dialog);
+}
+
+static void
+gdm_user_chooser_dialog_dispose (GObject *object)
+{
+ G_OBJECT_CLASS (gdm_user_chooser_dialog_parent_class)->dispose (object);
+}
+
+static void
+gdm_user_chooser_dialog_class_init (GdmUserChooserDialogClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = gdm_user_chooser_dialog_get_property;
+ object_class->set_property = gdm_user_chooser_dialog_set_property;
+ object_class->constructor = gdm_user_chooser_dialog_constructor;
+ object_class->dispose = gdm_user_chooser_dialog_dispose;
+ object_class->finalize = gdm_user_chooser_dialog_finalize;
+
+ g_type_class_add_private (klass, sizeof (GdmUserChooserDialogPrivate));
+}
+
+static void
+on_response (GdmUserChooserDialog *dialog,
+ gint response_id)
+{
+ switch (response_id) {
+ default:
+ break;
+ }
+}
+
+static void
+gdm_user_chooser_dialog_init (GdmUserChooserDialog *dialog)
+{
+
+ dialog->priv = GDM_USER_CHOOSER_DIALOG_GET_PRIVATE (dialog);
+
+ dialog->priv->chooser_widget = gdm_user_chooser_widget_new ();
+
+ gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), dialog->priv->chooser_widget);
+
+ gtk_dialog_add_buttons (GTK_DIALOG (dialog),
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OK, GTK_RESPONSE_OK,
+ NULL);
+ g_signal_connect (dialog,
+ "response",
+ G_CALLBACK (on_response),
+ dialog);
+
+ gtk_widget_show_all (GTK_WIDGET (dialog));
+}
+
+static void
+gdm_user_chooser_dialog_finalize (GObject *object)
+{
+ GdmUserChooserDialog *user_chooser_dialog;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GDM_IS_USER_CHOOSER_DIALOG (object));
+
+ user_chooser_dialog = GDM_USER_CHOOSER_DIALOG (object);
+
+ g_return_if_fail (user_chooser_dialog->priv != NULL);
+
+ G_OBJECT_CLASS (gdm_user_chooser_dialog_parent_class)->finalize (object);
+}
+
+GtkWidget *
+gdm_user_chooser_dialog_new (void)
+{
+ GObject *object;
+
+ object = g_object_new (GDM_TYPE_USER_CHOOSER_DIALOG,
+ NULL);
+
+ return GTK_WIDGET (object);
+}
Added: trunk/src/gdmuser/gdm-user-chooser-dialog.h
==============================================================================
--- (empty file)
+++ trunk/src/gdmuser/gdm-user-chooser-dialog.h Wed Feb 4 18:45:38 2009
@@ -0,0 +1,62 @@
+/* -*- 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __GDM_USER_CHOOSER_DIALOG_H
+#define __GDM_USER_CHOOSER_DIALOG_H
+
+#include <glib-object.h>
+#include <gtk/gtkdialog.h>
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_USER_CHOOSER_DIALOG (gdm_user_chooser_dialog_get_type ())
+#define GDM_USER_CHOOSER_DIALOG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_USER_CHOOSER_DIALOG, GdmUserChooserDialog))
+#define GDM_USER_CHOOSER_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_USER_CHOOSER_DIALOG, GdmUserChooserDialogClass))
+#define GDM_IS_USER_CHOOSER_DIALOG(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_USER_CHOOSER_DIALOG))
+#define GDM_IS_USER_CHOOSER_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_USER_CHOOSER_DIALOG))
+#define GDM_USER_CHOOSER_DIALOG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_USER_CHOOSER_DIALOG, GdmUserChooserDialogClass))
+
+typedef struct GdmUserChooserDialogPrivate GdmUserChooserDialogPrivate;
+
+typedef struct
+{
+ GtkDialog parent;
+ GdmUserChooserDialogPrivate *priv;
+} GdmUserChooserDialog;
+
+typedef struct
+{
+ GtkDialogClass parent_class;
+} GdmUserChooserDialogClass;
+
+GType gdm_user_chooser_dialog_get_type (void);
+
+GtkWidget * gdm_user_chooser_dialog_new (void);
+
+char * gdm_user_chooser_dialog_get_chosen_user_name (GdmUserChooserDialog *dialog);
+void gdm_user_chooser_dialog_set_show_other_user (GdmUserChooserDialog *dialog,
+ gboolean show);
+void gdm_user_chooser_dialog_set_show_user_guest (GdmUserChooserDialog *dialog,
+ gboolean show);
+void gdm_user_chooser_dialog_set_show_user_auto (GdmUserChooserDialog *dialog,
+ gboolean show);
+G_END_DECLS
+
+#endif /* __GDM_USER_CHOOSER_DIALOG_H */
Added: trunk/src/gdmuser/gdm-user-chooser-widget.c
==============================================================================
--- (empty file)
+++ trunk/src/gdmuser/gdm-user-chooser-widget.c Wed Feb 4 18:45:38 2009
@@ -0,0 +1,723 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann jhu edu>
+ * Copyright (C) 2007 Ray Strode <rstrode redhat com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+
+#include <gconf/gconf-client.h>
+
+#include "gdm-user-manager.h"
+#include "gdm-user-chooser-widget.h"
+
+
+#define KEY_DISABLE_USER_LIST "/apps/gdm/simple-greeter/disable_user_list"
+
+enum {
+ USER_NO_DISPLAY = 1 << 0,
+ USER_ACCOUNT_DISABLED = 1 << 1,
+};
+
+#define DEFAULT_USER_ICON "stock_person"
+
+#define GDM_USER_CHOOSER_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_USER_CHOOSER_WIDGET, GdmUserChooserWidgetPrivate))
+
+#define MAX_ICON_SIZE 128
+
+struct GdmUserChooserWidgetPrivate
+{
+ GdmUserManager *manager;
+ GtkIconTheme *icon_theme;
+
+ GdkPixbuf *logged_in_pixbuf;
+ GdkPixbuf *stock_person_pixbuf;
+
+ guint loaded : 1;
+ guint show_user_other : 1;
+ guint show_user_guest : 1;
+ guint show_user_auto : 1;
+ guint show_normal_users : 1;
+
+ guint load_idle_id;
+};
+
+enum {
+ PROP_0,
+ PROP_SHOW_USER_GUEST,
+ PROP_SHOW_USER_AUTO,
+ PROP_SHOW_USER_OTHER,
+};
+
+static void gdm_user_chooser_widget_class_init (GdmUserChooserWidgetClass *klass);
+static void gdm_user_chooser_widget_init (GdmUserChooserWidget *user_chooser_widget);
+static void gdm_user_chooser_widget_finalize (GObject *object);
+
+G_DEFINE_TYPE (GdmUserChooserWidget, gdm_user_chooser_widget, GDM_TYPE_CHOOSER_WIDGET)
+
+static int
+get_font_height_for_widget (GtkWidget *widget)
+{
+ PangoFontMetrics *metrics;
+ PangoContext *context;
+ int ascent;
+ int descent;
+ int height;
+
+ gtk_widget_ensure_style (widget);
+ context = gtk_widget_get_pango_context (widget);
+ metrics = pango_context_get_metrics (context,
+ widget->style->font_desc,
+ pango_context_get_language (context));
+
+ ascent = pango_font_metrics_get_ascent (metrics);
+ descent = pango_font_metrics_get_descent (metrics);
+ height = PANGO_PIXELS (ascent + descent);
+ pango_font_metrics_unref (metrics);
+ return height;
+}
+
+static int
+get_icon_height_for_widget (GtkWidget *widget)
+{
+ int font_height;
+ int height;
+
+ font_height = get_font_height_for_widget (widget);
+ height = 3 * font_height;
+ if (height > MAX_ICON_SIZE) {
+ height = MAX_ICON_SIZE;
+ }
+
+ g_debug ("GdmUserChooserWidget: font height %d; using icon size %d", font_height, height);
+
+ return height;
+}
+
+static void
+add_user_other (GdmUserChooserWidget *widget)
+{
+ gdm_chooser_widget_add_item (GDM_CHOOSER_WIDGET (widget),
+ GDM_USER_CHOOSER_USER_OTHER,
+ NULL,
+ _("Other..."),
+ _("Choose a different account"),
+ 0,
+ FALSE,
+ TRUE);
+}
+
+static void
+add_user_guest (GdmUserChooserWidget *widget)
+{
+ gdm_chooser_widget_add_item (GDM_CHOOSER_WIDGET (widget),
+ GDM_USER_CHOOSER_USER_GUEST,
+ widget->priv->stock_person_pixbuf,
+ _("Guest"),
+ _("Login as a temporary guest"),
+ 0,
+ FALSE,
+ TRUE);
+}
+
+static void
+add_user_auto (GdmUserChooserWidget *widget)
+{
+ gdm_chooser_widget_add_item (GDM_CHOOSER_WIDGET (widget),
+ GDM_USER_CHOOSER_USER_AUTO,
+ NULL,
+ _("Automatic Login"),
+ _("Automatically login to the system after selecting options"),
+ 0,
+ FALSE,
+ TRUE);
+}
+
+static void
+remove_user_other (GdmUserChooserWidget *widget)
+{
+ gdm_chooser_widget_remove_item (GDM_CHOOSER_WIDGET (widget),
+ GDM_USER_CHOOSER_USER_OTHER);
+}
+
+static void
+remove_user_guest (GdmUserChooserWidget *widget)
+{
+ gdm_chooser_widget_remove_item (GDM_CHOOSER_WIDGET (widget),
+ GDM_USER_CHOOSER_USER_GUEST);
+}
+
+static void
+remove_user_auto (GdmUserChooserWidget *widget)
+{
+ gdm_chooser_widget_remove_item (GDM_CHOOSER_WIDGET (widget),
+ GDM_USER_CHOOSER_USER_AUTO);
+}
+
+void
+gdm_user_chooser_widget_set_show_user_other (GdmUserChooserWidget *widget,
+ gboolean show_user)
+{
+ g_return_if_fail (GDM_IS_USER_CHOOSER_WIDGET (widget));
+
+ if (widget->priv->show_user_other != show_user) {
+ widget->priv->show_user_other = show_user;
+ if (show_user) {
+ add_user_other (widget);
+ } else {
+ remove_user_other (widget);
+ }
+ }
+}
+
+void
+gdm_user_chooser_widget_set_show_user_guest (GdmUserChooserWidget *widget,
+ gboolean show_user)
+{
+ g_return_if_fail (GDM_IS_USER_CHOOSER_WIDGET (widget));
+
+ if (widget->priv->show_user_guest != show_user) {
+ widget->priv->show_user_guest = show_user;
+ if (show_user) {
+ add_user_guest (widget);
+ } else {
+ remove_user_guest (widget);
+ }
+ }
+}
+
+void
+gdm_user_chooser_widget_set_show_user_auto (GdmUserChooserWidget *widget,
+ gboolean show_user)
+{
+ g_return_if_fail (GDM_IS_USER_CHOOSER_WIDGET (widget));
+
+ if (widget->priv->show_user_auto != show_user) {
+ widget->priv->show_user_auto = show_user;
+ if (show_user) {
+ add_user_auto (widget);
+ } else {
+ remove_user_auto (widget);
+ }
+ }
+}
+
+char *
+gdm_user_chooser_widget_get_chosen_user_name (GdmUserChooserWidget *widget)
+{
+ g_return_val_if_fail (GDM_IS_USER_CHOOSER_WIDGET (widget), NULL);
+
+ return gdm_chooser_widget_get_active_item (GDM_CHOOSER_WIDGET (widget));
+}
+
+void
+gdm_user_chooser_widget_set_chosen_user_name (GdmUserChooserWidget *widget,
+ const char *name)
+{
+ g_return_if_fail (GDM_IS_USER_CHOOSER_WIDGET (widget));
+
+ gdm_chooser_widget_set_active_item (GDM_CHOOSER_WIDGET (widget), name);
+}
+
+void
+gdm_user_chooser_widget_set_show_only_chosen (GdmUserChooserWidget *widget,
+ gboolean show_only) {
+ g_return_if_fail (GDM_IS_USER_CHOOSER_WIDGET (widget));
+
+ gdm_chooser_widget_set_hide_inactive_items (GDM_CHOOSER_WIDGET (widget),
+ show_only);
+
+}
+static void
+gdm_user_chooser_widget_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GdmUserChooserWidget *self;
+
+ self = GDM_USER_CHOOSER_WIDGET (object);
+
+ switch (prop_id) {
+ case PROP_SHOW_USER_AUTO:
+ gdm_user_chooser_widget_set_show_user_auto (self, g_value_get_boolean (value));
+ break;
+ case PROP_SHOW_USER_GUEST:
+ gdm_user_chooser_widget_set_show_user_guest (self, g_value_get_boolean (value));
+ break;
+ case PROP_SHOW_USER_OTHER:
+ gdm_user_chooser_widget_set_show_user_other (self, g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gdm_user_chooser_widget_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GdmUserChooserWidget *self;
+
+ self = GDM_USER_CHOOSER_WIDGET (object);
+
+ switch (prop_id) {
+ case PROP_SHOW_USER_AUTO:
+ g_value_set_boolean (value, self->priv->show_user_auto);
+ break;
+ case PROP_SHOW_USER_GUEST:
+ g_value_set_boolean (value, self->priv->show_user_guest);
+ break;
+ case PROP_SHOW_USER_OTHER:
+ g_value_set_boolean (value, self->priv->show_user_other);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+is_user_list_disabled (GdmUserChooserWidget *widget)
+{
+ GConfClient *client;
+ GError *error;
+ gboolean result;
+
+ client = gconf_client_get_default ();
+ error = NULL;
+ result = gconf_client_get_bool (client, KEY_DISABLE_USER_LIST, &error);
+ if (error != NULL) {
+ g_debug ("GdmUserChooserWidget: unable to get disable-user-list configuration: %s", error->message);
+ g_error_free (error);
+ }
+ g_object_unref (client);
+
+ return result;
+}
+
+static void
+add_user (GdmUserChooserWidget *widget,
+ GdmUser *user)
+{
+ GdkPixbuf *pixbuf;
+ char *tooltip;
+ gboolean is_logged_in;
+ int size;
+
+ if (!widget->priv->show_normal_users) {
+ return;
+ }
+
+ size = get_icon_height_for_widget (widget);
+ pixbuf = gdm_user_render_icon (user, size);
+ if (pixbuf == NULL && widget->priv->stock_person_pixbuf != NULL) {
+ pixbuf = g_object_ref (widget->priv->stock_person_pixbuf);
+ }
+
+ tooltip = g_strdup_printf (_("Log in as %s"),
+ gdm_user_get_user_name (user));
+
+ is_logged_in = gdm_user_get_num_sessions (user) > 0;
+
+ g_debug ("GdmUserChooserWidget: User added name:%s logged-in:%d pixbuf:%p",
+ gdm_user_get_user_name (user),
+ is_logged_in,
+ pixbuf);
+
+ gdm_chooser_widget_add_item (GDM_CHOOSER_WIDGET (widget),
+ gdm_user_get_user_name (user),
+ pixbuf,
+ gdm_user_get_real_name (user),
+ tooltip,
+ gdm_user_get_login_frequency (user),
+ is_logged_in,
+ FALSE);
+ g_free (tooltip);
+
+ if (pixbuf != NULL) {
+ g_object_unref (pixbuf);
+ }
+}
+
+static void
+on_user_added (GdmUserManager *manager,
+ GdmUser *user,
+ GdmUserChooserWidget *widget)
+{
+ /* wait for all users to be loaded */
+ if (! widget->priv->loaded) {
+ return;
+ }
+ add_user (widget, user);
+}
+
+static void
+on_user_removed (GdmUserManager *manager,
+ GdmUser *user,
+ GdmUserChooserWidget *widget)
+{
+ const char *user_name;
+
+ g_debug ("GdmUserChooserWidget: User removed: %s", gdm_user_get_user_name (user));
+ /* wait for all users to be loaded */
+ if (! widget->priv->loaded) {
+ return;
+ }
+
+ user_name = gdm_user_get_user_name (user);
+
+ gdm_chooser_widget_remove_item (GDM_CHOOSER_WIDGET (widget),
+ user_name);
+}
+
+static void
+on_user_is_logged_in_changed (GdmUserManager *manager,
+ GdmUser *user,
+ GdmUserChooserWidget *widget)
+{
+ const char *user_name;
+ gboolean is_logged_in;
+
+ g_debug ("GdmUserChooserWidget: User logged in changed: %s", gdm_user_get_user_name (user));
+
+ user_name = gdm_user_get_user_name (user);
+ is_logged_in = gdm_user_get_num_sessions (user) > 0;
+
+ gdm_chooser_widget_set_item_in_use (GDM_CHOOSER_WIDGET (widget),
+ user_name,
+ is_logged_in);
+}
+
+static void
+on_user_login_frequency_changed (GdmUserManager *manager,
+ GdmUser *user,
+ GdmUserChooserWidget *widget)
+{
+ const char *user_name;
+ gulong freq;
+
+ g_debug ("GdmUserChooserWidget: User login frequency changed: %s", gdm_user_get_user_name (user));
+
+ user_name = gdm_user_get_user_name (user);
+ freq = gdm_user_get_login_frequency (user);
+
+ gdm_chooser_widget_set_item_priority (GDM_CHOOSER_WIDGET (widget),
+ user_name,
+ freq);
+}
+
+static void
+on_users_loaded (GdmUserManager *manager,
+ GdmUserChooserWidget *widget)
+{
+ GSList *users;
+
+ widget->priv->loaded = TRUE;
+
+ g_debug ("GdmUserChooserWidget: Users loaded");
+
+ users = gdm_user_manager_list_users (manager);
+ while (users != NULL) {
+ add_user (widget, users->data);
+ users = g_slist_delete_link (users, users);
+ }
+
+ gtk_widget_grab_focus (GTK_WIDGET (widget));
+
+ gdm_chooser_widget_loaded (GDM_CHOOSER_WIDGET (widget));
+}
+
+static gboolean
+load_users (GdmUserChooserWidget *widget)
+{
+
+ if (widget->priv->show_normal_users) {
+ widget->priv->manager = gdm_user_manager_ref_default ();
+ g_signal_connect (widget->priv->manager,
+ "user-added",
+ G_CALLBACK (on_user_added),
+ widget);
+ g_signal_connect (widget->priv->manager,
+ "user-removed",
+ G_CALLBACK (on_user_removed),
+ widget);
+ g_signal_connect (widget->priv->manager,
+ "users-loaded",
+ G_CALLBACK (on_users_loaded),
+ widget);
+ g_signal_connect (widget->priv->manager,
+ "user-is-logged-in-changed",
+ G_CALLBACK (on_user_is_logged_in_changed),
+ widget);
+ g_signal_connect (widget->priv->manager,
+ "user-login-frequency-changed",
+ G_CALLBACK (on_user_login_frequency_changed),
+ widget);
+ } else {
+ gdm_chooser_widget_loaded (GDM_CHOOSER_WIDGET (widget));
+ }
+
+ widget->priv->load_idle_id = 0;
+
+ return FALSE;
+}
+
+static GObject *
+gdm_user_chooser_widget_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ GdmUserChooserWidget *widget;
+
+ widget = GDM_USER_CHOOSER_WIDGET (G_OBJECT_CLASS (gdm_user_chooser_widget_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_properties));
+
+ widget->priv->show_normal_users = !is_user_list_disabled (widget);
+
+ widget->priv->load_idle_id = g_idle_add ((GSourceFunc)load_users, widget);
+
+ return G_OBJECT (widget);
+}
+
+static void
+gdm_user_chooser_widget_dispose (GObject *object)
+{
+ GdmUserChooserWidget *widget;
+
+ widget = GDM_USER_CHOOSER_WIDGET (object);
+
+ G_OBJECT_CLASS (gdm_user_chooser_widget_parent_class)->dispose (object);
+
+ if (widget->priv->load_idle_id > 0) {
+ g_source_remove (widget->priv->load_idle_id);
+ widget->priv->load_idle_id = 0;
+ }
+
+ if (widget->priv->logged_in_pixbuf != NULL) {
+ g_object_unref (widget->priv->logged_in_pixbuf);
+ widget->priv->logged_in_pixbuf = NULL;
+ }
+
+ if (widget->priv->stock_person_pixbuf != NULL) {
+ g_object_unref (widget->priv->stock_person_pixbuf);
+ widget->priv->stock_person_pixbuf = NULL;
+ }
+}
+
+static void
+gdm_user_chooser_widget_class_init (GdmUserChooserWidgetClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = gdm_user_chooser_widget_get_property;
+ object_class->set_property = gdm_user_chooser_widget_set_property;
+ object_class->constructor = gdm_user_chooser_widget_constructor;
+ object_class->dispose = gdm_user_chooser_widget_dispose;
+ object_class->finalize = gdm_user_chooser_widget_finalize;
+
+
+ g_object_class_install_property (object_class,
+ PROP_SHOW_USER_AUTO,
+ g_param_spec_boolean ("show-user-auto",
+ "show user auto",
+ "show user auto",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ g_object_class_install_property (object_class,
+ PROP_SHOW_USER_GUEST,
+ g_param_spec_boolean ("show-user-guest",
+ "show user guest",
+ "show user guest",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ g_object_class_install_property (object_class,
+ PROP_SHOW_USER_OTHER,
+ g_param_spec_boolean ("show-user-other",
+ "show user other",
+ "show user other",
+ TRUE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ g_type_class_add_private (klass, sizeof (GdmUserChooserWidgetPrivate));
+}
+
+static GdkPixbuf *
+get_stock_person_pixbuf (GdmUserChooserWidget *widget)
+{
+ GdkPixbuf *pixbuf;
+ int size;
+
+ size = get_icon_height_for_widget (widget);
+
+ pixbuf = gtk_icon_theme_load_icon (widget->priv->icon_theme,
+ DEFAULT_USER_ICON,
+ size,
+ 0,
+ NULL);
+
+ return pixbuf;
+}
+
+static GdkPixbuf *
+get_logged_in_pixbuf (GdmUserChooserWidget *widget)
+{
+ GdkPixbuf *pixbuf;
+ int size;
+
+ size = get_icon_height_for_widget (widget);
+
+ pixbuf = gtk_icon_theme_load_icon (widget->priv->icon_theme,
+ "emblem-default",
+ size / 3,
+ 0,
+ NULL);
+
+ return pixbuf;
+}
+
+typedef struct {
+ GdkPixbuf *old_icon;
+ GdkPixbuf *new_icon;
+} IconUpdateData;
+
+static gboolean
+update_icons (GdmChooserWidget *widget,
+ const char *id,
+ GdkPixbuf **image,
+ char **name,
+ char **comment,
+ gulong *priority,
+ gboolean *is_in_use,
+ gboolean *is_separate,
+ IconUpdateData *data)
+{
+ if (data->old_icon == *image) {
+ *image = data->new_icon;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+load_icons (GdmUserChooserWidget *widget)
+{
+ GdkPixbuf *old_pixbuf;
+ IconUpdateData data;
+
+ if (widget->priv->logged_in_pixbuf != NULL) {
+ g_object_unref (widget->priv->logged_in_pixbuf);
+ }
+ widget->priv->logged_in_pixbuf = get_logged_in_pixbuf (widget);
+
+ old_pixbuf = widget->priv->stock_person_pixbuf;
+ widget->priv->stock_person_pixbuf = get_stock_person_pixbuf (widget);
+ /* update the icons in the model */
+ data.old_icon = old_pixbuf;
+ data.new_icon = widget->priv->stock_person_pixbuf;
+ gdm_chooser_widget_update_foreach_item (GDM_CHOOSER_WIDGET (widget),
+ (GdmChooserUpdateForeachFunc)update_icons,
+ &data);
+ if (old_pixbuf != NULL) {
+ g_object_unref (old_pixbuf);
+ }
+}
+
+static void
+on_icon_theme_changed (GtkIconTheme *icon_theme,
+ GdmUserChooserWidget *widget)
+{
+ g_debug ("GdmUserChooserWidget: icon theme changed");
+ load_icons (widget);
+}
+
+static void
+setup_icons (GdmUserChooserWidget *widget)
+{
+ if (gtk_widget_has_screen (GTK_WIDGET (widget))) {
+ widget->priv->icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (widget)));
+ } else {
+ widget->priv->icon_theme = gtk_icon_theme_get_default ();
+ }
+
+ if (widget->priv->icon_theme != NULL) {
+ g_signal_connect (widget->priv->icon_theme,
+ "changed",
+ G_CALLBACK (on_icon_theme_changed),
+ widget);
+ }
+
+ load_icons (widget);
+}
+
+static void
+gdm_user_chooser_widget_init (GdmUserChooserWidget *widget)
+{
+ widget->priv = GDM_USER_CHOOSER_WIDGET_GET_PRIVATE (widget);
+
+ gdm_chooser_widget_set_separator_position (GDM_CHOOSER_WIDGET (widget),
+ GDM_CHOOSER_WIDGET_POSITION_BOTTOM);
+ gdm_chooser_widget_set_in_use_message (GDM_CHOOSER_WIDGET (widget),
+ _("Currently logged in"));
+
+ setup_icons (widget);
+}
+
+static void
+gdm_user_chooser_widget_finalize (GObject *object)
+{
+ GdmUserChooserWidget *widget;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GDM_IS_USER_CHOOSER_WIDGET (object));
+
+ widget = GDM_USER_CHOOSER_WIDGET (object);
+
+ g_return_if_fail (widget->priv != NULL);
+
+ G_OBJECT_CLASS (gdm_user_chooser_widget_parent_class)->finalize (object);
+}
+
+GtkWidget *
+gdm_user_chooser_widget_new (void)
+{
+ GObject *object;
+
+ object = g_object_new (GDM_TYPE_USER_CHOOSER_WIDGET,
+ NULL);
+
+ return GTK_WIDGET (object);
+}
Added: trunk/src/gdmuser/gdm-user-chooser-widget.h
==============================================================================
--- (empty file)
+++ trunk/src/gdmuser/gdm-user-chooser-widget.h Wed Feb 4 18:45:38 2009
@@ -0,0 +1,70 @@
+/* -*- 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __GDM_USER_CHOOSER_WIDGET_H
+#define __GDM_USER_CHOOSER_WIDGET_H
+
+#include <glib-object.h>
+
+#include "gdm-chooser-widget.h"
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_USER_CHOOSER_WIDGET (gdm_user_chooser_widget_get_type ())
+#define GDM_USER_CHOOSER_WIDGET(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_USER_CHOOSER_WIDGET, GdmUserChooserWidget))
+#define GDM_USER_CHOOSER_WIDGET_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_USER_CHOOSER_WIDGET, GdmUserChooserWidgetClass))
+#define GDM_IS_USER_CHOOSER_WIDGET(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_USER_CHOOSER_WIDGET))
+#define GDM_IS_USER_CHOOSER_WIDGET_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_USER_CHOOSER_WIDGET))
+#define GDM_USER_CHOOSER_WIDGET_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_USER_CHOOSER_WIDGET, GdmUserChooserWidgetClass))
+
+typedef struct GdmUserChooserWidgetPrivate GdmUserChooserWidgetPrivate;
+
+typedef struct
+{
+ GdmChooserWidget parent;
+ GdmUserChooserWidgetPrivate *priv;
+} GdmUserChooserWidget;
+
+typedef struct
+{
+ GdmChooserWidgetClass parent_class;
+} GdmUserChooserWidgetClass;
+
+#define GDM_USER_CHOOSER_USER_OTHER "__other"
+#define GDM_USER_CHOOSER_USER_GUEST "__guest"
+#define GDM_USER_CHOOSER_USER_AUTO "__auto"
+
+GType gdm_user_chooser_widget_get_type (void);
+GtkWidget * gdm_user_chooser_widget_new (void);
+
+char * gdm_user_chooser_widget_get_chosen_user_name (GdmUserChooserWidget *widget);
+void gdm_user_chooser_widget_set_chosen_user_name (GdmUserChooserWidget *widget,
+ const char *user_name);
+void gdm_user_chooser_widget_set_show_only_chosen (GdmUserChooserWidget *widget,
+ gboolean show_only);
+void gdm_user_chooser_widget_set_show_user_other (GdmUserChooserWidget *widget,
+ gboolean show);
+void gdm_user_chooser_widget_set_show_user_guest (GdmUserChooserWidget *widget,
+ gboolean show);
+void gdm_user_chooser_widget_set_show_user_auto (GdmUserChooserWidget *widget,
+ gboolean show);
+G_END_DECLS
+
+#endif /* __GDM_USER_CHOOSER_WIDGET_H */
Added: trunk/src/gdmuser/gdm-user-manager.c
==============================================================================
--- (empty file)
+++ trunk/src/gdmuser/gdm-user-manager.c Wed Feb 4 18:45:38 2009
@@ -0,0 +1,1644 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007-2008 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif /* HAVE_PATHS_H */
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include "gdm-user-manager.h"
+#include "gdm-user-private.h"
+
+#define GDM_USER_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_USER_MANAGER, GdmUserManagerPrivate))
+
+#define CK_NAME "org.freedesktop.ConsoleKit"
+#define CK_PATH "/org/freedesktop/ConsoleKit"
+#define CK_INTERFACE "org.freedesktop.ConsoleKit"
+
+#define CK_MANAGER_PATH "/org/freedesktop/ConsoleKit/Manager"
+#define CK_MANAGER_INTERFACE "org.freedesktop.ConsoleKit.Manager"
+#define CK_SEAT_INTERFACE "org.freedesktop.ConsoleKit.Seat"
+#define CK_SESSION_INTERFACE "org.freedesktop.ConsoleKit.Session"
+
+/* Prefs Defaults */
+#define DEFAULT_ALLOW_ROOT TRUE
+#define DEFAULT_MAX_ICON_SIZE 128
+#define DEFAULT_USER_MAX_FILE 65536
+
+#ifdef __sun
+#define DEFAULT_MINIMAL_UID 100
+#else
+#define DEFAULT_MINIMAL_UID 500
+#endif
+
+#ifndef _PATH_SHELLS
+#define _PATH_SHELLS "/etc/shells"
+#endif
+#define PATH_PASSWD "/etc/passwd"
+
+#define DEFAULT_GLOBAL_FACE_DIR DATADIR "/faces"
+#define DEFAULT_USER_ICON "stock_person"
+#define DEFAULT_EXCLUDE { "bin", \
+ "root", \
+ "daemon", \
+ "adm", \
+ "lp", \
+ "sync", \
+ "shutdown", \
+ "halt", \
+ "mail", \
+ "news", \
+ "uucp", \
+ "operator", \
+ "nobody", \
+ "gdm", \
+ "postgres", \
+ "pvm", \
+ "rpm", \
+ "nfsnobody", \
+ "pcap", \
+ NULL }
+
+struct GdmUserManagerPrivate
+{
+ GHashTable *users;
+ GHashTable *sessions;
+ GHashTable *exclusions;
+ GHashTable *shells;
+ DBusGConnection *connection;
+ DBusGProxy *seat_proxy;
+ char *seat_id;
+
+ GFileMonitor *passwd_monitor;
+ GFileMonitor *shells_monitor;
+
+ guint reload_id;
+ guint ck_history_id;
+
+ guint8 users_dirty : 1;
+};
+
+enum {
+ LOADING_USERS,
+ USERS_LOADED,
+ USER_ADDED,
+ USER_REMOVED,
+ USER_IS_LOGGED_IN_CHANGED,
+ USER_LOGIN_FREQUENCY_CHANGED,
+ LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+static void gdm_user_manager_class_init (GdmUserManagerClass *klass);
+static void gdm_user_manager_init (GdmUserManager *user_manager);
+static void gdm_user_manager_finalize (GObject *object);
+
+static gpointer user_manager_object = NULL;
+
+G_DEFINE_TYPE (GdmUserManager, gdm_user_manager, G_TYPE_OBJECT)
+
+GQuark
+gdm_user_manager_error_quark (void)
+{
+ static GQuark ret = 0;
+ if (ret == 0) {
+ ret = g_quark_from_static_string ("gdm_user_manager_error");
+ }
+
+ return ret;
+}
+
+static gboolean
+start_new_login_session (GdmUserManager *manager)
+{
+ GError *error;
+ gboolean res;
+
+ res = g_spawn_command_line_async ("gdmflexiserver -s", &error);
+ if (! res) {
+ g_warning ("Unable to start new login: %s", error->message);
+ g_error_free (error);
+ }
+
+ return res;
+}
+
+/* needs to stay in sync with gdm-slave */
+static char *
+_get_primary_user_session_id (GdmUserManager *manager,
+ GdmUser *user)
+{
+ gboolean res;
+ gboolean can_activate_sessions;
+ GError *error;
+ GList *sessions;
+ GList *l;
+ char *primary_ssid;
+
+ if (manager->priv->seat_id == NULL || manager->priv->seat_id[0] == '\0') {
+ g_debug ("GdmUserManager: display seat id is not set; can't switch sessions");
+ return NULL;
+ }
+
+ primary_ssid = NULL;
+ sessions = NULL;
+
+ g_debug ("GdmUserManager: checking if seat can activate sessions");
+
+ error = NULL;
+ res = dbus_g_proxy_call (manager->priv->seat_proxy,
+ "CanActivateSessions",
+ &error,
+ G_TYPE_INVALID,
+ G_TYPE_BOOLEAN, &can_activate_sessions,
+ G_TYPE_INVALID);
+ if (! res) {
+ g_warning ("unable to determine if seat can activate sessions: %s",
+ error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ if (! can_activate_sessions) {
+ g_debug ("GdmUserManager: seat is unable to activate sessions");
+ goto out;
+ }
+
+ sessions = gdm_user_get_sessions (user);
+ if (sessions == NULL) {
+ g_warning ("unable to determine sessions for user: %s",
+ gdm_user_get_user_name (user));
+ goto out;
+ }
+
+ for (l = sessions; l != NULL; l = l->next) {
+ const char *ssid;
+
+ ssid = l->data;
+
+ /* FIXME: better way to choose? */
+ if (ssid != NULL) {
+ primary_ssid = g_strdup (ssid);
+ break;
+ }
+ }
+
+ out:
+
+ return primary_ssid;
+}
+
+static gboolean
+activate_session_id (GdmUserManager *manager,
+ const char *seat_id,
+ const char *session_id)
+{
+ DBusError local_error;
+ DBusMessage *message;
+ DBusMessage *reply;
+ gboolean ret;
+
+ ret = FALSE;
+ reply = NULL;
+
+ dbus_error_init (&local_error);
+ message = dbus_message_new_method_call ("org.freedesktop.ConsoleKit",
+ seat_id,
+ "org.freedesktop.ConsoleKit.Seat",
+ "ActivateSession");
+ if (message == NULL) {
+ goto out;
+ }
+
+ if (! dbus_message_append_args (message,
+ DBUS_TYPE_OBJECT_PATH, &session_id,
+ DBUS_TYPE_INVALID)) {
+ goto out;
+ }
+
+
+ dbus_error_init (&local_error);
+ reply = dbus_connection_send_with_reply_and_block (dbus_g_connection_get_connection (manager->priv->connection),
+ message,
+ -1,
+ &local_error);
+ if (reply == NULL) {
+ if (dbus_error_is_set (&local_error)) {
+ g_warning ("Unable to activate session: %s", local_error.message);
+ dbus_error_free (&local_error);
+ goto out;
+ }
+ }
+
+ ret = TRUE;
+ out:
+ if (message != NULL) {
+ dbus_message_unref (message);
+ }
+ if (reply != NULL) {
+ dbus_message_unref (reply);
+ }
+
+ return ret;
+}
+
+static gboolean
+session_is_login_window (GdmUserManager *manager,
+ const char *session_id)
+{
+ DBusGProxy *proxy;
+ GError *error;
+ gboolean res;
+ gboolean ret;
+ char *session_type;
+
+ ret = FALSE;
+
+ proxy = dbus_g_proxy_new_for_name (manager->priv->connection,
+ CK_NAME,
+ session_id,
+ CK_SESSION_INTERFACE);
+ if (proxy == NULL) {
+ g_warning ("Failed to connect to the ConsoleKit seat object");
+ goto out;
+ }
+
+ session_type = NULL;
+ error = NULL;
+ res = dbus_g_proxy_call (proxy,
+ "GetSessionType",
+ &error,
+ G_TYPE_INVALID,
+ G_TYPE_STRING, &session_type,
+ G_TYPE_INVALID);
+ if (! res) {
+ g_debug ("Failed to identify the session type: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ if (session_type == NULL || session_type[0] == '\0' || strcmp (session_type, "LoginWindow") != 0) {
+ goto out;
+ }
+
+ ret = TRUE;
+
+ out:
+ if (proxy != NULL) {
+ g_object_unref (proxy);
+ }
+
+ return ret;
+}
+
+static char *
+_get_login_window_session_id (GdmUserManager *manager)
+{
+ gboolean res;
+ gboolean can_activate_sessions;
+ GError *error;
+ GPtrArray *sessions;
+ char *primary_ssid;
+ int i;
+
+ if (manager->priv->seat_id == NULL || manager->priv->seat_id[0] == '\0') {
+ g_debug ("GdmUserManager: display seat id is not set; can't switch sessions");
+ return NULL;
+ }
+
+ primary_ssid = NULL;
+ sessions = NULL;
+
+ g_debug ("GdmSlave: checking if seat can activate sessions");
+
+ error = NULL;
+ res = dbus_g_proxy_call (manager->priv->seat_proxy,
+ "CanActivateSessions",
+ &error,
+ G_TYPE_INVALID,
+ G_TYPE_BOOLEAN, &can_activate_sessions,
+ G_TYPE_INVALID);
+ if (! res) {
+ g_warning ("unable to determine if seat can activate sessions: %s",
+ error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ if (! can_activate_sessions) {
+ g_debug ("GdmSlave: seat is unable to activate sessions");
+ goto out;
+ }
+
+ error = NULL;
+ res = dbus_g_proxy_call (manager->priv->seat_proxy,
+ "GetSessions",
+ &error,
+ G_TYPE_INVALID,
+ dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH), &sessions,
+ G_TYPE_INVALID);
+ if (! res) {
+ g_warning ("unable to determine sessions for user: %s",
+ error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ for (i = 0; i < sessions->len; i++) {
+ char *ssid;
+
+ ssid = g_ptr_array_index (sessions, i);
+
+ if (session_is_login_window (manager, ssid)) {
+ primary_ssid = g_strdup (ssid);
+ break;
+ }
+ }
+ g_ptr_array_foreach (sessions, (GFunc)g_free, NULL);
+ g_ptr_array_free (sessions, TRUE);
+
+ out:
+
+ return primary_ssid;
+}
+
+gboolean
+gdm_user_manager_goto_login_session (GdmUserManager *manager)
+{
+ gboolean ret;
+ gboolean res;
+ char *ssid;
+
+ g_return_val_if_fail (GDM_IS_USER_MANAGER (manager), FALSE);
+
+ ret = FALSE;
+
+ /* First look for any existing LoginWindow sessions on the seat.
+ If none are found, create a new one. */
+
+ ssid = _get_login_window_session_id (manager);
+ if (ssid != NULL) {
+ res = activate_session_id (manager, manager->priv->seat_id, ssid);
+ if (res) {
+ ret = TRUE;
+ }
+ }
+
+ if (! ret) {
+ res = start_new_login_session (manager);
+ if (res) {
+ ret = TRUE;
+ }
+ }
+
+ return ret;
+}
+
+gboolean
+gdm_user_manager_activate_user_session (GdmUserManager *manager,
+ GdmUser *user)
+{
+ gboolean ret;
+ char *ssid;
+ gboolean res;
+
+ g_return_val_if_fail (GDM_IS_USER_MANAGER (manager), FALSE);
+ g_return_val_if_fail (GDM_IS_USER (user), FALSE);
+
+ ret = FALSE;
+
+ ssid = _get_primary_user_session_id (manager, user);
+ if (ssid == NULL) {
+ goto out;
+ }
+
+ res = activate_session_id (manager, manager->priv->seat_id, ssid);
+ if (! res) {
+ g_debug ("GdmUserManager: unable to activate session: %s", ssid);
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ return ret;
+}
+
+static void
+on_user_sessions_changed (GdmUser *user,
+ GdmUserManager *manager)
+{
+ guint nsessions;
+
+ nsessions = gdm_user_get_num_sessions (user);
+
+ g_debug ("GdmUserManager: sessions changed user=%s num=%d",
+ gdm_user_get_user_name (user),
+ nsessions);
+
+ /* only signal on zero and one */
+ if (nsessions > 1) {
+ return;
+ }
+
+ g_signal_emit (manager, signals [USER_IS_LOGGED_IN_CHANGED], 0, user);
+}
+
+static void
+on_user_icon_changed (GdmUser *user,
+ GdmUserManager *manager)
+{
+ g_debug ("GdmUserManager: user icon changed");
+}
+
+static char *
+get_seat_id_for_session (DBusGConnection *connection,
+ const char *session_id)
+{
+ DBusGProxy *proxy;
+ GError *error;
+ char *seat_id;
+ gboolean res;
+
+ proxy = NULL;
+ seat_id = NULL;
+
+ proxy = dbus_g_proxy_new_for_name (connection,
+ CK_NAME,
+ session_id,
+ CK_SESSION_INTERFACE);
+ if (proxy == NULL) {
+ g_warning ("Failed to connect to the ConsoleKit session object");
+ goto out;
+ }
+
+ error = NULL;
+ res = dbus_g_proxy_call (proxy,
+ "GetSeatId",
+ &error,
+ G_TYPE_INVALID,
+ DBUS_TYPE_G_OBJECT_PATH, &seat_id,
+ G_TYPE_INVALID);
+ if (! res) {
+ g_debug ("Failed to identify the current seat: %s", error->message);
+ g_error_free (error);
+ }
+ out:
+ if (proxy != NULL) {
+ g_object_unref (proxy);
+ }
+
+ return seat_id;
+}
+
+static char *
+get_x11_display_for_session (DBusGConnection *connection,
+ const char *session_id)
+{
+ DBusGProxy *proxy;
+ GError *error;
+ char *x11_display;
+ gboolean res;
+
+ proxy = NULL;
+ x11_display = NULL;
+
+ proxy = dbus_g_proxy_new_for_name (connection,
+ CK_NAME,
+ session_id,
+ CK_SESSION_INTERFACE);
+ if (proxy == NULL) {
+ g_warning ("Failed to connect to the ConsoleKit session object");
+ goto out;
+ }
+
+ error = NULL;
+ res = dbus_g_proxy_call (proxy,
+ "GetX11Display",
+ &error,
+ G_TYPE_INVALID,
+ G_TYPE_STRING, &x11_display,
+ G_TYPE_INVALID);
+ if (! res) {
+ g_debug ("Failed to identify the x11 display: %s", error->message);
+ g_error_free (error);
+ }
+ out:
+ if (proxy != NULL) {
+ g_object_unref (proxy);
+ }
+
+ return x11_display;
+}
+
+static gboolean
+maybe_add_session_for_user (GdmUserManager *manager,
+ GdmUser *user,
+ const char *ssid)
+{
+ char *sid;
+ char *x11_display;
+ gboolean ret;
+
+ ret = FALSE;
+ sid = NULL;
+ x11_display = NULL;
+
+ /* skip if on another seat */
+ sid = get_seat_id_for_session (manager->priv->connection, ssid);
+ if (sid == NULL
+ || manager->priv->seat_id == NULL
+ || strcmp (sid, manager->priv->seat_id) != 0) {
+ g_debug ("GdmUserManager: not adding session on other seat: %s", ssid);
+ goto out;
+ }
+
+ /* skip if doesn't have an x11 display */
+ x11_display = get_x11_display_for_session (manager->priv->connection, ssid);
+ if (x11_display == NULL || x11_display[0] == '\0') {
+ g_debug ("GdmUserManager: not adding session without a x11 display: %s", ssid);
+ goto out;
+ }
+
+ if (g_hash_table_lookup (manager->priv->exclusions, gdm_user_get_user_name (user))) {
+ g_debug ("GdmUserManager: excluding user '%s'", gdm_user_get_user_name (user));
+ goto out;
+ }
+
+ g_hash_table_insert (manager->priv->sessions,
+ g_strdup (ssid),
+ g_strdup (gdm_user_get_user_name (user)));
+
+ _gdm_user_add_session (user, ssid);
+ g_debug ("GdmUserManager: added session for user: %s", gdm_user_get_user_name (user));
+
+ ret = TRUE;
+
+ out:
+ g_free (sid);
+ g_free (x11_display);
+
+ return ret;
+}
+
+static void
+add_sessions_for_user (GdmUserManager *manager,
+ GdmUser *user)
+{
+ DBusGProxy *proxy;
+ GError *error;
+ gboolean res;
+ guint32 uid;
+ GPtrArray *sessions;
+ int i;
+
+ proxy = dbus_g_proxy_new_for_name (manager->priv->connection,
+ CK_NAME,
+ CK_MANAGER_PATH,
+ CK_MANAGER_INTERFACE);
+ if (proxy == NULL) {
+ g_warning ("Failed to connect to the ConsoleKit manager object");
+ goto out;
+ }
+
+ uid = gdm_user_get_uid (user);
+
+ g_debug ("Getting list of sessions for user %u", uid);
+
+ error = NULL;
+ res = dbus_g_proxy_call (proxy,
+ "GetSessionsForUnixUser",
+ &error,
+ G_TYPE_UINT, uid,
+ G_TYPE_INVALID,
+ dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH),
+ &sessions,
+ G_TYPE_INVALID);
+ if (! res) {
+ g_debug ("Failed to find sessions for user: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ g_debug ("Found %d sessions for user %s", sessions->len, gdm_user_get_user_name (user));
+
+ for (i = 0; i < sessions->len; i++) {
+ char *ssid;
+
+ ssid = g_ptr_array_index (sessions, i);
+ maybe_add_session_for_user (manager, user, ssid);
+ }
+
+ g_ptr_array_foreach (sessions, (GFunc)g_free, NULL);
+ g_ptr_array_free (sessions, TRUE);
+
+ out:
+ if (proxy != NULL) {
+ g_object_unref (proxy);
+ }
+}
+
+static GdmUser *
+create_user (GdmUserManager *manager)
+{
+ GdmUser *user;
+
+ user = g_object_new (GDM_TYPE_USER, "manager", manager, NULL);
+ g_signal_connect (user,
+ "sessions-changed",
+ G_CALLBACK (on_user_sessions_changed),
+ manager);
+ g_signal_connect (user,
+ "icon-changed",
+ G_CALLBACK (on_user_icon_changed),
+ manager);
+ return user;
+}
+
+static void
+add_user (GdmUserManager *manager,
+ GdmUser *user)
+{
+ add_sessions_for_user (manager, user);
+ g_hash_table_insert (manager->priv->users,
+ g_strdup (gdm_user_get_user_name (user)),
+ g_object_ref (user));
+
+ g_signal_emit (manager, signals[USER_ADDED], 0, user);
+}
+
+static GdmUser *
+add_new_user_for_pwent (GdmUserManager *manager,
+ struct passwd *pwent)
+{
+ GdmUser *user;
+
+ g_debug ("Creating new user");
+
+ user = create_user (manager);
+ _gdm_user_update (user, pwent);
+
+ add_user (manager, user);
+
+ return user;
+}
+
+static char *
+get_current_seat_id (DBusGConnection *connection)
+{
+ DBusGProxy *proxy;
+ GError *error;
+ char *session_id;
+ char *seat_id;
+ gboolean res;
+
+ proxy = NULL;
+ session_id = NULL;
+ seat_id = NULL;
+
+ proxy = dbus_g_proxy_new_for_name (connection,
+ CK_NAME,
+ CK_MANAGER_PATH,
+ CK_MANAGER_INTERFACE);
+ if (proxy == NULL) {
+ g_warning ("Failed to connect to the ConsoleKit manager object");
+ goto out;
+ }
+
+ error = NULL;
+ res = dbus_g_proxy_call (proxy,
+ "GetCurrentSession",
+ &error,
+ G_TYPE_INVALID,
+ DBUS_TYPE_G_OBJECT_PATH,
+ &session_id,
+ G_TYPE_INVALID);
+ if (! res) {
+ g_debug ("Failed to identify the current session: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ seat_id = get_seat_id_for_session (connection, session_id);
+
+ out:
+ if (proxy != NULL) {
+ g_object_unref (proxy);
+ }
+ g_free (session_id);
+
+ return seat_id;
+}
+
+static gboolean
+get_uid_from_session_id (GdmUserManager *manager,
+ const char *session_id,
+ uid_t *uidp)
+{
+ DBusGProxy *proxy;
+ GError *error;
+ guint uid;
+ gboolean res;
+
+ proxy = dbus_g_proxy_new_for_name (manager->priv->connection,
+ CK_NAME,
+ session_id,
+ CK_SESSION_INTERFACE);
+ if (proxy == NULL) {
+ g_warning ("Failed to connect to the ConsoleKit session object");
+ return FALSE;
+ }
+
+ error = NULL;
+ res = dbus_g_proxy_call (proxy,
+ "GetUnixUser",
+ &error,
+ G_TYPE_INVALID,
+ G_TYPE_UINT, &uid,
+ G_TYPE_INVALID);
+ g_object_unref (proxy);
+
+ if (! res) {
+ g_warning ("Failed to query the session: %s", error->message);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ if (uidp != NULL) {
+ *uidp = (uid_t) uid;
+ }
+
+ return TRUE;
+}
+
+static void
+seat_session_added (DBusGProxy *seat_proxy,
+ const char *session_id,
+ GdmUserManager *manager)
+{
+ uid_t uid;
+ gboolean res;
+ struct passwd *pwent;
+ GdmUser *user;
+ gboolean is_new;
+
+ g_debug ("Session added: %s", session_id);
+
+ res = get_uid_from_session_id (manager, session_id, &uid);
+ if (! res) {
+ g_warning ("Unable to lookup user for session");
+ return;
+ }
+
+ errno = 0;
+ pwent = getpwuid (uid);
+ if (pwent == NULL) {
+ g_warning ("Unable to lookup user id %d: %s", (int)uid, g_strerror (errno));
+ return;
+ }
+
+ /* check exclusions up front */
+ if (g_hash_table_lookup (manager->priv->exclusions, pwent->pw_name)) {
+ g_debug ("GdmUserManager: excluding user '%s'", pwent->pw_name);
+ return;
+ }
+
+ user = g_hash_table_lookup (manager->priv->users, pwent->pw_name);
+ if (user == NULL) {
+ g_debug ("Creating new user");
+
+ user = create_user (manager);
+ _gdm_user_update (user, pwent);
+ is_new = TRUE;
+ } else {
+ is_new = FALSE;
+ }
+
+ res = maybe_add_session_for_user (manager, user, session_id);
+
+ /* only add the user if we added a session */
+ if (is_new) {
+ if (res) {
+ add_user (manager, user);
+ } else {
+ g_object_unref (user);
+ }
+ }
+}
+
+static void
+seat_session_removed (DBusGProxy *seat_proxy,
+ const char *session_id,
+ GdmUserManager *manager)
+{
+ GdmUser *user;
+ char *username;
+
+ g_debug ("Session removed: %s", session_id);
+
+ /* since the session object may already be gone
+ * we can't query CK directly */
+
+ username = g_hash_table_lookup (manager->priv->sessions, session_id);
+ if (username == NULL) {
+ return;
+ }
+
+ user = g_hash_table_lookup (manager->priv->users, username);
+ if (user == NULL) {
+ /* nothing to do */
+ return;
+ }
+
+ g_debug ("GdmUserManager: Session removed for %s", username);
+ _gdm_user_remove_session (user, session_id);
+}
+
+static void
+on_proxy_destroy (DBusGProxy *proxy,
+ GdmUserManager *manager)
+{
+ g_debug ("GdmUserManager: seat proxy destroyed");
+
+ manager->priv->seat_proxy = NULL;
+}
+
+static void
+get_seat_proxy (GdmUserManager *manager)
+{
+ DBusGProxy *proxy;
+ GError *error;
+
+ g_assert (manager->priv->seat_proxy == NULL);
+
+ error = NULL;
+ manager->priv->connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
+ if (manager->priv->connection == NULL) {
+ g_warning ("Failed to connect to the D-Bus daemon: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ manager->priv->seat_id = get_current_seat_id (manager->priv->connection);
+ if (manager->priv->seat_id == NULL) {
+ return;
+ }
+
+ g_debug ("GdmUserManager: Found current seat: %s", manager->priv->seat_id);
+
+ error = NULL;
+ proxy = dbus_g_proxy_new_for_name_owner (manager->priv->connection,
+ CK_NAME,
+ manager->priv->seat_id,
+ CK_SEAT_INTERFACE,
+ &error);
+
+ if (proxy == NULL) {
+ g_warning ("Failed to connect to the ConsoleKit seat object: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ g_signal_connect (proxy, "destroy", G_CALLBACK (on_proxy_destroy), manager);
+
+ dbus_g_proxy_add_signal (proxy,
+ "SessionAdded",
+ DBUS_TYPE_G_OBJECT_PATH,
+ G_TYPE_INVALID);
+ dbus_g_proxy_add_signal (proxy,
+ "SessionRemoved",
+ DBUS_TYPE_G_OBJECT_PATH,
+ G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal (proxy,
+ "SessionAdded",
+ G_CALLBACK (seat_session_added),
+ manager,
+ NULL);
+ dbus_g_proxy_connect_signal (proxy,
+ "SessionRemoved",
+ G_CALLBACK (seat_session_removed),
+ manager,
+ NULL);
+ manager->priv->seat_proxy = proxy;
+
+}
+
+/**
+ * gdm_manager_get_user:
+ * @manager: the manager to query.
+ * @username: the login name of the user to get.
+ *
+ * Retrieves a pointer to the #GdmUser object for the login named @username
+ * from @manager. This pointer is not a reference, and should not be released.
+ *
+ * Returns: a pointer to a #GdmUser object.
+ **/
+GdmUser *
+gdm_user_manager_get_user (GdmUserManager *manager,
+ const char *username)
+{
+ GdmUser *user;
+
+ g_return_val_if_fail (GDM_IS_USER_MANAGER (manager), NULL);
+ g_return_val_if_fail (username != NULL && username[0] != '\0', NULL);
+
+ user = g_hash_table_lookup (manager->priv->users, username);
+
+ if (user == NULL) {
+ struct passwd *pwent;
+
+ pwent = getpwnam (username);
+
+ if (pwent != NULL) {
+ user = add_new_user_for_pwent (manager, pwent);
+ }
+ }
+
+ return user;
+}
+
+GdmUser *
+gdm_user_manager_get_user_by_uid (GdmUserManager *manager,
+ uid_t uid)
+{
+ GdmUser *user;
+ struct passwd *pwent;
+
+ g_return_val_if_fail (GDM_IS_USER_MANAGER (manager), NULL);
+
+ pwent = getpwuid (uid);
+ if (pwent == NULL) {
+ g_warning ("GdmUserManager: unable to lookup uid %d", (int)uid);
+ return NULL;
+ }
+
+ user = g_hash_table_lookup (manager->priv->users, pwent->pw_name);
+
+ if (user == NULL) {
+ user = add_new_user_for_pwent (manager, pwent);
+ }
+
+ return user;
+}
+
+static void
+listify_hash_values_hfunc (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ GSList **list = user_data;
+
+ *list = g_slist_prepend (*list, value);
+}
+
+GSList *
+gdm_user_manager_list_users (GdmUserManager *manager)
+{
+ GSList *retval;
+
+ g_return_val_if_fail (GDM_IS_USER_MANAGER (manager), NULL);
+
+ retval = NULL;
+ g_hash_table_foreach (manager->priv->users, listify_hash_values_hfunc, &retval);
+
+ return g_slist_sort (retval, (GCompareFunc) gdm_user_collate);
+}
+
+static gboolean
+parse_value_as_ulong (const char *value,
+ gulong *ulongval)
+{
+ char *end_of_valid_long;
+ glong long_value;
+ gulong ulong_value;
+
+ errno = 0;
+ long_value = strtol (value, &end_of_valid_long, 10);
+
+ if (*value == '\0' || *end_of_valid_long != '\0') {
+ return FALSE;
+ }
+
+ ulong_value = long_value;
+ if (ulong_value != long_value || errno == ERANGE) {
+ return FALSE;
+ }
+
+ *ulongval = ulong_value;
+
+ return TRUE;
+}
+
+static gboolean
+parse_ck_history_line (const char *line,
+ char **user_namep,
+ gulong *frequencyp)
+{
+ GRegex *re;
+ GMatchInfo *match_info;
+ gboolean res;
+ gboolean ret;
+ GError *error;
+
+ ret = FALSE;
+ re = NULL;
+ match_info = NULL;
+
+ error = NULL;
+ re = g_regex_new ("(?P<username>[0-9a-zA-Z]+)[ ]+(?P<frequency>[0-9]+)", 0, 0, &error);
+ if (re == NULL) {
+ g_critical ("%s", error->message);
+ goto out;
+ }
+
+ g_regex_match (re, line, 0, &match_info);
+
+ res = g_match_info_matches (match_info);
+ if (! res) {
+ g_warning ("Unable to parse history: %s", line);
+ goto out;
+ }
+
+ if (user_namep != NULL) {
+ *user_namep = g_match_info_fetch_named (match_info, "username");
+ }
+
+ if (frequencyp != NULL) {
+ char *freq;
+ freq = g_match_info_fetch_named (match_info, "frequency");
+ res = parse_value_as_ulong (freq, frequencyp);
+ g_free (freq);
+ if (! res) {
+ goto out;
+ }
+ }
+
+ ret = TRUE;
+
+ out:
+ if (match_info != NULL) {
+ g_match_info_free (match_info);
+ }
+ if (re != NULL) {
+ g_regex_unref (re);
+ }
+ return ret;
+}
+
+static void
+process_ck_history_line (GdmUserManager *manager,
+ const char *line)
+{
+ gboolean res;
+ char *username;
+ gulong frequency;
+ GdmUser *user;
+
+ frequency = 0;
+ username = NULL;
+ res = parse_ck_history_line (line, &username, &frequency);
+ if (! res) {
+ return;
+ }
+
+ if (g_hash_table_lookup (manager->priv->exclusions, username)) {
+ g_debug ("GdmUserManager: excluding user '%s'", username);
+ g_free (username);
+ return;
+ }
+
+ user = gdm_user_manager_get_user (manager, username);
+ if (user == NULL) {
+ g_debug ("GdmUserManager: unable to lookup user '%s'", username);
+ g_free (username);
+ return;
+ }
+
+ g_object_set (user, "login-frequency", frequency, NULL);
+ g_signal_emit (manager, signals [USER_LOGIN_FREQUENCY_CHANGED], 0, user);
+ g_free (username);
+}
+
+static gboolean
+ck_history_watch (GIOChannel *source,
+ GIOCondition condition,
+ GdmUserManager *manager)
+{
+ GIOStatus status;
+ gboolean done = FALSE;
+
+ g_return_val_if_fail (manager != NULL, FALSE);
+
+ if (condition & G_IO_IN) {
+ char *str;
+ GError *error;
+
+ error = NULL;
+ status = g_io_channel_read_line (source, &str, NULL, NULL, &error);
+ if (error != NULL) {
+ g_warning ("GdmUserManager: unable to read line: %s", error->message);
+ g_error_free (error);
+ }
+
+ if (status == G_IO_STATUS_NORMAL) {
+ g_debug ("GdmUserManager: history output: %s", str);
+ process_ck_history_line (manager, str);
+ } else if (status == G_IO_STATUS_EOF) {
+ done = TRUE;
+ }
+
+ g_free (str);
+ } else if (condition & G_IO_HUP) {
+ done = TRUE;
+ }
+
+ if (done) {
+ g_signal_emit (G_OBJECT (manager), signals[USERS_LOADED], 0);
+
+ manager->priv->ck_history_id = 0;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+reload_ck_history (GdmUserManager *manager)
+{
+ char *command;
+ const char *seat_id;
+ GError *error;
+ gboolean res;
+ char **argv;
+ int standard_out;
+ GIOChannel *channel;
+
+ seat_id = NULL;
+ if (manager->priv->seat_id != NULL
+ && g_str_has_prefix (manager->priv->seat_id, "/org/freedesktop/ConsoleKit/")) {
+
+ seat_id = manager->priv->seat_id + strlen ("/org/freedesktop/ConsoleKit/");
+ }
+
+ if (seat_id == NULL) {
+ g_warning ("Unable to find users: no seat-id found");
+ return;
+ }
+
+ command = g_strdup_printf ("ck-history --frequent --seat='%s' --session-type=''",
+ seat_id);
+ g_debug ("GdmUserManager: running '%s'", command);
+ error = NULL;
+ if (! g_shell_parse_argv (command, NULL, &argv, &error)) {
+ g_warning ("Could not parse command: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ error = NULL;
+ res = g_spawn_async_with_pipes (NULL,
+ argv,
+ NULL,
+ G_SPAWN_SEARCH_PATH,
+ NULL,
+ NULL,
+ NULL, /* pid */
+ NULL,
+ &standard_out,
+ NULL,
+ &error);
+ g_strfreev (argv);
+ if (! res) {
+ g_warning ("Unable to run ck-history: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ channel = g_io_channel_unix_new (standard_out);
+ g_io_channel_set_close_on_unref (channel, TRUE);
+ g_io_channel_set_flags (channel,
+ g_io_channel_get_flags (channel) | G_IO_FLAG_NONBLOCK,
+ NULL);
+ manager->priv->ck_history_id = g_io_add_watch (channel,
+ G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ (GIOFunc)ck_history_watch,
+ manager);
+ g_io_channel_unref (channel);
+
+ out:
+ g_free (command);
+}
+
+static void
+reload_passwd (GdmUserManager *manager)
+{
+ struct passwd *pwent;
+ GSList *old_users;
+ GSList *new_users;
+ GSList *list;
+ FILE *fp;
+
+ old_users = NULL;
+ new_users = NULL;
+
+ errno = 0;
+ fp = fopen (PATH_PASSWD, "r");
+ if (fp == NULL) {
+ g_warning ("Unable to open %s: %s", PATH_PASSWD, g_strerror (errno));
+ goto out;
+ }
+
+ g_hash_table_foreach (manager->priv->users, listify_hash_values_hfunc, &old_users);
+ g_slist_foreach (old_users, (GFunc) g_object_ref, NULL);
+
+ /* Make sure we keep users who are logged in no matter what. */
+ for (list = old_users; list; list = list->next) {
+ if (gdm_user_get_num_sessions (list->data) > 0) {
+ g_object_freeze_notify (G_OBJECT (list->data));
+ new_users = g_slist_prepend (new_users, g_object_ref (list->data));
+ }
+ }
+
+ for (pwent = fgetpwent (fp); pwent != NULL; pwent = fgetpwent (fp)) {
+ GdmUser *user;
+
+ user = NULL;
+
+ /* Skip users below MinimalUID... */
+ if (pwent->pw_uid < DEFAULT_MINIMAL_UID) {
+ continue;
+ }
+
+ /* ...And users w/ invalid shells... */
+ if (pwent->pw_shell == NULL ||
+ !g_hash_table_lookup (manager->priv->shells, pwent->pw_shell)) {
+ g_debug ("GdmUserManager: skipping user with bad shell: %s", pwent->pw_name);
+ continue;
+ }
+
+ /* ...And explicitly excluded users */
+ if (g_hash_table_lookup (manager->priv->exclusions, pwent->pw_name)) {
+ g_debug ("GdmUserManager: explicitly skipping user: %s", pwent->pw_name);
+ continue;
+ }
+
+ user = g_hash_table_lookup (manager->priv->users, pwent->pw_name);
+
+ /* Update users already in the *new* list */
+ if (g_slist_find (new_users, user)) {
+ _gdm_user_update (user, pwent);
+ continue;
+ }
+
+ if (user == NULL) {
+ user = create_user (manager);
+ } else {
+ g_object_ref (user);
+ }
+
+ /* Freeze & update users not already in the new list */
+ g_object_freeze_notify (G_OBJECT (user));
+ _gdm_user_update (user, pwent);
+
+ new_users = g_slist_prepend (new_users, user);
+ }
+
+ /* Go through and handle added users */
+ for (list = new_users; list; list = list->next) {
+ if (! g_slist_find (old_users, list->data)) {
+ add_user (manager, list->data);
+ }
+ }
+
+ /* Go through and handle removed users */
+ for (list = old_users; list; list = list->next) {
+ if (! g_slist_find (new_users, list->data)) {
+ g_signal_emit (manager, signals[USER_REMOVED], 0, list->data);
+ g_hash_table_remove (manager->priv->users,
+ gdm_user_get_user_name (list->data));
+ }
+ }
+
+ out:
+ /* Cleanup */
+
+ fclose (fp);
+
+ g_slist_foreach (new_users, (GFunc) g_object_thaw_notify, NULL);
+ g_slist_foreach (new_users, (GFunc) g_object_unref, NULL);
+ g_slist_free (new_users);
+
+ g_slist_foreach (old_users, (GFunc) g_object_unref, NULL);
+ g_slist_free (old_users);
+}
+
+static void
+reload_users (GdmUserManager *manager)
+{
+ reload_ck_history (manager);
+ reload_passwd (manager);
+}
+
+static gboolean
+reload_users_timeout (GdmUserManager *manager)
+{
+ reload_users (manager);
+ manager->priv->reload_id = 0;
+
+ return FALSE;
+}
+
+static void
+queue_reload_users (GdmUserManager *manager)
+{
+ if (manager->priv->reload_id > 0) {
+ return;
+ }
+
+ g_signal_emit (G_OBJECT (manager), signals[LOADING_USERS], 0);
+ manager->priv->reload_id = g_idle_add ((GSourceFunc)reload_users_timeout, manager);
+}
+
+static void
+reload_shells (GdmUserManager *manager)
+{
+ char *shell;
+
+ setusershell ();
+
+ g_hash_table_remove_all (manager->priv->shells);
+ for (shell = getusershell (); shell != NULL; shell = getusershell ()) {
+ /* skip well known not-real shells */
+ if (shell == NULL
+ || strcmp (shell, "/sbin/nologin") == 0
+ || strcmp (shell, "/bin/false") == 0) {
+ g_debug ("GdmUserManager: skipping shell %s", shell);
+ continue;
+ }
+ g_hash_table_insert (manager->priv->shells,
+ g_strdup (shell),
+ GUINT_TO_POINTER (TRUE));
+ }
+
+ endusershell ();
+}
+
+static void
+on_shells_monitor_changed (GFileMonitor *monitor,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ GdmUserManager *manager)
+{
+ if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
+ event_type != G_FILE_MONITOR_EVENT_CREATED) {
+ return;
+ }
+
+ reload_shells (manager);
+ reload_passwd (manager);
+}
+
+static void
+on_passwd_monitor_changed (GFileMonitor *monitor,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ GdmUserManager *manager)
+{
+ if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
+ event_type != G_FILE_MONITOR_EVENT_CREATED) {
+ return;
+ }
+
+ reload_passwd (manager);
+}
+
+static void
+gdm_user_manager_class_init (GdmUserManagerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gdm_user_manager_finalize;
+
+ signals [LOADING_USERS] =
+ g_signal_new ("loading-users",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GdmUserManagerClass, loading_users),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ signals [USERS_LOADED] =
+ g_signal_new ("users-loaded",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GdmUserManagerClass, users_loaded),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ signals [USER_ADDED] =
+ g_signal_new ("user-added",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GdmUserManagerClass, user_added),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, GDM_TYPE_USER);
+ signals [USER_REMOVED] =
+ g_signal_new ("user-removed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GdmUserManagerClass, user_removed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, GDM_TYPE_USER);
+ signals [USER_IS_LOGGED_IN_CHANGED] =
+ g_signal_new ("user-is-logged-in-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GdmUserManagerClass, user_is_logged_in_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, GDM_TYPE_USER);
+ signals [USER_LOGIN_FREQUENCY_CHANGED] =
+ g_signal_new ("user-login-frequency-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GdmUserManagerClass, user_login_frequency_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, GDM_TYPE_USER);
+
+ g_type_class_add_private (klass, sizeof (GdmUserManagerPrivate));
+}
+
+static void
+gdm_user_manager_init (GdmUserManager *manager)
+{
+ int i;
+ GFile *file;
+ GError *error;
+ const char *exclude_default[] = DEFAULT_EXCLUDE;
+
+ manager->priv = GDM_USER_MANAGER_GET_PRIVATE (manager);
+
+ /* sessions */
+ manager->priv->sessions = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ g_free);
+
+ /* exclusions */
+ manager->priv->exclusions = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ NULL);
+ for (i = 0; exclude_default[i] != NULL; i++) {
+ g_hash_table_insert (manager->priv->exclusions,
+ g_strdup (exclude_default [i]),
+ GUINT_TO_POINTER (TRUE));
+ }
+
+ /* /etc/shells */
+ manager->priv->shells = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ NULL);
+ reload_shells (manager);
+ file = g_file_new_for_path (_PATH_SHELLS);
+ error = NULL;
+ manager->priv->shells_monitor = g_file_monitor_file (file,
+ G_FILE_MONITOR_NONE,
+ NULL,
+ &error);
+ if (manager->priv->shells_monitor != NULL) {
+ g_signal_connect (manager->priv->shells_monitor,
+ "changed",
+ G_CALLBACK (on_shells_monitor_changed),
+ manager);
+ } else {
+ g_warning ("Unable to monitor %s: %s", _PATH_SHELLS, error->message);
+ g_error_free (error);
+ }
+ g_object_unref (file);
+
+ /* /etc/passwd */
+ manager->priv->users = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ (GDestroyNotify) g_object_run_dispose);
+ file = g_file_new_for_path (PATH_PASSWD);
+ manager->priv->passwd_monitor = g_file_monitor_file (file,
+ G_FILE_MONITOR_NONE,
+ NULL,
+ &error);
+ if (manager->priv->passwd_monitor != NULL) {
+ g_signal_connect (manager->priv->passwd_monitor,
+ "changed",
+ G_CALLBACK (on_passwd_monitor_changed),
+ manager);
+ } else {
+ g_warning ("Unable to monitor %s: %s", PATH_PASSWD, error->message);
+ g_error_free (error);
+ }
+ g_object_unref (file);
+
+
+ get_seat_proxy (manager);
+
+ queue_reload_users (manager);
+
+ manager->priv->users_dirty = FALSE;
+}
+
+static void
+gdm_user_manager_finalize (GObject *object)
+{
+ GdmUserManager *manager;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GDM_IS_USER_MANAGER (object));
+
+ manager = GDM_USER_MANAGER (object);
+
+ g_return_if_fail (manager->priv != NULL);
+
+ if (manager->priv->seat_proxy != NULL) {
+ g_object_unref (manager->priv->seat_proxy);
+ }
+
+ if (manager->priv->ck_history_id != 0) {
+ g_source_remove (manager->priv->ck_history_id);
+ manager->priv->ck_history_id = 0;
+ }
+
+ if (manager->priv->reload_id > 0) {
+ g_source_remove (manager->priv->reload_id);
+ manager->priv->reload_id = 0;
+ }
+
+ g_hash_table_destroy (manager->priv->sessions);
+
+ g_file_monitor_cancel (manager->priv->passwd_monitor);
+ g_hash_table_destroy (manager->priv->users);
+
+ g_file_monitor_cancel (manager->priv->shells_monitor);
+ g_hash_table_destroy (manager->priv->shells);
+
+ g_free (manager->priv->seat_id);
+
+ G_OBJECT_CLASS (gdm_user_manager_parent_class)->finalize (object);
+}
+
+GdmUserManager *
+gdm_user_manager_ref_default (void)
+{
+ if (user_manager_object != NULL) {
+ g_object_ref (user_manager_object);
+ } else {
+ user_manager_object = g_object_new (GDM_TYPE_USER_MANAGER, NULL);
+ g_object_add_weak_pointer (user_manager_object,
+ (gpointer *) &user_manager_object);
+ }
+
+ return GDM_USER_MANAGER (user_manager_object);
+}
Added: trunk/src/gdmuser/gdm-user-manager.h
==============================================================================
--- (empty file)
+++ trunk/src/gdmuser/gdm-user-manager.h Wed Feb 4 18:45:38 2009
@@ -0,0 +1,87 @@
+/* -*- 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __GDM_USER_MANAGER_H
+#define __GDM_USER_MANAGER_H
+
+#include <glib-object.h>
+
+#include "gdm-user.h"
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_USER_MANAGER (gdm_user_manager_get_type ())
+#define GDM_USER_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_USER_MANAGER, GdmUserManager))
+#define GDM_USER_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_USER_MANAGER, GdmUserManagerClass))
+#define GDM_IS_USER_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_USER_MANAGER))
+#define GDM_IS_USER_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_USER_MANAGER))
+#define GDM_USER_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_USER_MANAGER, GdmUserManagerClass))
+
+typedef struct GdmUserManagerPrivate GdmUserManagerPrivate;
+
+typedef struct
+{
+ GObject parent;
+ GdmUserManagerPrivate *priv;
+} GdmUserManager;
+
+typedef struct
+{
+ GObjectClass parent_class;
+
+ void (* loading_users) (GdmUserManager *user_manager);
+ void (* users_loaded) (GdmUserManager *user_manager);
+ void (* user_added) (GdmUserManager *user_manager,
+ GdmUser *user);
+ void (* user_removed) (GdmUserManager *user_manager,
+ GdmUser *user);
+ void (* user_is_logged_in_changed) (GdmUserManager *user_manager,
+ GdmUser *user);
+ void (* user_login_frequency_changed) (GdmUserManager *user_manager,
+ GdmUser *user);
+} GdmUserManagerClass;
+
+typedef enum
+{
+ GDM_USER_MANAGER_ERROR_GENERAL,
+ GDM_USER_MANAGER_ERROR_KEY_NOT_FOUND
+} GdmUserManagerError;
+
+#define GDM_USER_MANAGER_ERROR gdm_user_manager_error_quark ()
+
+GQuark gdm_user_manager_error_quark (void);
+GType gdm_user_manager_get_type (void);
+
+GdmUserManager * gdm_user_manager_ref_default (void);
+
+GSList * gdm_user_manager_list_users (GdmUserManager *manager);
+GdmUser * gdm_user_manager_get_user (GdmUserManager *manager,
+ const char *user_name);
+GdmUser * gdm_user_manager_get_user_by_uid (GdmUserManager *manager,
+ uid_t uid);
+
+gboolean gdm_user_manager_activate_user_session (GdmUserManager *manager,
+ GdmUser *user);
+
+gboolean gdm_user_manager_goto_login_session (GdmUserManager *manager);
+
+G_END_DECLS
+
+#endif /* __GDM_USER_MANAGER_H */
Added: trunk/src/gdmuser/gdm-user-private.h
==============================================================================
--- (empty file)
+++ trunk/src/gdmuser/gdm-user-private.h Wed Feb 4 18:45:38 2009
@@ -0,0 +1,44 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2005 James M. Cape <jcape ignore-your tv>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Private interfaces to the GdmUser object
+ */
+
+#ifndef __GDM_USER_PRIVATE__
+#define __GDM_USER_PRIVATE__ 1
+
+#include <pwd.h>
+
+#include "gdm-user.h"
+
+G_BEGIN_DECLS
+
+void _gdm_user_update (GdmUser *user,
+ const struct passwd *pwent);
+void _gdm_user_add_session (GdmUser *user,
+ const char *session_id);
+void _gdm_user_remove_session (GdmUser *user,
+ const char *session_id);
+
+void _gdm_user_icon_changed (GdmUser *user);
+
+G_END_DECLS
+
+#endif /* !__GDM_USER_PRIVATE__ */
Added: trunk/src/gdmuser/gdm-user.c
==============================================================================
--- (empty file)
+++ trunk/src/gdmuser/gdm-user.c Wed Feb 4 18:45:38 2009
@@ -0,0 +1,1171 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2005 James M. Cape <jcape ignore-your tv>.
+ * Copyright (C) 2007-2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <config.h>
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+
+#include "gdm-user-manager.h"
+#include "gdm-user-private.h"
+
+#define GDM_USER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDM_TYPE_USER, GdmUserClass))
+#define GDM_IS_USER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDM_TYPE_USER))
+#define GDM_USER_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS ((object), GDM_TYPE_USER, GdmUserClass))
+
+#define GLOBAL_FACEDIR DATADIR "/faces"
+#define MAX_ICON_SIZE 128
+#define MAX_FILE_SIZE 65536
+#define MINIMAL_UID 100
+#define RELAX_GROUP TRUE
+#define RELAX_OTHER TRUE
+
+enum {
+ PROP_0,
+ PROP_MANAGER,
+ PROP_REAL_NAME,
+ PROP_USER_NAME,
+ PROP_UID,
+ PROP_HOME_DIR,
+ PROP_SHELL,
+ PROP_LOGIN_FREQUENCY,
+};
+
+enum {
+ ICON_CHANGED,
+ SESSIONS_CHANGED,
+ LAST_SIGNAL
+};
+
+struct _GdmUser {
+ GObject parent;
+
+ GdmUserManager *manager;
+
+ uid_t uid;
+ char *user_name;
+ char *real_name;
+ char *home_dir;
+ char *shell;
+ GList *sessions;
+ gulong login_frequency;
+
+ GFileMonitor *icon_monitor;
+};
+
+typedef struct _GdmUserClass
+{
+ GObjectClass parent_class;
+
+ void (* icon_changed) (GdmUser *user);
+ void (* sessions_changed) (GdmUser *user);
+} GdmUserClass;
+
+static void gdm_user_finalize (GObject *object);
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (GdmUser, gdm_user, G_TYPE_OBJECT)
+
+static int
+session_compare (const char *a,
+ const char *b)
+{
+ if (a == NULL) {
+ return 1;
+ } else if (b == NULL) {
+ return -1;
+ }
+
+ return strcmp (a, b);
+}
+
+void
+_gdm_user_add_session (GdmUser *user,
+ const char *ssid)
+{
+ GList *li;
+
+ g_return_if_fail (GDM_IS_USER (user));
+ g_return_if_fail (ssid != NULL);
+
+ li = g_list_find_custom (user->sessions, ssid, (GCompareFunc)session_compare);
+ if (li == NULL) {
+ g_debug ("GdmUser: adding session %s", ssid);
+ user->sessions = g_list_prepend (user->sessions, g_strdup (ssid));
+ g_signal_emit (user, signals[SESSIONS_CHANGED], 0);
+ } else {
+ g_debug ("GdmUser: session already present: %s", ssid);
+ }
+}
+
+void
+_gdm_user_remove_session (GdmUser *user,
+ const char *ssid)
+{
+ GList *li;
+
+ g_return_if_fail (GDM_IS_USER (user));
+ g_return_if_fail (ssid != NULL);
+
+ li = g_list_find_custom (user->sessions, ssid, (GCompareFunc)session_compare);
+ if (li != NULL) {
+ g_debug ("GdmUser: removing session %s", ssid);
+ g_free (li->data);
+ user->sessions = g_list_delete_link (user->sessions, li);
+ g_signal_emit (user, signals[SESSIONS_CHANGED], 0);
+ } else {
+ g_debug ("GdmUser: session not found: %s", ssid);
+ }
+}
+
+guint
+gdm_user_get_num_sessions (GdmUser *user)
+{
+ return g_list_length (user->sessions);
+}
+
+GList *
+gdm_user_get_sessions (GdmUser *user)
+{
+ return user->sessions;
+}
+
+static void
+_gdm_user_set_login_frequency (GdmUser *user,
+ gulong login_frequency)
+{
+ user->login_frequency = login_frequency;
+ g_object_notify (G_OBJECT (user), "login-frequency");
+}
+
+static void
+gdm_user_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GdmUser *user;
+
+ user = GDM_USER (object);
+
+ switch (param_id) {
+ case PROP_MANAGER:
+ user->manager = g_value_get_object (value);
+ g_assert (user->manager);
+ break;
+ case PROP_LOGIN_FREQUENCY:
+ _gdm_user_set_login_frequency (user, g_value_get_ulong (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gdm_user_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GdmUser *user;
+
+ user = GDM_USER (object);
+
+ switch (param_id) {
+ case PROP_MANAGER:
+ g_value_set_object (value, user->manager);
+ break;
+ case PROP_USER_NAME:
+ g_value_set_string (value, user->user_name);
+ break;
+ case PROP_REAL_NAME:
+ g_value_set_string (value, user->real_name);
+ break;
+ case PROP_HOME_DIR:
+ g_value_set_string (value, user->home_dir);
+ break;
+ case PROP_UID:
+ g_value_set_ulong (value, user->uid);
+ break;
+ case PROP_SHELL:
+ g_value_set_string (value, user->shell);
+ break;
+ case PROP_LOGIN_FREQUENCY:
+ g_value_set_ulong (value, user->login_frequency);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gdm_user_class_init (GdmUserClass *class)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = G_OBJECT_CLASS (class);
+
+ gobject_class->set_property = gdm_user_set_property;
+ gobject_class->get_property = gdm_user_get_property;
+ gobject_class->finalize = gdm_user_finalize;
+
+ g_object_class_install_property (gobject_class,
+ PROP_MANAGER,
+ g_param_spec_object ("manager",
+ _("Manager"),
+ _("The user manager object this user is controlled by."),
+ GDM_TYPE_USER_MANAGER,
+ (G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY)));
+
+ g_object_class_install_property (gobject_class,
+ PROP_REAL_NAME,
+ g_param_spec_string ("real-name",
+ "Real Name",
+ "The real name to display for this user.",
+ NULL,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_UID,
+ g_param_spec_ulong ("uid",
+ "User ID",
+ "The UID for this user.",
+ 0, G_MAXULONG, 0,
+ G_PARAM_READABLE));
+ g_object_class_install_property (gobject_class,
+ PROP_USER_NAME,
+ g_param_spec_string ("user-name",
+ "User Name",
+ "The login name for this user.",
+ NULL,
+ G_PARAM_READABLE));
+ g_object_class_install_property (gobject_class,
+ PROP_HOME_DIR,
+ g_param_spec_string ("home-directory",
+ "Home Directory",
+ "The home directory for this user.",
+ NULL,
+ G_PARAM_READABLE));
+ g_object_class_install_property (gobject_class,
+ PROP_SHELL,
+ g_param_spec_string ("shell",
+ "Shell",
+ "The shell for this user.",
+ NULL,
+ G_PARAM_READABLE));
+ g_object_class_install_property (gobject_class,
+ PROP_LOGIN_FREQUENCY,
+ g_param_spec_ulong ("login-frequency",
+ "login frequency",
+ "login frequency",
+ 0,
+ G_MAXULONG,
+ 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ signals [ICON_CHANGED] =
+ g_signal_new ("icon-changed",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GdmUserClass, icon_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ signals [SESSIONS_CHANGED] =
+ g_signal_new ("sessions-changed",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GdmUserClass, sessions_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+
+static void
+on_icon_monitor_changed (GFileMonitor *monitor,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ GdmUser *user)
+{
+ g_debug ("Icon changed: %d", event_type);
+
+ if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
+ event_type != G_FILE_MONITOR_EVENT_CREATED) {
+ return;
+ }
+
+ _gdm_user_icon_changed (user);
+}
+
+static void
+update_icon_monitor (GdmUser *user)
+{
+ GFile *file;
+ GError *error;
+ char *path;
+
+ if (user->home_dir == NULL) {
+ return;
+ }
+
+ if (user->icon_monitor != NULL) {
+ g_file_monitor_cancel (user->icon_monitor);
+ user->icon_monitor = NULL;
+ }
+
+ path = g_build_filename (user->home_dir, ".face", NULL);
+ g_debug ("adding monitor for '%s'", path);
+ file = g_file_new_for_path (path);
+ error = NULL;
+ user->icon_monitor = g_file_monitor_file (file,
+ G_FILE_MONITOR_NONE,
+ NULL,
+ &error);
+ if (user->icon_monitor != NULL) {
+ g_signal_connect (user->icon_monitor,
+ "changed",
+ G_CALLBACK (on_icon_monitor_changed),
+ user);
+ } else {
+ g_warning ("Unable to monitor %s: %s", path, error->message);
+ g_error_free (error);
+ }
+ g_object_unref (file);
+ g_free (path);
+}
+
+static void
+gdm_user_init (GdmUser *user)
+{
+ user->manager = NULL;
+ user->user_name = NULL;
+ user->real_name = NULL;
+ user->sessions = NULL;
+}
+
+static void
+gdm_user_finalize (GObject *object)
+{
+ GdmUser *user;
+
+ user = GDM_USER (object);
+
+ g_file_monitor_cancel (user->icon_monitor);
+
+ g_free (user->user_name);
+ g_free (user->real_name);
+
+ if (G_OBJECT_CLASS (gdm_user_parent_class)->finalize)
+ (*G_OBJECT_CLASS (gdm_user_parent_class)->finalize) (object);
+}
+
+/**
+ * _gdm_user_update:
+ * @user: the user object to update.
+ * @pwent: the user data to use.
+ *
+ * Updates the properties of @user using the data in @pwent.
+ *
+ * Since: 1.0
+ **/
+void
+_gdm_user_update (GdmUser *user,
+ const struct passwd *pwent)
+{
+ gchar *real_name;
+
+ g_return_if_fail (GDM_IS_USER (user));
+ g_return_if_fail (pwent != NULL);
+
+ g_object_freeze_notify (G_OBJECT (user));
+
+ /* Display Name */
+ if (pwent->pw_gecos && pwent->pw_gecos[0] != '\0') {
+ gchar *first_comma;
+
+ first_comma = strchr (pwent->pw_gecos, ',');
+ if (first_comma) {
+ real_name = g_strndup (pwent->pw_gecos,
+ (first_comma - pwent->pw_gecos));
+ } else {
+ real_name = g_strdup (pwent->pw_gecos);
+ }
+
+ if (real_name[0] == '\0') {
+ g_free (real_name);
+ real_name = NULL;
+ }
+ } else {
+ real_name = NULL;
+ }
+
+ if ((real_name && !user->real_name) ||
+ (!real_name && user->real_name) ||
+ (real_name &&
+ user->real_name &&
+ strcmp (real_name, user->real_name) != 0)) {
+ g_free (user->real_name);
+ user->real_name = real_name;
+ g_object_notify (G_OBJECT (user), "real-name");
+ } else {
+ g_free (real_name);
+ }
+
+ /* UID */
+ if (pwent->pw_uid != user->uid) {
+ user->uid = pwent->pw_uid;
+ g_object_notify (G_OBJECT (user), "uid");
+ }
+
+ /* Username */
+ if ((pwent->pw_name && !user->user_name) ||
+ (!pwent->pw_name && user->user_name) ||
+ (pwent->pw_name &&
+ user->user_name &&
+ strcmp (user->user_name, pwent->pw_name) != 0)) {
+ g_free (user->user_name);
+ user->user_name = g_strdup (pwent->pw_name);
+ g_object_notify (G_OBJECT (user), "user-name");
+ }
+
+ /* Home Directory */
+ if ((pwent->pw_dir && !user->home_dir) ||
+ (!pwent->pw_dir && user->home_dir) ||
+ strcmp (user->home_dir, pwent->pw_dir) != 0) {
+ g_free (user->home_dir);
+ user->home_dir = g_strdup (pwent->pw_dir);
+ g_object_notify (G_OBJECT (user), "home-directory");
+ g_signal_emit (user, signals[ICON_CHANGED], 0);
+ }
+
+ /* Shell */
+ if ((pwent->pw_shell && !user->shell) ||
+ (!pwent->pw_shell && user->shell) ||
+ (pwent->pw_shell &&
+ user->shell &&
+ strcmp (user->shell, pwent->pw_shell) != 0)) {
+ g_free (user->shell);
+ user->shell = g_strdup (pwent->pw_shell);
+ g_object_notify (G_OBJECT (user), "shell");
+ }
+
+ update_icon_monitor (user);
+
+ g_object_thaw_notify (G_OBJECT (user));
+}
+
+/**
+ * _gdm_user_icon_changed:
+ * @user: the user to emit the signal for.
+ *
+ * Emits the "icon-changed" signal for @user.
+ *
+ * Since: 1.0
+ **/
+void
+_gdm_user_icon_changed (GdmUser *user)
+{
+ g_return_if_fail (GDM_IS_USER (user));
+
+ g_signal_emit (user, signals[ICON_CHANGED], 0);
+}
+
+/**
+ * gdm_user_get_uid:
+ * @user: the user object to examine.
+ *
+ * Retrieves the ID of @user.
+ *
+ * Returns: a pointer to an array of characters which must not be modified or
+ * freed, or %NULL.
+ *
+ * Since: 1.0
+ **/
+
+uid_t
+gdm_user_get_uid (GdmUser *user)
+{
+ g_return_val_if_fail (GDM_IS_USER (user), -1);
+
+ return user->uid;
+}
+
+/**
+ * gdm_user_get_real_name:
+ * @user: the user object to examine.
+ *
+ * Retrieves the display name of @user.
+ *
+ * Returns: a pointer to an array of characters which must not be modified or
+ * freed, or %NULL.
+ *
+ * Since: 1.0
+ **/
+G_CONST_RETURN gchar *
+gdm_user_get_real_name (GdmUser *user)
+{
+ g_return_val_if_fail (GDM_IS_USER (user), NULL);
+
+ return (user->real_name ? user->real_name : user->user_name);
+}
+
+/**
+ * gdm_user_get_user_name:
+ * @user: the user object to examine.
+ *
+ * Retrieves the login name of @user.
+ *
+ * Returns: a pointer to an array of characters which must not be modified or
+ * freed, or %NULL.
+ *
+ * Since: 1.0
+ **/
+
+G_CONST_RETURN gchar *
+gdm_user_get_user_name (GdmUser *user)
+{
+ g_return_val_if_fail (GDM_IS_USER (user), NULL);
+
+ return user->user_name;
+}
+
+/**
+ * gdm_user_get_home_directory:
+ * @user: the user object to examine.
+ *
+ * Retrieves the home directory of @user.
+ *
+ * Returns: a pointer to an array of characters which must not be modified or
+ * freed, or %NULL.
+ *
+ * Since: 1.0
+ **/
+
+G_CONST_RETURN gchar *
+gdm_user_get_home_directory (GdmUser *user)
+{
+ g_return_val_if_fail (GDM_IS_USER (user), NULL);
+
+ return user->home_dir;
+}
+
+/**
+ * gdm_user_get_shell:
+ * @user: the user object to examine.
+ *
+ * Retrieves the login shell of @user.
+ *
+ * Returns: a pointer to an array of characters which must not be modified or
+ * freed, or %NULL.
+ *
+ * Since: 1.0
+ **/
+
+G_CONST_RETURN gchar *
+gdm_user_get_shell (GdmUser *user)
+{
+ g_return_val_if_fail (GDM_IS_USER (user), NULL);
+
+ return user->shell;
+}
+
+gulong
+gdm_user_get_login_frequency (GdmUser *user)
+{
+ g_return_val_if_fail (GDM_IS_USER (user), 0);
+
+ return user->login_frequency;
+}
+
+int
+gdm_user_collate (GdmUser *user1,
+ GdmUser *user2)
+{
+ const char *str1;
+ const char *str2;
+ gulong num1;
+ gulong num2;
+
+ g_return_val_if_fail (user1 == NULL || GDM_IS_USER (user1), 0);
+ g_return_val_if_fail (user2 == NULL || GDM_IS_USER (user2), 0);
+
+ if (user1->real_name != NULL) {
+ str1 = user1->real_name;
+ } else {
+ str1 = user1->user_name;
+ }
+
+ if (user2->real_name != NULL) {
+ str2 = user2->real_name;
+ } else {
+ str2 = user2->user_name;
+ }
+
+ num1 = user1->login_frequency;
+ num2 = user2->login_frequency;
+ g_debug ("Login freq 1=%u 2=%u", (guint)num1, (guint)num2);
+ if (num1 > num2) {
+ return -1;
+ }
+
+ if (num1 < num2) {
+ return 1;
+ }
+
+ /* if login frequency is equal try names */
+ if (str1 == NULL && str2 != NULL) {
+ return -1;
+ }
+
+ if (str1 != NULL && str2 == NULL) {
+ return 1;
+ }
+
+ if (str1 == NULL && str2 == NULL) {
+ return 0;
+ }
+
+ return g_utf8_collate (str1, str2);
+}
+
+static gboolean
+check_user_file (const char *filename,
+ uid_t user,
+ gssize max_file_size,
+ gboolean relax_group,
+ gboolean relax_other)
+{
+ struct stat fileinfo;
+
+ if (max_file_size < 0) {
+ max_file_size = G_MAXSIZE;
+ }
+
+ /* Exists/Readable? */
+ if (stat (filename, &fileinfo) < 0) {
+ return FALSE;
+ }
+
+ /* Is a regular file */
+ if (G_UNLIKELY (!S_ISREG (fileinfo.st_mode))) {
+ return FALSE;
+ }
+
+ /* Owned by user? */
+ if (G_UNLIKELY (fileinfo.st_uid != user)) {
+ return FALSE;
+ }
+
+ /* Group not writable or relax_group? */
+ if (G_UNLIKELY ((fileinfo.st_mode & S_IWGRP) == S_IWGRP && !relax_group)) {
+ return FALSE;
+ }
+
+ /* Other not writable or relax_other? */
+ if (G_UNLIKELY ((fileinfo.st_mode & S_IWOTH) == S_IWOTH && !relax_other)) {
+ return FALSE;
+ }
+
+ /* Size is kosher? */
+ if (G_UNLIKELY (fileinfo.st_size > max_file_size)) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static char *
+get_filesystem_type (const char *path)
+{
+ GFile *file;
+ GFileInfo *file_info;
+ GError *error;
+ char *filesystem_type;
+
+ file = g_file_new_for_path (path);
+ error = NULL;
+ file_info = g_file_query_filesystem_info (file,
+ G_FILE_ATTRIBUTE_FILESYSTEM_TYPE,
+ NULL,
+ &error);
+ if (file_info == NULL) {
+ g_warning ("Unable to query filesystem type for %s: %s", path, error->message);
+ g_error_free (error);
+ g_object_unref (file);
+ return NULL;
+ }
+
+ filesystem_type = g_strdup (g_file_info_get_attribute_string (file_info,
+ G_FILE_ATTRIBUTE_FILESYSTEM_TYPE));
+ if (filesystem_type == NULL) {
+ g_warning ("GIO returned NULL filesystem type for %s", path);
+ }
+
+ g_object_unref (file);
+ g_object_unref (file_info);
+
+ return filesystem_type;
+}
+
+static GdkPixbuf *
+render_icon_from_home (GdmUser *user,
+ int icon_size)
+{
+ GdkPixbuf *retval;
+ char *path;
+ gboolean is_local;
+ gboolean is_autofs;
+ gboolean res;
+ char *filesystem_type;
+
+ is_local = FALSE;
+
+ /* special case: look at parent of home to detect autofs
+ this is so we don't try to trigger an automount */
+ path = g_path_get_dirname (user->home_dir);
+ filesystem_type = get_filesystem_type (path);
+ is_autofs = (filesystem_type != NULL && strcmp (filesystem_type, "autofs") == 0);
+ g_free (filesystem_type);
+ g_free (path);
+
+ if (is_autofs) {
+ return NULL;
+ }
+
+ /* now check that home dir itself is local */
+ filesystem_type = get_filesystem_type (user->home_dir);
+ is_local = ((filesystem_type != NULL) &&
+ (strcmp (filesystem_type, "nfs") != 0) &&
+ (strcmp (filesystem_type, "afs") != 0) &&
+ (strcmp (filesystem_type, "autofs") != 0) &&
+ (strcmp (filesystem_type, "unknown") != 0) &&
+ (strcmp (filesystem_type, "ncpfs") != 0));
+ g_free (filesystem_type);
+
+ /* only look at local home directories so we don't try to
+ read from remote (e.g. NFS) volumes */
+ if (! is_local) {
+ return NULL;
+ }
+
+ /* First, try "~/.face" */
+ path = g_build_filename (user->home_dir, ".face", NULL);
+ res = check_user_file (path,
+ user->uid,
+ MAX_FILE_SIZE,
+ RELAX_GROUP,
+ RELAX_OTHER);
+ if (res) {
+ retval = gdk_pixbuf_new_from_file_at_size (path,
+ icon_size,
+ icon_size,
+ NULL);
+ } else {
+ retval = NULL;
+ }
+ g_free (path);
+
+ /* Next, try "~/.face.icon" */
+ if (retval == NULL) {
+ path = g_build_filename (user->home_dir,
+ ".face.icon",
+ NULL);
+ res = check_user_file (path,
+ user->uid,
+ MAX_FILE_SIZE,
+ RELAX_GROUP,
+ RELAX_OTHER);
+ if (res) {
+ retval = gdk_pixbuf_new_from_file_at_size (path,
+ icon_size,
+ icon_size,
+ NULL);
+ } else {
+ retval = NULL;
+ }
+
+ g_free (path);
+ }
+
+ /* Still nothing, try the user's personal GDM config */
+ if (retval == NULL) {
+ path = g_build_filename (user->home_dir,
+ ".gnome",
+ "gdm",
+ NULL);
+ res = check_user_file (path,
+ user->uid,
+ MAX_FILE_SIZE,
+ RELAX_GROUP,
+ RELAX_OTHER);
+ if (res) {
+ GKeyFile *keyfile;
+ char *icon_path;
+
+ keyfile = g_key_file_new ();
+ g_key_file_load_from_file (keyfile,
+ path,
+ G_KEY_FILE_NONE,
+ NULL);
+
+ icon_path = g_key_file_get_string (keyfile,
+ "face",
+ "picture",
+ NULL);
+ res = check_user_file (icon_path,
+ user->uid,
+ MAX_FILE_SIZE,
+ RELAX_GROUP,
+ RELAX_OTHER);
+ if (icon_path && res) {
+ retval = gdk_pixbuf_new_from_file_at_size (path,
+ icon_size,
+ icon_size,
+ NULL);
+ } else {
+ retval = NULL;
+ }
+
+ g_free (icon_path);
+ g_key_file_free (keyfile);
+ } else {
+ retval = NULL;
+ }
+
+ g_free (path);
+ }
+
+ return retval;
+}
+
+static void
+curved_rectangle (cairo_t *cr,
+ double x0,
+ double y0,
+ double width,
+ double height,
+ double radius)
+{
+ double x1;
+ double y1;
+
+ x1 = x0 + width;
+ y1 = y0 + height;
+
+ if (!width || !height) {
+ return;
+ }
+
+ if (width / 2 < radius) {
+ if (height / 2 < radius) {
+ cairo_move_to (cr, x0, (y0 + y1) / 2);
+ cairo_curve_to (cr, x0 ,y0, x0, y0, (x0 + x1) / 2, y0);
+ cairo_curve_to (cr, x1, y0, x1, y0, x1, (y0 + y1) / 2);
+ cairo_curve_to (cr, x1, y1, x1, y1, (x1 + x0) / 2, y1);
+ cairo_curve_to (cr, x0, y1, x0, y1, x0, (y0 + y1) / 2);
+ } else {
+ cairo_move_to (cr, x0, y0 + radius);
+ cairo_curve_to (cr, x0, y0, x0, y0, (x0 + x1) / 2, y0);
+ cairo_curve_to (cr, x1, y0, x1, y0, x1, y0 + radius);
+ cairo_line_to (cr, x1, y1 - radius);
+ cairo_curve_to (cr, x1, y1, x1, y1, (x1 + x0) / 2, y1);
+ cairo_curve_to (cr, x0, y1, x0, y1, x0, y1 - radius);
+ }
+ } else {
+ if (height / 2 < radius) {
+ cairo_move_to (cr, x0, (y0 + y1) / 2);
+ cairo_curve_to (cr, x0, y0, x0 , y0, x0 + radius, y0);
+ cairo_line_to (cr, x1 - radius, y0);
+ cairo_curve_to (cr, x1, y0, x1, y0, x1, (y0 + y1) / 2);
+ cairo_curve_to (cr, x1, y1, x1, y1, x1 - radius, y1);
+ cairo_line_to (cr, x0 + radius, y1);
+ cairo_curve_to (cr, x0, y1, x0, y1, x0, (y0 + y1) / 2);
+ } else {
+ cairo_move_to (cr, x0, y0 + radius);
+ cairo_curve_to (cr, x0 , y0, x0 , y0, x0 + radius, y0);
+ cairo_line_to (cr, x1 - radius, y0);
+ cairo_curve_to (cr, x1, y0, x1, y0, x1, y0 + radius);
+ cairo_line_to (cr, x1, y1 - radius);
+ cairo_curve_to (cr, x1, y1, x1, y1, x1 - radius, y1);
+ cairo_line_to (cr, x0 + radius, y1);
+ cairo_curve_to (cr, x0, y1, x0, y1, x0, y1 - radius);
+ }
+ }
+
+ cairo_close_path (cr);
+}
+
+static cairo_surface_t *
+surface_from_pixbuf (GdkPixbuf *pixbuf)
+{
+ cairo_surface_t *surface;
+ cairo_t *cr;
+
+ surface = cairo_image_surface_create (gdk_pixbuf_get_has_alpha (pixbuf) ?
+ CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24,
+ gdk_pixbuf_get_width (pixbuf),
+ gdk_pixbuf_get_height (pixbuf));
+ cr = cairo_create (surface);
+ gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
+ cairo_paint (cr);
+ cairo_destroy (cr);
+
+ return surface;
+}
+
+/**
+ * go_cairo_convert_data_to_pixbuf:
+ * @src: a pointer to pixel data in cairo format
+ * @dst: a pointer to pixel data in pixbuf format
+ * @width: image width
+ * @height: image height
+ * @rowstride: data rowstride
+ *
+ * Converts the pixel data stored in @src in CAIRO_FORMAT_ARGB32 cairo format
+ * to GDK_COLORSPACE_RGB pixbuf format and move them
+ * to @dst. If @src == @dst, pixel are converted in place.
+ **/
+
+static void
+go_cairo_convert_data_to_pixbuf (unsigned char *dst,
+ unsigned char const *src,
+ int width,
+ int height,
+ int rowstride)
+{
+ int i,j;
+ unsigned int t;
+ unsigned char a, b, c;
+
+ g_return_if_fail (dst != NULL);
+
+#define MULT(d,c,a,t) G_STMT_START { t = (a)? c * 255 / a: 0; d = t;} G_STMT_END
+
+ if (src == dst || src == NULL) {
+ for (i = 0; i < height; i++) {
+ for (j = 0; j < width; j++) {
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ MULT(a, dst[2], dst[3], t);
+ MULT(b, dst[1], dst[3], t);
+ MULT(c, dst[0], dst[3], t);
+ dst[0] = a;
+ dst[1] = b;
+ dst[2] = c;
+#else
+ MULT(a, dst[1], dst[0], t);
+ MULT(b, dst[2], dst[0], t);
+ MULT(c, dst[3], dst[0], t);
+ dst[3] = dst[0];
+ dst[0] = a;
+ dst[1] = b;
+ dst[2] = c;
+#endif
+ dst += 4;
+ }
+ dst += rowstride - width * 4;
+ }
+ } else {
+ for (i = 0; i < height; i++) {
+ for (j = 0; j < width; j++) {
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ MULT(dst[0], src[2], src[3], t);
+ MULT(dst[1], src[1], src[3], t);
+ MULT(dst[2], src[0], src[3], t);
+ dst[3] = src[3];
+#else
+ MULT(dst[0], src[1], src[0], t);
+ MULT(dst[1], src[2], src[0], t);
+ MULT(dst[2], src[3], src[0], t);
+ dst[3] = src[0];
+#endif
+ src += 4;
+ dst += 4;
+ }
+ src += rowstride - width * 4;
+ dst += rowstride - width * 4;
+ }
+ }
+#undef MULT
+}
+
+static void
+cairo_to_pixbuf (guint8 *src_data,
+ GdkPixbuf *dst_pixbuf)
+{
+ unsigned char *src;
+ unsigned char *dst;
+ guint w;
+ guint h;
+ guint rowstride;
+
+ w = gdk_pixbuf_get_width (dst_pixbuf);
+ h = gdk_pixbuf_get_height (dst_pixbuf);
+ rowstride = gdk_pixbuf_get_rowstride (dst_pixbuf);
+
+ dst = gdk_pixbuf_get_pixels (dst_pixbuf);
+ src = src_data;
+
+ go_cairo_convert_data_to_pixbuf (dst, src, w, h, rowstride);
+}
+
+static GdkPixbuf *
+frame_pixbuf (GdkPixbuf *source)
+{
+ GdkPixbuf *dest;
+ cairo_t *cr;
+ cairo_surface_t *surface;
+ guint w;
+ guint h;
+ guint rowstride;
+ int frame_width;
+ double radius;
+ guint8 *data;
+
+ frame_width = 2;
+
+ w = gdk_pixbuf_get_width (source) + frame_width * 2;
+ h = gdk_pixbuf_get_height (source) + frame_width * 2;
+ radius = w / 3.0;
+
+ dest = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
+ TRUE,
+ 8,
+ w,
+ h);
+ rowstride = gdk_pixbuf_get_rowstride (dest);
+
+
+ data = g_new0 (guint8, h * rowstride);
+
+ surface = cairo_image_surface_create_for_data (data,
+ CAIRO_FORMAT_ARGB32,
+ w,
+ h,
+ rowstride);
+ cr = cairo_create (surface);
+ cairo_surface_destroy (surface);
+
+ /* set up image */
+ cairo_rectangle (cr, 0, 0, w, h);
+ cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.0);
+ cairo_fill (cr);
+
+ curved_rectangle (cr, frame_width, frame_width,
+ w - frame_width * 2, h - frame_width * 2,
+ radius);
+ cairo_set_source_rgba (cr, 0.5, 0.5, 0.5, 0.3);
+ cairo_fill_preserve (cr);
+
+ surface = surface_from_pixbuf (source);
+ cairo_set_source_surface (cr, surface, frame_width, frame_width);
+ cairo_fill (cr);
+ cairo_surface_destroy (surface);
+
+ cairo_to_pixbuf (data, dest);
+
+ cairo_destroy (cr);
+ g_free (data);
+
+ return dest;
+}
+
+GdkPixbuf *
+gdm_user_render_icon (GdmUser *user,
+ gint icon_size)
+{
+ GdkPixbuf *pixbuf;
+ GdkPixbuf *framed;
+ char *path;
+ char *tmp;
+ gboolean res;
+
+ g_return_val_if_fail (GDM_IS_USER (user), NULL);
+ g_return_val_if_fail (icon_size > 12, NULL);
+
+ path = NULL;
+
+ pixbuf = render_icon_from_home (user, icon_size);
+ if (pixbuf != NULL) {
+ goto out;
+ }
+
+ /* Try ${GlobalFaceDir}/${username} */
+ path = g_build_filename (GLOBAL_FACEDIR, user->user_name, NULL);
+ res = check_user_file (path,
+ user->uid,
+ MAX_FILE_SIZE,
+ RELAX_GROUP,
+ RELAX_OTHER);
+ if (res) {
+ pixbuf = gdk_pixbuf_new_from_file_at_size (path,
+ icon_size,
+ icon_size,
+ NULL);
+ } else {
+ pixbuf = NULL;
+ }
+
+ g_free (path);
+ if (pixbuf != NULL) {
+ goto out;
+ }
+
+ /* Finally, ${GlobalFaceDir}/${username}.png */
+ tmp = g_strconcat (user->user_name, ".png", NULL);
+ path = g_build_filename (GLOBAL_FACEDIR, tmp, NULL);
+ g_free (tmp);
+ res = check_user_file (path,
+ user->uid,
+ MAX_FILE_SIZE,
+ RELAX_GROUP,
+ RELAX_OTHER);
+ if (res) {
+ pixbuf = gdk_pixbuf_new_from_file_at_size (path,
+ icon_size,
+ icon_size,
+ NULL);
+ } else {
+ pixbuf = NULL;
+ }
+ out:
+ g_free (path);
+
+ if (pixbuf != NULL) {
+ framed = frame_pixbuf (pixbuf);
+ if (framed != NULL) {
+ g_object_unref (pixbuf);
+ pixbuf = framed;
+ }
+ }
+
+ return pixbuf;
+}
Added: trunk/src/gdmuser/gdm-user.h
==============================================================================
--- (empty file)
+++ trunk/src/gdmuser/gdm-user.h Wed Feb 4 18:45:38 2009
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2005 James M. Cape <jcape ignore-your tv>.
+ * Copyright (C) 2007-2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Facade object for user data, owned by GdmUserManager
+ */
+
+#ifndef __GDM_USER__
+#define __GDM_USER__ 1
+
+#include <sys/types.h>
+#include <gtk/gtkwidget.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_USER (gdm_user_get_type ())
+#define GDM_USER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDM_TYPE_USER, GdmUser))
+#define GDM_IS_USER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDM_TYPE_USER))
+
+typedef struct _GdmUser GdmUser;
+
+GType gdm_user_get_type (void) G_GNUC_CONST;
+
+uid_t gdm_user_get_uid (GdmUser *user);
+G_CONST_RETURN char *gdm_user_get_user_name (GdmUser *user);
+G_CONST_RETURN char *gdm_user_get_real_name (GdmUser *user);
+G_CONST_RETURN char *gdm_user_get_home_directory (GdmUser *user);
+G_CONST_RETURN char *gdm_user_get_shell (GdmUser *user);
+guint gdm_user_get_num_sessions (GdmUser *user);
+GList *gdm_user_get_sessions (GdmUser *user);
+gulong gdm_user_get_login_frequency (GdmUser *user);
+
+GdkPixbuf *gdm_user_render_icon (GdmUser *user,
+ gint icon_size);
+
+gint gdm_user_collate (GdmUser *user1,
+ GdmUser *user2);
+
+G_END_DECLS
+
+#endif
Added: trunk/src/shell-status-menu.c
==============================================================================
--- (empty file)
+++ trunk/src/shell-status-menu.c Wed Feb 4 18:45:38 2009
@@ -0,0 +1,632 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* Adapted from gdm/gui/user-switch-applet/applet.c */
+/*
+ *
+ * Copyright (C) 2004-2005 James M. Cape <jcape ignore-your tv>.
+ * Copyright (C) 2008,2009 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "shell-status-menu.h"
+
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include <glib/gi18n.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+
+#include <gconf/gconf.h>
+#include <gconf/gconf-client.h>
+
+#include <dbus/dbus-glib.h>
+
+#define GDMUSER_I_KNOW_THIS_IS_UNSTABLE
+#include <gdmuser/gdm-user-manager.h>
+
+#include "shell-global.h"
+
+#define LOCKDOWN_DIR "/desktop/gnome/lockdown"
+#define LOCKDOWN_KEY LOCKDOWN_DIR "/disable_user_switching"
+
+struct _ShellStatusMenuPrivate {
+ GConfClient *client;
+ GdmUserManager *manager;
+ GdmUser *user;
+
+ ClutterTexture *user_icon;
+ BigBox *name_box;
+ ClutterLabel *name;
+
+ GtkWidget *menu;
+ GtkWidget *account_item;
+ GtkWidget *control_panel_item;
+ GtkWidget *lock_screen_item;
+ GtkWidget *login_screen_item;
+ GtkWidget *quit_session_item;
+
+ guint client_notify_lockdown_id;
+
+ guint current_status_state;
+
+ guint user_icon_changed_id;
+ guint user_notify_id;
+
+ gboolean has_other_users;
+
+ GtkIconSize icon_size;
+ guint pixel_size;
+};
+
+enum {
+ PROP_0
+};
+
+G_DEFINE_TYPE(ShellStatusMenu, shell_status_menu, BIG_TYPE_BOX);
+
+/* Signals */
+enum
+{
+ DEACTIVATED,
+ LAST_SIGNAL
+};
+
+static guint shell_status_menu_signals [LAST_SIGNAL] = { 0 };
+
+static void
+reset_icon (ShellStatusMenu *status)
+{
+ ShellStatusMenuPrivate *priv = status->priv;
+ GdkPixbuf *pixbuf;
+
+ if (priv->user == NULL)
+ return;
+
+ if (priv->user_icon != NULL)
+ {
+ pixbuf = gdm_user_render_icon (priv->user, 24);
+
+ if (pixbuf == NULL)
+ return;
+
+ shell_clutter_texture_set_from_pixbuf (priv->user_icon, pixbuf);
+
+ g_object_unref (pixbuf);
+ }
+}
+
+static void
+update_label (ShellStatusMenu *status)
+{
+ ShellStatusMenuPrivate *priv = status->priv;
+ char *markup;
+
+ markup = g_strdup_printf ("<b>%s</b>",
+ gdm_user_get_real_name (GDM_USER (priv->user)));
+ clutter_label_set_use_markup (priv->name, TRUE);
+ clutter_label_set_text (priv->name, markup);
+ g_free (markup);
+}
+
+static void
+on_user_icon_changed (GdmUser *user,
+ ShellStatusMenu *status)
+{
+ g_debug ("User icon changed");
+ reset_icon (status);
+}
+
+static void
+user_notify_display_name_cb (GObject *object,
+ GParamSpec *pspec,
+ ShellStatusMenu *status)
+{
+ update_label (status);
+}
+
+static void
+setup_current_user (ShellStatusMenu *status)
+{
+ ShellStatusMenuPrivate *priv = status->priv;
+ const char *name;
+
+ priv->user = gdm_user_manager_get_user_by_uid (priv->manager, getuid ());
+ if (priv->user != NULL)
+ {
+ g_object_ref (priv->user);
+ name = gdm_user_get_real_name (priv->user);
+ }
+ else
+ {
+ name = _("Unknown");
+ }
+/*
+ priv->menuitem = gtk_image_menu_item_new_with_label (name);
+ label = GTK_BIN (adata->menuitem)->child;
+ gtk_menu_shell_append (GTK_MENU_SHELL (adata->menubar), adata->menuitem);
+ gtk_widget_show (adata->menuitem);
+*/
+
+ update_label (status);
+
+ if (priv->user != NULL)
+ {
+ reset_icon (status);
+
+ priv->user_icon_changed_id =
+ g_signal_connect (priv->user,
+ "icon-changed",
+ G_CALLBACK (on_user_icon_changed),
+ status);
+ priv->user_notify_id =
+ g_signal_connect (priv->user,
+ "notify::display-name",
+ G_CALLBACK (user_notify_display_name_cb),
+ status);
+ }
+}
+
+static void
+maybe_lock_screen (ShellStatusMenu *status)
+{
+ char *args[3];
+ GError *err;
+ GdkScreen *screen;
+ gboolean use_gscreensaver = TRUE;
+ gboolean res;
+
+ g_debug ("Attempting to lock screen");
+
+ args[0] = g_find_program_in_path ("gnome-screensaver-command");
+ if (args[0] == NULL)
+ {
+ args[0] = g_find_program_in_path ("xscreensaver-command");
+ use_gscreensaver = FALSE;
+ }
+
+ if (args[0] == NULL)
+ return;
+
+ if (use_gscreensaver)
+ args[1] = "--lock";
+ else
+ args[1] = "-lock";
+ args[2] = NULL;
+
+ screen = gdk_screen_get_default ();
+
+ err = NULL;
+ res = gdk_spawn_on_screen (screen, g_get_home_dir (), args, NULL, 0, NULL,
+ NULL, NULL, &err);
+ if (!res)
+ {
+ g_warning (_("Can't lock screen: %s"), err->message);
+ g_error_free (err);
+ }
+
+ if (use_gscreensaver)
+ args[1] = "--throttle";
+ else
+ args[1] = "-throttle";
+
+ err = NULL;
+ res = gdk_spawn_on_screen (screen, g_get_home_dir (), args, NULL,
+ (G_SPAWN_STDERR_TO_DEV_NULL | G_SPAWN_STDOUT_TO_DEV_NULL), NULL, NULL,
+ NULL, &err);
+ if (!res)
+ {
+ g_warning (_("Can't temporarily set screensaver to blank screen: %s"),
+ err->message);
+ g_error_free (err);
+ }
+
+ g_free (args[0]);
+}
+
+static void
+on_lock_screen_activate (GtkMenuItem *item,
+ ShellStatusMenu *status)
+{
+ maybe_lock_screen (status);
+}
+
+static void
+do_switch (ShellStatusMenu *status,
+ GdmUser *user)
+{
+ ShellStatusMenuPrivate *priv = status->priv;
+ guint num_sessions;
+
+ g_debug ("Do user switch");
+
+ if (user == NULL)
+ {
+ gdm_user_manager_goto_login_session (priv->manager);
+ goto out;
+ }
+
+ num_sessions = gdm_user_get_num_sessions (user);
+ if (num_sessions > 0)
+ gdm_user_manager_activate_user_session (priv->manager, user);
+ else
+ gdm_user_manager_goto_login_session (priv->manager);
+out:
+ maybe_lock_screen (status);
+}
+
+static void
+on_login_screen_activate (GtkMenuItem *item,
+ ShellStatusMenu *status)
+{
+ GdmUser *user;
+
+ user = NULL;
+
+ do_switch (status, user);
+}
+
+static void
+spawn_external (ShellStatusMenu *status, const char *program)
+{
+ char *args[2];
+ GError *error;
+ GdkScreen *screen;
+ gboolean res;
+
+ args[0] = g_find_program_in_path (program);
+ if (args[0] == NULL)
+ return;
+ args[1] = NULL;
+
+ screen = gdk_screen_get_default ();
+
+ error = NULL;
+ res = gdk_spawn_on_screen (screen, g_get_home_dir (), args, NULL, 0, NULL,
+ NULL, NULL, &error);
+ if (!res)
+ {
+ g_warning ("Failed to exec %s: %s", program, error->message);
+ g_clear_error (&error);
+ }
+
+ g_free (args[0]);
+
+}
+
+static void
+on_control_panel_activate (GtkMenuItem *item,
+ ShellStatusMenu *status)
+{
+ spawn_external (status, "gnome-control-center");
+}
+
+static void
+on_account_activate (GtkMenuItem *item,
+ ShellStatusMenu *status)
+{
+ spawn_external (status, "gnome-about-me");
+}
+
+
+static void
+on_quit_session_activate (GtkMenuItem *item,
+ ShellStatusMenu *status)
+{
+ char *args[3];
+ GError *error;
+ GdkScreen *screen;
+ gboolean res;
+
+ args[0] = g_find_program_in_path ("gnome-session-save");
+ if (args[0] == NULL)
+ return;
+
+ args[1] = "--logout-dialog";
+ args[2] = NULL;
+
+ screen = gdk_screen_get_default ();
+
+ error = NULL;
+ res = gdk_spawn_on_screen (screen, g_get_home_dir (), args, NULL, 0, NULL,
+ NULL, NULL, &error);
+ if (!res)
+ {
+ g_warning (_("Can't logout: %s"), error->message);
+ g_error_free (error);
+ }
+
+ g_free (args[0]);
+}
+
+static void
+update_switch_user (ShellStatusMenu *status)
+{
+ ShellStatusMenuPrivate *priv = status->priv;
+ GSList *users;
+
+ users = gdm_user_manager_list_users (priv->manager);
+ priv->has_other_users = FALSE;
+ if (users != NULL)
+ priv->has_other_users = g_slist_length (users) > 1;
+ g_slist_free (users);
+
+ if (priv->has_other_users)
+ gtk_widget_show (priv->login_screen_item);
+ else
+ gtk_widget_hide (priv->login_screen_item);
+}
+
+static void
+on_manager_user_added (GdmUserManager *manager,
+ GdmUser *user,
+ ShellStatusMenu *status)
+{
+ update_switch_user (status);
+}
+
+static void
+on_manager_user_removed (GdmUserManager *manager,
+ GdmUser *user,
+ ShellStatusMenu *status)
+{
+ update_switch_user (status);
+}
+
+static void
+on_manager_users_loaded (GdmUserManager *manager,
+ ShellStatusMenu *status)
+{
+ update_switch_user (status);
+}
+
+static void
+menu_style_set_cb (GtkWidget *menu, GtkStyle *old_style,
+ ShellStatusMenu *status)
+{
+ ShellStatusMenuPrivate *priv = status->priv;
+ GtkSettings *settings;
+ int width;
+ int height;
+
+ priv->icon_size = gtk_icon_size_from_name ("panel-menu");
+ if (priv->icon_size == GTK_ICON_SIZE_INVALID)
+ priv->icon_size = gtk_icon_size_register ("panel-menu", 24, 24);
+
+ if (gtk_widget_has_screen (menu))
+ settings = gtk_settings_get_for_screen (gtk_widget_get_screen (menu));
+ else
+ settings = gtk_settings_get_default ();
+
+ if (!gtk_icon_size_lookup_for_settings (settings, priv->icon_size, &width,
+ &height))
+ priv->pixel_size = -1;
+ else
+ priv->pixel_size = MAX(width, height);
+}
+
+static void
+menuitem_style_set_cb (GtkWidget *menuitem,
+ GtkStyle *old_style,
+ ShellStatusMenu *status)
+{
+ GtkWidget *image;
+ const char *icon_name;
+ ShellStatusMenuPrivate *priv = status->priv;
+
+ if (menuitem == priv->login_screen_item)
+ icon_name = "system-users";
+ else if (menuitem == priv->lock_screen_item)
+ icon_name = "system-lock-screen";
+ else if (menuitem == priv->quit_session_item)
+ icon_name = "system-log-out";
+ else if (menuitem == priv->account_item)
+ icon_name = "user-info";
+ else if (menuitem == priv->control_panel_item)
+ icon_name = "preferences-desktop";
+ else
+ icon_name = GTK_STOCK_MISSING_IMAGE;
+
+ image = gtk_image_menu_item_get_image (GTK_IMAGE_MENU_ITEM (menuitem));
+ gtk_image_set_pixel_size (GTK_IMAGE (image), priv->pixel_size);
+ gtk_image_set_from_icon_name (GTK_IMAGE (image), icon_name, priv->icon_size);
+}
+
+static void
+on_deactivate (GtkMenuShell *menushell, gpointer user_data)
+{
+ ShellStatusMenu *shell = SHELL_STATUS_MENU (user_data);
+ g_signal_emit (G_OBJECT (shell), shell_status_menu_signals[DEACTIVATED], 0);
+}
+
+static void
+create_sub_menu (ShellStatusMenu *status)
+{
+ ShellStatusMenuPrivate *priv = status->priv;
+ GtkWidget *item;
+
+ priv->menu = gtk_menu_new ();
+ g_signal_connect (priv->menu, "style-set", G_CALLBACK (menu_style_set_cb),
+ status);
+
+ g_signal_connect (priv->manager, "users-loaded",
+ G_CALLBACK (on_manager_users_loaded), status);
+ g_signal_connect (priv->manager, "user-added",
+ G_CALLBACK (on_manager_user_added), status);
+ g_signal_connect (priv->manager, "user-removed",
+ G_CALLBACK (on_manager_user_removed), status);
+
+ priv->account_item = gtk_image_menu_item_new_with_label (_("Account Information..."));
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (priv->account_item),
+ gtk_image_new ());
+ gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), priv->account_item);
+ g_signal_connect (priv->account_item, "style-set",
+ G_CALLBACK (menuitem_style_set_cb), status);
+ g_signal_connect (priv->account_item, "activate",
+ G_CALLBACK (on_account_activate), status);
+ gtk_widget_show (priv->account_item);
+
+ priv->control_panel_item = gtk_image_menu_item_new_with_label (_(
+ "System Preferences..."));
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (priv->control_panel_item),
+ gtk_image_new ());
+ gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), priv->control_panel_item);
+ g_signal_connect (priv->control_panel_item, "style-set",
+ G_CALLBACK (menuitem_style_set_cb), status);
+ g_signal_connect (priv->control_panel_item, "activate",
+ G_CALLBACK (on_control_panel_activate), status);
+ gtk_widget_show (priv->control_panel_item);
+
+ item = gtk_separator_menu_item_new ();
+ gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), item);
+ gtk_widget_show (item);
+
+ priv->lock_screen_item
+ = gtk_image_menu_item_new_with_label (_("Lock Screen"));
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (priv->lock_screen_item),
+ gtk_image_new ());
+ gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), priv->lock_screen_item);
+ g_signal_connect (priv->lock_screen_item, "style-set",
+ G_CALLBACK (menuitem_style_set_cb), status);
+ g_signal_connect (priv->lock_screen_item, "activate",
+ G_CALLBACK (on_lock_screen_activate), status);
+ gtk_widget_show (priv->lock_screen_item);
+
+ priv->login_screen_item = gtk_image_menu_item_new_with_label (_("Switch User"));
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (priv->login_screen_item),
+ gtk_image_new ());
+ gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), priv->login_screen_item);
+ g_signal_connect (priv->login_screen_item, "style-set",
+ G_CALLBACK (menuitem_style_set_cb), status);
+ g_signal_connect (priv->login_screen_item, "activate",
+ G_CALLBACK (on_login_screen_activate), status);
+ /* Only show switch user if there are other users */
+
+ priv->quit_session_item = gtk_image_menu_item_new_with_label (_("Quit..."));
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (priv->quit_session_item),
+ gtk_image_new ());
+ gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), priv->quit_session_item);
+ g_signal_connect (priv->quit_session_item, "style-set",
+ G_CALLBACK (menuitem_style_set_cb), status);
+ g_signal_connect (priv->quit_session_item, "activate",
+ G_CALLBACK (on_quit_session_activate), status);
+ gtk_widget_show (priv->quit_session_item);
+
+ g_signal_connect (G_OBJECT (priv->menu), "deactivate",
+ G_CALLBACK (on_deactivate), status);
+}
+
+static void
+shell_status_menu_init (ShellStatusMenu *status)
+{
+ ShellStatusMenuPrivate *priv;
+
+ status->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (status, SHELL_TYPE_STATUS_MENU,
+ ShellStatusMenuPrivate);
+
+ g_object_set (G_OBJECT (status),
+ "orientation", BIG_BOX_ORIENTATION_HORIZONTAL,
+ NULL);
+ priv->client = gconf_client_get_default ();
+
+ priv->user_icon = CLUTTER_TEXTURE (clutter_texture_new ());
+ big_box_append (BIG_BOX (status), CLUTTER_ACTOR (status->priv->user_icon), 0);
+
+ priv->name_box = BIG_BOX (big_box_new (BIG_BOX_ORIENTATION_VERTICAL));
+ g_object_set (G_OBJECT (priv->name_box), "y-align", BIG_BOX_ALIGNMENT_CENTER, NULL);
+ big_box_append (BIG_BOX (status), CLUTTER_ACTOR (priv->name_box), BIG_BOX_PACK_EXPAND);
+ priv->name = CLUTTER_LABEL (clutter_label_new ());
+ big_box_append (BIG_BOX (priv->name_box), CLUTTER_ACTOR (priv->name), BIG_BOX_PACK_EXPAND);
+
+ priv->manager = gdm_user_manager_ref_default ();
+ setup_current_user (status);
+
+ create_sub_menu (status);
+}
+
+static void
+shell_status_menu_finalize (GObject *object)
+{
+ ShellStatusMenu *status = SHELL_STATUS_MENU (object);
+ ShellStatusMenuPrivate *priv = status->priv;
+
+ gconf_client_notify_remove (priv->client, priv->client_notify_lockdown_id);
+
+ g_signal_handler_disconnect (priv->user, priv->user_notify_id);
+ g_signal_handler_disconnect (priv->user, priv->user_icon_changed_id);
+
+ if (priv->user != NULL) {
+ g_object_unref (priv->user);
+ }
+ g_object_unref (priv->client);
+ g_object_unref (priv->manager);
+
+ G_OBJECT_CLASS (shell_status_menu_parent_class)->finalize (object);
+}
+
+static void
+shell_status_menu_class_init (ShellStatusMenuClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (ShellStatusMenuPrivate));
+
+ gobject_class->finalize = shell_status_menu_finalize;
+
+ shell_status_menu_signals[DEACTIVATED] =
+ g_signal_new ("deactivated",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ShellStatusMenuClass, deactivated),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+ShellStatusMenu *
+shell_status_menu_new (void)
+{
+ return g_object_new (SHELL_TYPE_STATUS_MENU, NULL);
+}
+
+static void
+position_menu (GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer user_data)
+{
+ ShellStatusMenu *status = SHELL_STATUS_MENU (user_data);
+ int src_x, src_y;
+
+ clutter_actor_get_transformed_position (CLUTTER_ACTOR (status), &src_x, &src_y);
+
+ *x = src_x;
+ *y = src_y;
+}
+
+void
+shell_status_menu_toggle (ShellStatusMenu *status, ClutterEvent *event)
+{
+ ShellStatusMenuPrivate *priv = status->priv;
+
+ if (GTK_WIDGET_VISIBLE (GTK_WIDGET (priv->menu)))
+ {
+ gtk_widget_hide (GTK_WIDGET (priv->menu));
+ }
+ else
+ {
+ gtk_widget_show (GTK_WIDGET (priv->menu));
+ gtk_menu_popup (GTK_MENU (priv->menu), NULL, NULL, position_menu,
+ status, 1, event->button.time);
+ }
+}
\ No newline at end of file
Added: trunk/src/shell-status-menu.h
==============================================================================
--- (empty file)
+++ trunk/src/shell-status-menu.h Wed Feb 4 18:45:38 2009
@@ -0,0 +1,42 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+#ifndef __SHELL_STATUS_MENU_H__
+#define __SHELL_STATUS_MENU_H__
+
+#include <clutter/clutter.h>
+#include "big/box.h"
+
+G_BEGIN_DECLS
+
+#define SHELL_TYPE_STATUS_MENU (shell_status_menu_get_type ())
+#define SHELL_STATUS_MENU(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_STATUS_MENU, ShellStatusMenu))
+#define SHELL_STATUS_MENU_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_STATUS_MENU, ShellStatusMenuClass))
+#define SHELL_IS_STATUS_MENU(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SHELL_TYPE_STATUS_MENU))
+#define SHELL_IS_STATUS_MENU_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_STATUS_MENU))
+#define SHELL_STATUS_MENU_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_STATUS_MENU, ShellStatusMenuClass))
+
+typedef struct _ShellStatusMenu ShellStatusMenu;
+typedef struct _ShellStatusMenuPrivate ShellStatusMenuPrivate;
+typedef struct _ShellStatusMenuClass ShellStatusMenuClass;
+
+struct _ShellStatusMenu
+{
+ BigBox parent_instance;
+
+ ShellStatusMenuPrivate *priv;
+};
+
+struct _ShellStatusMenuClass
+{
+ BigBoxClass parent_class;
+
+ void (*deactivated) (ShellStatusMenu *status, gpointer user_data);
+};
+
+GType shell_status_menu_get_type (void);
+
+void shell_status_menu_toggle (ShellStatusMenu *menu, ClutterEvent *event);
+
+G_END_DECLS
+
+#endif /* __SHELL_STATUS_MENU_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]